uawdijnntqw1x1x1
IP : 216.73.216.219
Hostname : webm009.cluster128.gra.hosting.ovh.net
Kernel : Linux webm009.cluster128.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
OS : Linux
PATH:
/
home
/
chauffn
/
vuzelia
/
2023
/
cf455
/
..
/
language
/
..
/
cc5e2
/
libraries.zip
/
/
PK0A#]�b�a��import.legacy.phpnu�[���<?php /** * Bootstrap file for the Joomla Platform [with legacy libraries]. Including this file into your application * will make Joomla Platform libraries [including legacy libraries] available for use. * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ trigger_error( sprintf( 'Bootstrapping Joomla using the %1$s file is deprecated. Use %2$s instead.', __FILE__, __DIR__ . '/bootstrap.php' ), E_USER_DEPRECATED ); /** * Set the platform root path as a constant if necessary. * * @deprecated 4.4.0 will be removed in 6.0 * Use defined('_JEXEC') or die; to detect if the CMS is loaded correctly **/ if (!defined('JPATH_PLATFORM')) { define('JPATH_PLATFORM', __DIR__); } // Detect the native operating system type. $os = strtoupper(substr(PHP_OS, 0, 3)); if (!defined('IS_WIN')) { define('IS_WIN', $os === 'WIN'); } if (!defined('IS_UNIX')) { define('IS_UNIX', $os !== 'MAC' && $os !== 'WIN'); } // Import the library loader if necessary. if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; } // Make sure that the Joomla Loader has been successfully loaded. if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Loader not loaded.'); } // Setup the autoloaders. JLoader::setup(); // Register the PasswordHash lib JLoader::register('PasswordHash', JPATH_PLATFORM . '/phpass/PasswordHash.php'); PK0A#]��4�� import.phpnu�[���<?php /** * Bootstrap file for the Joomla Platform. Including this file into your application will make Joomla * Platform libraries available for use. * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ trigger_error( sprintf( 'Bootstrapping Joomla using the %1$s file is deprecated. Use %2$s instead.', __FILE__, __DIR__ . '/bootstrap.php' ), E_USER_DEPRECATED ); /** * Set the platform root path as a constant if necessary. * * @deprecated 4.4.0 will be removed in 6.0 * Use defined('_JEXEC') or die; to detect if the CMS is loaded correctly **/ if (!defined('JPATH_PLATFORM')) { define('JPATH_PLATFORM', __DIR__); } // Detect the native operating system type. $os = strtoupper(substr(PHP_OS, 0, 3)); if (!defined('IS_WIN')) { define('IS_WIN', $os === 'WIN'); } if (!defined('IS_UNIX')) { define('IS_UNIX', IS_WIN === false); } // Import the library loader if necessary. if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; } // Make sure that the Joomla Platform has been successfully loaded. if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Platform not loaded.'); } // Setup the autoloaders. JLoader::setup(); // Register the PasswordHash lib JLoader::register('PasswordHash', JPATH_PLATFORM . '/phpass/PasswordHash.php'); PK0A#]cP�O� � bootstrap.phpnu�[���<?php /** * Bootstrap file for the Joomla! CMS [with legacy libraries]. * Including this file into your application will make Joomla libraries available for use. * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Set the platform root path as a constant if necessary. * * @deprecated 4.4.0 will be removed in 6.0 * Use defined('_JEXEC') or die; to detect if the CMS is loaded correctly **/ defined('JPATH_PLATFORM') or define('JPATH_PLATFORM', __DIR__); // Detect the native operating system type. $os = strtoupper(substr(PHP_OS, 0, 3)); defined('IS_WIN') or define('IS_WIN', ($os === 'WIN')); defined('IS_UNIX') or define('IS_UNIX', (($os !== 'MAC') && ($os !== 'WIN'))); // Import the library loader if necessary. if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; // If JLoader still does not exist panic. if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Platform not loaded.'); } } // Setup the autoloaders. JLoader::setup(); // Create the Composer autoloader /** @var \Composer\Autoload\ClassLoader $loader */ $loader = require JPATH_LIBRARIES . '/vendor/autoload.php'; // We need to pull our decorated class loader into memory before unregistering Composer's loader class_exists('\\Joomla\\CMS\\Autoload\\ClassLoader'); $loader->unregister(); // Decorate Composer autoloader spl_autoload_register([new \Joomla\CMS\Autoload\ClassLoader($loader), 'loadClass'], true, true); // Register the class aliases for Framework classes that have replaced their Platform equivalents require_once JPATH_LIBRARIES . '/classmap.php'; /** * Register the global exception handler. And set error level to server default error level. * The error level may be changed later in boot up process, after application config will be loaded. * Do not remove the variable, to allow to use it further, after including this file. */ $errorHandler = \Symfony\Component\ErrorHandler\ErrorHandler::register(); \Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer::setTemplate(__DIR__ . '/../templates/system/fatal.php'); // Register the error handler which processes E_USER_DEPRECATED errors if (error_reporting() & E_USER_DEPRECATED) { set_error_handler(['Joomla\CMS\Exception\ExceptionHandler', 'handleUserDeprecatedErrors'], E_USER_DEPRECATED); } // Suppress phar stream wrapper for non .phar files $behavior = new \TYPO3\PharStreamWrapper\Behavior(); \TYPO3\PharStreamWrapper\Manager::initialize( $behavior->withAssertion(new \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor()) ); if (in_array('phar', stream_get_wrappers())) { stream_wrapper_unregister('phar'); stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper'); } // Define the Joomla version if not already defined. defined('JVERSION') or define('JVERSION', (new \Joomla\CMS\Version())->getShortVersion()); // Set up the message queue logger for web requests if (array_key_exists('REQUEST_METHOD', $_SERVER)) { \Joomla\CMS\Log\Log::addLogger(['logger' => 'messagequeue'], \Joomla\CMS\Log\Log::ALL, ['jerror']); } // Register the Crypto lib JLoader::register('Crypto', JPATH_PLATFORM . '/php-encryption/Crypto.php'); // Register the PasswordHash library. JLoader::register('PasswordHash', JPATH_PLATFORM . '/phpass/PasswordHash.php'); PK0A#]�V� index.htmlnu�[���<!DOCTYPE html><title></title> PK0A#]�H8� � extensions.classmap.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access. defined('_JEXEC') or die; // Class map of the core extensions JLoader::registerAlias('ActionLogPlugin', '\\Joomla\\Component\\Actionlogs\\Administrator\\Plugin\\ActionLogPlugin', '5.0'); JLoader::registerAlias('FieldsPlugin', '\\Joomla\\Component\\Fields\\Administrator\\Plugin\\FieldsPlugin', '5.0'); JLoader::registerAlias('FieldsListPlugin', '\\Joomla\\Component\\Fields\\Administrator\\Plugin\\FieldsListPlugin', '5.0'); JLoader::registerAlias('PrivacyExportDomain', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Domain', '5.0'); JLoader::registerAlias('PrivacyExportField', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Field', '5.0'); JLoader::registerAlias('PrivacyExportItem', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Item', '5.0'); JLoader::registerAlias('PrivacyPlugin', '\\Joomla\\Component\\Privacy\\Administrator\\Plugin\\PrivacyPlugin', '5.0'); JLoader::registerAlias('PrivacyRemovalStatus', '\\Joomla\\Component\\Privacy\\Administrator\\Removal\\Status', '5.0'); JLoader::registerAlias('PrivacyTableRequest', '\\Joomla\\Component\\Privacy\\Administrator\\Table\\RequestTable', '5.0'); JLoader::registerAlias('TagsTableTag', '\\Joomla\\Component\\Tags\\Administrator\\Table\\TagTable', '5.0'); JLoader::registerAlias('ContentHelperRoute', '\\Joomla\\Component\\Content\\Site\\Helper\\RouteHelper', '5.0'); JLoader::registerAlias('FinderIndexerAdapter', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Adapter', '5.0'); JLoader::registerAlias('FinderIndexerHelper', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Helper', '5.0'); JLoader::registerAlias('FinderIndexer', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Indexer', '5.0'); JLoader::registerAlias('FinderIndexerParser', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Parser', '5.0'); JLoader::registerAlias('FinderIndexerQuery', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Query', '5.0'); JLoader::registerAlias('FinderIndexerResult', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Result', '5.0'); JLoader::registerAlias('FinderIndexerTaxonomy', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Taxonomy', '5.0'); JLoader::registerAlias('FinderIndexerToken', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Token', '5.0'); PK0A#]�O[��#src/Pagination/PaginationObject.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Pagination; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Pagination object representing a particular item in the pagination lists. * * @since 1.5 */ class PaginationObject { /** * @var string The link text. * @since 1.5 */ public $text; /** * @var integer The number of rows as a base offset. * @since 1.5 */ public $base; /** * @var string The link URL. * @since 1.5 */ public $link; /** * @var integer The prefix used for request variables. * @since 1.6 */ public $prefix; /** * @var boolean Flag whether the object is the 'active' page * @since 3.0 */ public $active; /** * Class constructor. * * @param string $text The link text. * @param string $prefix The prefix used for request variables. * @param integer $base The number of rows as a base offset. * @param string $link The link URL. * @param boolean $active Flag whether the object is the 'active' page * * @since 1.5 */ public function __construct($text, $prefix = '', $base = null, $link = null, $active = false) { $this->text = $text; $this->prefix = $prefix; $this->base = $base; $this->link = $link; $this->active = $active; } } PK0A#]�Z��a�asrc/Pagination/Pagination.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Pagination; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; use Joomla\CMS\Router\Route; use Joomla\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Pagination Class. Provides a common interface for content pagination for the Joomla! CMS. * * @since 1.5 */ class Pagination { /** * @var integer The record number to start displaying from. * @since 1.5 */ public $limitstart = null; /** * @var integer Number of rows to display per page. * @since 1.5 */ public $limit = null; /** * @var integer Total number of rows. * @since 1.5 */ public $total = null; /** * @var integer Prefix used for request variables. * @since 1.6 */ public $prefix = null; /** * @var integer Value pagination object begins at * @since 3.0 */ public $pagesStart; /** * @var integer Value pagination object ends at * @since 3.0 */ public $pagesStop; /** * @var integer Current page * @since 3.0 */ public $pagesCurrent; /** * @var integer Total number of pages * @since 3.0 */ public $pagesTotal; /** * @var boolean The flag indicates whether to add limitstart=0 to URL * @since 3.9.0 */ public $hideEmptyLimitstart = false; /** * @var boolean View all flag * @since 3.0 */ protected $viewall = false; /** * Additional URL parameters to be added to the pagination URLs generated by the class. These * may be useful for filters and extra values when dealing with lists and GET requests. * * @var array * @since 3.0 */ protected $additionalUrlParams = []; /** * List of parameters that will be added from request automatically. * When exists they will be added to the $additionalUrlParams list, while pagination initialisation. * * In format key => filter * * @var string[] * * @since 4.4.9 */ protected $paramsFromRequest = [ 'format' => 'CMD', 'option' => 'CMD', 'controller' => 'CMD', 'view' => 'CMD', 'layout' => 'STRING', 'task' => 'CMD', 'template' => 'CMD', 'templateStyle' => 'INT', 'tmpl' => 'CMD', 'tpl' => 'CMD', 'id' => 'STRING', 'Itemid' => 'INT', ]; /** * @var CMSApplication The application object * @since 3.4 */ protected $app = null; /** * Pagination data object * * @var object * @since 3.4 */ protected $data; /** * Constructor. * * @param integer $total The total number of items. * @param integer $limitstart The offset of the item to start at. * @param integer $limit The number of items to display per page. * @param string $prefix The prefix used for request variables. * @param CMSApplication $app The application object * * @since 1.5 */ public function __construct($total, $limitstart, $limit, $prefix = '', CMSApplication $app = null) { // Value/type checking. $this->total = (int) $total; $this->limitstart = (int) max($limitstart, 0); $this->limit = (int) max($limit, 0); $this->prefix = $prefix; $this->app = $app ?: Factory::getApplication(); if ($this->limit > $this->total) { $this->limitstart = 0; } if (!$this->limit) { $this->limit = $total; $this->limitstart = 0; } /* * If limitstart is greater than total (i.e. we are asked to display records that don't exist) * then set limitstart to display the last natural page of results */ if ($this->limitstart > $this->total - $this->limit) { $this->limitstart = max(0, (int) (ceil($this->total / $this->limit) - 1) * $this->limit); } // Set the total pages and current page values. if ($this->limit > 0) { $this->pagesTotal = (int) ceil($this->total / $this->limit); $this->pagesCurrent = (int) ceil(($this->limitstart + 1) / $this->limit); } // Set the pagination iteration loop values. $displayedPages = 10; $this->pagesStart = $this->pagesCurrent - ($displayedPages / 2); if ($this->pagesStart < 1) { $this->pagesStart = 1; } if ($this->pagesStart + $displayedPages > $this->pagesTotal) { $this->pagesStop = $this->pagesTotal; if ($this->pagesTotal < $displayedPages) { $this->pagesStart = 1; } else { $this->pagesStart = $this->pagesTotal - $displayedPages + 1; } } else { $this->pagesStop = $this->pagesStart + $displayedPages - 1; } // If we are viewing all records set the view all flag to true. if ($limit === 0) { $this->viewall = true; } $this->setUrlParamsFromRequest(); } /** * Set URL parameters from request. * * @return void * * @since 4.4.9 */ protected function setUrlParamsFromRequest() { // Get the requested parameters from the router $client = $this->app->getName(); $router = Factory::getContainer()->get(ucfirst($client) . 'Router'); $filter = new InputFilter(); // It is applicable only for CMS router. API router works differently. if (!$router instanceof \Joomla\CMS\Router\Router) { return; } // Filter them and add to the params list foreach ($router->getVars() as $key => $value) { // Check if the parameter is allowed if (empty($this->paramsFromRequest[$key])) { continue; } $filterMethod = $this->paramsFromRequest[$key]; $this->setAdditionalUrlParam($key, $filter->clean($value, $filterMethod)); } } /** * Method to set an additional URL parameter to be added to all pagination class generated * links. * * @param string $key The name of the URL parameter for which to set a value. * @param mixed $value The value to set for the URL parameter. * * @return mixed The old value for the parameter. * * @since 1.6 */ public function setAdditionalUrlParam($key, $value) { // Get the old value to return and set the new one for the URL parameter. $result = $this->additionalUrlParams[$key] ?? null; // If the passed parameter value is null unset the parameter, otherwise set it to the given value. if ($value === null) { unset($this->additionalUrlParams[$key]); } else { $this->additionalUrlParams[$key] = $value; } return $result; } /** * Method to get an additional URL parameter (if it exists) to be added to * all pagination class generated links. * * @param string $key The name of the URL parameter for which to get the value. * * @return mixed The value if it exists or null if it does not. * * @since 1.6 */ public function getAdditionalUrlParam($key) { return $this->additionalUrlParams[$key] ?? null; } /** * Return the rationalised offset for a row with a given index. * * @param integer $index The row index * * @return integer Rationalised offset for a row with a given index. * * @since 1.5 */ public function getRowOffset($index) { return $index + 1 + $this->limitstart; } /** * Return the pagination data object, only creating it if it doesn't already exist. * * @return \stdClass Pagination data object. * * @since 1.5 */ public function getData() { if (!$this->data) { $this->data = $this->_buildDataObject(); } return $this->data; } /** * Create and return the pagination pages counter string, ie. Page 2 of 4. * * @return string Pagination pages counter string. * * @since 1.5 */ public function getPagesCounter() { $html = null; if ($this->pagesTotal > 1) { $html .= Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $this->pagesCurrent, $this->pagesTotal); } return $html; } /** * Create and return the pagination result set counter string, e.g. Results 1-10 of 42 * * @return string Pagination result set counter string. * * @since 1.5 */ public function getResultsCounter() { $html = null; $fromResult = $this->limitstart + 1; // If the limit is reached before the end of the list. if ($this->limitstart + $this->limit < $this->total) { $toResult = $this->limitstart + $this->limit; } else { $toResult = $this->total; } // If there are results found. if ($this->total > 0) { $msg = Text::sprintf('JLIB_HTML_RESULTS_OF', $fromResult, $toResult, $this->total); $html .= "\n" . $msg; } else { $html .= "\n" . Text::_('JLIB_HTML_NO_RECORDS_FOUND'); } return $html; } /** * Create and return the pagination page list string, ie. Previous, Next, 1 2 3 ... x. * * @return string Pagination page list string. * * @since 1.5 */ public function getPagesLinks() { // Build the page navigation list. $data = $this->_buildDataObject(); $list = []; $list['prefix'] = $this->prefix; $chromePath = JPATH_THEMES . '/' . $this->app->getTemplate() . '/html/pagination.php'; if (is_file($chromePath)) { include_once $chromePath; } // Build the select list if ($data->all->base !== null) { $list['all']['active'] = true; $list['all']['data'] = $this->_item_active($data->all); } else { $list['all']['active'] = false; $list['all']['data'] = $this->_item_inactive($data->all); } if ($data->start->base !== null) { $list['start']['active'] = true; $list['start']['data'] = $this->_item_active($data->start); } else { $list['start']['active'] = false; $list['start']['data'] = $this->_item_inactive($data->start); } if ($data->previous->base !== null) { $list['previous']['active'] = true; $list['previous']['data'] = $this->_item_active($data->previous); } else { $list['previous']['active'] = false; $list['previous']['data'] = $this->_item_inactive($data->previous); } // Make sure it exists $list['pages'] = []; foreach ($data->pages as $i => $page) { if ($page->base !== null) { $list['pages'][$i]['active'] = true; $list['pages'][$i]['data'] = $this->_item_active($page); } else { $list['pages'][$i]['active'] = false; $list['pages'][$i]['data'] = $this->_item_inactive($page); } } if ($data->next->base !== null) { $list['next']['active'] = true; $list['next']['data'] = $this->_item_active($data->next); } else { $list['next']['active'] = false; $list['next']['data'] = $this->_item_inactive($data->next); } if ($data->end->base !== null) { $list['end']['active'] = true; $list['end']['data'] = $this->_item_active($data->end); } else { $list['end']['active'] = false; $list['end']['data'] = $this->_item_inactive($data->end); } if ($this->total > $this->limit) { return $this->_list_render($list); } else { return ''; } } /** * Get the pagination links * * @param string $layoutId Layout to render the links * @param array $options Optional array with settings for the layout * * @return string Pagination links. * * @since 3.3 */ public function getPaginationLinks($layoutId = 'joomla.pagination.links', $options = []) { // Allow to receive a null layout $layoutId = $layoutId ?? 'joomla.pagination.links'; $list = [ 'prefix' => $this->prefix, 'limit' => $this->limit, 'limitstart' => $this->limitstart, 'total' => $this->total, 'limitfield' => $this->getLimitBox(), 'pagescounter' => $this->getPagesCounter(), 'pages' => $this->getPaginationPages(), 'pagesTotal' => $this->pagesTotal, ]; return LayoutHelper::render($layoutId, ['list' => $list, 'options' => $options]); } /** * Create and return the pagination pages list, ie. Previous, Next, 1 2 3 ... x. * * @return array Pagination pages list. * * @since 3.3 */ public function getPaginationPages() { $list = []; if ($this->total > $this->limit) { // Build the page navigation list. $data = $this->_buildDataObject(); // All $list['all']['active'] = $data->all->base !== null; $list['all']['data'] = $data->all; // Start $list['start']['active'] = $data->start->base !== null; $list['start']['data'] = $data->start; // Previous link $list['previous']['active'] = $data->previous->base !== null; $list['previous']['data'] = $data->previous; // Make sure it exists $list['pages'] = []; foreach ($data->pages as $i => $page) { $list['pages'][$i]['active'] = $page->base !== null; $list['pages'][$i]['data'] = $page; } $list['next']['active'] = $data->next->base !== null; $list['next']['data'] = $data->next; $list['end']['active'] = $data->end->base !== null; $list['end']['data'] = $data->end; } return $list; } /** * Return the pagination footer. * * @return string Pagination footer. * * @since 1.5 */ public function getListFooter() { // Keep B/C for overrides done with chromes $chromePath = JPATH_THEMES . '/' . $this->app->getTemplate() . '/html/pagination.php'; if (is_file($chromePath)) { include_once $chromePath; if (\function_exists('pagination_list_footer')) { @trigger_error( 'pagination_list_footer is deprecated. Use the layout joomla.pagination.links instead.', E_USER_DEPRECATED ); $list = [ 'prefix' => $this->prefix, 'limit' => $this->limit, 'limitstart' => $this->limitstart, 'total' => $this->total, 'limitfield' => $this->getLimitBox(), 'pagescounter' => $this->getPagesCounter(), 'pageslinks' => $this->getPagesLinks(), ]; return pagination_list_footer($list); } } return $this->getPaginationLinks(); } /** * Creates a dropdown box for selecting how many records to show per page. * * @return string The HTML for the limit # input box. * * @since 1.5 */ public function getLimitBox() { $limits = []; // Make the option list. for ($i = 5; $i <= 30; $i += 5) { $limits[] = HTMLHelper::_('select.option', "$i"); } $limits[] = HTMLHelper::_('select.option', '50', Text::_('J50')); $limits[] = HTMLHelper::_('select.option', '100', Text::_('J100')); $limits[] = HTMLHelper::_('select.option', '0', Text::_('JALL')); $selected = $this->viewall ? 0 : $this->limit; // Build the select list. if ($this->app->isClient('administrator')) { $html = HTMLHelper::_( 'select.genericlist', $limits, $this->prefix . 'limit', 'class="form-select" onchange="Joomla.submitform();"', 'value', 'text', $selected ); } else { $html = HTMLHelper::_( 'select.genericlist', $limits, $this->prefix . 'limit', 'class="form-select" onchange="this.form.submit()"', 'value', 'text', $selected ); } return $html; } /** * Return the icon to move an item UP. * * @param integer $i The row index. * @param boolean $condition True to show the icon. * @param string $task The task to fire. * @param string $alt The image alternative text string. * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string Either the icon to move an item up or a space. * * @since 1.5 */ public function orderUpIcon($i, $condition = true, $task = 'orderup', $alt = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb') { if (($i > 0 || ($i + $this->limitstart > 0)) && $condition) { return HTMLHelper::_('jgrid.orderUp', $i, $task, '', $alt, $enabled, $checkbox); } else { return ' '; } } /** * Return the icon to move an item DOWN. * * @param integer $i The row index. * @param integer $n The number of items in the list. * @param boolean $condition True to show the icon. * @param string $task The task to fire. * @param string $alt The image alternative text string. * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string Either the icon to move an item down or a space. * * @since 1.5 */ public function orderDownIcon($i, $n, $condition = true, $task = 'orderdown', $alt = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb') { if (($i < $n - 1 || $i + $this->limitstart < $this->total - 1) && $condition) { return HTMLHelper::_('jgrid.orderDown', $i, $task, '', $alt, $enabled, $checkbox); } else { return ' '; } } /** * Create the HTML for a list footer * * @param array $list Pagination list data structure. * * @return string HTML for a list footer * * @since 1.5 */ protected function _list_footer($list) { $html = "<div class=\"list-footer\">\n"; $html .= "\n<div class=\"limit\">" . Text::_('JGLOBAL_DISPLAY_NUM') . $list['limitfield'] . "</div>"; $html .= $list['pageslinks']; $html .= "\n<div class=\"counter\">" . $list['pagescounter'] . "</div>"; $html .= "\n<input type=\"hidden\" name=\"" . $list['prefix'] . "limitstart\" value=\"" . $list['limitstart'] . "\">"; $html .= "\n</div>"; return $html; } /** * Create the html for a list footer * * @param array $list Pagination list data structure. * * @return string HTML for a list start, previous, next,end * * @since 1.5 */ protected function _list_render($list) { return LayoutHelper::render('joomla.pagination.list', ['list' => $list]); } /** * Method to create an active pagination link to the item * * @param PaginationObject $item The object with which to make an active link. * * @return string HTML link * * @since 1.5 */ protected function _item_active(PaginationObject $item) { return LayoutHelper::render('joomla.pagination.link', ['data' => $item, 'active' => true]); } /** * Method to create an inactive pagination string * * @param PaginationObject $item The item to be processed * * @return string * * @since 1.5 */ protected function _item_inactive(PaginationObject $item) { return LayoutHelper::render('joomla.pagination.link', ['data' => $item, 'active' => false]); } /** * Create and return the pagination data object. * * @return \stdClass Pagination data object. * * @since 1.5 */ protected function _buildDataObject() { $data = new \stdClass(); // Prepare the routes $params = []; if (!empty($this->additionalUrlParams)) { foreach ($this->additionalUrlParams as $key => $value) { $params[$key] = $value; } } $params = http_build_query($params); $data->all = new PaginationObject(Text::_('JLIB_HTML_VIEW_ALL'), $this->prefix); if (!$this->viewall) { $data->all->base = '0'; $data->all->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart='); } // Set the start and previous data objects. $data->start = new PaginationObject(Text::_('JLIB_HTML_START'), $this->prefix); $data->previous = new PaginationObject(Text::_('JPREV'), $this->prefix); if ($this->pagesCurrent > 1) { $page = ($this->pagesCurrent - 2) * $this->limit; if ($this->hideEmptyLimitstart) { $data->start->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart='); } else { $data->start->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart=0'); } $data->start->base = '0'; $data->previous->base = $page; if ($page === 0 && $this->hideEmptyLimitstart) { $data->previous->link = $data->start->link; } else { $data->previous->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart=' . $page); } } // Set the next and end data objects. $data->next = new PaginationObject(Text::_('JNEXT'), $this->prefix); $data->end = new PaginationObject(Text::_('JLIB_HTML_END'), $this->prefix); if ($this->pagesCurrent < $this->pagesTotal) { $next = $this->pagesCurrent * $this->limit; $end = ($this->pagesTotal - 1) * $this->limit; $data->next->base = $next; $data->next->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart=' . $next); $data->end->base = $end; $data->end->link = Route::_('index.php?' . $params . '&' . $this->prefix . 'limitstart=' . $end); } $data->pages = []; $stop = $this->pagesStop; for ($i = $this->pagesStart; $i <= $stop; $i++) { $offset = ($i - 1) * $this->limit; $data->pages[$i] = new PaginationObject($i, $this->prefix); if ($i != $this->pagesCurrent || $this->viewall) { $data->pages[$i]->base = $offset; if ($offset === 0 && $this->hideEmptyLimitstart) { $data->pages[$i]->link = $data->start->link; } else { $data->pages[$i]->link = Route::_( 'index.php?' . $params . '&' . $this->prefix . 'limitstart=' . $offset ); } } else { $data->pages[$i]->active = true; } } return $data; } } PK0A#]���ljljsrc/Factory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Client\ClientHelper; use Joomla\CMS\Date\Date; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\FactoryInterface; use Joomla\CMS\Filesystem\Stream; use Joomla\CMS\Language\Language; use Joomla\CMS\Language\LanguageFactoryInterface; use Joomla\CMS\Mail\Mail; use Joomla\CMS\Mail\MailerFactoryInterface; use Joomla\CMS\Session\Session; use Joomla\CMS\User\User; use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform Factory class. * * @since 1.7.0 */ abstract class Factory { /** * Global application object * * @var CMSApplicationInterface * @since 1.7.0 */ public static $application = null; /** * Global cache object * * @var Cache * @since 1.7.0 */ public static $cache = null; /** * Global configuration object * * @var \JConfig * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the configuration object within the application * Example: * Factory::getApplication()->getConfig(); */ public static $config = null; /** * Global container object * * @var Container * @since 4.0.0 */ public static $container = null; /** * Container for Date instances * * @var array * @since 1.7.3 */ public static $dates = []; /** * Global session object * * @var Session * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the session service in the DI container or get from the application object * Example: * Factory::getApplication()->getSession(); */ public static $session = null; /** * Global language object * * @var Language * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the language service in the DI container or get from the application object * Example: * Factory::getApplication()->getLanguage(); */ public static $language = null; /** * Global document object * * @var Document * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the document service in the DI container or get from the application object * Example: * Factory::getApplication()->getDocument(); */ public static $document = null; /** * Global database object * * @var DatabaseDriver * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the database service in the DI container * Example: * Factory::getContainer()->get(DatabaseInterface::class); */ public static $database = null; /** * Global mailer object * * @var Mail * @since 1.7.0 */ public static $mailer = null; /** * Get the global application object. When the global application doesn't exist, an exception is thrown. * * @return CMSApplicationInterface object * * @since 1.7.0 * @throws \Exception */ public static function getApplication() { if (!self::$application) { throw new \Exception('Failed to start application', 500); } return self::$application; } /** * Get a configuration object * * Returns the global {@link \JConfig} object, only creating it if it doesn't already exist. * * @param string $file The path to the configuration file * @param string $type The type of the configuration file * @param string $namespace The namespace of the configuration file * * @return Registry * * @see Registry * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the configuration object within the application * Example: * Factory::getApplication()->getConfig(); */ public static function getConfig($file = null, $type = 'PHP', $namespace = '') { @trigger_error( sprintf( '%s() is deprecated. The configuration object should be read from the application.', __METHOD__ ), E_USER_DEPRECATED ); /** * If there is an application object, fetch the configuration from there. * Check it's not null because LanguagesModel can make it null and if it's null * we would want to re-init it from configuration.php. */ if (self::$application && self::$application->getConfig() !== null) { return self::$application->getConfig(); } if (!self::$config) { if ($file === null) { $file = JPATH_CONFIGURATION . '/configuration.php'; } self::$config = self::createConfig($file, $type, $namespace); } return self::$config; } /** * Get a container object * * Returns the global service container object, only creating it if it doesn't already exist. * * This method is only suggested for use in code whose responsibility is to create new services * and needs to be able to resolve the dependencies, and should therefore only be used when the * container is not accessible by other means. Valid uses of this method include: * * - A static `getInstance()` method calling a factory service from the container, * see `Joomla\CMS\Toolbar\Toolbar::getInstance()` as an example * - An application front controller loading and executing the Joomla application class, * see the `cli/joomla.php` file as an example * - Retrieving optional constructor dependencies when not injected into a class during a transitional * period to retain backward compatibility, in this case a deprecation notice should also be emitted to * notify developers of changes needed in their code * * This method is not suggested for use as a one-for-one replacement of static calls, such as * replacing calls to `Factory::getDbo()` with calls to `Factory::getContainer()->get(DatabaseInterface::class)`, * code should be refactored to support dependency injection instead of making this change. * * @return Container * * @since 4.0.0 */ public static function getContainer(): Container { if (!self::$container) { self::$container = self::createContainer(); } return self::$container; } /** * Get a session object. * * Returns the global {@link Session} object, only creating it if it doesn't already exist. * * @param array $options An array containing session options * * @return Session object * * @see Session * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the session service in the DI container or get from the application object * Example: * Factory::getApplication()->getSession(); */ public static function getSession(array $options = []) { @trigger_error( sprintf( '%1$s() is deprecated. Load the session from the dependency injection container or via %2$s::getApplication()->getSession().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); return self::getApplication()->getSession(); } /** * Get a language object. * * Returns the global {@link Language} object, only creating it if it doesn't already exist. * * @return Language object * * @see Language * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the language service in the DI container or get from the application object * Example: * Factory::getApplication()->getLanguage(); */ public static function getLanguage() { @trigger_error( sprintf( '%1$s() is deprecated. Load the language from the dependency injection container or via %2$s::getApplication()->getLanguage().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); if (!self::$language) { self::$language = self::createLanguage(); } return self::$language; } /** * Get a document object. * * Returns the global {@link \Joomla\CMS\Document\Document} object, only creating it if it doesn't already exist. * * @return Document object * * @see Document * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the document service in the DI container or get from the application object * Example: * Factory::getApplication()->getDocument(); */ public static function getDocument() { @trigger_error( sprintf( '%1$s() is deprecated. Load the document from the dependency injection container or via %2$s::getApplication()->getDocument().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); if (!self::$document) { self::$document = self::createDocument(); } return self::$document; } /** * Get a user object. * * Returns the global {@link User} object, only creating it if it doesn't already exist. * * @param integer $id The user to load - Can be an integer or string - If string, it is converted to ID automatically. * * @return User object * * @see User * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Load the user service from the dependency injection container or get from the application object * Example: * Factory::getApplication()->getIdentity(); */ public static function getUser($id = null) { @trigger_error( sprintf( '%1$s() is deprecated. Load the user from the dependency injection container or via %2$s::getApplication()->getIdentity().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); $instance = self::getApplication()->getSession()->get('user'); if (\is_null($id)) { if (!($instance instanceof User)) { $instance = User::getInstance(); } } elseif (!($instance instanceof User) || \is_string($id) || $instance->id !== $id) { // Check if we have a string as the id or if the numeric id is the current instance $instance = User::getInstance($id); } return $instance; } /** * Get a cache object * * Returns the global {@link CacheController} object * * @param string $group The cache group name * @param string $handler The handler to use * @param string $storage The storage method * * @return \Joomla\CMS\Cache\CacheController object * * @see Cache * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the cache controller factory instead * Example: * Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($handler, $options); */ public static function getCache($group = '', $handler = 'callback', $storage = null) { @trigger_error( sprintf( '%s() is deprecated. The cache controller should be fetched from the factory.', __METHOD__ ), E_USER_DEPRECATED ); $hash = md5($group . $handler . $storage); if (isset(self::$cache[$hash])) { return self::$cache[$hash]; } $handler = ($handler === 'function') ? 'callback' : $handler; $options = ['defaultgroup' => $group]; if (isset($storage)) { $options['storage'] = $storage; } $cache = self::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($handler, $options); self::$cache[$hash] = $cache; return self::$cache[$hash]; } /** * Get a database object. * * Returns the global {@link DatabaseDriver} object, only creating it if it doesn't already exist. * * @return DatabaseDriver * * @see DatabaseDriver * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the database service in the DI container * Example: * Factory::getContainer()->get(DatabaseInterface::class); */ public static function getDbo() { @trigger_error( sprintf( '%1$s() is deprecated. Load the database from the dependency injection container.', __METHOD__ ), E_USER_DEPRECATED ); if (!self::$database) { if (self::getContainer()->has(DatabaseInterface::class)) { self::$database = self::getContainer()->get(DatabaseInterface::class); } else { self::$database = self::createDbo(); } } return self::$database; } /** * Get a mailer object. * * Returns the global {@link Mail} object, only creating it if it doesn't already exist. * * @return Mail object * * @see Mail * @since 1.7.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use the mailer service in the DI container and create a mailer from there * Example: * Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer(); */ public static function getMailer() { if (!self::$mailer) { self::$mailer = self::createMailer(); } $copy = clone self::$mailer; return $copy; } /** * Return the {@link Date} object * * @param mixed $time The initial time for the Date object * @param mixed $tzOffset The timezone offset. * * @return Date object * * @see Date * @since 1.7.0 */ public static function getDate($time = 'now', $tzOffset = null) { static $classname; static $mainLocale; $language = self::getLanguage(); $locale = $language->getTag(); if (!isset($classname) || $locale != $mainLocale) { // Store the locale for future reference $mainLocale = $locale; if ($mainLocale !== false) { $classname = str_replace('-', '_', $mainLocale) . 'Date'; if (!class_exists($classname)) { // The class does not exist, default to Date $classname = 'Joomla\\CMS\\Date\\Date'; } } else { // No tag, so default to Date $classname = 'Joomla\\CMS\\Date\\Date'; } } $key = $time . '-' . ($tzOffset instanceof \DateTimeZone ? $tzOffset->getName() : (string) $tzOffset); if (!isset(self::$dates[$classname][$key])) { self::$dates[$classname][$key] = new $classname($time, $tzOffset); } $date = clone self::$dates[$classname][$key]; return $date; } /** * Create a configuration object * * @param string $file The path to the configuration file. * @param string $type The type of the configuration file. * @param string $namespace The namespace of the configuration file. * * @return Registry * * @see Registry * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Use the configuration object within the application. * Example: Factory::getApplication()->getConfig(); */ protected static function createConfig($file, $type = 'PHP', $namespace = '') { @trigger_error( sprintf( '%s() is deprecated. The configuration object should be read from the application.', __METHOD__ ), E_USER_DEPRECATED ); if (is_file($file)) { include_once $file; } // Create the registry with a default namespace of config $registry = new Registry(); // Sanitize the namespace. $namespace = ucfirst((string) preg_replace('/[^A-Z_]/i', '', $namespace)); // Build the config name. $name = 'JConfig' . $namespace; // Handle the PHP configuration type. if ($type === 'PHP' && class_exists($name)) { // Create the JConfig object $config = new $name(); // Load the configuration values into the registry $registry->loadObject($config); } return $registry; } /** * Create a container object * * @return Container * * @since 4.0.0 */ protected static function createContainer(): Container { $container = (new Container()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Application()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Authentication()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\CacheController()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Config()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Console()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Database()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Dispatcher()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Document()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Form()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Logger()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Language()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Mailer()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Menu()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Pathway()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\HTMLRegistry()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Session()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Toolbar()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\WebAssetRegistry()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\Router()) ->registerServiceProvider(new \Joomla\CMS\Service\Provider\User()); return $container; } /** * Create a database object * * @return DatabaseDriver * * @see DatabaseDriver * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the database service in the DI container * Example: * Factory::getContainer()->get(DatabaseInterface::class); */ protected static function createDbo() { @trigger_error( sprintf( '%1$s() is deprecated, register a service provider to create a %2$s instance instead.', __METHOD__, DatabaseInterface::class ), E_USER_DEPRECATED ); $conf = self::getConfig(); $host = $conf->get('host'); $user = $conf->get('user'); $password = $conf->get('password'); $database = $conf->get('db'); $prefix = $conf->get('dbprefix'); $driver = $conf->get('dbtype'); $options = ['driver' => $driver, 'host' => $host, 'user' => $user, 'password' => $password, 'database' => $database, 'prefix' => $prefix]; if ((int) $conf->get('dbencryption') !== 0) { $options['ssl'] = [ 'enable' => true, 'verify_server_cert' => (bool) $conf->get('dbsslverifyservercert'), ]; foreach (['cipher', 'ca', 'key', 'cert'] as $value) { $confVal = trim($conf->get('dbssl' . $value, '')); if ($confVal !== '') { $options['ssl'][$value] = $confVal; } } } try { $db = DatabaseDriver::getInstance($options); } catch (\RuntimeException $e) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } jexit('Database Error: ' . $e->getMessage()); } return $db; } /** * Create a mailer object * * @return Mail object * * @see Mail * @since 1.7.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use the mailer service in the DI container and create a mailer from there * Example: * Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer(); */ protected static function createMailer() { $mailer = self::getContainer()->get(MailerFactoryInterface::class)->createMailer(self::getConfig()); // This needs to be set here for backwards compatibility Mail::$instances['Joomla'] = $mailer; return $mailer; } /** * Create a language object * * @return Language object * * @see Language * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Load the language service from the dependency injection container or via $app->getLanguage() * Example: Factory::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($locale, $debug) */ protected static function createLanguage() { @trigger_error( sprintf( '%1$s() is deprecated. Load the language from the dependency injection container or via %2$s::getApplication()->getLanguage().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); $conf = self::getConfig(); $locale = $conf->get('language'); $debug = $conf->get('debug_lang'); $lang = self::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($locale, $debug); return $lang; } /** * Create a document object * * @return Document object * * @see Document * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Load the document service from the dependency injection container or via $app->getDocument() * Example: Factory::getContainer()->get(FactoryInterface::class)->createDocument($type, $attributes); */ protected static function createDocument() { @trigger_error( sprintf( '%1$s() is deprecated. Load the document from the dependency injection container or via %2$s::getApplication()->getDocument().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); $lang = self::getLanguage(); $input = self::getApplication()->getInput(); $type = $input->get('format', 'html', 'cmd'); $version = new Version(); $attributes = [ 'charset' => 'utf-8', 'lineend' => 'unix', 'tab' => "\t", 'language' => $lang->getTag(), 'direction' => $lang->isRtl() ? 'rtl' : 'ltr', 'mediaversion' => $version->getMediaVersion(), ]; return self::getContainer()->get(FactoryInterface::class)->createDocument($type, $attributes); } /** * Creates a new stream object with appropriate prefix * * @param boolean $usePrefix Prefix the connections for writing * @param boolean $useNetwork Use network if available for writing; use false to disable (e.g. FTP, SCP) * @param string $userAgentSuffix String to append to user agent * @param boolean $maskUserAgent User agent masking (prefix Mozilla) * * @return Stream * * @see Stream * @since 1.7.0 */ public static function getStream($usePrefix = true, $useNetwork = true, $userAgentSuffix = 'Joomla', $maskUserAgent = false) { // Setup the context; Joomla! UA and overwrite $context = []; $version = new Version(); // Set the UA for HTTP and overwrite for FTP $context['http']['user_agent'] = $version->getUserAgent($userAgentSuffix, $maskUserAgent); $context['ftp']['overwrite'] = true; if ($usePrefix) { $FTPOptions = ClientHelper::getCredentials('ftp'); $SCPOptions = ClientHelper::getCredentials('scp'); if ($FTPOptions['enabled'] == 1 && $useNetwork) { $prefix = 'ftp://' . $FTPOptions['user'] . ':' . $FTPOptions['pass'] . '@' . $FTPOptions['host']; $prefix .= $FTPOptions['port'] ? ':' . $FTPOptions['port'] : ''; $prefix .= $FTPOptions['root']; } elseif ($SCPOptions['enabled'] == 1 && $useNetwork) { $prefix = 'ssh2.sftp://' . $SCPOptions['user'] . ':' . $SCPOptions['pass'] . '@' . $SCPOptions['host']; $prefix .= $SCPOptions['port'] ? ':' . $SCPOptions['port'] : ''; $prefix .= $SCPOptions['root']; } else { $prefix = JPATH_ROOT . '/'; } $retval = new Stream($prefix, JPATH_ROOT, $context); } else { $retval = new Stream('', '', $context); } return $retval; } } PK0A#]�y�T��src/Http/TransportInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http; use Joomla\Http\TransportInterface as FrameworkTransportInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP transport class interface. * * @since 1.7.3 * * @deprecated 4.0 will be removed in 6.0 * Implement Joomla\Http\TransportInterface instead */ interface TransportInterface extends FrameworkTransportInterface { } PK0A#]�_��src/Http/Http.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http; use Joomla\Http\Http as FrameworkHttp; use Joomla\Http\TransportInterface as FrameworkTransportInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP client class. * * @since 1.7.3 */ class Http extends FrameworkHttp { /** * Constructor. * * @param array|\ArrayAccess $options Client options array. If the registry contains any headers.* elements, * these will be added to the request headers. * @param FrameworkTransportInterface $transport The HTTP transport object. * * @since 1.7.3 * @throws \InvalidArgumentException */ public function __construct($options = [], FrameworkTransportInterface $transport = null) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } $this->options = $options; if (!isset($transport)) { $transport = HttpFactory::getAvailableDriver($this->options); } // Ensure the transport is a framework TransportInterface instance or bail out if (!($transport instanceof FrameworkTransportInterface)) { throw new \InvalidArgumentException('A valid TransportInterface object was not set.'); } $this->transport = $transport; } } PK0A#]��aasrc/Http/Response.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http; use Joomla\Http\Response as FrameworkResponse; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP response data object class. * * @since 1.7.3 * * @deprecated 4.0 will be removed in 6.0 * Use Joomla\Http\Response instead */ class Response extends FrameworkResponse { } PK0A#]���%%&src/Http/Transport/SocketTransport.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http\Transport; use Joomla\CMS\Factory; use Joomla\CMS\Http\Response; use Joomla\CMS\Http\TransportInterface; use Joomla\CMS\Uri\Uri; use Joomla\Http\AbstractTransport; use Joomla\Http\Exception\InvalidResponseCodeException; use Joomla\Uri\UriInterface; use Laminas\Diactoros\Stream as StreamResponse; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP transport class for using sockets directly. * * @since 1.7.3 */ class SocketTransport extends AbstractTransport implements TransportInterface { /** * @var array Reusable socket connections. * @since 1.7.3 */ protected $connections; /** * Send a request to the server and return a Response object with the response. * * @param string $method The HTTP method for sending the request. * @param UriInterface $uri The URI to the resource to request. * @param mixed $data Either an associative array or a string to be sent with the request. * @param array $headers An array of request headers to send with the request. * @param integer $timeout Read timeout in seconds. * @param string $userAgent The optional user agent string to send with the request. * * @return Response * * @since 1.7.3 * @throws \RuntimeException */ public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) { $connection = $this->connect($uri, $timeout); // Make sure the connection is alive and valid. if (\is_resource($connection)) { // Make sure the connection has not timed out. $meta = stream_get_meta_data($connection); if ($meta['timed_out']) { throw new \RuntimeException('Server connection timed out.'); } } else { throw new \RuntimeException('Not connected to server.'); } // Get the request path from the URI object. $path = $uri->toString(['path', 'query']); // If we have data to send make sure our request is setup for it. if (!empty($data)) { // If the data is not a scalar value encode it to be sent with the request. if (!is_scalar($data)) { $data = http_build_query($data); } if (!isset($headers['Content-Type'])) { $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; } // Add the relevant headers. $headers['Content-Length'] = \strlen($data); } // Build the request payload. $request = []; $request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/1.0'; $request[] = 'Host: ' . $uri->getHost(); // If an explicit user agent is given use it. if (isset($userAgent)) { $headers['User-Agent'] = $userAgent; } // If there are custom headers to send add them to the request payload. if (\is_array($headers)) { foreach ($headers as $k => $v) { $request[] = $k . ': ' . $v; } } // Set any custom transport options foreach ($this->getOption('transport.socket', []) as $value) { $request[] = $value; } // If we have data to send add it to the request payload. if (!empty($data)) { $request[] = null; $request[] = $data; } // Authentication, if needed if ($this->getOption('userauth') && $this->getOption('passwordauth')) { $request[] = 'Authorization: Basic ' . base64_encode($this->getOption('userauth') . ':' . $this->getOption('passwordauth')); } // Send the request to the server. fwrite($connection, implode("\r\n", $request) . "\r\n\r\n"); // Get the response data from the server. $content = ''; while (!feof($connection)) { $content .= fgets($connection, 4096); } $content = $this->getResponse($content); // Follow Http redirects if ($content->code >= 301 && $content->code < 400 && isset($content->headers['Location'])) { return $this->request($method, new Uri($content->headers['Location']), $data, $headers, $timeout, $userAgent); } return $content; } /** * Method to get a response object from a server response. * * @param string $content The complete server response, including headers. * * @return Response * * @since 1.7.3 * @throws InvalidResponseCodeException */ protected function getResponse($content) { if (empty($content)) { throw new \UnexpectedValueException('No content in response.'); } // Split the response into headers and body. $response = explode("\r\n\r\n", $content, 2); // Get the response headers as an array. $headers = explode("\r\n", $response[0]); // Set the body for the response. $body = empty($response[1]) ? '' : $response[1]; // Get the response code from the first offset of the response headers. preg_match('/[0-9]{3}/', array_shift($headers), $matches); $code = $matches[0]; if (!is_numeric($code)) { // No valid response code was detected. throw new InvalidResponseCodeException('No HTTP response code found.'); } $statusCode = (int) $code; $verifiedHeaders = $this->processHeaders($headers); $streamInterface = new StreamResponse('php://memory', 'rw'); $streamInterface->write($body); return new Response($streamInterface, $statusCode, $verifiedHeaders); } /** * Method to connect to a server and get the resource. * * @param UriInterface $uri The URI to connect with. * @param integer $timeout Read timeout in seconds. * * @return resource Socket connection resource. * * @since 1.7.3 * @throws \RuntimeException */ protected function connect(UriInterface $uri, $timeout = null) { $errno = null; $err = null; // Get the host from the uri. $host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost(); // If the port is not explicitly set in the URI detect it. if (!$uri->getPort()) { $port = ($uri->getScheme() === 'https') ? 443 : 80; } else { // Use the set port. $port = $uri->getPort(); } // Build the connection key for resource memory caching. $key = md5($host . $port); // If the connection already exists, use it. if (!empty($this->connections[$key]) && \is_resource($this->connections[$key])) { // Connection reached EOF, cannot be used anymore $meta = stream_get_meta_data($this->connections[$key]); if ($meta['eof']) { if (!fclose($this->connections[$key])) { throw new \RuntimeException('Cannot close connection'); } } elseif (!$meta['timed_out']) { // Make sure the connection has not timed out. return $this->connections[$key]; } } if (!is_numeric($timeout)) { $timeout = ini_get('default_socket_timeout'); } // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); // PHP sends a warning if the uri does not exists; we silence it and throw an exception instead. // Attempt to connect to the server $connection = @fsockopen($host, $port, $errno, $err, $timeout); if (!$connection) { if (!$php_errormsg) { // Error but nothing from php? Create our own $php_errormsg = sprintf('Could not connect to resource %s: %s (error code %d)', $uri, $err, $errno); } // Restore error tracking to give control to the exception handler ini_set('track_errors', $track_errors); throw new \RuntimeException($php_errormsg); } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); // Since the connection was successful let's store it in case we need to use it later. $this->connections[$key] = $connection; // If an explicit timeout is set, set it. if (isset($timeout)) { stream_set_timeout($this->connections[$key], (int) $timeout); } return $this->connections[$key]; } /** * Method to check if http transport socket available for use * * @return boolean True if available else false * * @since 3.0.0 */ public static function isSupported() { return \function_exists('fsockopen') && \is_callable('fsockopen') && !Factory::getApplication()->get('proxy_enable'); } } PK0A#]����1)1)$src/Http/Transport/CurlTransport.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http\Transport; use Composer\CaBundle\CaBundle; use Joomla\CMS\Factory; use Joomla\CMS\Http\Response; use Joomla\CMS\Http\TransportInterface; use Joomla\CMS\Uri\Uri; use Joomla\Http\AbstractTransport; use Joomla\Http\Exception\InvalidResponseCodeException; use Joomla\Uri\UriInterface; use Laminas\Diactoros\Stream as StreamResponse; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP transport class for using cURL. * * @since 1.7.3 */ class CurlTransport extends AbstractTransport implements TransportInterface { /** * Send a request to the server and return a Response object with the response. * * @param string $method The HTTP method for sending the request. * @param UriInterface $uri The URI to the resource to request. * @param mixed $data Either an associative array or a string to be sent with the request. * @param array $headers An array of request headers to send with the request. * @param integer $timeout Read timeout in seconds. * @param string $userAgent The optional user agent string to send with the request. * * @return Response * * @since 1.7.3 * @throws \RuntimeException */ public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) { // Setup the cURL handle. $ch = curl_init(); $options = []; // Set the request method. switch (strtoupper($method)) { case 'GET': $options[CURLOPT_HTTPGET] = true; break; case 'POST': $options[CURLOPT_POST] = true; break; case 'PUT': default: $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method); break; } // Don't wait for body when $method is HEAD $options[CURLOPT_NOBODY] = ($method === 'HEAD'); // Initialize the certificate store $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getBundledCaBundlePath()); // If data exists let's encode it and make sure our Content-type header is set. if (isset($data)) { // If the data is a scalar value simply add it to the cURL post fields. if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0)) { $options[CURLOPT_POSTFIELDS] = $data; } else { // Otherwise we need to encode the value first. $options[CURLOPT_POSTFIELDS] = http_build_query($data); } if (!isset($headers['Content-Type'])) { $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; } // Add the relevant headers. if (is_scalar($options[CURLOPT_POSTFIELDS])) { $headers['Content-Length'] = \strlen($options[CURLOPT_POSTFIELDS]); } } // Build the headers string for the request. $headerArray = []; if (isset($headers)) { foreach ($headers as $key => $value) { $headerArray[] = $key . ': ' . $value; } // Add the headers string into the stream context options array. $options[CURLOPT_HTTPHEADER] = $headerArray; } // Curl needs the accepted encoding header as option if (isset($headers['Accept-Encoding'])) { $options[CURLOPT_ENCODING] = $headers['Accept-Encoding']; } // If an explicit timeout is given user it. if (isset($timeout)) { $options[CURLOPT_TIMEOUT] = (int) $timeout; $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout; } // If an explicit user agent is given use it. if (isset($userAgent)) { $options[CURLOPT_USERAGENT] = $userAgent; } // Set the request URL. $options[CURLOPT_URL] = (string) $uri; // We want our headers. :-) $options[CURLOPT_HEADER] = true; // Return it... echoing it would be tacky. $options[CURLOPT_RETURNTRANSFER] = true; // Override the Expect header to prevent cURL from confusing itself in its own stupidity. // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/ $options[CURLOPT_HTTPHEADER][] = 'Expect:'; // Follow redirects if server config allows if ($this->redirectsAllowed()) { $options[CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true); } // Proxy configuration $app = Factory::getApplication(); if ($app->get('proxy_enable')) { $options[CURLOPT_PROXY] = $app->get('proxy_host') . ':' . $app->get('proxy_port'); if ($user = $app->get('proxy_user')) { $options[CURLOPT_PROXYUSERPWD] = $user . ':' . $app->get('proxy_pass'); } } // Set any custom transport options foreach ($this->getOption('transport.curl', []) as $key => $value) { $options[$key] = $value; } // Authentication, if needed if ($this->getOption('userauth') && $this->getOption('passwordauth')) { $options[CURLOPT_USERPWD] = $this->getOption('userauth') . ':' . $this->getOption('passwordauth'); $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; } // Set the cURL options. curl_setopt_array($ch, $options); // Execute the request and close the connection. $content = curl_exec($ch); // Check if the content is a string. If it is not, it must be an error. if (!\is_string($content)) { $message = curl_error($ch); if (empty($message)) { // Error but nothing from cURL? Create our own $message = 'No HTTP response received'; } throw new \RuntimeException($message); } // Get the request information. $info = curl_getinfo($ch); // Close the connection. curl_close($ch); $response = $this->getResponse($content, $info); // Manually follow redirects if server doesn't allow to follow location using curl if ($response->code >= 301 && $response->code < 400 && isset($response->headers['Location']) && (bool) $this->getOption('follow_location', true)) { $redirect_uri = new Uri($response->headers['Location']); if (\in_array($redirect_uri->getScheme(), ['file', 'scp'])) { throw new \RuntimeException('Curl redirect cannot be used in file or scp requests.'); } $response = $this->request($method, $redirect_uri, $data, $headers, $timeout, $userAgent); } return $response; } /** * Method to get a response object from a server response. * * @param string $content The complete server response, including headers * as a string if the response has no errors. * @param array $info The cURL request information. * * @return Response * * @since 1.7.3 * @throws InvalidResponseCodeException */ protected function getResponse($content, $info) { // Try to get header size if (isset($info['header_size'])) { $headerString = trim(substr($content, 0, $info['header_size'])); $headerArray = explode("\r\n\r\n", $headerString); // Get the last set of response headers as an array. $headers = explode("\r\n", array_pop($headerArray)); // Set the body for the response. $body = substr($content, $info['header_size']); } else { // Fallback and try to guess header count by redirect count // Get the number of redirects that occurred. $redirects = $info['redirect_count'] ?? 0; /* * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will * also be included. So we split the response into header + body + the number of redirects and only use the last two * sections which should be the last set of headers and the actual body. */ $response = explode("\r\n\r\n", $content, 2 + $redirects); // Set the body for the response. $body = array_pop($response); // Get the last set of response headers as an array. $headers = explode("\r\n", array_pop($response)); } // Get the response code from the first offset of the response headers. preg_match('/[0-9]{3}/', array_shift($headers), $matches); $code = \count($matches) ? $matches[0] : null; if (!is_numeric($code)) { // No valid response code was detected. throw new InvalidResponseCodeException('No HTTP response code found.'); } $statusCode = (int) $code; $verifiedHeaders = $this->processHeaders($headers); $streamInterface = new StreamResponse('php://memory', 'rw'); $streamInterface->write($body); return new Response($streamInterface, $statusCode, $verifiedHeaders); } /** * Method to check if HTTP transport cURL is available for use * * @return boolean true if available, else false * * @since 3.0.0 */ public static function isSupported() { return \function_exists('curl_version') && curl_version(); } /** * Check if redirects are allowed * * @return boolean * * @since 3.0.0 */ private function redirectsAllowed() { $curlVersion = curl_version(); // If open_basedir is enabled we also need to check if libcurl version is 7.19.4 or higher if (!ini_get('open_basedir') || version_compare($curlVersion['version'], '7.19.4', '>=')) { return true; } return false; } } PK0A#]�˛���&src/Http/Transport/StreamTransport.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http\Transport; use Composer\CaBundle\CaBundle; use Joomla\CMS\Factory; use Joomla\CMS\Http\Response; use Joomla\CMS\Http\TransportInterface; use Joomla\Http\AbstractTransport; use Joomla\Http\Exception\InvalidResponseCodeException; use Joomla\Uri\Uri; use Joomla\Uri\UriInterface; use Laminas\Diactoros\Stream as StreamResponse; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP transport class for using PHP streams. * * @since 1.7.3 */ class StreamTransport extends AbstractTransport implements TransportInterface { /** * Send a request to the server and return a Response object with the response. * * @param string $method The HTTP method for sending the request. * @param UriInterface $uri The URI to the resource to request. * @param mixed $data Either an associative array or a string to be sent with the request. * @param array $headers An array of request headers to send with the request. * @param integer $timeout Read timeout in seconds. * @param string $userAgent The optional user agent string to send with the request. * * @return Response * * @since 1.7.3 * @throws \RuntimeException */ public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) { // Create the stream context options array with the required method offset. $options = ['method' => strtoupper($method)]; // If data exists let's encode it and make sure our Content-Type header is set. if (isset($data)) { // If the data is a scalar value simply add it to the stream context options. if (is_scalar($data)) { $options['content'] = $data; } else { // Otherwise we need to encode the value first. $options['content'] = http_build_query($data); } if (!isset($headers['Content-Type'])) { $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; } // Add the relevant headers. $headers['Content-Length'] = \strlen($options['content']); } // If an explicit timeout is given user it. if (isset($timeout)) { $options['timeout'] = (int) $timeout; } // If an explicit user agent is given use it. if (isset($userAgent)) { $options['user_agent'] = $userAgent; } // Ignore HTTP errors so that we can capture them. $options['ignore_errors'] = 1; // Follow redirects. $options['follow_location'] = (int) $this->getOption('follow_location', 1); // Set any custom transport options foreach ($this->getOption('transport.stream', []) as $key => $value) { $options[$key] = $value; } // Add the proxy configuration, if any. $app = Factory::getApplication(); if ($app->get('proxy_enable')) { $options['proxy'] = $app->get('proxy_host') . ':' . $app->get('proxy_port'); $options['request_fulluri'] = true; // Put any required authorization into the headers array to be handled later // @todo: do we need to support any auth type other than Basic? if ($user = $app->get('proxy_user')) { $auth = base64_encode($app->get('proxy_user') . ':' . $app->get('proxy_pass')); $headers['Proxy-Authorization'] = 'Basic ' . $auth; } } // Build the headers string for the request. $headerEntries = []; if (isset($headers)) { foreach ($headers as $key => $value) { $headerEntries[] = $key . ': ' . $value; } // Add the headers string into the stream context options array. $options['header'] = implode("\r\n", $headerEntries); } // Get the current context options. $contextOptions = stream_context_get_options(stream_context_get_default()); // Add our options to the current ones, if any. $contextOptions['http'] = isset($contextOptions['http']) ? array_merge($contextOptions['http'], $options) : $options; // Create the stream context for the request. $context = stream_context_create( [ 'http' => $options, 'ssl' => [ 'verify_peer' => true, 'cafile' => $this->getOption('stream.certpath', CaBundle::getBundledCaBundlePath()), 'verify_depth' => 5, 'verify_peer_name' => true, ], ] ); // Authentication, if needed if ($uri instanceof Uri && $this->getOption('userauth') && $this->getOption('passwordauth')) { $uri->setUser($this->getOption('userauth')); $uri->setPass($this->getOption('passwordauth')); } // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); // Open the stream for reading. $stream = @fopen((string) $uri, 'r', false, $context); if (!$stream) { if (!$php_errormsg) { // Error but nothing from php? Create our own $php_errormsg = sprintf('Could not connect to resource: %s', $uri); } // Restore error tracking to give control to the exception handler ini_set('track_errors', $track_errors); throw new \RuntimeException($php_errormsg); } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); // Get the metadata for the stream, including response headers. $metadata = stream_get_meta_data($stream); // Get the contents from the stream. $content = stream_get_contents($stream); // Close the stream. fclose($stream); if (isset($metadata['wrapper_data']['headers'])) { $headers = $metadata['wrapper_data']['headers']; } elseif (isset($metadata['wrapper_data'])) { $headers = $metadata['wrapper_data']; } else { $headers = []; } return $this->getResponse($headers, $content); } /** * Method to get a response object from a server response. * * @param array $headers The response headers as an array. * @param string $body The response body as a string. * * @return Response * * @since 1.7.3 * @throws InvalidResponseCodeException */ protected function getResponse(array $headers, $body) { // Get the response code from the first offset of the response headers. preg_match('/[0-9]{3}/', array_shift($headers), $matches); $code = $matches[0]; if (!is_numeric($code)) { // No valid response code was detected. throw new InvalidResponseCodeException('No HTTP response code found.'); } $statusCode = (int) $code; $verifiedHeaders = $this->processHeaders($headers); $streamInterface = new StreamResponse('php://memory', 'rw'); $streamInterface->write($body); return new Response($streamInterface, $statusCode, $verifiedHeaders); } /** * Method to check if http transport stream available for use * * @return boolean true if available else false * * @since 3.0.0 */ public static function isSupported() { return \function_exists('fopen') && \is_callable('fopen') && ini_get('allow_url_fopen'); } } PK0A#]�7+4IIsrc/Http/HttpFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Http; use Joomla\CMS\Version; use Joomla\Http\TransportInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTTP factory class. * * @since 3.0.0 */ class HttpFactory { /** * Method to create a JHttp instance. * * @param array|\ArrayAccess $options Client options array. * @param array|string $adapters Adapter (string) or queue of adapters (array) to use for communication. * * @return Http * * @since 3.0.0 * @throws \RuntimeException */ public static function getHttp($options = [], $adapters = null) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } // Set default userAgent if nothing else is set if (!isset($options['userAgent'])) { $version = new Version(); $options['userAgent'] = $version->getUserAgent('Joomla', true, false); } if (!$driver = static::getAvailableDriver($options, $adapters)) { throw new \RuntimeException('No transport driver available.'); } return new Http($options, $driver); } /** * Finds an available http transport object for communication * * @param array|\ArrayAccess $options Options for creating TransportInterface object * @param array|string $default Adapter (string) or queue of adapters (array) to use * * @return TransportInterface|boolean Interface sub-class or boolean false if no adapters are available * * @since 3.0.0 */ public static function getAvailableDriver($options = [], $default = null) { if (\is_null($default)) { $availableAdapters = static::getHttpTransports(); } else { settype($default, 'array'); $availableAdapters = $default; } // Check if there is at least one available http transport adapter if (!\count($availableAdapters)) { return false; } foreach ($availableAdapters as $adapter) { /** @var $class TransportInterface */ $class = __NAMESPACE__ . '\\Transport\\' . ucfirst($adapter) . 'Transport'; if (!class_exists($class)) { $class = 'JHttpTransport' . ucfirst($adapter); } if (class_exists($class) && $class::isSupported()) { return new $class($options); } } return false; } /** * Get the http transport handlers * * @return array An array of available transport handlers * * @since 3.0.0 */ public static function getHttpTransports() { $names = []; $iterator = new \DirectoryIterator(__DIR__ . '/Transport'); /** @type $file \DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if ($file->isFile() && $file->getExtension() === 'php') { $names[] = substr($fileName, 0, strrpos($fileName, 'Transport.')); } } // Keep alphabetical order across all environments sort($names); // If curl is available set it to the first position if ($key = array_search('Curl', $names)) { unset($names[$key]); array_unshift($names, 'Curl'); } return $names; } } PK0A#]<6m�*�*src/Categories/CategoryNode.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; use Joomla\CMS\Factory; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Tree\NodeInterface; use Joomla\CMS\Tree\NodeTrait; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Helper class to load Categorytree * * @since 1.6 */ class CategoryNode extends CMSObject implements NodeInterface { use NodeTrait; /** * Primary key * * @var integer * @since 1.6 */ public $id; /** * The id of the category in the asset table * * @var integer * @since 1.6 */ public $asset_id; /** * The id of the parent of category in the asset table, 0 for category root * * @var integer * @since 1.6 */ public $parent_id; /** * The lft value for this category in the category tree * * @var integer * @since 1.6 */ public $lft; /** * The rgt value for this category in the category tree * * @var integer * @since 1.6 */ public $rgt; /** * The depth of this category's position in the category tree * * @var integer * @since 1.6 */ public $level; /** * The extension this category is associated with * * @var integer * @since 1.6 */ public $extension; /** * The menu title for the category (a short name) * * @var string * @since 1.6 */ public $title; /** * The alias for the category * * @var string * @since 1.6 */ public $alias; /** * Description of the category. * * @var string * @since 1.6 */ public $description; /** * The publication status of the category * * @var boolean * @since 1.6 */ public $published; /** * Whether the category is or is not checked out * * @var boolean * @since 1.6 */ public $checked_out; /** * The time at which the category was checked out * * @var string * @since 1.6 */ public $checked_out_time; /** * Access level for the category * * @var integer * @since 1.6 */ public $access; /** * JSON string of parameters * * @var string * @since 1.6 */ public $params; /** * Metadata description * * @var string * @since 1.6 */ public $metadesc; /** * Keywords for metadata * * @var string * @since 1.6 */ public $metakey; /** * JSON string of other metadata * * @var string * @since 1.6 */ public $metadata; /** * The ID of the user who created the category * * @var integer * @since 1.6 */ public $created_user_id; /** * The time at which the category was created * * @var string * @since 1.6 */ public $created_time; /** * The ID of the user who last modified the category * * @var integer * @since 1.6 */ public $modified_user_id; /** * The time at which the category was modified * * @var string * @since 1.6 */ public $modified_time; /** * Number of times the category has been viewed * * @var integer * @since 1.6 */ public $hits; /** * The language for the category in xx-XX format * * @var string * @since 1.6 */ public $language; /** * Number of items in this category or descendants of this category * * @var integer * @since 1.6 */ public $numitems; /** * Slug for the category (used in URL) * * @var string * @since 1.6 */ public $slug; /** * Array of assets * * @var array * @since 1.6 */ public $assets; /** * Path from root to this category * * @var array * @since 1.6 */ protected $_path = []; /** * Flag if all children have been loaded * * @var boolean * @since 1.6 */ protected $_allChildrenloaded = false; /** * Constructor of this tree * * @var Categories * @since 1.6 */ protected $_constructor; /** * Class constructor * * @param array $category The category data. * @param Categories $constructor The tree constructor. * * @since 1.6 */ public function __construct($category = null, $constructor = null) { if ($category) { $this->setProperties($category); if ($constructor) { $this->_constructor = $constructor; } } } /** * Set the parent of this category * * If the category already has a parent, the link is unset * * @param NodeInterface $parent CategoryNode for the parent to be set or null * * @return void * * @since 1.6 */ public function setParent(NodeInterface $parent) { if (!\is_null($this->_parent)) { $key = array_search($this, $this->_parent->_children); unset($this->_parent->_children[$key]); } $this->_parent = $parent; $this->_parent->_children[] = &$this; if (\count($this->_parent->_children) > 1) { end($this->_parent->_children); $this->_leftSibling = prev($this->_parent->_children); $this->_leftSibling->_rightsibling = &$this; } if ($this->parent_id != 1) { $this->_path = $parent->getPath(); } $this->_path[$this->id] = $this->id . ':' . $this->alias; } /** * Get the children of this node * * @param boolean $recursive False by default * * @return CategoryNode[] The children * * @since 1.6 */ public function &getChildren($recursive = false) { if (!$this->_allChildrenloaded) { $temp = $this->_constructor->get($this->id, true); if ($temp) { $this->_children = $temp->getChildren(); $this->_leftSibling = $temp->getSibling(false); $this->_rightSibling = $temp->getSibling(true); $this->setAllLoaded(); } } if ($recursive) { $items = []; foreach ($this->_children as $child) { $items[] = $child; $items = array_merge($items, $child->getChildren(true)); } return $items; } return $this->_children; } /** * Returns the right or left sibling of a category * * @param boolean $right If set to false, returns the left sibling * * @return CategoryNode|null CategoryNode object with the sibling information or null if there is no sibling on that side. * * @since 1.6 */ public function getSibling($right = true) { if (!$this->_allChildrenloaded) { $temp = $this->_constructor->get($this->id, true); $this->_children = $temp->getChildren(); $this->_leftSibling = $temp->getSibling(false); $this->_rightSibling = $temp->getSibling(true); $this->setAllLoaded(); } if ($right) { return $this->_rightSibling; } return $this->_leftSibling; } /** * Returns the category parameters * * @return Registry * * @since 1.6 */ public function getParams() { if (!($this->params instanceof Registry)) { $this->params = new Registry($this->params); } return $this->params; } /** * Returns the category metadata * * @return Registry A Registry object containing the metadata * * @since 1.6 */ public function getMetadata() { if (!($this->metadata instanceof Registry)) { $this->metadata = new Registry($this->metadata); } return $this->metadata; } /** * Returns the category path to the root category * * @return array * * @since 1.6 */ public function getPath() { return $this->_path; } /** * Returns the user that created the category * * @param boolean $modifiedUser Returns the modified_user when set to true * * @return \Joomla\CMS\User\User A User object containing a userid * * @since 1.6 */ public function getAuthor($modifiedUser = false) { if ($modifiedUser) { return Factory::getUser($this->modified_user_id); } return Factory::getUser($this->created_user_id); } /** * Set to load all children * * @return void * * @since 1.6 */ public function setAllLoaded() { $this->_allChildrenloaded = true; foreach ($this->_children as $child) { $child->setAllLoaded(); } } /** * Returns the number of items. * * @param boolean $recursive If false number of children, if true number of descendants * * @return integer Number of children or descendants * * @since 1.6 */ public function getNumItems($recursive = false) { if ($recursive) { $count = $this->numitems; foreach ($this->getChildren() as $child) { $count += $child->getNumItems(true); } return $count; } return $this->numitems; } /** * Serialize the node. * * @since 4.3.2 */ public function __serialize() { $vars = get_object_vars($this); // Store constructor as array of options. if ($this->_constructor) { $vars['_constructor'] = $this->_constructor->getOptions(); } return $vars; } /** * Unserialize the node. * * @param array $data * * @since 4.3.2 */ public function __unserialize($data) { foreach ($data as $key => $value) { $this->$key = $value; } // Restore constructor from array of options. if ($this->_constructor) { $this->_constructor = Categories::getInstance($this->_constructor['extension'], $this->_constructor); } } } PK0A#]GѡQ��'src/Categories/CategoryServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; use Joomla\CMS\Form\Form; use Joomla\CMS\Helper\ContentHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for component categories service. * * @since 4.0.0 */ trait CategoryServiceTrait { /** * The categories factory * * @var CategoryFactoryInterface * * @since 4.0.0 */ private $categoryFactory; /** * Returns the category service. * * @param array $options The options * @param string $section The section * * @return CategoryInterface * * @since 4.0.0 * @throws SectionNotFoundException */ public function getCategory(array $options = [], $section = ''): CategoryInterface { return $this->categoryFactory->createCategory($options, $section); } /** * Sets the internal category factory. * * @param CategoryFactoryInterface $categoryFactory The categories factory * * @return void * * @since 4.0.0 */ public function setCategoryFactory(CategoryFactoryInterface $categoryFactory) { $this->categoryFactory = $categoryFactory; } /** * Adds Count Items for Category Manager. * * @param \stdClass[] $items The category objects * @param string $section The section * * @return void * * @since 4.0.0 * @throws \Exception */ public function countItems(array $items, string $section) { $config = (object) [ 'related_tbl' => $this->getTableNameForSection($section), 'state_col' => $this->getStateColumnForSection($section), 'group_col' => 'catid', 'relation_type' => 'category_or_group', ]; ContentHelper::countRelations($items, $config); } /** * Prepares the category form * * @param Form $form The form to change * @param array|object $data The form data * * @return void */ public function prepareForm(Form $form, $data) { } /** * Returns the table for the count items functions for the given section. * * @param string $section The section * * @return string|null * * @since 4.0.0 */ protected function getTableNameForSection(string $section = null) { return null; } /** * Returns the state column for the count items functions for the given section. * * @param string $section The section * * @return string|null * * @since 4.0.0 */ protected function getStateColumnForSection(string $section = null) { return 'state'; } } PK0A#] �8N��+src/Categories/CategoryServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; use Joomla\CMS\Form\Form; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to component specific categories. * * @since 4.0.0 */ interface CategoryServiceInterface { /** * Returns the category service. * * @param array $options The options * @param string $section The section * * @return CategoryInterface * * @since 4.0.0 * @throws SectionNotFoundException */ public function getCategory(array $options = [], $section = ''): CategoryInterface; /** * Adds Count Items for Category Manager. * * @param \stdClass[] $items The category objects * @param string $section The section * * @return void * * @since 4.0.0 * @throws \Exception */ public function countItems(array $items, string $section); /** * Prepares the category form * * @param Form $form The form to change * @param array|object $data The form data * * @return void */ public function prepareForm(Form $form, $data); } PK0A#],r�!ww$src/Categories/CategoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The category interface. * * @since 3.10.0 */ interface CategoryInterface { /** * Loads a specific category and all its children in a CategoryNode object. * * @param mixed $id an optional id integer or equal to 'root' * @param boolean $forceload True to force the _load method to execute * * @return CategoryNode|null CategoryNode object or null if $id is not valid * * @since 3.10.0 */ public function get($id = 'root', $forceload = false); } PK0A#]��4C''+src/Categories/CategoryFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Category factory interface * * @since 3.10.0 */ interface CategoryFactoryInterface { /** * Creates a category. * * @param array $options The options * @param string $section The section * * @return CategoryInterface * * @since 3.10.0 * * @throws SectionNotFoundException */ public function createCategory(array $options = [], string $section = ''): CategoryInterface; } PK0A#]�� g��+src/Categories/SectionNotFoundException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Representing when a category section cannot be found. * * @since 3.10.0 */ class SectionNotFoundException extends \RuntimeException { } PK0A#]�ҽ���"src/Categories/CategoryFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Option based categories factory. * * @since 3.10.0 */ class CategoryFactory implements CategoryFactoryInterface { use DatabaseAwareTrait; /** * The namespace to create the categories from. * * @var string * @since 4.0.0 */ private $namespace; /** * The namespace must be like: * Joomla\Component\Content * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct($namespace) { $this->namespace = $namespace; } /** * Creates a category. * * @param array $options The options * @param string $section The section * * @return CategoryInterface * * @since 3.10.0 * * @throws SectionNotFoundException */ public function createCategory(array $options = [], string $section = ''): CategoryInterface { $className = trim($this->namespace, '\\') . '\\Site\\Service\\' . ucfirst($section) . 'Category'; if (!class_exists($className)) { throw new SectionNotFoundException(); } $category = new $className($options); if ($category instanceof DatabaseAwareInterface) { $category->setDatabase($this->getDatabase()); } return $category; } } PK0A#]�ߢ��:�:src/Categories/Categories.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Categories; use Joomla\CMS\Factory; use Joomla\CMS\Language\Multilanguage; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Categories Class. * * @since 1.6 */ class Categories implements CategoryInterface, DatabaseAwareInterface { use DatabaseAwareTrait; /** * Array to hold the object instances * * @var Categories[] * @since 1.6 */ public static $instances = []; /** * Array of category nodes * * @var CategoryNode[] * @since 1.6 */ protected $_nodes; /** * Array of checked categories -- used to save values when _nodes are null * * @var boolean[] * @since 1.6 */ protected $_checkedCategories; /** * Name of the extension the categories belong to * * @var string * @since 1.6 */ protected $_extension; /** * Name of the linked content table to get category content count * * @var string * @since 1.6 */ protected $_table; /** * Name of the category field * * @var string * @since 1.6 */ protected $_field; /** * Name of the key field * * @var string * @since 1.6 */ protected $_key; /** * Name of the items state field * * @var string * @since 1.6 */ protected $_statefield; /** * Array of options * * @var array * @since 1.6 */ protected $_options = []; /** * Class constructor * * @param array $options Array of options * * @since 1.6 */ public function __construct($options) { $this->_extension = $options['extension']; $this->_table = $options['table']; $this->_field = isset($options['field']) && $options['field'] ? $options['field'] : 'catid'; $this->_key = isset($options['key']) && $options['key'] ? $options['key'] : 'id'; $this->_statefield = $options['statefield'] ?? 'state'; $options['access'] = $options['access'] ?? 'true'; $options['published'] = $options['published'] ?? 1; $options['countItems'] = $options['countItems'] ?? 0; $options['currentlang'] = Multilanguage::isEnabled() ? Factory::getLanguage()->getTag() : 0; $this->_options = $options; } /** * Returns a reference to a Categories object * * @param string $extension Name of the categories extension * @param array $options An array of options * * @return Categories|boolean Categories object on success, boolean false if an object does not exist * * @since 1.6 * * @deprecated 4.0 will be removed in 6.0 * Use the ComponentInterface to get the categories * Example: Factory::getApplication()->bootComponent($component)->getCategory($options, $section); */ public static function getInstance($extension, $options = []) { $hash = md5(strtolower($extension) . serialize($options)); if (isset(self::$instances[$hash])) { return self::$instances[$hash]; } $categories = null; try { $parts = explode('.', $extension, 2); $component = Factory::getApplication()->bootComponent($parts[0]); if ($component instanceof CategoryServiceInterface) { $categories = $component->getCategory($options, \count($parts) > 1 ? $parts[1] : ''); } } catch (SectionNotFoundException $e) { $categories = null; } self::$instances[$hash] = $categories; return self::$instances[$hash]; } /** * Loads a specific category and all its children in a CategoryNode object. * * @param mixed $id an optional id integer or equal to 'root' * @param boolean $forceload True to force the _load method to execute * * @return CategoryNode|null CategoryNode object or null if $id is not valid * * @since 1.6 */ public function get($id = 'root', $forceload = false) { if ($id !== 'root') { $id = (int) $id; if ($id == 0) { $id = 'root'; } } // If this $id has not been processed yet, execute the _load method if ((!isset($this->_nodes[$id]) && !isset($this->_checkedCategories[$id])) || $forceload) { $this->_load($id); } // If we already have a value in _nodes for this $id, then use it. if (isset($this->_nodes[$id])) { return $this->_nodes[$id]; } return null; } /** * Returns the extension of the category. * * @return string The extension * * @since 3.9.0 */ public function getExtension() { return $this->_extension; } /** * Returns options. * * @return array * * @since 4.3.2 */ public function getOptions() { return $this->_options; } /** * Load method * * @param int|string $id Id of category to load * * @return void * * @since 1.6 */ protected function _load($id) { try { $db = $this->getDatabase(); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $db = Factory::getContainer()->get(DatabaseInterface::class); } $app = Factory::getApplication(); $user = Factory::getUser(); $extension = $this->_extension; if ($id !== 'root') { $id = (int) $id; if ($id === 0) { $id = 'root'; } } // Record that has this $id has been checked $this->_checkedCategories[$id] = true; $query = $db->getQuery(true) ->select( [ $db->quoteName('c.id'), $db->quoteName('c.asset_id'), $db->quoteName('c.access'), $db->quoteName('c.alias'), $db->quoteName('c.checked_out'), $db->quoteName('c.checked_out_time'), $db->quoteName('c.created_time'), $db->quoteName('c.created_user_id'), $db->quoteName('c.description'), $db->quoteName('c.extension'), $db->quoteName('c.hits'), $db->quoteName('c.language'), $db->quoteName('c.level'), $db->quoteName('c.lft'), $db->quoteName('c.metadata'), $db->quoteName('c.metadesc'), $db->quoteName('c.metakey'), $db->quoteName('c.modified_time'), $db->quoteName('c.note'), $db->quoteName('c.params'), $db->quoteName('c.parent_id'), $db->quoteName('c.path'), $db->quoteName('c.published'), $db->quoteName('c.rgt'), $db->quoteName('c.title'), $db->quoteName('c.modified_user_id'), $db->quoteName('c.version'), ] ); $case_when = ' CASE WHEN '; $case_when .= $query->charLength($db->quoteName('c.alias'), '!=', '0'); $case_when .= ' THEN '; $c_id = $query->castAsChar($db->quoteName('c.id')); $case_when .= $query->concatenate([$c_id, $db->quoteName('c.alias')], ':'); $case_when .= ' ELSE '; $case_when .= $c_id . ' END as ' . $db->quoteName('slug'); $query->select($case_when) ->where('(' . $db->quoteName('c.extension') . ' = :extension OR ' . $db->quoteName('c.extension') . ' = ' . $db->quote('system') . ')') ->bind(':extension', $extension); if ($this->_options['access']) { $groups = $user->getAuthorisedViewLevels(); $query->whereIn($db->quoteName('c.access'), $groups); } if ($this->_options['published'] == 1) { $query->where($db->quoteName('c.published') . ' = 1'); } $query->order($db->quoteName('c.lft')); // Note: s for selected id if ($id !== 'root') { // Get the selected category $query->from($db->quoteName('#__categories', 's')) ->where($db->quoteName('s.id') . ' = :id') ->bind(':id', $id, ParameterType::INTEGER); if ($app->isClient('site') && Multilanguage::isEnabled()) { // For the most part, we use c.lft column, which index is properly used instead of c.rgt $query->join( 'INNER', $db->quoteName('#__categories', 'c'), '(' . $db->quoteName('s.lft') . ' < ' . $db->quoteName('c.lft') . ' AND ' . $db->quoteName('c.lft') . ' < ' . $db->quoteName('s.rgt') . ' AND ' . $db->quoteName('c.language') . ' IN (' . implode(',', $query->bindArray([Factory::getLanguage()->getTag(), '*'], ParameterType::STRING)) . '))' . ' OR (' . $db->quoteName('c.lft') . ' <= ' . $db->quoteName('s.lft') . ' AND ' . $db->quoteName('s.rgt') . ' <= ' . $db->quoteName('c.rgt') . ')' ); } else { $query->join( 'INNER', $db->quoteName('#__categories', 'c'), '(' . $db->quoteName('s.lft') . ' <= ' . $db->quoteName('c.lft') . ' AND ' . $db->quoteName('c.lft') . ' < ' . $db->quoteName('s.rgt') . ')' . ' OR (' . $db->quoteName('c.lft') . ' < ' . $db->quoteName('s.lft') . ' AND ' . $db->quoteName('s.rgt') . ' < ' . $db->quoteName('c.rgt') . ')' ); } } else { $query->from($db->quoteName('#__categories', 'c')); if ($app->isClient('site') && Multilanguage::isEnabled()) { $query->whereIn($db->quoteName('c.language'), [Factory::getLanguage()->getTag(), '*'], ParameterType::STRING); } } // Note: i for item if ($this->_options['countItems'] == 1) { $subQuery = $db->getQuery(true) ->select('COUNT(' . $db->quoteName($db->escape('i.' . $this->_key)) . ')') ->from($db->quoteName($db->escape($this->_table), 'i')) ->where($db->quoteName($db->escape('i.' . $this->_field)) . ' = ' . $db->quoteName('c.id')); if ($this->_options['published'] == 1) { $subQuery->where($db->quoteName($db->escape('i.' . $this->_statefield)) . ' = 1'); } if ($this->_options['currentlang'] !== 0) { $subQuery->where( $db->quoteName('i.language') . ' IN (' . implode(',', $query->bindArray([$this->_options['currentlang'], '*'], ParameterType::STRING)) . ')' ); } $query->select('(' . $subQuery . ') AS ' . $db->quoteName('numitems')); } // Get the results $db->setQuery($query); $results = $db->loadObjectList('id'); $childrenLoaded = false; if (\count($results)) { // Foreach categories foreach ($results as $result) { // Deal with root category if ($result->id == 1) { $result->id = 'root'; } // Deal with parent_id if ($result->parent_id == 1) { $result->parent_id = 'root'; } // Create the node if (!isset($this->_nodes[$result->id])) { // Create the CategoryNode and add to _nodes $this->_nodes[$result->id] = new CategoryNode($result, $this); // If this is not root and if the current node's parent is in the list or the current node parent is 0 if ($result->id !== 'root' && (isset($this->_nodes[$result->parent_id]) || $result->parent_id == 1)) { // Compute relationship between node and its parent - set the parent in the _nodes field $this->_nodes[$result->id]->setParent($this->_nodes[$result->parent_id]); } // If the node's parent id is not in the _nodes list and the node is not root (doesn't have parent_id == 0), // then remove the node from the list if (!(isset($this->_nodes[$result->parent_id]) || $result->parent_id == 0)) { unset($this->_nodes[$result->id]); continue; } if ($result->id == $id || $childrenLoaded) { $this->_nodes[$result->id]->setAllLoaded(); $childrenLoaded = true; } } elseif ($result->id == $id || $childrenLoaded) { // Create the CategoryNode $this->_nodes[$result->id] = new CategoryNode($result, $this); if ($result->id !== 'root' && (isset($this->_nodes[$result->parent_id]) || $result->parent_id)) { // Compute relationship between node and its parent $this->_nodes[$result->id]->setParent($this->_nodes[$result->parent_id]); } // If the node's parent id is not in the _nodes list and the node is not root (doesn't have parent_id == 0), // then remove the node from the list if (!(isset($this->_nodes[$result->parent_id]) || $result->parent_id == 0)) { unset($this->_nodes[$result->id]); continue; } if ($result->id == $id || $childrenLoaded) { $this->_nodes[$result->id]->setAllLoaded(); $childrenLoaded = true; } } } } else { $this->_nodes[$id] = null; } } } PK0A#]p�u���src/HTML/Registry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service registry for JHtml services * * @since 4.0.0 */ final class Registry { /** * Mapping array of the core CMS JHtml helpers * * @var array * @since 4.0.0 */ private $serviceMap = [ 'access' => Helpers\Access::class, 'actionsdropdown' => Helpers\ActionsDropdown::class, 'adminlanguage' => Helpers\AdminLanguage::class, 'behavior' => Helpers\Behavior::class, 'bootstrap' => Helpers\Bootstrap::class, 'category' => Helpers\Category::class, 'content' => Helpers\Content::class, 'contentlanguage' => Helpers\ContentLanguage::class, 'date' => Helpers\Date::class, 'debug' => Helpers\Debug::class, 'draggablelist' => Helpers\DraggableList::class, 'dropdown' => Helpers\Dropdown::class, 'email' => Helpers\Email::class, 'form' => Helpers\Form::class, 'formbehavior' => Helpers\FormBehavior::class, 'grid' => Helpers\Grid::class, 'icons' => Helpers\Icons::class, 'jgrid' => Helpers\JGrid::class, 'jquery' => Helpers\Jquery::class, 'links' => Helpers\Links::class, 'list' => Helpers\ListHelper::class, 'menu' => Helpers\Menu::class, 'number' => Helpers\Number::class, 'searchtools' => Helpers\SearchTools::class, 'select' => Helpers\Select::class, 'sidebar' => Helpers\Sidebar::class, 'sortablelist' => Helpers\SortableList::class, 'string' => Helpers\StringHelper::class, 'tag' => Helpers\Tag::class, 'tel' => Helpers\Telephone::class, 'uitab' => Helpers\UiTab::class, 'user' => Helpers\User::class, 'workflowstage' => Helpers\WorkflowStage::class, ]; /** * Get the service for a given key * * @param string $key The service key to look up * * @return string|object * * @since 4.0.0 */ public function getService(string $key) { if (!$this->hasService($key)) { throw new \InvalidArgumentException("The '$key' service key is not registered."); } return $this->serviceMap[$key]; } /** * Check if the registry has a service for the given key * * @param string $key The service key to look up * * @return boolean * * @since 4.0.0 */ public function hasService(string $key): bool { return isset($this->serviceMap[$key]); } /** * Register a service * * @param string $key The service key to be registered * @param string|object $handler The handler for the service as either a PHP class name or class object * @param boolean $replace Flag indicating the service key may replace an existing definition * * @return void * * @since 4.0.0 */ public function register(string $key, $handler, bool $replace = false) { // If the key exists already and we aren't instructed to replace existing services, bail early if (isset($this->serviceMap[$key]) && !$replace) { throw new \RuntimeException("The '$key' service key is already registered."); } // If the handler is a string, it must be a class that exists if (\is_string($handler) && !class_exists($handler)) { throw new \RuntimeException("The '$handler' class for service key '$key' does not exist."); } // Otherwise the handler must be a class object if (!\is_string($handler) && !\is_object($handler)) { throw new \RuntimeException( sprintf( 'The handler for service key %1$s must be a PHP class name or class object, a %2$s was given.', $key, \gettype($handler) ) ); } $this->serviceMap[$key] = $handler; } } PK0A#]�25n3�3�src/HTML/HTMLHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML; use Joomla\CMS\Environment\Browser; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; use Joomla\CMS\Uri\Uri; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for all HTML drawing classes * * @since 1.5 */ abstract class HTMLHelper { /** * Option values related to the generation of HTML output. Recognized * options are: * fmtDepth, integer. The current indent depth. * fmtEol, string. The end of line string, default is linefeed. * fmtIndent, string. The string to use for indentation, default is * tab. * * @var array * @since 1.5 */ public static $formatOptions = ['format.depth' => 0, 'format.eol' => "\n", 'format.indent' => "\t"]; /** * An array to hold included paths * * @var string[] * @since 1.5 * @deprecated 4.0 will be removed in 6.0 */ protected static $includePaths = []; /** * An array to hold method references * * @var callable[] * @since 1.6 * @deprecated 4.0 will be removed in 6.0 */ protected static $registry = []; /** * The service registry for custom and overridden JHtml helpers * * @var Registry * @since 4.0.0 */ protected static $serviceRegistry; /** * Method to extract a key * * @param string $key The name of helper method to load, (prefix).(class).function * prefix and class are optional and can be used to load custom html helpers. * * @return array Contains lowercase key, prefix, file, function. * * @since 1.6 * @deprecated 4.0 will be removed in 6.0 * Use the service registry instead * HTMLHelper::getServiceRegistry()->getService($file); */ protected static function extract($key) { $key = preg_replace('#[^A-Z0-9_\.]#i', '', $key); // Check to see whether we need to load a helper file $parts = explode('.', $key); if (\count($parts) === 3) { @trigger_error( 'Support for a three segment service key is deprecated and will be removed in Joomla 5.0, use the service registry instead', E_USER_DEPRECATED ); } $prefix = \count($parts) === 3 ? array_shift($parts) : 'JHtml'; $file = \count($parts) === 2 ? array_shift($parts) : ''; $func = array_shift($parts); return [strtolower($prefix . '.' . $file . '.' . $func), $prefix, $file, $func]; } /** * Class loader method * * Additional arguments may be supplied and are passed to the sub-class. * Additional include paths are also able to be specified for third-party use * * @param string $key The name of helper method to load, (prefix).(class).function * prefix and class are optional and can be used to load custom * html helpers. * @param mixed $methodArgs The arguments to pass forward to the method being called * * @return mixed Result of HTMLHelper::call($function, $args) * * @since 1.5 * @throws \InvalidArgumentException */ final public static function _(string $key, ...$methodArgs) { list($key, $prefix, $file, $func) = static::extract($key); if (\array_key_exists($key, static::$registry)) { $function = static::$registry[$key]; return static::call($function, $methodArgs); } /* * Support fetching services from the registry if a custom class prefix was not given (a three segment key), * the service comes from a class other than this one, and a service has been registered for the file. */ if ($prefix === 'JHtml' && $file !== '' && static::getServiceRegistry()->hasService($file)) { $service = static::getServiceRegistry()->getService($file); $toCall = [$service, $func]; if (!\is_callable($toCall)) { throw new \InvalidArgumentException(sprintf('%s::%s not found.', $file, $func), 500); } static::register($key, $toCall); return static::call($toCall, $methodArgs); } $className = $prefix . ucfirst($file); if (!class_exists($className)) { $path = Path::find(static::$includePaths, strtolower($file) . '.php'); if (!$path) { throw new \InvalidArgumentException(sprintf('%s %s not found.', $prefix, $file), 500); } \JLoader::register($className, $path); if (!class_exists($className)) { throw new \InvalidArgumentException(sprintf('%s not found.', $className), 500); } } // If calling a method from this class, do not allow access to internal methods if ($className === __CLASS__) { if (!((new \ReflectionMethod($className, $func))->isPublic())) { throw new \InvalidArgumentException('Access to internal class methods is not allowed.'); } } $toCall = [$className, $func]; if (!\is_callable($toCall)) { throw new \InvalidArgumentException(sprintf('%s::%s not found.', $className, $func), 500); } static::register($key, $toCall); return static::call($toCall, $methodArgs); } /** * Registers a function to be called with a specific key * * @param string $key The name of the key * @param callable $function Function or method * * @return boolean True if the function is callable * * @since 1.6 * @deprecated 4.0 will be removed in 6.0 * Use the service registry instead * HTMLHelper::getServiceRegistry()->register($key, $function); */ public static function register($key, callable $function) { @trigger_error( 'Support for registering functions is deprecated and will be removed in Joomla 5.0, use the service registry instead', E_USER_DEPRECATED ); list($key) = static::extract($key); static::$registry[$key] = $function; return true; } /** * Removes a key for a method from registry. * * @param string $key The name of the key * * @return boolean True if a set key is unset * * @since 1.6 * @deprecated 4.0 will be removed in 6.0 * Use the service registry instead */ public static function unregister($key) { @trigger_error( 'Support for registering functions is deprecated and will be removed in Joomla 5.0, use the service registry instead', E_USER_DEPRECATED ); list($key) = static::extract($key); if (isset(static::$registry[$key])) { unset(static::$registry[$key]); return true; } return false; } /** * Test if the key is registered. * * @param string $key The name of the key * * @return boolean True if the key is registered. * * @since 1.6 */ public static function isRegistered($key) { list($key) = static::extract($key); return isset(static::$registry[$key]); } /** * Retrieves the service registry. * * @return Registry * * @since 4.0.0 */ public static function getServiceRegistry(): Registry { if (!static::$serviceRegistry) { static::$serviceRegistry = Factory::getContainer()->get(Registry::class); } return static::$serviceRegistry; } /** * Function caller method * * @param callable $function Function or method to call * @param array $args Arguments to be passed to function * * @return mixed Function result or false on error. * * @link https://www.php.net/manual/en/function.call-user-func-array.php * @since 1.6 * @throws \InvalidArgumentException */ protected static function call(callable $function, $args) { // Workaround to allow calling helper methods have arguments passed by reference $temp = []; foreach ($args as &$arg) { $temp[] = &$arg; } return \call_user_func_array($function, $temp); } /** * Write a `<a>` element * * @param string $url The relative URL to use for the href attribute * @param string $text The target attribute to use * @param array|string $attribs Attributes to be added to the `<a>` element * * @return string * * @since 1.5 */ public static function link($url, $text, $attribs = null) { if (\is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } return '<a href="' . $url . '" ' . $attribs . '>' . $text . '</a>'; } /** * Write a `<iframe>` element * * @param string $url The relative URL to use for the src attribute. * @param string $name The target attribute to use. * @param array|string $attribs Attributes to be added to the `<iframe>` element * @param string $noFrames The message to display if the iframe tag is not supported. * * @return string * * @since 1.5 */ public static function iframe($url, $name, $attribs = null, $noFrames = '') { if (\is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } return '<iframe src="' . $url . '" ' . $attribs . ' name="' . $name . '">' . $noFrames . '</iframe>'; } /** * Compute the files to be included * * @param string $folder Folder name to search in (i.e. images, css, js). * @param string $file Path to file. * @param boolean $relative Flag if the path to the file is relative to the /media folder (and searches in template). * @param boolean $detectBrowser Flag if the browser should be detected to include specific browser files. * @param boolean $detectDebug Flag if debug mode is enabled to include uncompressed files if debug is on. * * @return array files to be included. * * @see Browser * @since 1.6 */ protected static function includeRelativeFiles($folder, $file, $relative, $detectBrowser, $detectDebug) { // Set debug flag $debugMode = false; // Detect debug mode if ($detectDebug && JDEBUG) { $debugMode = true; } // If http is present in filename if (strpos($file, 'http') === 0 || strpos($file, '//') === 0) { $includes = [$file]; } else { // Extract extension and strip the file $strip = File::stripExt($file); $ext = File::getExt($file); // Prepare array of files $includes = []; // Detect browser and compute potential files if ($detectBrowser) { $navigator = Browser::getInstance(); $browser = $navigator->getBrowser(); $major = $navigator->getMajor(); $minor = $navigator->getMinor(); $minExt = ''; if (\strlen($strip) > 4 && preg_match('#\.min$#', $strip)) { $minExt = '.min'; $strip = preg_replace('#\.min$#', '', $strip); } // Try to include files named filename.ext, filename_browser.ext, filename_browser_major.ext, filename_browser_major_minor.ext // where major and minor are the browser version names $potential = [ $strip . $minExt, $strip . '_' . $browser . $minExt, $strip . '_' . $browser . '_' . $major . $minExt, $strip . '_' . $browser . '_' . $major . '_' . $minor . $minExt, ]; } else { $potential = [$strip]; } // If relative search in template directory or media directory if ($relative) { $app = Factory::getApplication(); $template = $app->getTemplate(true); $templaPath = JPATH_THEMES; if ($template->inheritable || !empty($template->parent)) { $client = $app->isClient('administrator') === true ? 'administrator' : 'site'; $templaPath = JPATH_ROOT . "/media/templates/$client"; } // For each potential files foreach ($potential as $strip) { $files = []; $files[] = $strip . '.' . $ext; /** * Loop on 1 or 2 files and break on first found. * Add the content of the MD5SUM file located in the same folder to url to ensure cache browser refresh * This MD5SUM file must represent the signature of the folder content */ foreach ($files as $file) { if (!empty($template->parent)) { $found = static::addFileToBuffer("$templaPath/$template->template/$folder/$file", $ext, $debugMode); if (empty($found)) { $found = static::addFileToBuffer("$templaPath/$template->parent/$folder/$file", $ext, $debugMode); } } else { $found = static::addFileToBuffer("$templaPath/$template->template/$folder/$file", $ext, $debugMode); } if (!empty($found)) { $includes[] = $found; break; } else { // If the file contains any /: it can be in a media extension subfolder if (strpos($file, '/')) { // Divide the file extracting the extension as the first part before / list($extension, $file) = explode('/', $file, 2); // If the file yet contains any /: it can be a plugin if (strpos($file, '/')) { // Divide the file extracting the element as the first part before / list($element, $file) = explode('/', $file, 2); // Try to deal with plugins group in the media folder $found = static::addFileToBuffer(JPATH_ROOT . "/media/$extension/$element/$folder/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } // Try to deal with classical file in a media subfolder called element $found = static::addFileToBuffer(JPATH_ROOT . "/media/$extension/$folder/$element/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } // Try to deal with system files in the template folder if (!empty($template->parent)) { $found = static::addFileToBuffer("$templaPath/$template->template/$folder/system/$element/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } $found = static::addFileToBuffer("$templaPath/$template->parent/$folder/system/$element/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } else { // Try to deal with system files in the media folder $found = static::addFileToBuffer(JPATH_ROOT . "/media/system/$folder/$element/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } } else { // Try to deal with files in the extension's media folder $found = static::addFileToBuffer(JPATH_ROOT . "/media/$extension/$folder/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } // Try to deal with system files in the template folder if (!empty($template->parent)) { $found = static::addFileToBuffer("$templaPath/$template->template/$folder/system/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } $found = static::addFileToBuffer("$templaPath/$template->parent/$folder/system/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } else { // Try to deal with system files in the template folder $found = static::addFileToBuffer("$templaPath/$template->template/$folder/system/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } // Try to deal with system files in the media folder $found = static::addFileToBuffer(JPATH_ROOT . "/media/system/$folder/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } } else { // Try to deal with system files in the media folder $found = static::addFileToBuffer(JPATH_ROOT . "/media/system/$folder/$file", $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } } } } } else { // If not relative and http is not present in filename foreach ($potential as $strip) { $files = []; $files[] = $strip . '.' . $ext; /** * Loop on 1 or 2 files and break on first found. * Add the content of the MD5SUM file located in the same folder to url to ensure cache browser refresh * This MD5SUM file must represent the signature of the folder content */ foreach ($files as $file) { $path = JPATH_ROOT . "/$file"; $found = static::addFileToBuffer($path, $ext, $debugMode); if (!empty($found)) { $includes[] = $found; break; } } } } } return $includes; } /** * Gets a URL, cleans the Joomla specific params and returns an object * * @param string $url The relative or absolute URL to use for the src attribute. * * @return object * @example { * url: 'string', * attributes: [ * width: integer, * height: integer, * ] * } * * @since 4.0.0 */ public static function cleanImageURL($url) { $obj = new \stdClass(); $obj->attributes = [ 'width' => 0, 'height' => 0, ]; if ($url === null) { $url = ''; } if (!strpos($url, '?')) { $obj->url = $url; return $obj; } $mediaUri = new Uri($url); // Old image URL format if ($mediaUri->hasVar('joomla_image_height')) { $height = (int) $mediaUri->getVar('joomla_image_height'); $width = (int) $mediaUri->getVar('joomla_image_width'); $mediaUri->delVar('joomla_image_height'); $mediaUri->delVar('joomla_image_width'); } else { // New Image URL format $fragmentUri = new Uri($mediaUri->getFragment()); $width = (int) $fragmentUri->getVar('width', 0); $height = (int) $fragmentUri->getVar('height', 0); } if ($width > 0) { $obj->attributes['width'] = $width; } if ($height > 0) { $obj->attributes['height'] = $height; } $mediaUri->setFragment(''); $obj->url = $mediaUri->toString(); return $obj; } /** * Write a `<img>` element * * @param string $file The relative or absolute URL to use for the src attribute. * @param string $alt The alt text. * @param array|string $attribs Attributes to be added to the `<img>` element * @param boolean $relative Flag if the path to the file is relative to the /media folder (and searches in template). * @param integer $returnPath Defines the return value for the method: * -1: Returns a `<img>` tag without looking for relative files * 0: Returns a `<img>` tag while searching for relative files * 1: Returns the file path to the image while searching for relative files * * @return string|null HTML markup for the image, relative path to the image, or null if path is to be returned but image is not found * * @since 1.5 */ public static function image($file, $alt, $attribs = null, $relative = false, $returnPath = 0) { // Ensure is an integer $returnPath = (int) $returnPath; // The path of the file $path = $file; // The arguments of the file path $arguments = ''; // Get the arguments positions $pos1 = strpos($file, '?'); $pos2 = strpos($file, '#'); // Check if there are arguments if ($pos1 !== false || $pos2 !== false) { // Get the path only $path = substr($file, 0, min($pos1, $pos2)); // Determine the arguments is mostly the part behind the # $arguments = str_replace($path, '', $file); } // Get the relative file name when requested if ($returnPath !== -1) { // Search for relative file names $includes = static::includeRelativeFiles('images', $path, $relative, false, false); // Grab the first found path and if none exists default to null $path = \count($includes) ? $includes[0] : null; } // Compile the file name $file = ($path === null ? null : $path . $arguments); // If only the file is required, return here if ($returnPath === 1) { return $file; } // Ensure we have a valid default for concatenating if ($attribs === null || $attribs === false) { $attribs = []; } // When it is a string, we need convert it to an array if (is_string($attribs)) { $attributes = []; // Go through each argument foreach (explode(' ', $attribs) as $attribute) { // When an argument without a value, default to an empty string if (strpos($attribute, '=') === false) { $attributes[$attribute] = ''; continue; } // Set the attribute list($key, $value) = explode('=', $attribute); $attributes[$key] = trim($value, '"'); } // Add the attributes from the string to the original attributes $attribs = $attributes; } // Fill the attributes with the file and alt text $attribs['src'] = $file; $attribs['alt'] = $alt; // Render the layout with the attributes return LayoutHelper::render('joomla.html.image', $attribs); } /** * Write a `<link>` element to load a CSS file * * @param string $file Path to file * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9') * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1) * * @return array|string|null nothing if $returnPath is false, null, path or array of path if specific CSS browser files were detected * * @see Browser * @since 1.5 */ public static function stylesheet($file, $options = [], $attribs = []) { $options['relative'] = $options['relative'] ?? false; $options['pathOnly'] = $options['pathOnly'] ?? false; $options['detectBrowser'] = $options['detectBrowser'] ?? false; $options['detectDebug'] = $options['detectDebug'] ?? true; $includes = static::includeRelativeFiles('css', $file, $options['relative'], $options['detectBrowser'], $options['detectDebug']); // If only path is required if ($options['pathOnly']) { if (\count($includes) === 0) { return; } if (\count($includes) === 1) { return $includes[0]; } return $includes; } // If inclusion is required $document = Factory::getApplication()->getDocument(); foreach ($includes as $include) { // If there is already a version hash in the script reference (by using deprecated MD5SUM). if ($pos = strpos($include, '?') !== false) { $options['version'] = substr($include, $pos + 1); } $document->addStyleSheet($include, $options, $attribs); } } /** * Write a `<script>` element to load a JavaScript file * * @param string $file Path to file. * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9') * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1) * * @return array|string|null Nothing if $returnPath is false, null, path or array of path if specific JavaScript browser files were detected * * @see HTMLHelper::stylesheet() * @since 1.5 */ public static function script($file, $options = [], $attribs = []) { $options['relative'] = $options['relative'] ?? false; $options['pathOnly'] = $options['pathOnly'] ?? false; $options['detectBrowser'] = $options['detectBrowser'] ?? false; $options['detectDebug'] = $options['detectDebug'] ?? true; $includes = static::includeRelativeFiles('js', $file, $options['relative'], $options['detectBrowser'], $options['detectDebug']); // If only path is required if ($options['pathOnly']) { if (\count($includes) === 0) { return; } if (\count($includes) === 1) { return $includes[0]; } return $includes; } // If inclusion is required $document = Factory::getApplication()->getDocument(); foreach ($includes as $include) { // If there is already a version hash in the script reference (by using deprecated MD5SUM). if ($pos = strpos($include, '?') !== false) { $options['version'] = substr($include, $pos + 1); } $document->addScript($include, $options, $attribs); } } /** * Set format related options. * * Updates the formatOptions array with all valid values in the passed array. * * @param array $options Option key/value pairs. * * @return void * * @see HTMLHelper::$formatOptions * @since 1.5 */ public static function setFormatOptions($options) { foreach ($options as $key => $val) { if (isset(static::$formatOptions[$key])) { static::$formatOptions[$key] = $val; } } } /** * Returns formatted date according to a given format and time zone. * * @param string $input String in a format accepted by date(), defaults to "now". * @param string $format The date format specification string (see {@link PHP_MANUAL#date}). * @param mixed $tz Time zone to be used for the date. Special cases: boolean true for user * setting, boolean false for server setting. * @param boolean $gregorian True to use Gregorian calendar. * * @return string A date translated by the given format and time zone. * * @see strftime * @since 1.5 */ public static function date($input = 'now', $format = null, $tz = true, $gregorian = false) { $app = Factory::getApplication(); // UTC date converted to user time zone. if ($tz === true) { // Get a date object based on UTC. $date = Factory::getDate($input, 'UTC'); // Set the correct time zone based on the user configuration. $date->setTimezone($app->getIdentity()->getTimezone()); } elseif ($tz === false) { // UTC date converted to server time zone. // Get a date object based on UTC. $date = Factory::getDate($input, 'UTC'); // Set the correct time zone based on the server configuration. $date->setTimezone(new \DateTimeZone($app->get('offset'))); } elseif ($tz === null) { // No date conversion. $date = Factory::getDate($input); } else { // UTC date converted to given time zone. // Get a date object based on UTC. $date = Factory::getDate($input, 'UTC'); // Set the correct time zone based on the server configuration. $date->setTimezone(new \DateTimeZone($tz)); } // If no format is given use the default locale based format. if (!$format) { $format = Text::_('DATE_FORMAT_LC1'); } elseif (Factory::getLanguage()->hasKey($format)) { // $format is an existing language key $format = Text::_($format); } if ($gregorian) { return $date->format($format, true); } return $date->calendar($format, true); } /** * Creates a tooltip with an image as button * * @param string $tooltip The tip string. * @param mixed $title The title of the tooltip or an associative array with keys contained in * {'title','image','text','href','alt'} and values corresponding to parameters of the same name. * @param string $image The image for the tip, if no text is provided. * @param string $text The text for the tip. * @param string $href A URL that will be used to create the link. * @param string $alt The alt attribute for img tag. * @param string $class CSS class for the tool tip. * * @return string * * @since 1.5 */ public static function tooltip($tooltip, $title = '', $image = 'tooltip.png', $text = '', $href = '', $alt = 'Tooltip', $class = 'hasTooltip') { if (\is_array($title)) { foreach (['image', 'text', 'href', 'alt', 'class'] as $param) { if (isset($title[$param])) { $$param = $title[$param]; } } if (isset($title['title'])) { $title = $title['title']; } else { $title = ''; } } if (!$text) { $alt = htmlspecialchars($alt, ENT_COMPAT, 'UTF-8'); $text = static::image($image, $alt, null, true); } if ($href) { $tip = '<a href="' . $href . '">' . $text . '</a>'; } else { $tip = $text; } if ($class === 'hasTip') { // Still using MooTools tooltips! $tooltip = htmlspecialchars($tooltip, ENT_COMPAT, 'UTF-8'); if ($title) { $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $tooltip = $title . '::' . $tooltip; } } else { $tooltip = self::tooltipText($title, $tooltip, 0); } return '<span class="' . $class . '" title="' . $tooltip . '">' . $tip . '</span>'; } /** * Converts a double colon separated string or 2 separate strings to a string ready for bootstrap tooltips * * @param string $title The title of the tooltip (or combined '::' separated string). * @param string $content The content to tooltip. * @param boolean $translate If true will pass texts through Text. * @param boolean $escape If true will pass texts through htmlspecialchars. * * @return string The tooltip string * * @since 3.1.2 */ public static function tooltipText($title = '', $content = '', $translate = true, $escape = true) { // Initialise return value. $result = ''; // Don't process empty strings if ($content !== '' || $title !== '') { // Split title into title and content if the title contains '::' (old Mootools format). if ($content === '' && !(strpos($title, '::') === false)) { list($title, $content) = explode('::', $title, 2); } // Pass texts through Text if required. if ($translate) { $title = Text::_($title); $content = Text::_($content); } // Use only the content if no title is given. if ($title === '') { $result = $content; } elseif ($title === $content) { // Use only the title, if title and text are the same. $result = '<strong>' . $title . '</strong>'; } elseif ($content !== '') { // Use a formatted string combining the title and content. $result = '<strong>' . $title . '</strong><br>' . $content; } else { $result = $title; } // Escape everything, if required. if ($escape) { $result = htmlspecialchars($result); } } return $result; } /** * Displays a calendar control field * * @param string $value The date value * @param string $name The name of the text field * @param string $id The id of the text field * @param string $format The date format * @param mixed $attribs Additional HTML attributes * The array can have the following keys: * readonly Sets the readonly parameter for the input tag * disabled Sets the disabled parameter for the input tag * autofocus Sets the autofocus parameter for the input tag * autocomplete Sets the autocomplete parameter for the input tag * filter Sets the filter for the input tag * * @return string HTML markup for a calendar field * * @since 1.5 * */ public static function calendar($value, $name, $id, $format = '%Y-%m-%d', $attribs = []) { $app = Factory::getApplication(); $lang = $app->getLanguage(); $tag = $lang->getTag(); $calendar = $lang->getCalendar(); $direction = strtolower($app->getDocument()->getDirection()); // Get the appropriate file for the current language date helper $helperPath = 'system/fields/calendar-locales/date/gregorian/date-helper.min.js'; if ($calendar && is_dir(JPATH_ROOT . '/media/system/js/fields/calendar-locales/date/' . strtolower($calendar))) { $helperPath = 'system/fields/calendar-locales/date/' . strtolower($calendar) . '/date-helper.min.js'; } $readonly = isset($attribs['readonly']) && $attribs['readonly'] === 'readonly'; $disabled = isset($attribs['disabled']) && $attribs['disabled'] === 'disabled'; $autocomplete = isset($attribs['autocomplete']) && $attribs['autocomplete'] === ''; $autofocus = isset($attribs['autofocus']) && $attribs['autofocus'] === ''; $required = isset($attribs['required']) && $attribs['required'] === ''; $filter = isset($attribs['filter']) && $attribs['filter'] === ''; $todayBtn = $attribs['todayBtn'] ?? true; $weekNumbers = $attribs['weekNumbers'] ?? true; $showTime = $attribs['showTime'] ?? false; $fillTable = $attribs['fillTable'] ?? true; $timeFormat = $attribs['timeFormat'] ?? 24; $singleHeader = $attribs['singleHeader'] ?? false; $hint = $attribs['placeholder'] ?? ''; $class = $attribs['class'] ?? ''; $onchange = $attribs['onChange'] ?? ''; $minYear = $attribs['minYear'] ?? null; $maxYear = $attribs['maxYear'] ?? null; $showTime = ($showTime) ? "1" : "0"; $todayBtn = ($todayBtn) ? "1" : "0"; $weekNumbers = ($weekNumbers) ? "1" : "0"; $fillTable = ($fillTable) ? "1" : "0"; $singleHeader = ($singleHeader) ? "1" : "0"; // Format value when not nulldate ('0000-00-00 00:00:00'), otherwise blank it as it would result in 1970-01-01. if ($value && $value !== Factory::getDbo()->getNullDate() && strtotime($value) !== false) { $tz = date_default_timezone_get(); date_default_timezone_set('UTC'); /** * Try to convert strftime format to date format, if success, use DateTimeImmutable to format * the passed datetime to avoid deprecated warnings on PHP 8.1. We only support converting most * common used format here. */ $dateFormat = self::strftimeFormatToDateFormat($format); if ($dateFormat !== false) { $date = \DateTimeImmutable::createFromFormat('U', strtotime($value)); $inputValue = $date->format($dateFormat); } else { $inputValue = strftime($format, strtotime($value)); } date_default_timezone_set($tz); } else { $inputValue = ''; } $data = [ 'id' => $id, 'name' => $name, 'class' => $class, 'value' => $inputValue, 'format' => $format, 'filter' => $filter, 'required' => $required, 'readonly' => $readonly, 'disabled' => $disabled, 'hint' => $hint, 'autofocus' => $autofocus, 'autocomplete' => $autocomplete, 'todaybutton' => $todayBtn, 'weeknumbers' => $weekNumbers, 'showtime' => $showTime, 'filltable' => $fillTable, 'timeformat' => $timeFormat, 'singleheader' => $singleHeader, 'tag' => $tag, 'helperPath' => $helperPath, 'direction' => $direction, 'onchange' => $onchange, 'minYear' => $minYear, 'maxYear' => $maxYear, 'dataAttribute' => '', 'dataAttributes' => '', 'calendar' => $calendar, 'firstday' => $lang->getFirstDay(), 'weekend' => explode(',', $lang->getWeekEnd()), ]; return LayoutHelper::render('joomla.form.field.calendar', $data, null, null); } /** * Add a directory where HTMLHelper should search for helpers. You may * either pass a string or an array of directories. * * @param string $path A path to search. * * @return array An array with directory elements * * @since 1.5 * @deprecated 4.0 will be removed in 6.0 * Use the service registry instead */ public static function addIncludePath($path = '') { @trigger_error( 'Support for registering lookup paths is deprecated and will be removed in Joomla 5.0, use the service registry instead', E_USER_DEPRECATED ); // Loop through the path directories foreach ((array) $path as $dir) { if (!empty($dir) && !\in_array($dir, static::$includePaths)) { array_unshift(static::$includePaths, Path::clean($dir)); } } return static::$includePaths; } /** * Method that searches if file exists in given path and returns the relative path. If a minified version exists it will be preferred. * * @param string $path The actual path of the file * @param string $ext The extension of the file * @param boolean $debugMode Signifies if debug is enabled * * @return string The relative path of the file * * @since 4.0.0 */ protected static function addFileToBuffer($path = '', $ext = '', $debugMode = false) { $position = strrpos($path, '.min.'); // We are handling a name.min.ext file: if ($position !== false) { $minifiedPath = $path; $nonMinifiedPath = substr_replace($path, '', $position, 4); if ($debugMode) { return self::checkFileOrder($minifiedPath, $nonMinifiedPath); } return self::checkFileOrder($nonMinifiedPath, $minifiedPath); } $minifiedPath = pathinfo($path, PATHINFO_DIRNAME) . '/' . pathinfo($path, PATHINFO_FILENAME) . '.min.' . $ext; if ($debugMode) { return self::checkFileOrder($minifiedPath, $path); } return self::checkFileOrder($path, $minifiedPath); } /** * Method that takes a file path and converts it to a relative path * * @param string $path The actual path of the file * * @return string The relative path of the file * * @since 4.0.0 */ protected static function convertToRelativePath($path) { $relativeFilePath = Uri::root(true) . str_replace(JPATH_ROOT, '', $path); // On windows devices we need to replace "\" with "/" otherwise some browsers will not load the asset return str_replace(DIRECTORY_SEPARATOR, '/', $relativeFilePath); } /** * Method that takes two paths and checks if the files exist with different order * * @param string $first the path of the minified file * @param string $second the path of the non minified file * * @return string * * @since 4.0.0 */ private static function checkFileOrder($first, $second) { if (is_file($second)) { return static::convertToRelativePath($second); } if (is_file($first)) { return static::convertToRelativePath($first); } return ''; } /** * Convert most popular strftime format to php date format as strftime is deprecated and we have * to be able to provide same backward compatibility with existing format strings. * * @param $strftimeformat string The format compatible with strftime. * * @return mixed The format compatible with PHP's Date functions if success, false otherwise * * @since 4.2.9 */ public static function strftimeFormatToDateFormat(string $strftimeformat) { $format = str_replace( [ '%Y', '%m', '%d', '%H', '%M', '%S', ], [ 'Y', 'm', 'd', 'H', 'i', 's', ], $strftimeformat ); /** * If there is % character left after replacing, that mean one of unsupported format is used * the conversion false */ if (strpos($format, '%') !== false) { return false; } return $format; } } PK0A#]�����#src/HTML/HTMLRegistryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a HTML Registry aware class. * * @since 4.0.0 */ trait HTMLRegistryAwareTrait { /** * The registry * * @var Registry * @since 4.0.0 */ private $registry; /** * Get the registry. * * @return Registry * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the registry has not been set. */ public function getRegistry() { if ($this->registry) { return $this->registry; } throw new \UnexpectedValueException('HTML registry not set in ' . __CLASS__); } /** * Set the registry to use. * * @param Registry $registry The registry * * @return void * * @since 4.0.0 */ public function setRegistry(Registry $registry = null) { $this->registry = $registry; } } PK0A#]��� � src/HTML/Helpers/Content.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Date\Date; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class to fire onContentPrepare for non-article based content. * * @since 1.5 */ abstract class Content { /** * Fire onContentPrepare for content that isn't part of an article. * * @param string $text The content to be transformed. * @param array $params The content params. * @param string $context The context of the content to be transformed. * * @return string The content after transformation. * * @since 1.5 */ public static function prepare($text, $params = null, $context = 'text') { if ($params === null) { $params = new CMSObject(); } $article = new \stdClass(); $article->text = $text; PluginHelper::importPlugin('content'); Factory::getApplication()->triggerEvent('onContentPrepare', [$context, &$article, &$params, 0]); return $article->text; } /** * Returns an array of months. * * @param Registry $state The state object. * * @return array * * @since 3.9.0 */ public static function months($state) { /** @var \Joomla\Component\Content\Administrator\Extension\ContentComponent $contentComponent */ $contentComponent = Factory::getApplication()->bootComponent('com_content'); /** @var \Joomla\Component\Content\Site\Model\ArticlesModel $model */ $model = $contentComponent->getMVCFactory() ->createModel('Articles', 'Site', ['ignore_request' => true]); foreach ($state as $key => $value) { $model->setState($key, $value); } $model->setState('filter.category_id', $state->get('category.id')); $model->setState('list.start', 0); $model->setState('list.limit', -1); $model->setState('list.direction', 'asc'); $model->setState('list.filter', ''); $items = []; foreach ($model->countItemsByMonth() as $item) { $date = new Date($item->d); $items[] = HTMLHelper::_('select.option', $item->d, $date->format('F Y') . ' [' . $item->c . ']'); } return $items; } } PK0A#]|g���src/HTML/Helpers/Category.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for categories * * @since 1.5 */ abstract class Category { /** * Cached array of the category items. * * @var array * @since 1.5 */ protected static $items = []; /** * Returns an array of categories for the given extension. * * @param string $extension The extension option e.g. com_something. * @param array $config An array of configuration options. By default, only * published and unpublished categories are returned. * * @return array * * @since 1.5 */ public static function options($extension, $config = ['filter.published' => [0, 1]]) { $hash = md5($extension . '.' . serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $db = Factory::getDbo(); $user = Factory::getUser(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.level'), $db->quoteName('a.language'), ] ) ->from($db->quoteName('#__categories', 'a')) ->where($db->quoteName('a.parent_id') . ' > 0'); // Filter on extension. $query->where($db->quoteName('a.extension') . ' = :extension') ->bind(':extension', $extension); // Filter on user access level if (!$user->authorise('core.admin')) { $query->whereIn($db->quoteName('a.access'), $user->getAuthorisedViewLevels()); } // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $config['filter.published'], ParameterType::INTEGER); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->whereIn($db->quoteName('a.published'), $config['filter.published']); } } // Filter on the language if (isset($config['filter.language'])) { if (is_string($config['filter.language'])) { $query->where($db->quoteName('a.language') . ' = :language') ->bind(':language', $config['filter.language']); } elseif (is_array($config['filter.language'])) { $query->whereIn($db->quoteName('a.language'), $config['filter.language'], ParameterType::STRING); } } // Filter on the access if (isset($config['filter.access'])) { if (is_numeric($config['filter.access'])) { $query->where($db->quoteName('a.access') . ' = :access') ->bind(':access', $config['filter_access'], ParameterType::INTEGER); } elseif (is_array($config['filter.access'])) { $config['filter.access'] = ArrayHelper::toInteger($config['filter.access']); $query->whereIn($db->quoteName('a.access'), $config['filter.access']); } } $query->order($db->quoteName('a.lft')); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = []; foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; if ($item->language !== '*') { $item->title .= ' (' . $item->language . ')'; } static::$items[$hash][] = HTMLHelper::_('select.option', $item->id, $item->title); } } return static::$items[$hash]; } /** * Returns an array of categories for the given extension. * * @param string $extension The extension option. * @param array $config An array of configuration options. By default, only published and unpublished categories are returned. * * @return array Categories for the extension * * @since 1.6 */ public static function categories($extension, $config = ['filter.published' => [0, 1]]) { $hash = md5($extension . '.' . serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $user = Factory::getUser(); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.level'), $db->quoteName('a.parent_id'), $db->quoteName('a.language'), ] ) ->from($db->quoteName('#__categories', 'a')) ->where($db->quoteName('a.parent_id') . ' > 0'); // Filter on extension. $query->where($db->quoteName('extension') . ' = :extension') ->bind(':extension', $extension); // Filter on user level. if (!$user->authorise('core.admin')) { $query->whereIn($db->quoteName('a.access'), $user->getAuthorisedViewLevels()); } // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $config['filter.published'], ParameterType::INTEGER); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->whereIn($db->quoteName('a.published'), $config['filter.published']); } } $query->order($db->quoteName('a.lft')); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = []; foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; if ($item->language !== '*') { $item->title .= ' (' . $item->language . ')'; } static::$items[$hash][] = HTMLHelper::_('select.option', $item->id, $item->title); } // Special "Add to root" option: static::$items[$hash][] = HTMLHelper::_('select.option', '1', Text::_('JLIB_HTML_ADD_TO_ROOT')); } return static::$items[$hash]; } } PK0A#]��5&�&�&src/HTML/Helpers/Grid.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for creating HTML Grids * * @since 1.5 */ abstract class Grid { /** * Method to sort a column in a grid * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param string $selected The selected ordering * @param string $task An optional task override * @param string $newDirection An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $form An optional form selector * * @return string * * @since 1.5 */ public static function sort($title, $order, $direction = 'asc', $selected = '', $task = null, $newDirection = 'asc', $tip = '', $form = null) { HTMLHelper::_('bootstrap.tooltip', '.hasTooltip'); $direction = strtolower($direction); $icon = ['arrow-up-3', 'arrow-down-3']; $index = (int) ($direction === 'desc'); if ($order != $selected) { $direction = $newDirection; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } if ($form) { $form = ', document.getElementById(\'' . $form . '\')'; } $html = '<a href="#" onclick="Joomla.tableOrdering(\'' . $order . '\',\'' . $direction . '\',\'' . $task . '\'' . $form . ');return false;"' . ' class="hasTooltip" title="' . htmlspecialchars(Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) . '" data-bs-placement="top">'; if (isset($title['0']) && $title['0'] === '<') { $html .= $title; } else { $html .= Text::_($title); } if ($order == $selected) { $html .= '<span class="icon-' . $icon[$index] . '"></span>'; } $html .= '</a>'; return $html; } /** * Method to check all checkboxes in a grid * * @param string $name The name of the form element * @param string $action The action to perform on clicking the checkbox * * @return string * * @since 3.1.2 */ public static function checkall($name = 'checkall-toggle', $action = 'Joomla.checkAll(this)') { HTMLHelper::_('behavior.core'); return '<input class="form-check-input" autocomplete="off" type="checkbox" name="' . $name . '" value="" title="' . Text::_('JGLOBAL_CHECK_ALL') . '" onclick="' . $action . '">'; } /** * Method to create a checkbox for a grid row. * * @param integer $rowNum The row index * @param integer $recId The record id * @param boolean $checkedOut True if item is checked out * @param string $name The name of the form element * @param string $stub The name of stub identifier * @param string $title The name of the item * @param string $formId An optional form selector. * * @return mixed String of html with a checkbox if item is not checked out, null if checked out. * * @since 1.5 */ public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb', $title = '', $formId = null) { if ($formId !== null) { return $checkedOut ? '' : '<label for="' . $stub . $rowNum . '"><span class="visually-hidden">' . Text::_('JSELECT') . ' ' . htmlspecialchars($title, ENT_COMPAT, 'UTF-8') . '</span></label>' . '<input class="form-check-input" type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId . '" onclick="Joomla.isChecked(this.checked, \'' . $formId . '\');">'; } return $checkedOut ? '' : '<label for="' . $stub . $rowNum . '"><span class="visually-hidden">' . Text::_('JSELECT') . ' ' . htmlspecialchars($title, ENT_COMPAT, 'UTF-8') . '</span></label>' . '<input class="form-check-input" autocomplete="off" type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId . '" onclick="Joomla.isChecked(this.checked);">'; } /** * Displays a checked out icon. * * @param object $row A data object (must contain checked out as a property). * @param integer $i The index of the row. * @param string $identifier The property name of the primary key or index of the row. * * @return string * * @since 1.5 */ public static function checkedOut(&$row, $i, $identifier = 'id') { $user = Factory::getUser(); $userid = $user->get('id'); if ($row instanceof Table) { $result = $row->isCheckedOut($userid); } else { $result = false; } if ($result) { return static::_checkedOut($row); } else { if ($identifier === 'id') { return HTMLHelper::_('grid.id', $i, $row->$identifier); } else { return HTMLHelper::_('grid.id', $i, $row->$identifier, $result, $identifier); } } } /** * Method to create a clickable icon to change the state of an item * * @param mixed $value Either the scalar value or an object (for backward compatibility, deprecated) * @param integer $i The index * @param string $img1 Image for a positive or on value * @param string $img0 Image for the empty or off value * @param string $prefix An optional prefix for the task * * @return string * * @since 1.5 */ public static function published($value, $i, $img1 = 'tick.png', $img0 = 'publish_x.png', $prefix = '') { if (is_object($value)) { $value = $value->published; } $img = $value ? $img1 : $img0; $task = $value ? 'unpublish' : 'publish'; $alt = $value ? Text::_('JPUBLISHED') : Text::_('JUNPUBLISHED'); $action = $value ? Text::_('JLIB_HTML_UNPUBLISH_ITEM') : Text::_('JLIB_HTML_PUBLISH_ITEM'); return '<a href="#" onclick="return Joomla.listItemTask(\'cb' . $i . '\',\'' . $prefix . $task . '\')" title="' . $action . '">' . HTMLHelper::_('image', 'admin/' . $img, $alt, null, true) . '</a>'; } /** * Method to create a select list of states for filtering * By default the filter shows only published and unpublished items * * @param string $filterState The initial filter state * @param string $published The Text string for published * @param string $unpublished The Text string for Unpublished * @param string $archived The Text string for Archived * @param string $trashed The Text string for Trashed * * @return string * * @since 1.5 */ public static function state($filterState = '*', $published = 'JPUBLISHED', $unpublished = 'JUNPUBLISHED', $archived = null, $trashed = null) { $state = ['' => '- ' . Text::_('JLIB_HTML_SELECT_STATE') . ' -', 'P' => Text::_($published), 'U' => Text::_($unpublished)]; if ($archived) { $state['A'] = Text::_($archived); } if ($trashed) { $state['T'] = Text::_($trashed); } return HTMLHelper::_( 'select.genericlist', $state, 'filter_state', [ 'list.attr' => 'class="form-select" size="1" onchange="Joomla.submitform();"', 'list.select' => $filterState, 'option.key' => null, ] ); } /** * Method to create an icon for saving a new ordering in a grid * * @param array $rows The array of rows of rows * @param string $image The image [UNUSED] * @param string $task The task to use, defaults to save order * * @return string * * @since 1.5 */ public static function order($rows, $image = 'filesave.png', $task = 'saveorder') { return '<a href="javascript:saveorder(' . (count($rows) - 1) . ', \'' . $task . '\')" rel="tooltip" class="saveorder btn btn-sm btn-secondary float-end" title="' . Text::_('JLIB_HTML_SAVE_ORDER') . '"><span class="icon-sort"></span></a>'; } /** * Method to create a checked out icon with optional overlib in a grid. * * @param object $row The row object * @param boolean $overlib True if an overlib with checkout information should be created. * * @return string HTMl for the icon and overlib * * @since 1.5 */ protected static function _checkedOut(&$row, $overlib = true) { $hover = ''; if ($overlib) { $date = HTMLHelper::_('date', $row->checked_out_time, Text::_('DATE_FORMAT_LC1')); $time = HTMLHelper::_('date', $row->checked_out_time, 'H:i'); $hover = '<span class="editlinktip hasTooltip" title="' . HTMLHelper::_('tooltipText', 'JLIB_HTML_CHECKED_OUT', $row->editor) . '<br>' . $date . '<br>' . $time . '">'; } return $hover . HTMLHelper::_('image', 'admin/checked_out.png', null, null, true) . '</span>'; } } PK0A#]�� :src/HTML/Helpers/Jquery.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for jQuery JavaScript behaviors * * @since 3.0 */ abstract class Jquery { /** * Array containing information for loaded files * * @var array * @since 3.0 */ protected static $loaded = []; /** * Method to load the jQuery JavaScript framework into the document head * * If debugging mode is on an uncompressed version of jQuery is included for easier debugging. * * @param boolean $noConflict True to load jQuery in noConflict mode [optional] * @param mixed $debug Is debugging mode on? [optional] * @param boolean $migrate True to enable the jQuery Migrate plugin * * @return void * * @since 3.0 * * @deprecated 4.0 will be removed in 6.0 * Use webasset manager instead * Example: * $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); * $wa->useScript('jquery'); * $wa->useScript('jquery-noconflict'); * $wa->useScript('jquery-migrate'); */ public static function framework($noConflict = true, $debug = null, $migrate = false) { $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useScript('jquery'); // Check if we are loading in noConflict if ($noConflict) { $wa->useScript('jquery-noconflict'); } // Check if we are loading Migrate if ($migrate) { $wa->useScript('jquery-migrate'); } } /** * Auto set CSRF token to ajaxSetup so all jQuery ajax call will contains CSRF token. * * @param string $name The CSRF meta tag name. * * @return void * * @throws \InvalidArgumentException * * @since 3.8.0 */ public static function token($name = 'csrf.token') { // Only load once if (!empty(static::$loaded[__METHOD__][$name])) { return; } static::framework(); HTMLHelper::_('form.csrf', $name); $doc = Factory::getDocument(); $doc->addScriptDeclaration( <<<JS ;(function ($) { $.ajaxSetup({ headers: { 'X-CSRF-Token': Joomla.getOptions('$name') } }); })(jQuery); JS ); static::$loaded[__METHOD__][$name] = true; } } PK0A#]v� ��u�usrc/HTML/Helpers/Select.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for creating HTML select lists * * @since 1.5 */ abstract class Select { /** * Default values for options. Organized by option group. * * @var array * @since 1.5 */ protected static $optionDefaults = [ 'option' => [ 'option.attr' => null, 'option.disable' => 'disable', 'option.id' => null, 'option.key' => 'value', 'option.key.toHtml' => true, 'option.label' => null, 'option.label.toHtml' => true, 'option.text' => 'text', 'option.text.toHtml' => true, 'option.class' => 'class', 'option.onclick' => 'onclick', ], ]; /** * Generates a yes/no radio list. * * @param string $name The value of the HTML name attribute * @param array $attribs Additional HTML attributes for the `<select>` tag * @param string $selected The key that is selected * @param string $yes Language key for Yes * @param string $no Language key for no * @param mixed $id The id for the field or false for no id * * @return string HTML for the radio list * * @since 1.5 * @see \Joomla\CMS\Form\Field\RadioField */ public static function booleanlist($name, $attribs = [], $selected = null, $yes = 'JYES', $no = 'JNO', $id = false) { $arr = [HTMLHelper::_('select.option', '0', Text::_($no)), HTMLHelper::_('select.option', '1', Text::_($yes))]; return HTMLHelper::_('select.radiolist', $arr, $name, $attribs, 'value', 'text', (int) $selected, $id); } /** * Generates an HTML selection list. * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see HTMLHelper::$formatOptions}. * Selection options, see {@see JHtmlSelect::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * * @since 1.5 */ public static function genericlist( $data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false ) { // Set default options $options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'id' => false]); if (is_array($attribs) && func_num_args() === 3) { // Assume we have an options array $options = array_merge($options, $attribs); } else { // Get options from the parameters $options['id'] = $idtag; $options['list.attr'] = $attribs; $options['list.translate'] = $translate; $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); // If the selectbox contains "form-select-color-state" then load the JS file if (strpos($attribs, 'form-select-color-state') !== false) { Factory::getDocument()->getWebAssetManager() ->registerScript( 'webcomponent.select-colour-es5', 'system/fields/select-colour-es5.min.js', ['dependencies' => ['wcpolyfill']], ['defer' => true, 'nomodule' => true] ) ->registerAndUseScript( 'webcomponent.select-colour', 'system/fields/select-colour.min.js', ['dependencies' => ['webcomponent.select-colour-es5']], ['type' => 'module'] ); } $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'] . static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Generates a grouped HTML selection list from nested arrays. * * @param array $data An array of groups, each of which is an array of options. * @param string $name The value of the HTML name attribute * @param array $options Options, an array of key/value pairs. Valid options are: * Format options, {@see HTMLHelper::$formatOptions}. * Selection options. See {@see JHtmlSelect::options()}. * group.id: The property in each group to use as the group id * attribute. Defaults to none. * group.label: The property in each group to use as the group * label. Defaults to "text". If set to null, the data array index key is * used. * group.items: The property in each group to use as the array of * items in the group. Defaults to "items". If set to null, group.id and * group. label are forced to null and the data element is assumed to be a * list of selections. * id: Value to use as the select element id attribute. Defaults to * the same as the name. * list.attr: Attributes for the select element. Can be a string or * an array of key/value pairs. Defaults to none. * list.select: either the value of one selected option or an array * of selected options. Default: none. * list.translate: Boolean. If set, text and labels are translated via * Text::_(). * * @return string HTML for the select list * * @since 1.5 * @throws \RuntimeException If a group has contents that cannot be processed. */ public static function groupedlist($data, $name, $options = []) { // Set default options and overwrite with anything passed in $options = array_merge( HTMLHelper::$formatOptions, ['format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false], $options ); // Apply option rules if ($options['group.items'] === null) { $options['group.label'] = null; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); // Disable groups in the options. $options['groups'] = false; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']; $groupIndent = str_repeat($options['format.indent'], $options['format.depth']++); foreach ($data as $dataKey => $group) { $label = $dataKey; $id = ''; $noGroup = is_int($dataKey); if ($options['group.items'] == null) { // Sub-list is an associative array $subList = $group; } elseif (is_array($group)) { // Sub-list is in an element of an array. $subList = $group[$options['group.items']]; if (isset($group[$options['group.label']])) { $label = $group[$options['group.label']]; $noGroup = false; } if (isset($options['group.id']) && isset($group[$options['group.id']])) { $id = $group[$options['group.id']]; $noGroup = false; } } elseif (is_object($group)) { // Sub-list is in a property of an object $subList = $group->{$options['group.items']}; if (isset($group->{$options['group.label']})) { $label = $group->{$options['group.label']}; $noGroup = false; } if (isset($options['group.id']) && isset($group->{$options['group.id']})) { $id = $group->{$options['group.id']}; $noGroup = false; } } else { throw new \RuntimeException('Invalid group contents.', 1); } if ($noGroup) { $html .= static::options($subList, $options); } else { $html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="' . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol'] . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol']; } } $html .= $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Generates a selection list of integers. * * @param integer $start The start integer * @param integer $end The end integer * @param integer $inc The increment * @param string $name The value of the HTML name attribute * @param mixed $attribs Additional HTML attributes for the `<select>` tag, an array of * attributes, or an array of options. Treated as options if it is the last * argument passed. * @param mixed $selected The key that is selected * @param string $format The printf format to be applied to the number * * @return string HTML for the select list * * @since 1.5 */ public static function integerlist($start, $end, $inc, $name, $attribs = null, $selected = null, $format = '') { // Set default options $options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'option.format' => '', 'id' => false]); if (is_array($attribs) && func_num_args() === 5) { // Assume we have an options array $options = array_merge($options, $attribs); // Extract the format and remove it from downstream options $format = $options['option.format']; unset($options['option.format']); } else { // Get options from the parameters $options['list.attr'] = $attribs; $options['list.select'] = $selected; } $start = (int) $start; $end = (int) $end; $inc = (int) $inc; $data = []; for ($i = $start; $i <= $end; $i += $inc) { $data[$i] = $format ? sprintf($format, $i) : $i; } // Tell genericlist() to use array keys $options['option.key'] = null; return HTMLHelper::_('select.genericlist', $data, $name, $options); } /** * Create an object that represents an option in an option list. * * @param string $value The value of the option * @param string $text The text for the option * @param mixed $optKey If a string, the returned object property name for * the value. If an array, options. Valid options are: * attr: String|array. Additional attributes for this option. * Defaults to none. * disable: Boolean. If set, this option is disabled. * label: String. The value for the option label. * option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * option.disable: The property that will hold the disabled state. * Defaults to "disable". * option.key: The property that will hold the selection value. * Defaults to "value". * option.label: The property in each option array to use as the * selection label attribute. If a "label" option is provided, defaults to * "label", if no label is given, defaults to null (none). * option.text: The property that will hold the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The property that will hold the displayed text. This * parameter is ignored if an options array is passed. * @param boolean $disable Not used. * * @return \stdClass * * @since 1.5 */ public static function option($value, $text = '', $optKey = 'value', $optText = 'text', $disable = false) { $options = [ 'attr' => null, 'disable' => false, 'option.attr' => null, 'option.disable' => 'disable', 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', ]; if (is_array($optKey)) { // Merge in caller's options $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['disable'] = $disable; } $obj = new \stdClass(); $obj->{$options['option.key']} = $value; $obj->{$options['option.text']} = trim($text) ? $text : $value; /* * If a label is provided, save it. If no label is provided and there is * a label name, initialise to an empty string. */ $hasProperty = $options['option.label'] !== null; if (isset($options['label'])) { $labelProperty = $hasProperty ? $options['option.label'] : 'label'; $obj->$labelProperty = $options['label']; } elseif ($hasProperty) { $obj->{$options['option.label']} = ''; } // Set attributes only if there is a property and a value if ($options['attr'] !== null) { $obj->{$options['option.attr']} = $options['attr']; } // Set disable only if it has a property and a value if ($options['disable'] !== null) { $obj->{$options['option.disable']} = $options['disable']; } return $obj; } /** * Generates the option tags for an HTML select list (with no select tag * surrounding the options). * * @param array $arr An array of objects, arrays, or values. * @param mixed $optKey If a string, this is the name of the object variable for * the option value. If null, the index of the array of objects is used. If * an array, this is a set of options, as key/value pairs. Valid options are: * -Format options, {@see HTMLHelper::$formatOptions}. * -groups: Boolean. If set, looks for keys with the value * "<optgroup>" and synthesizes groups from them. Deprecated. Defaults * true for backwards compatibility. * -list.select: either the value of one selected option or an array * of selected options. Default: none. * -list.translate: Boolean. If set, text and labels are translated via * Text::_(). Default is false. * -option.id: The property in each option array to use as the * selection id attribute. Defaults to none. * -option.key: The property in each option array to use as the * selection value. Defaults to "value". If set to null, the index of the * option array is used. * -option.label: The property in each option array to use as the * selection label attribute. Defaults to null (none). * -option.text: The property in each option array to use as the * displayed text. Defaults to "text". If set to null, the option array is * assumed to be a list of displayable scalars. * -option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * -option.disable: The property that will hold the disabled state. * Defaults to "disable". * -option.key: The property that will hold the selection value. * Defaults to "value". * -option.text: The property that will hold the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string) * @param boolean $translate Translate the option values. * * @return string HTML for the select list * * @since 1.5 */ public static function options($arr, $optKey = 'value', $optText = 'text', $selected = null, $translate = false) { $options = array_merge( HTMLHelper::$formatOptions, static::$optionDefaults['option'], ['format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false] ); if (is_array($optKey)) { // Set default options and overwrite with anything passed in $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; $options['list.translate'] = $translate; } $html = ''; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']); foreach ($arr as $elementKey => &$element) { $attr = ''; $extra = ''; $label = ''; $id = ''; if (is_array($element)) { $key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']]; $text = $element[$options['option.text']]; if (isset($element[$options['option.attr']])) { $attr = $element[$options['option.attr']]; } if (isset($element[$options['option.id']])) { $id = $element[$options['option.id']]; } if (isset($element[$options['option.label']])) { $label = $element[$options['option.label']]; } if (isset($element[$options['option.disable']]) && $element[$options['option.disable']]) { $extra .= ' disabled="disabled"'; } } elseif (is_object($element)) { $key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']}; $text = $element->{$options['option.text']}; if (isset($element->{$options['option.attr']})) { $attr = $element->{$options['option.attr']}; } if (isset($element->{$options['option.id']})) { $id = $element->{$options['option.id']}; } if (isset($element->{$options['option.label']})) { $label = $element->{$options['option.label']}; } if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']}) { $extra .= ' disabled="disabled"'; } if (isset($element->{$options['option.class']}) && $element->{$options['option.class']}) { $extra .= ' class="' . $element->{$options['option.class']} . '"'; } if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']}) { $extra .= ' onclick="' . $element->{$options['option.onclick']} . '"'; } } else { // This is a simple associative array $key = $elementKey; $text = $element; } /* * The use of options that contain optgroup HTML elements was * somewhat hacked for J1.5. J1.6 introduces the grouplist() method * to handle this better. The old solution is retained through the * "groups" option, which defaults true in J1.6, but should be * deprecated at some point in the future. */ $key = (string) $key; if ($key === '<OPTGROUP>' && $options['groups']) { $html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? Text::_($text) : $text) . '">' . $options['format.eol']; $baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']); } elseif ($key === '</OPTGROUP>' && $options['groups']) { $baseIndent = str_repeat($options['format.indent'], --$options['format.depth']); $html .= $baseIndent . '</optgroup>' . $options['format.eol']; } else { // If no string after hyphen - take hyphen out $splitText = explode(' - ', $text, 2); $text = $splitText[0]; if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1])) { $text .= ' - ' . $splitText[1]; } if (!empty($label) && $options['list.translate']) { $label = Text::_($label); } if ($options['option.label.toHtml']) { $label = htmlentities($label); } if (is_array($attr)) { $attr = ArrayHelper::toString($attr); } else { $attr = trim($attr); } $extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra; if (is_array($options['list.select'])) { foreach ($options['list.select'] as $val) { $key2 = is_object($val) ? $val->{$options['option.key']} : $val; if ($key == $key2) { $extra .= ' selected="selected"'; break; } } } elseif ((string) $key === (string) $options['list.select']) { $extra .= ' selected="selected"'; } if ($options['list.translate']) { $text = Text::_($text); } // Generate the option, encoding as required $html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"' . $extra . '>'; $html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text; $html .= '</option>' . $options['format.eol']; } } return $html; } /** * Generates an HTML radio list. * * @param array $data An array of objects * @param string $name The value of the HTML name attribute * @param string $attribs Additional HTML attributes for the `<select>` tag * @param mixed $optKey The key that is selected * @param string $optText The name of the object variable for the option value * @param string $selected The name of the object variable for the option text * @param boolean $idtag Value of the field id or null by default * @param boolean $translate True if options will be translated * * @return string HTML for the select list * * @since 1.5 */ public static function radiolist( $data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false ) { if (is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } $id_text = $idtag ?: $name; $html = '<div class="controls">'; foreach ($data as $obj) { $html .= '<div class="form-check form-check-inline">'; $k = $obj->$optKey; $t = $translate ? Text::_($obj->$optText) : $obj->$optText; $id = (isset($obj->id) ? $obj->id : null); $extra = ''; $id = $id ? $obj->id : $id_text . $k; if (is_array($selected)) { foreach ($selected as $val) { $k2 = is_object($val) ? $val->$optKey : $val; if ($k == $k2) { $extra .= ' selected="selected" '; break; } } } else { $extra .= ((string) $k === (string) $selected ? ' checked="checked" ' : ''); } $html .= '<input type="radio" class="form-check-input" name="' . $name . '" id="' . $id . '" value="' . $k . '" ' . $extra . $attribs . '>'; $html .= '<label for="' . $id . '" class="form-check-label" id="' . $id . '-lbl">' . $t . '</label>'; $html .= '</div>'; } $html .= '</div>'; return $html; } } PK0A#]�� src/HTML/Helpers/User.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Access\Access; use Joomla\CMS\Factory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class working with users * * @since 2.5 */ abstract class User { /** * Displays a list of user groups. * * @param boolean $includeSuperAdmin true to include super admin groups, false to exclude them * * @return array An array containing a list of user groups. * * @since 2.5 */ public static function groups($includeSuperAdmin = false) { $options = array_values(UserGroupsHelper::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; $groups[] = HTMLHelper::_('select.option', $options[$i]->value, $options[$i]->text); } // Exclude super admin groups if requested if (!$includeSuperAdmin) { $filteredGroups = []; foreach ($groups as $group) { if (!Access::checkGroup($group->value, 'core.admin')) { $filteredGroups[] = $group; } } $groups = $filteredGroups; } return $groups; } /** * Get a list of users. * * @return string * * @since 2.5 */ public static function userlist() { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.name', 'text'), ] ) ->from($db->quoteName('#__users', 'a')) ->where($db->quoteName('a.block') . ' = 0') ->order($db->quoteName('a.name')); $db->setQuery($query); return $db->loadObjectList(); } } PK0A#]8 �$��src/HTML/Helpers/Tag.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for tags * * @since 3.1 */ abstract class Tag { /** * Cached array of the tag items. * * @var array * @since 3.1 */ protected static $items = []; /** * Returns an array of tags. * * @param array $config An array of configuration options. By default, only * published and unpublished categories are returned. * * @return array * * @since 3.1 */ public static function options($config = ['filter.published' => [0, 1]]) { $hash = md5(serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.level'), ] ) ->from($db->quoteName('#__tags', 'a')) ->where($db->quoteName('a.parent_id') . ' > 0'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where('a.published = :published') ->bind(':published', $config['filter.published'], ParameterType::INTEGER); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->whereIn($db->quoteName('a.published'), $config['filter.published']); } } // Filter on the language if (isset($config['filter.language'])) { if (is_string($config['filter.language'])) { $query->where($db->quoteName('a.language') . ' = :language') ->bind(':language', $config['filter.language']); } elseif (is_array($config['filter.language'])) { $query->whereIn($db->quoteName('a.language'), $config['filter.language'], ParameterType::STRING); } } $query->order($db->quoteName('a.lft')); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = []; foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; static::$items[$hash][] = HTMLHelper::_('select.option', $item->id, $item->title); } } return static::$items[$hash]; } /** * Returns an array of tags. * * @param array $config An array of configuration options. By default, only published and unpublished tags are returned. * * @return array Tag data * * @since 3.1 */ public static function tags($config = ['filter.published' => [0, 1]]) { $hash = md5(serialize($config)); $config = (array) $config; $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.level'), $db->quoteName('a.parent_id'), ] ) ->from($db->quoteName('#__tags', 'a')) ->where($db->quoteName('a.parent_id') . ' > 0'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $config['filter.published'], ParameterType::INTEGER); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->whereIn($db->quoteName('a.published'), $config['filter.published']); } } $query->order($db->quoteName('a.lft')); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = []; foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; static::$items[$hash][] = HTMLHelper::_('select.option', $item->id, $item->title); } return static::$items[$hash]; } /** * This is just a proxy for the formbehavior.ajaxchosen method * * @param string $selector DOM id of the tag field * @param boolean $allowCustom Flag to allow custom values * * @return void * * @since 3.1 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement */ public static function ajaxfield($selector = '#jform_tags', $allowCustom = true) { // Get the component parameters $params = ComponentHelper::getParams('com_tags'); $minTermLength = (int) $params->get('min_term_length', 3); Text::script('JGLOBAL_KEEP_TYPING'); Text::script('JGLOBAL_LOOKING_FOR'); // Include scripts HTMLHelper::_('behavior.core'); HTMLHelper::_('jquery.framework'); HTMLHelper::_('formbehavior.chosen'); HTMLHelper::_('script', 'legacy/ajax-chosen.min.js', ['version' => 'auto', 'relative' => true]); Factory::getDocument()->addScriptOptions( 'ajax-chosen', [ 'url' => Uri::root() . 'index.php?option=com_tags&task=tags.searchAjax', 'debug' => JDEBUG, 'selector' => $selector, 'type' => 'GET', 'dataType' => 'json', 'jsonTermKey' => 'like', 'afterTypeDelay' => 500, 'minTermLength' => $minTermLength, ] ); // Allow custom values ? if ($allowCustom) { HTMLHelper::_('script', 'system/fields/tag.min.js', ['version' => 'auto', 'relative' => true]); Factory::getDocument()->addScriptOptions( 'field-tag-custom', [ 'minTermLength' => $minTermLength, 'selector' => $selector, 'allowCustom' => Factory::getUser()->authorise('core.create', 'com_tags') ? $allowCustom : false, ] ); } } } PK0A#]�q&���$src/HTML/Helpers/ContentLanguage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class working with content language select lists * * @since 1.6 */ abstract class ContentLanguage { /** * Cached array of the content language items. * * @var array * @since 1.6 */ protected static $items = null; /** * Get a list of the available content language items. * * @param boolean $all True to include All (*) * @param boolean $translate True to translate All * * @return array * * @see \Joomla\CMS\Form\Field\ContentlanguageField * @since 1.6 */ public static function existing($all = false, $translate = false) { if (empty(static::$items)) { // Get the database object and a new query object. $db = Factory::getDbo(); $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('a.lang_code', 'value'), $db->quoteName('a.title', 'text'), $db->quoteName('a.title_native'), ] ) ->from($db->quoteName('#__languages', 'a')) ->where($db->quoteName('a.published') . ' >= 0') ->order($db->quoteName('a.title')); // Set the query and load the options. $db->setQuery($query); static::$items = $db->loadObjectList(); } if ($all) { $all_option = [new CMSObject(['value' => '*', 'text' => $translate ? Text::alt('JALL', 'language') : 'JALL_LANGUAGE'])]; return array_merge($all_option, static::$items); } else { return static::$items; } } } PK0A#]H�%�d'd'src/HTML/Helpers/Dropdown.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML utility class for building a dropdown menu * * @since 3.0 */ abstract class Dropdown { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = []; /** * @var string HTML markup for the dropdown list * @since 3.0 */ protected static $dropDownList = null; /** * Method to inject needed script * * @return void * * @since 3.0 */ public static function init() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Depends on Bootstrap HTMLHelper::_('bootstrap.framework'); Factory::getDocument()->addScriptDeclaration(" (function($){ $(document).ready(function (){ $('.has-context') .mouseenter(function (){ $('.btn-group',$(this)).show(); }) .mouseleave(function (){ $('.btn-group',$(this)).hide(); $('.btn-group',$(this)).removeClass('open'); }); contextAction =function (cbId, task) { $('input[name=\"cid[]\"]').removeAttr('checked'); $('#' + cbId).attr('checked','checked'); Joomla.submitbutton(task); } }); })(jQuery); "); // Set static array static::$loaded[__METHOD__] = true; } /** * Method to start a new dropdown menu * * @return void * * @since 3.0 */ public static function start() { // Only start once if (isset(static::$loaded[__METHOD__]) && static::$loaded[__METHOD__] == true) { return; } $dropDownList = '<div class="btn-group" style="margin-left:6px;display:none"> <a href="#" data-bs-toggle="dropdown" class="dropdown-toggle btn btn-secondary btn-sm"> <span class="caret"></span> </a> <ul class="dropdown-menu">'; static::$dropDownList = $dropDownList; static::$loaded[__METHOD__] = true; } /** * Method to render current dropdown menu * * @return string HTML markup for the dropdown list * * @since 3.0 */ public static function render() { $dropDownList = static::$dropDownList; $dropDownList .= '</ul></div>'; static::$dropDownList = null; static::$loaded[__CLASS__ . '::start'] = false; return $dropDownList; } /** * Append an edit item to the current dropdown menu * * @param integer $id Record ID * @param string $prefix Task prefix * @param string $customLink The custom link if dont use default Joomla action format * * @return void * * @since 3.0 */ public static function edit($id, $prefix = '', $customLink = '') { static::start(); if (!$customLink) { $option = Factory::getApplication()->getInput()->getCmd('option'); $link = 'index.php?option=' . $option; } else { $link = $customLink; } $link .= '&task=' . $prefix . 'edit&id=' . $id; $link = Route::_($link); static::addCustomItem(Text::_('JACTION_EDIT'), $link); } /** * Append a publish item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function publish($checkboxId, $prefix = '') { $task = $prefix . 'publish'; static::addCustomItem(Text::_('JTOOLBAR_PUBLISH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append an unpublish item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unpublish($checkboxId, $prefix = '') { $task = $prefix . 'unpublish'; static::addCustomItem(Text::_('JTOOLBAR_UNPUBLISH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append a featured item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function featured($checkboxId, $prefix = '') { $task = $prefix . 'featured'; static::addCustomItem(Text::_('JFEATURED'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append an unfeatured item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unfeatured($checkboxId, $prefix = '') { $task = $prefix . 'unfeatured'; static::addCustomItem(Text::_('JUNFEATURED'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append an archive item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function archive($checkboxId, $prefix = '') { $task = $prefix . 'archive'; static::addCustomItem(Text::_('JTOOLBAR_ARCHIVE'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append an unarchive item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unarchive($checkboxId, $prefix = '') { $task = $prefix . 'unpublish'; static::addCustomItem(Text::_('JTOOLBAR_UNARCHIVE'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append a trash item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function trash($checkboxId, $prefix = '') { $task = $prefix . 'trash'; static::addCustomItem(Text::_('JTOOLBAR_TRASH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append an untrash item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function untrash($checkboxId, $prefix = '') { $task = $prefix . 'publish'; static::addCustomItem(Text::_('JTOOLBAR_UNTRASH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Append a checkin item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function checkin($checkboxId, $prefix = '') { $task = $prefix . 'checkin'; static::addCustomItem(Text::_('JTOOLBAR_CHECKIN'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); } /** * Writes a divider between dropdown items * * @return void * * @since 3.0 */ public static function divider() { static::$dropDownList .= '<li class="divider"></li>'; } /** * Append a custom item to current dropdown menu * * @param string $label The label of item * @param string $link The link of item * @param string $linkAttributes Custom link attributes * @param string $className Class name of item * @param boolean $ajaxLoad True if using ajax load when item clicked * @param string $jsCallBackFunc Javascript function name, called when ajax load successfully * * @return void * * @since 3.0 */ public static function addCustomItem( $label, $link = 'javascript:void(0)', $linkAttributes = '', $className = '', $ajaxLoad = false, $jsCallBackFunc = null ) { static::start(); if ($ajaxLoad) { $href = ' href = "javascript:void(0)" onclick="loadAjax(\'' . $link . '\', \'' . $jsCallBackFunc . '\')"'; } else { $href = ' href = "' . $link . '" '; } $dropDownList = static::$dropDownList; $dropDownList .= '<li class="' . $className . '"><a ' . $linkAttributes . $href . ' >'; $dropDownList .= $label; $dropDownList .= '</a></li>'; static::$dropDownList = $dropDownList; } } PK0A#]�(C/ / src/HTML/Helpers/Telephone.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML helper class for rendering telephone numbers. * * @since 1.6 */ abstract class Telephone { /** * Converts strings of integers into more readable telephone format * * By default, the ITU-T format will automatically be used. * However, one of the allowed unit types may also be used instead. * * @param integer $number The integers in a phone number with dot separated country code * ccc.nnnnnnn where ccc represents country code and nnn represents the local number. * @param string $displayplan The numbering plan used to display the numbers. * * @return string The formatted telephone number. * * @see \Joomla\CMS\Form\Rule\TelRule * @since 1.6 */ public static function tel($number, $displayplan) { $display = []; $number = explode('.', $number); $countrycode = $number[0]; $number = $number[1]; if ($displayplan === 'ITU-T' || $displayplan === 'International' || $displayplan === 'int' || $displayplan === 'missdn' || $displayplan == null) { $display[0] = '+'; $display[1] = $countrycode; $display[2] = ' '; $display[3] = implode(' ', str_split($number, 2)); } elseif ($displayplan === 'NANP' || $displayplan === 'northamerica' || $displayplan === 'US') { $display[0] = '('; $display[1] = substr($number, 0, 3); $display[2] = ') '; $display[3] = substr($number, 3, 3); $display[4] = '-'; $display[5] = substr($number, 6, 4); } elseif ($displayplan === 'EPP' || $displayplan === 'IETF') { $display[0] = '+'; $display[1] = $countrycode; $display[2] = '.'; $display[3] = $number; } elseif ($displayplan === 'ARPA' || $displayplan === 'ENUM') { $number = implode('.', str_split(strrev($number), 1)); $display[0] = '+'; $display[1] = $number; $display[2] = '.'; $display[3] = $countrycode; $display[4] = '.e164.arpa'; } return implode('', $display); } } PK0A#]/!�||src/HTML/Helpers/Sidebar.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Layout\FileLayout; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class to render a list view sidebar * * @since 3.0 */ abstract class Sidebar { /** * Menu entries * * @var array * @since 3.0 */ protected static $entries = []; /** * Filters * * @var array * @since 3.0 */ protected static $filters = []; /** * Value for the action attribute of the form. * * @var string * @since 3.0 */ protected static $action = ''; /** * Render the sidebar. * * @return string The necessary HTML to display the sidebar * * @since 3.0 */ public static function render() { // Collect display data $data = new \stdClass(); $data->list = static::getEntries(); $data->filters = static::getFilters(); $data->action = static::getAction(); $data->displayMenu = count($data->list); $data->displayFilters = count($data->filters); $data->hide = Factory::getApplication()->getInput()->getBool('hidemainmenu'); // Create a layout object and ask it to render the sidebar $layout = new FileLayout('joomla.sidebars.submenu'); return $layout->render($data); } /** * Method to add a menu item to submenu. * * @param string $name Name of the menu item. * @param string $link URL of the menu item. * @param bool $active True if the item is active, false otherwise. * * @return void * * @since 3.0 */ public static function addEntry($name, $link = '', $active = false) { static::$entries[] = [$name, $link, $active]; } /** * Returns an array of all submenu entries * * @return array * * @since 3.0 */ public static function getEntries() { return static::$entries; } /** * Method to add a filter to the submenu * * @param string $label Label for the menu item. * @param string $name Name for the filter. Also used as id. * @param string $options Options for the select field. * @param bool $noDefault Don't show the label as the empty option * * @return void * * @since 3.0 */ public static function addFilter($label, $name, $options, $noDefault = false) { static::$filters[] = ['label' => $label, 'name' => $name, 'options' => $options, 'noDefault' => $noDefault]; } /** * Returns an array of all filters * * @return array * * @since 3.0 */ public static function getFilters() { return static::$filters; } /** * Set value for the action attribute of the filter form * * @param string $action Value for the action attribute of the form * * @return void * * @since 3.0 */ public static function setAction($action) { static::$action = $action; } /** * Get value for the action attribute of the filter form * * @return string * * @since 3.0 */ public static function getAction() { return static::$action; } } PK0A#]��p� � src/HTML/Helpers/Date.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Date\Date as DateHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extended Utility class for handling date display. * * @since 2.5 */ abstract class Date { /** * Function to convert a static time into a relative measurement * * @param string $date The date to convert * @param string $unit The optional unit of measurement to return * if the value of the diff is greater than one * @param string $time An optional time to compare to, defaults to now * @param string $format An optional format for the HTMLHelper::date output * * @return string The converted time string * * @since 2.5 */ public static function relative($date, $unit = null, $time = null, $format = null) { if ($time === null) { // Get now $time = new DateHelper('now'); } // Get the difference in seconds between now and the time $diff = strtotime($time) - strtotime($date); // Less than a minute if ($diff < 60) { return Text::_('JLIB_HTML_DATE_RELATIVE_LESSTHANAMINUTE'); } // Round to minutes $diff = round($diff / 60); // 1 to 59 minutes if ($diff < 60 || $unit === 'minute') { return Text::plural('JLIB_HTML_DATE_RELATIVE_MINUTES', $diff); } // Round to hours $diff = round($diff / 60); // 1 to 23 hours if ($diff < 24 || $unit === 'hour') { return Text::plural('JLIB_HTML_DATE_RELATIVE_HOURS', $diff); } // Round to days $diff = round($diff / 24); // 1 to 6 days if ($diff < 7 || $unit === 'day') { return Text::plural('JLIB_HTML_DATE_RELATIVE_DAYS', $diff); } // Round to weeks $diff = round($diff / 7); // 1 to 4 weeks if ($diff <= 4 || $unit === 'week') { return Text::plural('JLIB_HTML_DATE_RELATIVE_WEEKS', $diff); } // Over a month, return the absolute time return HTMLHelper::_('date', $date, $format); } } PK0A#]F�H$$src/HTML/Helpers/Number.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML helper class for rendering numbers. * * @since 1.6 */ abstract class Number { /** * Converts bytes to more distinguishable formats such as: * kilobytes, megabytes, etc. * * By default, the proper format will automatically be chosen. * However, one of the allowed unit types (viz. 'b', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') may also be used instead. * IEC standard unit types ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB') can be used as well. * * @param string $bytes The number of bytes. Can be either numeric or suffixed format: 32M, 60K, 12G or 812b * @param string $unit The type of unit to return, few special values are: * Blank string '' for no unit, * 'auto' to choose automatically (default) * 'binary' to choose automatically but use binary unit prefix * @param integer $precision The number of digits to be used after the decimal place. * @param bool $iec Whether to be aware of IEC standards. IEC prefixes are always acceptable in input. * When IEC is ON: KiB = 1024 B, KB = 1000 B * When IEC is OFF: KiB = 1024 B, KB = 1024 B * * @return string The number of bytes in the proper units. * * @since 1.6 * @link https://en.wikipedia.org/wiki/Binary_prefix */ public static function bytes($bytes, $unit = 'auto', $precision = 2, $iec = false) { /* * Allowed 123.45, 123.45 M, 123.45 Mi, 123.45 MB, 123.45 MiB, 1.2345E+12MB, 1.2345E+12 MB , 1.2345E+12 MiB etc. * Meaning any number in decimal digits or in sci. notation, optional space, optional 1-3 letter unit suffix */ if (is_numeric($bytes)) { $oBytes = $bytes; } else { preg_match('/(.*?)\s?((?:[KMGTPEZY]i?)?B?)$/i', trim($bytes), $matches); list(, $oBytes, $oUnit) = $matches; if ($oUnit && is_numeric($oBytes)) { $oBase = $iec && strpos($oUnit, 'i') === false ? 1000 : 1024; $factor = pow($oBase, stripos('BKMGTPEZY', $oUnit[0])); $oBytes *= $factor; } } if (empty($oBytes) || !is_numeric($oBytes)) { return 0; } $oBytes = round($oBytes); // If no unit is requested return early if ($unit === '') { return (string) $oBytes; } $stdSuffixes = ['b', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; $iecSuffixes = ['b', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; // User supplied method if (in_array($unit, $iecSuffixes)) { $base = 1024; $i = array_search($unit, $iecSuffixes, true); $suffix = $unit; } elseif (in_array($unit, $stdSuffixes)) { $base = $iec ? 1000 : 1024; $i = array_search($unit, $stdSuffixes, true); $suffix = $unit; } elseif ($unit === 'binary') { $base = 1024; $i = (int) floor(log($oBytes, $base)); $suffix = $iecSuffixes[$i]; } else { // Default method $base = $iec ? 1000 : 1024; $i = (int) floor(log($oBytes, $base)); $suffix = $stdSuffixes[$i]; } return number_format( round($oBytes / pow($base, $i), (int) $precision), (int) $precision, Text::_('DECIMALS_SEPARATOR'), Text::_('THOUSANDS_SEPARATOR') ) . ' ' . $suffix; } } PK0A#]0���src/HTML/Helpers/UiTab.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for the Joomla core UI Tab element. * * @since 4.0.0 */ abstract class UiTab { /** * @var array Array containing information for loaded files * @since 4.0.0 */ protected static $loaded = []; /** * Creates a core UI tab pane * * @param string $selector The pane identifier. * @param array $params The parameters for the pane * * @return string * * @since 4.0.0 */ public static function startTabSet($selector = 'myTab', $params = []) { $sig = md5(serialize([$selector, $params])); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include the custom element Factory::getDocument()->getWebAssetManager() ->useStyle('webcomponent.joomla-tab') ->useScript('webcomponent.joomla-tab'); // Setup options object $opt = ['active' => (isset($params['active']) && ($params['active'])) ? (string) $params['active'] : '']; // Set static array static::$loaded[__METHOD__][$sig] = true; static::$loaded[__METHOD__][$selector]['active'] = $opt['active']; } $orientation = isset($params['orientation']) ? $params['orientation'] : 'horizontal'; $recall = isset($params['recall']) ? 'recall' : ''; $breakpoint = isset($params['breakpoint']) ? 'breakpoint="' . $params['breakpoint'] . '"' : ''; if (!isset($params['breakpoint']) && $breakpoint === '') { $breakpoint = 'breakpoint="768"'; } return '<joomla-tab id="' . $selector . '" orientation="' . $orientation . '" ' . $recall . ' ' . $breakpoint . '>'; } /** * Close the current tab pane * * @return string HTML to close the pane * * @since 4.0.0 */ public static function endTabSet() { return '</joomla-tab>'; } /** * Begins the display of a new tab content panel. * * @param string $selector Identifier of the panel. * @param string $id The ID of the div element * @param string $title The title text for the button * * @return string HTML to start a new panel * * @since 4.0.0 */ public static function addTab($selector, $id, $title) { $active = (static::$loaded[__CLASS__ . '::startTabSet'][$selector]['active'] == $id) ? ' active' : ''; return '<joomla-tab-element id="' . $id . '"' . $active . ' name="' . htmlspecialchars($title, ENT_COMPAT, 'UTF-8') . '">'; } /** * Close the current tab content panel * * @return string HTML to close the pane * * @since 4.0.0 */ public static function endTab() { return '</joomla-tab-element>'; } } PK0A#]�DoXL-L-src/HTML/Helpers/Behavior.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for JavaScript behaviors * * @since 1.5 */ abstract class Behavior { /** * Array containing information for loaded files * * @var array * @since 2.5 */ protected static $loaded = []; /** * Method to load core.js into the document head. * * Core.js defines the 'Joomla' namespace and contains functions which are used across extensions * * @return void * * @since 3.3 * * @deprecated 4.0 will be removed in 6.0 * Use the webasset manager instead * Example: Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('core'); */ public static function core() { Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('core'); } /** * Add unobtrusive JavaScript support for form validation. * * To enable form validation the form tag must have class="form-validate". * Each field that needs to be validated needs to have class="validate". * Additional handlers can be added to the handler for username, password, * numeric and email. To use these add class="validate-email" and so on. * * @return void * * @since 3.4 * * @deprecated 4.0 will be removed in 6.0 * Use the webasset manager instead * Example: Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('form.validate'); */ public static function formvalidator() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } Factory::getDocument()->getWebAssetManager()->useScript('form.validate'); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for a combobox effect. * * Note that this control is only reliable in absolutely positioned elements. * Avoid using a combobox in a slider or dynamic pane. * * @return void * * @since 1.5 * * @deprecated 4.0 will be removed in 6.0 * Use the webasset manager instead * Example: Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('awesomeplete'); */ public static function combobox() { Factory::getDocument()->getWebAssetManager()->usePreset('awesomplete'); } /** * JavaScript behavior to allow shift select in grids * * @param string $id The id of the form for which a multiselect behaviour is to be applied. * * @return void * * @since 1.7 * * @deprecated 4.0 will be removed in 6.0 * Use the webasset manager instead * Example: * Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('multiselect'); * Factory::getDocument()->addScriptOptions('js-multiselect', ['formName' => $id]); */ public static function multiselect($id = 'adminForm') { // Only load once if (isset(static::$loaded[__METHOD__][$id])) { return; } Factory::getDocument()->getWebAssetManager()->useScript('multiselect'); // Pass the required options to the javascript Factory::getDocument()->addScriptOptions('js-multiselect', ['formName' => $id]); // Set static array static::$loaded[__METHOD__][$id] = true; } /** * Keep session alive, for example, while editing or creating an article. * * @return void * * @since 1.5 * * @deprecated 4.0 will be removed in 6.0 * Use the webasset manager instead * Example: Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('keepalive'); */ public static function keepalive() { Factory::getApplication()->getDocument()->getWebAssetManager()->useScript('keepalive'); } /** * Highlight some words via Javascript. * * @param array $terms Array of words that should be highlighted. * @param string $start ID of the element that marks the begin of the section in which words * should be highlighted. Note this element will be removed from the DOM. * @param string $end ID of the element that end this section. * Note this element will be removed from the DOM. * @param string $className Class name of the element highlights are wrapped in. * @param string $tag Tag that will be used to wrap the highlighted words. * * @return void * * @since 2.5 * * @deprecated 4.0 will be removed in 6.0 * Use the script directly */ public static function highlighter(array $terms, $start = 'highlighter-start', $end = 'highlighter-end', $className = 'highlight', $tag = 'span') { $terms = array_filter($terms, 'strlen'); if (!empty($terms)) { $doc = Factory::getDocument(); $doc->getWebAssetManager()->useScript('highlight'); $doc->addScriptOptions( 'highlight', [[ 'class' => 'js-highlight', 'highLight' => $terms, 'compatibility' => true, 'start' => $start, 'end' => $end, ]] ); } } /** * Add javascript polyfills. * * @param string|array $polyfillTypes The polyfill type(s). Examples: event, array('event', 'classlist'). * @param string $conditionalBrowser An IE conditional expression. Example: lt IE 9 (lower than IE 9). * * @return void * * @since 3.7.0 */ public static function polyfill($polyfillTypes = null, $conditionalBrowser = null) { if ($polyfillTypes === null) { return; } foreach ((array) $polyfillTypes as $polyfillType) { $sig = md5(serialize([$polyfillType, $conditionalBrowser])); // Only load once if (isset(static::$loaded[__METHOD__][$sig])) { continue; } // If include according to browser. $scriptOptions = ['version' => 'auto', 'relative' => true]; $scriptOptions = $conditionalBrowser !== null ? array_replace($scriptOptions, ['conditional' => $conditionalBrowser]) : $scriptOptions; /** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */ $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->registerAndUseScript('polyfill.' . $polyfillType, 'vendor/polyfills/polyfill-' . $polyfillType . '.js', $scriptOptions); // Set static array static::$loaded[__METHOD__][$sig] = true; } } /** * Internal method to translate the JavaScript Calendar * * @return string JavaScript that translates the object * * @since 1.5 */ protected static function calendartranslation() { static $jsscript = 0; // Guard clause, avoids unnecessary nesting if ($jsscript) { return false; } $jsscript = 1; // To keep the code simple here, run strings through Text::_() using array_map() $callback = ['Text', '_']; $weekdays_full = array_map( $callback, [ 'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY', ] ); $weekdays_short = array_map( $callback, [ 'SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', ] ); $months_long = array_map( $callback, [ 'JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER', ] ); $months_short = array_map( $callback, [ 'JANUARY_SHORT', 'FEBRUARY_SHORT', 'MARCH_SHORT', 'APRIL_SHORT', 'MAY_SHORT', 'JUNE_SHORT', 'JULY_SHORT', 'AUGUST_SHORT', 'SEPTEMBER_SHORT', 'OCTOBER_SHORT', 'NOVEMBER_SHORT', 'DECEMBER_SHORT', ] ); // This will become an object in Javascript but define it first in PHP for readability $today = " " . Text::_('JLIB_HTML_BEHAVIOR_TODAY') . " "; $text = [ 'INFO' => Text::_('JLIB_HTML_BEHAVIOR_ABOUT_THE_CALENDAR'), 'ABOUT' => "DHTML Date/Time Selector\n" . "(c) dynarch.com 20022005 / Author: Mihai Bazon\n" . "For latest version visit: http://www.dynarch.com/projects/calendar/\n" . "Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." . "\n\n" . Text::_('JLIB_HTML_BEHAVIOR_DATE_SELECTION') . Text::_('JLIB_HTML_BEHAVIOR_YEAR_SELECT') . Text::_('JLIB_HTML_BEHAVIOR_MONTH_SELECT') . Text::_('JLIB_HTML_BEHAVIOR_HOLD_MOUSE'), 'ABOUT_TIME' => "\n\n" . "Time selection:\n" . " Click on any of the time parts to increase it\n" . " or Shiftclick to decrease it\n" . " or click and drag for faster selection.", 'PREV_YEAR' => Text::_('JLIB_HTML_BEHAVIOR_PREV_YEAR_HOLD_FOR_MENU'), 'PREV_MONTH' => Text::_('JLIB_HTML_BEHAVIOR_PREV_MONTH_HOLD_FOR_MENU'), 'GO_TODAY' => Text::_('JLIB_HTML_BEHAVIOR_GO_TODAY'), 'NEXT_MONTH' => Text::_('JLIB_HTML_BEHAVIOR_NEXT_MONTH_HOLD_FOR_MENU'), 'SEL_DATE' => Text::_('JLIB_HTML_BEHAVIOR_SELECT_DATE'), 'DRAG_TO_MOVE' => Text::_('JLIB_HTML_BEHAVIOR_DRAG_TO_MOVE'), 'PART_TODAY' => $today, 'DAY_FIRST' => Text::_('JLIB_HTML_BEHAVIOR_DISPLAY_S_FIRST'), 'WEEKEND' => Factory::getLanguage()->getWeekEnd(), 'CLOSE' => Text::_('JLIB_HTML_BEHAVIOR_CLOSE'), 'TODAY' => Text::_('JLIB_HTML_BEHAVIOR_TODAY'), 'TIME_PART' => Text::_('JLIB_HTML_BEHAVIOR_SHIFT_CLICK_OR_DRAG_TO_CHANGE_VALUE'), 'DEF_DATE_FORMAT' => "%Y%m%d", 'TT_DATE_FORMAT' => Text::_('JLIB_HTML_BEHAVIOR_TT_DATE_FORMAT'), 'WK' => Text::_('JLIB_HTML_BEHAVIOR_WK'), 'TIME' => Text::_('JLIB_HTML_BEHAVIOR_TIME'), ]; return 'Calendar._DN = ' . json_encode($weekdays_full) . ';' . ' Calendar._SDN = ' . json_encode($weekdays_short) . ';' . ' Calendar._FD = 0;' . ' Calendar._MN = ' . json_encode($months_long) . ';' . ' Calendar._SMN = ' . json_encode($months_short) . ';' . ' Calendar._TT = ' . json_encode($text) . ';'; } } PK0A#]��))src/HTML/Helpers/Access.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Access\Access as AccessCheck; use Joomla\CMS\Factory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extended Utility class for all HTML drawing classes. * * @since 1.6 */ abstract class Access { /** * A cached array of the asset groups * * @var array * @since 1.6 */ protected static $asset_groups = null; /** * Displays a list of the available access view levels * * @param string $name The form field name. * @param string $selected The name of the selected section. * @param string $attribs Additional attributes to add to the select field. * @param mixed $params True to add "All Sections" option or an array of options * @param mixed $id The form field id or false if not used * * @return string The required HTML for the SELECT tag. * * @see \Joomla\CMS\Form\Field\AccesslevelField * @since 1.6 */ public static function level($name, $selected, $attribs = '', $params = true, $id = false) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.title', 'text'), ] ) ->from($db->quoteName('#__viewlevels', 'a')) ->group( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.ordering'), ] ) ->order( [ $db->quoteName('a.ordering') . ' ASC', $db->quoteName('a.title') . ' ASC', ] ); // Get the options. $db->setQuery($query); $options = $db->loadObjectList(); // If params is an array, push these options to the array if (is_array($params)) { $options = array_merge($params, $options); } elseif ($params) { // If all levels is allowed, push it into the array. array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_LEVELS'))); } return HTMLHelper::_( 'select.genericlist', $options, $name, [ 'list.attr' => $attribs, 'list.select' => $selected, 'id' => $id, ] ); } /** * Displays a list of the available user groups. * * @param string $name The form field name. * @param string $selected The name of the selected section. * @param string $attribs Additional attributes to add to the select field. * @param boolean $allowAll True to add "All Groups" option. * @param mixed $id The form field id * * @return string The required HTML for the SELECT tag. * * @see \Joomla\CMS\Form\Field\UsergrouplistField * @since 1.6 */ public static function usergroup($name, $selected, $attribs = '', $allowAll = true, $id = false) { $options = array_values(UserGroupsHelper::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; } // If all usergroups is allowed, push it into the array. if ($allowAll) { array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_GROUPS'))); } return HTMLHelper::_('select.genericlist', $options, $name, ['list.attr' => $attribs, 'list.select' => $selected, 'id' => $id]); } /** * Returns a UL list of user groups with checkboxes * * @param string $name The name of the checkbox controls array * @param array $selected An array of the checked boxes * @param boolean $checkSuperAdmin If false only super admins can add to super admin groups * * @return string * * @since 1.6 */ public static function usergroups($name, $selected, $checkSuperAdmin = false) { static $count; $count++; $isSuperAdmin = Factory::getUser()->authorise('core.admin'); $groups = array_values(UserGroupsHelper::getInstance()->getAll()); $html = []; for ($i = 0, $n = count($groups); $i < $n; $i++) { $item = &$groups[$i]; // If checkSuperAdmin is true, only add item if the user is superadmin or the group is not super admin if ((!$checkSuperAdmin) || $isSuperAdmin || (!AccessCheck::checkGroup($item->id, 'core.admin'))) { // Set up the variable attributes. ID may not start with a number (CSS) $eid = 'group_' . $item->id . '_' . $count; // Don't call in_array unless something is selected $checked = ''; if ($selected) { $checked = in_array($item->id, $selected) ? ' checked="checked"' : ''; } $rel = ($item->parent_id > 0) ? ' rel="group_' . $item->parent_id . '_' . $count . '"' : ''; // Build the HTML for the item. $html[] = ' <div class="control-group">'; $html[] = ' <div class="controls">'; $html[] = ' <label class="form-check-label checkbox" for="' . $eid . '">'; $html[] = ' <input class="form-check-input" type="checkbox" name="' . $name . '[]" value="' . $item->id . '" id="' . $eid . '"'; $html[] = ' ' . $checked . $rel . '>'; $html[] = ' ' . LayoutHelper::render('joomla.html.treeprefix', ['level' => $item->level + 1]) . $item->title; $html[] = ' </label>'; $html[] = ' </div>'; $html[] = ' </div>'; } } return implode("\n", $html); } /** * Returns a UL list of actions with checkboxes * * @param string $name The name of the checkbox controls array * @param array $selected An array of the checked boxes * @param string $component The component the permissions apply to * @param string $section The section (within a component) the permissions apply to * * @return string * * @see AccessCheck * @since 1.6 */ public static function actions($name, $selected, $component, $section = 'global') { static $count; $count++; $actions = AccessCheck::getActionsFromFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml', "/access/section[@name='" . $section . "']/" ); $html = []; $html[] = '<ul class="checklist access-actions">'; for ($i = 0, $n = count($actions); $i < $n; $i++) { $item = &$actions[$i]; // Setup the variable attributes. $eid = $count . 'action_' . $item->id; $checked = in_array($item->id, $selected) ? ' checked="checked"' : ''; // Build the HTML for the item. $html[] = ' <li>'; $html[] = ' <input type="checkbox" name="' . $name . '[]" value="' . $item->id . '" id="' . $eid . '"'; $html[] = ' ' . $checked . '>'; $html[] = ' <label for="' . $eid . '">'; $html[] = ' ' . Text::_($item->title); $html[] = ' </label>'; $html[] = ' </li>'; } $html[] = '</ul>'; return implode("\n", $html); } /** * Gets a list of the asset groups as an array of JHtml compatible options. * * @return mixed An array or false if an error occurs * * @since 1.6 */ public static function assetgroups() { if (empty(static::$asset_groups)) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.title', 'text'), ] ) ->from($db->quoteName('#__viewlevels', 'a')) ->group( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.ordering'), ] ) ->order($db->quoteName('a.ordering') . ' ASC'); $db->setQuery($query); static::$asset_groups = $db->loadObjectList(); } return static::$asset_groups; } /** * Displays a Select list of the available asset groups * * @param string $name The name of the select element * @param mixed $selected The selected asset group id * @param string $attribs Optional attributes for the select field * @param array $config An array of options for the control * * @return mixed An HTML string or null if an error occurs * * @since 1.6 */ public static function assetgrouplist($name, $selected, $attribs = null, $config = []) { static $count; $options = static::assetgroups(); if (isset($config['title'])) { array_unshift($options, HTMLHelper::_('select.option', '', $config['title'])); } return HTMLHelper::_( 'select.genericlist', $options, $name, [ 'id' => isset($config['id']) ? $config['id'] : 'assetgroups_' . (++$count), 'list.attr' => $attribs === null ? 'class="inputbox" size="3"' : $attribs, 'list.select' => (int) $selected, ] ); } } PK0A#]6.}�src/HTML/Helpers/Icons.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Layout\FileLayout; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for icons. * * @since 2.5 */ abstract class Icons { /** * Method to generate html code for a list of buttons * * @param array $buttons Array of buttons * * @return string * * @since 2.5 */ public static function buttons($buttons) { if (empty($buttons)) { return ''; } $html = []; foreach ($buttons as $button) { $html[] = HTMLHelper::_('icons.button', $button); } return implode($html); } /** * Method to generate html code for a list of buttons * * @param array $button Button properties * * @return string * * @since 2.5 */ public static function button($button) { if (isset($button['access'])) { if (is_bool($button['access'])) { if ($button['access'] == false) { return ''; } } else { // Get the user object to verify permissions $user = Factory::getUser(); // Take each pair of permission, context values. for ($i = 0, $n = count($button['access']); $i < $n; $i += 2) { if (!$user->authorise($button['access'][$i], $button['access'][$i + 1])) { return ''; } } } } // Instantiate a new FileLayout instance and render the layout $layout = new FileLayout('joomla.quickicons.icon'); return $layout->render($button); } } PK0A#]�+0M��!src/HTML/Helpers/FormBehavior.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for form related behaviors * * @since 3.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement * Use choice.js instead * Example: * Factory::getDocument()->getWebAssetManager()->enableAsset('choicesjs'); * HTMLHelper::_('webcomponent', 'system/webcomponents/joomla-field-fancy-select.min.js', ['version' => 'auto', 'relative' => true]); */ abstract class FormBehavior { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = []; /** * Method to load the Chosen JavaScript framework and supporting CSS into the document head * * If debugging mode is on an uncompressed version of Chosen is included for easier debugging. * * @param string $selector Class for Chosen elements. * @param mixed $debug Is debugging mode on? [optional] * @param array $options the possible Chosen options as name => value [optional] * * @return void * * @since 3.0 */ public static function chosen($selector = '.advancedSelect', $debug = null, $options = []) { if (isset(static::$loaded[__METHOD__][$selector])) { return; } // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = JDEBUG; } // Default settings if (!isset($options['disable_search_threshold'])) { $options['disable_search_threshold'] = 10; } // Allow searching contains space in query if (!isset($options['search_contains'])) { $options['search_contains'] = true; } if (!isset($options['allow_single_deselect'])) { $options['allow_single_deselect'] = true; } if (!isset($options['placeholder_text_multiple'])) { $options['placeholder_text_multiple'] = Text::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS'); } if (!isset($options['placeholder_text_single'])) { $options['placeholder_text_single'] = Text::_('JGLOBAL_SELECT_AN_OPTION'); } if (!isset($options['no_results_text'])) { $options['no_results_text'] = Text::_('JGLOBAL_SELECT_NO_RESULTS_MATCH'); } // Options array to json options string $options_str = \json_encode($options, ($debug && \defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : false)); // Add chosen.js assets /** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */ $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->usePreset('chosen') ->registerAndUseScript('joomla-chosen', 'legacy/joomla-chosen.min.js', [], [], ['chosen']) ->addInlineScript( " jQuery(document).ready(function (){ jQuery('" . $selector . "').jchosen(" . $options_str . "); }); " ); static::$loaded[__METHOD__][$selector] = true; } /** * Method to load the AJAX Chosen library * * If debugging mode is on an uncompressed version of AJAX Chosen is included for easier debugging. * * @param Registry $options Options in a Registry object * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 */ public static function ajaxchosen(Registry $options, $debug = null) { // Retrieve options/defaults $selector = $options->get('selector', '.tagfield'); $type = $options->get('type', 'GET'); $url = $options->get('url', null); $dataType = $options->get('dataType', 'json'); $jsonTermKey = $options->get('jsonTermKey', 'term'); $afterTypeDelay = $options->get('afterTypeDelay', '500'); $minTermLength = $options->get('minTermLength', '3'); // Ajax URL is mandatory if (!empty($url)) { if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Requires chosen to work static::chosen($selector, $debug); Text::script('JGLOBAL_KEEP_TYPING'); Text::script('JGLOBAL_LOOKING_FOR'); // Include scripts HTMLHelper::_('behavior.core'); HTMLHelper::_('jquery.framework'); HTMLHelper::_('script', 'legacy/ajax-chosen.min.js', ['version' => 'auto', 'relative' => true, 'detectDebug' => $debug]); Factory::getDocument()->addScriptOptions( 'ajax-chosen', [ 'url' => $url, 'debug' => $debug, 'options' => $options, 'selector' => $selector, 'type' => $type, 'dataType' => $dataType, 'jsonTermKey' => $jsonTermKey, 'afterTypeDelay' => $afterTypeDelay, 'minTermLength' => $minTermLength, ] ); static::$loaded[__METHOD__][$selector] = true; } } } PK0A#]�c�y{ { "src/HTML/Helpers/DraggableList.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Session\Session; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML utility class for creating a sortable table list * * @since 4.0.0 */ abstract class DraggableList { /** * Array containing information for loaded files * * @var array * @since 4.0.0 */ protected static $loaded = []; /** * Method to load the Dragula script and make table sortable * * @param string $tableId DOM id of the table * @param string $formId DOM id of the form * @param string $sortDir Sort direction * @param string $saveOrderingUrl Save ordering url, ajax-load after an item dropped * @param string $redundant Not used * @param boolean $nestedList Set whether the list is a nested list * * @return void * * @since 4.0.0 */ public static function draggable( string $tableId = '', string $formId = '', string $sortDir = 'asc', string $saveOrderingUrl = '', $redundant = null, bool $nestedList = false ) { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } $doc = Factory::getDocument(); // Please consider using data attributes instead of passing arguments here! if (!empty($tableId) && !empty($saveOrderingUrl) && !empty($formId) && !empty($sortDir)) { $doc->addScriptOptions( 'draggable-list', [ 'id' => '#' . $tableId . ' tbody', 'formId' => $formId, 'direction' => $sortDir, 'url' => $saveOrderingUrl . '&' . Session::getFormToken() . '=1', 'nested' => $nestedList, ] ); } $doc->getWebAssetManager() ->usePreset('dragula') ->useScript('joomla.draggable'); // Set static array static::$loaded[__METHOD__] = true; } } PK0A#]�D�m� � src/HTML/Helpers/Email.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\String\PunycodeHelper; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for cloaking email addresses * * @since 1.5 */ abstract class Email { /** * Simple JavaScript email cloaker * * By default replaces an email with a mailto link with email cloaked * * @param string $mail The -mail address to cloak. * @param boolean $mailto True if text and mailing address differ * @param string $text Text for the link * @param boolean $email True if text is an email address * @param string $attribsBefore Any attributes before the email address * @param string $attribsAfter Any attributes before the email address * * @return string The cloaked email. * * @since 1.5 */ public static function cloak($mail, $mailto = true, $text = '', $email = true, $attribsBefore = '', $attribsAfter = '') { // Handle IDN addresses: punycode for href but utf-8 for text displayed. if ($mailto && (empty($text) || $email)) { // Use dedicated $text whereas $mail is used as href and must be punycoded. $text = PunycodeHelper::emailToUTF8($text ?: $mail); } elseif (!$mailto) { // In that case we don't use link - so convert $mail back to utf-8. $mail = PunycodeHelper::emailToUTF8($mail); } // Split email by @ symbol $mail = explode('@', $mail); $name = @$mail[0]; $domain = @$mail[1]; // Include the email cloaking script Factory::getDocument()->getWebAssetManager() ->useScript('webcomponent.hidden-mail'); return '<joomla-hidden-mail ' . $attribsBefore . ' is-link="' . $mailto . '" is-email="' . $email . '" first="' . base64_encode($name) . '" last="' . base64_encode($domain) . '" text="' . base64_encode($text) . '" base="' . Uri::root(true) . '" ' . $attribsAfter . '>' . Text::_('JLIB_HTML_CLOAKING') . '</joomla-hidden-mail>'; } } PK0A#]�X+Vsrc/HTML/Helpers/Form.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Document\HtmlDocument; use Joomla\CMS\Factory; use Joomla\CMS\Session\Session; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for form elements * * @since 1.5 */ abstract class Form { /** * Array containing information for loaded files. * * @var array * * @since 3.8.0 */ protected static $loaded = []; /** * Displays a hidden token field to reduce the risk of CSRF exploits * * Use in conjunction with Session::checkToken() * * @param array $attribs Input element attributes. * * @return string A hidden input field with a token * * @see Session::checkToken() * @since 1.5 */ public static function token(array $attribs = []) { $attributes = ''; if ($attribs !== []) { $attributes .= ' ' . ArrayHelper::toString($attribs); } return '<input type="hidden" name="' . Session::getFormToken() . '" value="1"' . $attributes . '>'; } /** * Add CSRF form token to Joomla script options that developers can get it by Javascript. * * @param string $name The script option key name. * * @return void * * @since 3.8.0 */ public static function csrf($name = 'csrf.token') { if (isset(static::$loaded[__METHOD__][$name])) { return; } /** @var HtmlDocument $doc */ $doc = Factory::getDocument(); if (!$doc instanceof HtmlDocument || $doc->getType() !== 'html') { return; } $doc->addScriptOptions($name, Session::getFormToken()); static::$loaded[__METHOD__][$name] = true; } } PK0A#]�S � � "src/HTML/Helpers/WorkflowStage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class working with workflow states select lists * * @since 4.0.0 */ abstract class WorkflowStage { /** * Get a list of the available workflow stages. * * @param array $options An array of options for the control * * @return array * * @since 4.0.0 */ public static function existing($options) { // Get the database object and a new query object. $db = Factory::getDbo(); $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('ws.id', 'workflow_stage_id'), $db->quoteName('ws.title', 'workflow_stage_title'), $db->quoteName('w.id', 'workflow_id'), $db->quoteName('w.title', 'workflow_title'), ] ) ->from($db->quoteName('#__workflow_stages', 'ws')) ->join('LEFT', $db->quoteName('#__workflows', 'w'), $db->quoteName('w.id') . ' = ' . $db->quoteName('ws.workflow_id')) ->where($db->quoteName('w.published') . ' = 1') ->order($db->quoteName('ws.ordering')); // Set the query and load the options. $stages = $db->setQuery($query)->loadObjectList(); $workflowStages = []; // Grouping the stages by workflow foreach ($stages as $stage) { // Using workflow ID to differentiate workflows having same title $workflowStageKey = Text::_($stage->workflow_title) . ' (' . $stage->workflow_id . ')'; if (!array_key_exists($workflowStageKey, $workflowStages)) { $workflowStages[$workflowStageKey] = []; } $workflowStages[$workflowStageKey][] = HTMLHelper::_('select.option', $stage->workflow_stage_id, Text::_($stage->workflow_stage_title)); } $prefix = [[ HTMLHelper::_('select.option', '', $options['title']), ]]; return array_merge($prefix, $workflowStages); } } PK0A#]��B7B7src/HTML/Helpers/Menu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class working with menu select lists * * @since 1.5 */ abstract class Menu { /** * Cached array of the menus. * * @var array * @since 1.6 */ protected static $menus = []; /** * Cached array of the menus items. * * @var array * @since 1.6 */ protected static $items = []; /** * Get a list of the available menus. * * @param int $clientId The client id * * @return array * * @since 1.6 */ public static function menus($clientId = 0) { $key = serialize($clientId); if (!isset(static::$menus[$key])) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('id'), $db->quoteName('menutype', 'value'), $db->quoteName('title', 'text'), $db->quoteName('client_id'), ] ) ->from($db->quoteName('#__menu_types')) ->order( [ $db->quoteName('client_id'), $db->quoteName('title'), ] ); if (isset($clientId)) { $clientId = (int) $clientId; $query->where($db->quoteName('client_id') . ' = :client') ->bind(':client', $clientId, ParameterType::INTEGER); } static::$menus[$key] = $db->setQuery($query)->loadObjectList(); } return static::$menus[$key]; } /** * Returns an array of menu items grouped by menu. * * @param array $config An array of configuration options [published, checkacl, clientid]. * * @return array * * @since 1.6 */ public static function menuItems($config = []) { $key = serialize($config); if (empty(static::$items[$key])) { // B/C - not passed = 0, null can be passed for both clients $clientId = array_key_exists('clientid', $config) ? $config['clientid'] : 0; $menus = static::menus($clientId); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.title', 'text'), $db->quoteName('a.level'), $db->quoteName('a.menutype'), $db->quoteName('a.client_id'), ] ) ->from($db->quoteName('#__menu', 'a')) ->where($db->quoteName('a.parent_id') . ' > 0'); // Filter on the client id if (isset($clientId)) { $query->where($db->quoteName('a.client_id') . ' = :client') ->bind(':client', $clientId, ParameterType::INTEGER); } // Filter on the published state if (isset($config['published'])) { if (is_numeric($config['published'])) { $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $config['published'], ParameterType::INTEGER); } elseif ($config['published'] === '') { $query->where($db->quoteName('a.published') . ' IN (0,1)'); } } $query->order($db->quoteName('a.lft')); $db->setQuery($query); $items = $db->loadObjectList(); // Collate menu items based on menutype $lookup = []; foreach ($items as &$item) { if (!isset($lookup[$item->menutype])) { $lookup[$item->menutype] = []; } $lookup[$item->menutype][] = &$item; // Translate the menu item title when client is administrator if ($clientId === 1) { $item->text = Text::_($item->text); } $item->text = str_repeat('- ', $item->level) . $item->text; } static::$items[$key] = []; $user = Factory::getUser(); $aclcheck = !empty($config['checkacl']) ? (int) $config['checkacl'] : 0; foreach ($menus as &$menu) { if ($aclcheck) { $action = $aclcheck == $menu->id ? 'edit' : 'create'; if (!$user->authorise('core.' . $action, 'com_menus.menu.' . $menu->id)) { continue; } } // Start group: $optGroup = new \stdClass(); $optGroup->value = '<OPTGROUP>'; $optGroup->text = $menu->text; static::$items[$key][] = $optGroup; // Special "Add to this Menu" option: static::$items[$key][] = HTMLHelper::_('select.option', $menu->value . '.1', Text::_('JLIB_HTML_ADD_TO_THIS_MENU')); // Menu items: if (isset($lookup[$menu->value])) { foreach ($lookup[$menu->value] as &$item) { static::$items[$key][] = HTMLHelper::_('select.option', $menu->value . '.' . $item->value, $item->text); } } // Finish group: $closeOptGroup = new \stdClass(); $closeOptGroup->value = '</OPTGROUP>'; $closeOptGroup->text = $menu->text; static::$items[$key][] = $closeOptGroup; } } return static::$items[$key]; } /** * Displays an HTML select list of menu items. * * @param string $name The name of the control. * @param string $selected The value of the selected option. * @param string $attribs Attributes for the control. * @param array $config An array of options for the control [id, published, checkacl, clientid]. * * @return string * * @since 1.6 */ public static function menuItemList($name, $selected = null, $attribs = null, $config = []) { static $count; $options = static::menuItems($config); return HTMLHelper::_( 'select.genericlist', $options, $name, [ 'id' => $config['id'] ?? 'assetgroups_' . (++$count), 'list.attr' => $attribs ?? 'class="inputbox" size="1"', 'list.select' => (int) $selected, 'list.translate' => false, ] ); } /** * Build the select list for Menu Ordering * * @param object $row The row object * @param integer $id The id for the row. Must exist to enable menu ordering * * @return string * * @since 1.5 */ public static function ordering(&$row, $id) { if ($id) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('ordering', 'value'), $db->quoteName('title', 'text'), ] ) ->from($db->quoteName('#__menu')) ->where( [ $db->quoteName('menutype') . ' = :menutype', $db->quoteName('parent_id') . ' = :parent', $db->quoteName('published') . ' != -2', ] ) ->order($db->quoteName('ordering')) ->bind(':menutype', $row->menutype) ->bind(':parent', $row->parent_id, ParameterType::INTEGER); $order = HTMLHelper::_('list.genericordering', $query); $ordering = HTMLHelper::_( 'select.genericlist', $order, 'ordering', ['list.attr' => 'class="inputbox" size="1"', 'list.select' => (int) $row->ordering] ); } else { $ordering = '<input type="hidden" name="ordering" value="' . $row->ordering . '">' . Text::_('JGLOBAL_NEWITEMSLAST_DESC'); } return $ordering; } /** * Build the multiple select list for Menu Links/Pages * * @param boolean $all True if all can be selected * @param boolean $unassigned True if unassigned can be selected * @param int $clientId The client id * * @return string * * @since 1.5 */ public static function linkOptions($all = false, $unassigned = false, $clientId = 0) { $db = Factory::getDbo(); // Get a list of the menu items $query = $db->getQuery(true) ->select( [ $db->quoteName('m.id'), $db->quoteName('m.parent_id'), $db->quoteName('m.title'), $db->quoteName('m.menutype'), $db->quoteName('m.client_id'), ] ) ->from($db->quoteName('#__menu', 'm')) ->where($db->quoteName('m.published') . ' = 1') ->order( [ $db->quoteName('m.client_id'), $db->quoteName('m.menutype'), $db->quoteName('m.parent_id'), ] ); if (isset($clientId)) { $clientId = (int) $clientId; $query->where($db->quoteName('m.client_id') . ' = :client') ->bind(':client', $clientId, ParameterType::INTEGER); } $db->setQuery($query); $mitems = $db->loadObjectList(); if (!$mitems) { $mitems = []; } // Establish the hierarchy of the menu $children = []; // First pass - collect children foreach ($mitems as $v) { $pt = $v->parent_id; $list = @$children[$pt] ? $children[$pt] : []; $list[] = $v; $children[$pt] = $list; } // Second pass - get an indent list of the items $list = static::treerecurse((int) $mitems[0]->parent_id, '', [], $children, 9999, 0, 0); // Code that adds menu name to Display of Page(s) $mitems = []; if ($all | $unassigned) { $mitems[] = HTMLHelper::_('select.option', '<OPTGROUP>', Text::_('JOPTION_MENUS')); if ($all) { $mitems[] = HTMLHelper::_('select.option', 0, Text::_('JALL')); } if ($unassigned) { $mitems[] = HTMLHelper::_('select.option', -1, Text::_('JOPTION_UNASSIGNED')); } $mitems[] = HTMLHelper::_('select.option', '</OPTGROUP>'); } $lastMenuType = null; $tmpMenuType = null; foreach ($list as $list_a) { if ($list_a->menutype != $lastMenuType) { if ($tmpMenuType) { $mitems[] = HTMLHelper::_('select.option', '</OPTGROUP>'); } $mitems[] = HTMLHelper::_('select.option', '<OPTGROUP>', $list_a->menutype); $lastMenuType = $list_a->menutype; $tmpMenuType = $list_a->menutype; } $mitems[] = HTMLHelper::_('select.option', $list_a->id, $list_a->title); } if ($lastMenuType !== null) { $mitems[] = HTMLHelper::_('select.option', '</OPTGROUP>'); } return $mitems; } /** * Build the list representing the menu tree * * @param integer $id Id of the menu item * @param string $indent The indentation string * @param array $list The list to process * @param array $children The children of the current item * @param integer $maxlevel The maximum number of levels in the tree * @param integer $level The starting level * @param int $type Set the type of spacer to use. Use 1 for |_ or 0 for - * * @return array * * @since 1.5 */ public static function treerecurse($id, $indent, $list, &$children, $maxlevel = 9999, $level = 0, $type = 1) { if ($level <= $maxlevel && isset($children[$id]) && is_array($children[$id])) { if ($type) { $pre = '<sup>|_</sup> '; $spacer = '.      '; } else { $pre = '- '; $spacer = '  '; } foreach ($children[$id] as $v) { $id = $v->id; if ($v->parent_id == 0) { $txt = $v->title; } else { $txt = $pre . $v->title; } $list[$id] = $v; $list[$id]->treename = $indent . $txt; if (isset($children[$id]) && is_array($children[$id])) { $list[$id]->children = count($children[$id]); $list = static::treerecurse($id, $indent . $spacer, $list, $children, $maxlevel, $level + 1, $type); } else { $list[$id]->children = 0; } } } return $list; } } PK0A#]��d��"src/HTML/Helpers/AdminLanguage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class working with administrator language select lists * * @since 3.8.0 */ abstract class AdminLanguage { /** * Cached array of the administrator language items. * * @var array * @since 3.8.0 */ protected static $items = null; /** * Get a list of the available administrator language items. * * @param boolean $all True to include All (*) * @param boolean $translate True to translate All * * @return array * * @since 3.8.0 */ public static function existing($all = false, $translate = false) { if (empty(static::$items)) { $languages = []; $admin_languages = LanguageHelper::getKnownLanguages(JPATH_ADMINISTRATOR); foreach ($admin_languages as $tag => $language) { $languages[$tag] = $language['nativeName']; } ksort($languages); static::$items = $languages; } if ($all) { $all_option = [new CMSObject(['value' => '*', 'text' => $translate ? Text::alt('JALL', 'language') : 'JALL_LANGUAGE'])]; return array_merge($all_option, static::$items); } else { return static::$items; } } } PK0A#]�~��!src/HTML/Helpers/SortableList.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML utility class for creating a sortable table list * * @since 3.0 * * @deprecated 4.0 will be removed in 6.0 * Sortable List will be deprecated in favour of a new dragula script in 4.0 */ abstract class SortableList { /** * Method to load the Sortable script and make table sortable * * @param string $tableId DOM id of the table * @param string $formId DOM id of the form * @param string $sortDir Sort direction * @param string $saveOrderingUrl Save ordering url, ajax-load after an item dropped * @param boolean $proceedSaveOrderButton Set whether a save order button is displayed * @param boolean $nestedList Set whether the list is a nested list * * @return void * * @since 3.0 * * @deprecated 4.0 will be removed in 6.0 * Use the new dragula script * Example: JHtml::_('draggablelist.draggable') and add a class of js-draggable to the tbody element of the table */ public static function sortable($tableId, $formId, $sortDir = 'asc', $saveOrderingUrl = null, $proceedSaveOrderButton = true, $nestedList = false) { HTMLHelper::_('draggablelist.draggable', $tableId, $formId, $sortDir, $saveOrderingUrl, $proceedSaveOrderButton, $nestedList); } } PK0A#]��& src/HTML/Helpers/SearchTools.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\Layout\LayoutHelper; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Searchtools elements. * * @since 3.2 */ abstract class SearchTools { /** * @var array Array containing information for loaded files * @since 3.2 */ protected static $loaded = []; /** * Load searchtools for a specific form * * @param mixed $selector Is debugging mode on? [optional] * @param array $options Optional array of parameters for search tools * * @return void * * @since 3.2 */ public static function form($selector = '.js-stools-form', $options = []) { $sig = md5(serialize([$selector, $options])); // Only load once if (!isset(static::$loaded[__METHOD__][$sig])) { // Add the form selector to the search tools options $options['formSelector'] = $selector; // Generate options with default values $options = static::optionsToRegistry($options); // Load the script && css files Factory::getApplication()->getDocument()->getWebAssetManager() ->useStyle('searchtools') ->useScript('searchtools'); Factory::getDocument()->addScriptOptions('searchtools', $options); static::$loaded[__METHOD__][$sig] = true; } } /** * Function to receive & pre-process javascript options * * @param mixed $options Associative array/Registry object with options * * @return Registry Options converted to Registry object */ private static function optionsToRegistry($options) { // Support options array if (is_array($options)) { $options = new Registry($options); } if (!($options instanceof Registry)) { $options = new Registry(); } return $options; } /** * Method to sort a column in a grid * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param mixed $selected The selected ordering * @param string $task An optional task override * @param string $newDirection An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $icon Icon to show * @param string $formName Name of the form to submit * * @return string */ public static function sort( $title, $order, $direction = 'asc', $selected = 0, $task = null, $newDirection = 'asc', $tip = '', $icon = null, $formName = 'adminForm' ) { $direction = strtolower($direction); $orderIcons = ['icon-caret-up', 'icon-caret-down']; $index = (int) ($direction === 'desc'); if ($order !== $selected) { $direction = $newDirection; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } // Create an object to pass it to the layouts $data = new \stdClass(); $data->order = $order; $data->direction = $direction; $data->selected = $selected; $data->task = $task; $data->tip = $tip; $data->title = $title; $data->orderIcon = $orderIcons[$index]; $data->icon = $icon; $data->formName = $formName; return LayoutHelper::render('joomla.searchtools.grid.sort', $data); } } PK0A#]m}Y��$src/HTML/Helpers/ActionsDropdown.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML utility class for building a dropdown menu * * @since 3.2 */ abstract class ActionsDropdown { /** * @var string HTML markup for the dropdown list * @since 3.2 */ protected static $dropDownList = []; /** * Method to render current dropdown menu * * @param string $item An item to render. * * @return string HTML markup for the dropdown list * * @since 3.2 */ public static function render($item = '') { $html = []; $html[] = '<button data-bs-toggle="dropdown" class="dropdown-toggle btn btn-sm btn-secondary">'; $html[] = '<span class="caret"></span>'; if ($item) { $html[] = '<span class="visually-hidden">' . Text::sprintf('JACTIONS', $item) . '</span>'; } $html[] = '</button>'; $html[] = '<ul class="dropdown-menu">'; $html[] = implode('', static::$dropDownList); $html[] = '</ul>'; static::$dropDownList = null; return implode('', $html); } /** * Append a publish item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function publish($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'publish'; static::addCustomItem(Text::_('JTOOLBAR_PUBLISH'), 'publish', $id, $task); } /** * Append an unpublish item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unpublish($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unpublish'; static::addCustomItem(Text::_('JTOOLBAR_UNPUBLISH'), 'unpublish', $id, $task); } /** * Append a feature item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function feature($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'featured'; static::addCustomItem(Text::_('JFEATURE'), 'featured', $id, $task); } /** * Append an unfeature item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unfeature($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unfeatured'; static::addCustomItem(Text::_('JUNFEATURE'), 'unfeatured', $id, $task); } /** * Append an archive item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function archive($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'archive'; static::addCustomItem(Text::_('JTOOLBAR_ARCHIVE'), 'archive', $id, $task); } /** * Append an unarchive item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unarchive($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unpublish'; static::addCustomItem(Text::_('JTOOLBAR_UNARCHIVE'), 'unarchive', $id, $task); } /** * Append a duplicate item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function duplicate($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'duplicate'; static::addCustomItem(Text::_('JTOOLBAR_DUPLICATE'), 'copy', $id, $task); } /** * Append a trash item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function trash($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'trash'; static::addCustomItem(Text::_('JTOOLBAR_TRASH'), 'trash', $id, $task); } /** * Append an untrash item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function untrash($id, $prefix = '') { self::publish($id, $prefix); } /** * Writes a divider between dropdown items * * @return void * * @since 3.0 */ public static function divider() { static::$dropDownList[] = '<li class="divider"></li>'; } /** * Append a custom item to current dropdown menu. * * @param string $label The label of the item. * @param string $icon The icon classname. * @param string $id The item id. * @param string $task The task. * * @return void * * @since 3.2 */ public static function addCustomItem($label, $icon = '', $id = '', $task = '') { Factory::getDocument()->getWebAssetManager()->useScript('list-view'); static::$dropDownList[] = '<li>' . HTMLHelper::link( '#', ($icon ? LayoutHelper::render('joomla.icon.iconclass', ['icon' => $icon]) : '') . $label, [ 'data-item-id' => $id, 'data-item-task' => $task, 'class' => 'js-grid-item-action', ] ) . '</li>'; } } PK0A#]6�!�׃׃src/HTML/Helpers/Bootstrap.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for Bootstrap elements. * * @since 3.0 */ abstract class Bootstrap { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = []; /** * Add javascript support for Bootstrap alerts * * @param string $selector Common class for the alerts * * @return void * * @throws \Exception * * @since 3.0 */ public static function alert($selector = ''): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } $doc = Factory::getDocument(); if ($selector !== '') { $scriptOptions = $doc->getScriptOptions('bootstrap.alert'); $options = [$selector]; if (is_array($scriptOptions)) { $options = array_merge($scriptOptions, $options); } $doc->addScriptOptions('bootstrap.alert', $options, false); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.alert'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap buttons * * @param string $selector Common class for the buttons * * @return void * * @throws \Exception * * @since 3.1 */ public static function button($selector = ''): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } $doc = Factory::getDocument(); if ($selector !== '') { $scriptOptions = $doc->getScriptOptions('bootstrap.button'); $options = [$selector]; if (is_array($scriptOptions)) { $options = array_merge($scriptOptions, $options); } $doc->addScriptOptions('bootstrap.button', $options, false); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.button'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap carousels * * @param string $selector Common class for the carousels. * @param array $params An array of options for the carousel. * * @return void * * @throws \Exception * * @since 3.0 * * Options for the carousel can be: * - interval number 5000 The amount of time to delay between automatically cycling an item. * If false, carousel will not automatically cycle. * - keyboard boolean true Whether the carousel should react to keyboard events. * - pause string| hover Pauses the cycling of the carousel on mouseenter and resumes the cycling * boolean of the carousel on mouseleave. * - slide string| false Autoplays the carousel after the user manually cycles the first item. * boolean If "carousel", autoplays the carousel on load. */ public static function carousel($selector = '', $params = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'interval' => (isset($params['interval']) ? (int) $params['interval'] : 5000), 'keyboard' => (isset($params['keyboard']) ? (bool) $params['keyboard'] : true), 'pause' => (isset($params['pause']) ? $params['pause'] : 'hover'), 'slide' => (isset($params['slide']) ? (bool) $params['slide'] : false), 'wrap' => (isset($params['wrap']) ? (bool) $params['wrap'] : true), 'touch' => (isset($params['touch']) ? (bool) $params['touch'] : true), ]; Factory::getDocument()->addScriptOptions('bootstrap.carousel', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.carousel'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap collapse * * @param string $selector Common class for the collapse * @param string[] $params Additional parameters - see below * * @return void * * @throws \Exception * * @since 4.0.0 * * Options for the collapse can be: * - parent string false If parent is provided, then all collapsible elements under the specified parent will * be closed when this collapsible item is shown. * - toggle boolean true Toggles the collapsible element on invocation */ public static function collapse($selector = '', $params = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = []; $opt['parent'] = isset($params['parent']) ? $params['parent'] : false; $opt['toggle'] = isset($params['toggle']) ? (bool) $params['toggle'] : true; Factory::getDocument()->addScriptOptions('bootstrap.collapse', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.collapse'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap dropdowns * * @param string $selector Common class for the dropdowns * @param array $params The options for the dropdowns * * @return void * * @since 4.0.0 * * Options for the collapse can be: * - flip boolean true Allow Dropdown to flip in case of an overlapping on the reference element * - boundary string scrollParent Overflow constraint boundary of the dropdown menu * - reference string toggle Reference element of the dropdown menu. Accepts 'toggle' or 'parent' * - display string dynamic By default, we use Popper for dynamic positioning. Disable this with static */ public static function dropdown($selector = '', $params = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = []; $opt['flip'] = isset($params['flip']) ? $params['flip'] : true; $opt['boundary'] = isset($params['boundary']) ? $params['boundary'] : 'scrollParent'; $opt['reference'] = isset($params['reference']) ? $params['reference'] : 'toggle'; $opt['display'] = isset($params['display']) ? $params['display'] : 'dynamic'; $opt['popperConfig'] = isset($params['popperConfig']) ? (bool) $params['popperConfig'] : true; Factory::getDocument()->addScriptOptions('bootstrap.dropdown', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.dropdown'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap modal * * @param string $selector The ID selector for the modal. * @param array $options An array of options for the modal. * * @return void * * @since 4.0.0 * * Options for the modal can be: * - backdrop string| true Includes a modal-backdrop element. Alternatively, specify static * boolean for a backdrop which doesn't close the modal on click. * - keyboard boolean true Closes the modal when escape key is pressed * - focus boolean true Closes the modal when escape key is pressed */ public static function modal($selector = '', $options = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'backdrop' => (isset($options['backdrop']) ? $options['backdrop'] : false), 'keyboard' => (isset($options['keyboard']) ? (bool) $options['keyboard'] : true), 'focus' => (isset($options['focus']) ? (bool) $options['focus'] : true), ]; Factory::getDocument()->addScriptOptions('bootstrap.modal', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.modal'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap offcanvas * * @param string $selector The ID selector for the offcanvas. * @param array $options An array of options for the offcanvas. * * @return void * * @since 4.0.0 * * Options for the offcanvas can be: * - backdrop boolean true Apply a backdrop on body while offcanvas is open * - keyboard boolean true Closes the offcanvas when escape key is pressed * - scroll boolean false Allow body scrolling while offcanvas is open */ public static function offcanvas($selector = '', $options = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'backdrop' => (isset($options['backdrop']) ? (bool) $options['backdrop'] : true), 'keyboard' => (isset($options['keyboard']) ? (bool) $options['keyboard'] : true), 'scroll' => (isset($options['scroll']) ? (bool) $options['scroll'] : false), ]; Factory::getDocument()->addScriptOptions('bootstrap.offcanvas', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.offcanvas'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap popovers * * Use element's Title as popover content * * @param string $selector Selector for the popover * @param array $options The options for the popover * * @return void * * @since 3.0 * * - Options for the popover can be: * - animation boolean true Apply a CSS fade transition to the popover * - container string| false Appends the popover to a specific element. Eg.: 'body' * boolean * - content string null Default content value if data-bs-content attribute isn't present * - delay number 0 Delay showing and hiding the popover (ms) * does not apply to manual trigger type * - html boolean true Insert HTML into the popover. If false, innerText property will be used * to insert content into the DOM. * - placement string right How to position the popover - auto | top | bottom | left | right. * When auto is specified, it will dynamically reorient the popover * - selector string false If a selector is provided, popover objects will be delegated to the * specified targets. * - template string null Base HTML to use when creating the popover. * - title string null Default title value if `title` tag isn't present * - trigger string click How popover is triggered - click | hover | focus | manual * - offset integer 0 Offset of the popover relative to its target. */ public static function popover($selector = '', $options = []): void { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'animation' => isset($options['animation']) ? (bool) $options['animation'] : true, 'container' => isset($options['container']) ? $options['container'] : 'body', 'content' => isset($options['content']) ? $options['content'] : null, 'delay' => isset($options['delay']) ? (int) $options['delay'] : ['show' => 50, 'hide' => 200], 'html' => isset($options['html']) ? (bool) $options['html'] : true, 'placement' => isset($options['placement']) ? $options['placement'] : null, 'selector' => isset($options['selector']) ? $options['selector'] : false, 'template' => isset($options['template']) ? $options['template'] : null, 'title' => isset($options['title']) ? $options['title'] : null, 'trigger' => isset($options['trigger']) ? $options['trigger'] : 'click', 'offset' => isset($options['offset']) ? $options['offset'] : [0, 10], 'fallbackPlacement' => isset($options['fallbackPlacement']) ? $options['fallbackPlacement'] : null, 'boundary' => isset($options['boundary']) ? $options['boundary'] : 'scrollParent', 'customClass' => isset($options['customClass']) ? $options['customClass'] : null, 'sanitize' => isset($options['sanitize']) ? (bool) $options['sanitize'] : null, 'allowList' => isset($options['allowList']) ? $options['allowList'] : null, ]; Factory::getDocument()->addScriptOptions('bootstrap.popover', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.popover'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap Scrollspy * * @param string $selector The ID selector for the ScrollSpy element. * @param array $options An array of options for the ScrollSpy. * * @return void * * @since 3.0 * * Options for the Scrollspy can be: * - offset number Pixels to offset from top when calculating position of scroll. * - method string Finds which section the spied element is in. * - target string Specifies element to apply Scrollspy plugin. */ public static function scrollspy($selector = '', $options = []): void { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'offset' => isset($options['offset']) ? (int) $options['offset'] : 10, 'method' => isset($options['method']) ? $options['method'] : 'auto', 'target' => isset($options['target']) ? $options['target'] : null, ]; Factory::getDocument()->addScriptOptions('bootstrap.scrollspy', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.scrollspy'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap tab * * @param string $selector Common class for the tabs * @param array $options Options for the tabs * * @return void * * @throws \Exception * * @since 4.0.0 */ public static function tab($selector = '', $options = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { Factory::getDocument()->addScriptOptions('bootstrap.tabs', [$selector => (object) $options]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.tab'); static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap tooltips * * Add a title attribute to any element in the form * title="title::text" * * @param string $selector The ID selector for the tooltip. * @param array $options An array of options for the tooltip. * * @return void * * @since 3.0 * * Options for the tooltip can be: * - animation boolean apply a css fade transition to the popover * - container string|boolean Appends the popover to a specific element: { container: 'body' } * - delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type * If a number is supplied, delay is applied to both hide/show * Object structure is: delay: { show: 500, hide: 100 } * - html boolean Insert HTML into the popover. If false, jQuery's text method will be used to * insert content into the dom. * - placement string|function how to position the popover - top | bottom | left | right * - selector string If a selector is provided, popover objects will be * delegated to the specified targets. * - template string Base HTML to use when creating the popover. * - title string|function default title value if `title` tag isn't present * - trigger string how popover is triggered - hover | focus | manual * - constraints array An array of constraints - passed through to Popper. * - offset string Offset of the popover relative to its target. */ public static function tooltip($selector = '', $options = []): void { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'animation' => isset($options['animation']) ? (bool) $options['animation'] : true, 'container' => isset($options['container']) ? $options['container'] : 'body', 'delay' => isset($options['delay']) ? (int) $options['delay'] : 0, 'html' => isset($options['html']) ? (bool) $options['html'] : true, 'placement' => isset($options['placement']) ? $options['placement'] : null, 'selector' => isset($options['selector']) ? $options['selector'] : false, 'template' => isset($options['template']) ? $options['template'] : null, 'title' => isset($options['title']) ? $options['title'] : null, 'trigger' => isset($options['trigger']) ? $options['trigger'] : 'hover focus', 'fallbackPlacement' => isset($options['fallbackPlacement']) ? $options['fallbackPlacement'] : null, 'boundary' => isset($options['boundary']) ? $options['boundary'] : 'clippingParents', 'customClass' => isset($options['customClass']) ? $options['customClass'] : null, 'sanitize' => isset($options['sanitize']) ? (bool) $options['sanitize'] : true, 'allowList' => isset($options['allowList']) ? $options['allowList'] : null, ]; Factory::getDocument()->addScriptOptions('bootstrap.tooltip', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.popover'); // Set static array static::$loaded[__METHOD__][$selector] = true; } /** * Add javascript support for Bootstrap toasts * * @param string $selector Common class for the toasts * @param array $options Options for the toasts * * @return void * * @throws \Exception * * @since 4.0.0 */ public static function toast($selector = '', $options = []): void { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return; } if ($selector !== '') { // Setup options object $opt = [ 'animation' => isset($options['animation']) ? (string) $options['animation'] : null, 'autohide' => isset($options['autohide']) ? (bool) $options['autohide'] : true, 'delay' => isset($options['delay']) ? (int) $options['delay'] : 5000, ]; Factory::getDocument()->addScriptOptions('bootstrap.toast', [$selector => (object) array_filter((array) $opt)]); } // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.toast'); static::$loaded[__METHOD__][$selector] = true; } /** * Method to load the ALL the Bootstrap Components * * If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging. * * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement * Load the different scripts with their individual method calls */ public static function framework($debug = null): void { $wa = Factory::getApplication() ->getDocument() ->getWebAssetManager(); array_map( function ($script) use ($wa) { $wa->useScript('bootstrap.' . $script); }, ['alert', 'button', 'carousel', 'collapse', 'dropdown', 'modal', 'offcanvas', 'popover', 'scrollspy', 'tab', 'toast'] ); } /** * Loads CSS files needed by Bootstrap * * @param boolean $includeMainCss If true, main bootstrap.css files are loaded * @param string $direction rtl or ltr direction. If empty, ltr is assumed * @param array $attribs Optional array of attributes to be passed to HTMLHelper::_('stylesheet') * * @return void * * @since 3.0 */ public static function loadCss($includeMainCss = true, $direction = 'ltr', $attribs = []): void { // Load Bootstrap main CSS if ($includeMainCss) { Factory::getDocument()->getWebAssetManager()->useStyle('bootstrap.css'); } } /** * Add javascript support for Bootstrap accordions and insert the accordion * * @param string $selector The ID selector for the tooltip. Expects a valid ID without the #! * @param array $options An array of options for the tooltip. * * @return string HTML for the accordion * * @since 3.0 * * Options for the tooltip can be: * - parent selector If selector then all collapsible elements under the specified parent will be closed when this * collapsible item is shown. (similar to traditional accordion behavior) * - toggle boolean Toggles the collapsible element on invocation * - active string Sets the active slide during load */ public static function startAccordion($selector = 'myAccordian', $options = []): string { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return ''; } // Include Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.collapse'); // Setup options object $opt = []; $opt['parent'] = isset($options['parent']) ? ($options['parent'] == true ? '#' . preg_replace('/^[\.#]/', '', $selector) : $options['parent']) : ''; $opt['toggle'] = isset($options['toggle']) ? (bool) $options['toggle'] : !($opt['parent'] === false || isset($options['active'])); $opt['active'] = isset($options['active']) ? (string) $options['active'] : ''; // Initialise with the Joomla specifics $opt['isJoomla'] = true; Factory::getDocument()->addScriptOptions('bootstrap.accordion', ['#' . preg_replace('/^[\.#]/', '', $selector) => (object) array_filter((array) $opt)]); static::$loaded[__METHOD__][$selector] = $opt; return '<div id="' . $selector . '" class="accordion" role="tablist">'; } /** * Close the current accordion * * @return string HTML to close the accordion * * @since 3.0 */ public static function endAccordion(): string { return '</div>'; } /** * Begins the display of a new accordion slide. * * @param string $selector Identifier of the accordion group. * @param string $text Text to display. * @param string $id Identifier of the slide. * @param string $class Class of the accordion group. * * @return string HTML to add the slide * * @since 3.0 */ public static function addSlide($selector, $text, $id, $class = ''): string { $in = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] === $id ? ' show' : ''; $collapsed = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] === $id ? '' : ' collapsed'; $parent = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] ? 'data-bs-parent="' . static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] . '"' : ''; $class = (!empty($class)) ? ' ' . $class : ''; $ariaExpanded = $in === 'show' ? true : false; return <<<HTMLSTR <div class="accordion-item $class"> <h2 class="accordion-header" id="$id-heading"> <button class="accordion-button $collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#$id" aria-expanded="$ariaExpanded" aria-controls="$id" role="tab"> $text </button> </h2> <div id="$id" class="accordion-collapse collapse $in" aria-labelledby="$id-heading" $parent role="tabpanel"> <div class="accordion-body"> HTMLSTR; } /** * Close the current slide * * @return string HTML to close the slide * * @since 3.0 */ public static function endSlide(): string { return <<<HTMLSTR </div> </div> </div> HTMLSTR; } /** * Method to render a Bootstrap modal * * @param string $selector The ID selector for the modal. Expects a valid ID without the #! * @param array $options An array of options for the modal. * @param string $body Markup for the modal body. Appended after the `<iframe>` if the URL option is set * * @return string HTML markup for a modal * * @since 3.0 * * Options for the modal can be: * - backdrop string| true Includes a modal-backdrop element. Alternatively, specify static * boolean for a backdrop which doesn't close the modal on click. * - keyboard boolean true Closes the modal when escape key is pressed * - focus boolean true Closes the modal when escape key is pressed * - title string null The modal title * - closeButton boolean true Display modal close button (default = true) * - footer string null Optional markup for the modal footer * - url string null URL of a resource to be inserted as an `<iframe>` inside the modal body * - height string null Height of the `<iframe>` containing the remote resource * - width string null Width of the `<iframe>` containing the remote resource */ public static function renderModal($selector = 'modal', $options = [], $body = ''): string { // Only load once if (!empty(static::$loaded[__METHOD__][$selector])) { return ''; } // Initialise with the Joomla specifics $options['isJoomla'] = true; // Include Basic Bootstrap component HTMLHelper::_('bootstrap.modal', '#' . preg_replace('/^[\.#]/', '', $selector), $options); $layoutData = [ 'selector' => $selector, 'params' => $options, 'body' => $body, ]; static::$loaded[__METHOD__][$selector] = true; return LayoutHelper::render('libraries.html.bootstrap.modal.main', $layoutData); } /** * Creates a tab pane * * @param string $selector The pane identifier. Expects a valid ID without the #! * @param array $params The parameters for the pane * * @return string * * @since 3.1 */ public static function startTabSet($selector = 'myTab', $params = []): string { $sig = md5(serialize([$selector, $params])); if (!isset(static::$loaded[__METHOD__][$sig])) { // Setup options object $opt = []; $opt['active'] = (isset($params['active']) && ($params['active'])) ? (string) $params['active'] : ''; // Initialise with the Joomla specifics $opt['isJoomla'] = true; // Include the Bootstrap Tab Component HTMLHelper::_('bootstrap.tab', '#' . preg_replace('/^[\.#]/', '', $selector), $opt); // Set static array static::$loaded[__METHOD__][$sig] = true; static::$loaded[__METHOD__][$selector]['active'] = $opt['active']; return LayoutHelper::render('libraries.html.bootstrap.tab.starttabset', ['selector' => $selector]); } } /** * Close the current tab pane * * @return string HTML to close the pane * * @since 3.1 */ public static function endTabSet(): string { return LayoutHelper::render('libraries.html.bootstrap.tab.endtabset'); } /** * Begins the display of a new tab content panel. * * @param string $selector Identifier of the panel. Expects a valid ID without the #! * @param string $id The ID of the div element. Expects a valid ID without the #! * @param string $title The title text for the new UL tab * * @return string HTML to start a new panel * * @since 3.1 */ public static function addTab($selector, $id, $title): string { static $tabLayout = null; $tabLayout = $tabLayout === null ? new FileLayout('libraries.html.bootstrap.tab.addtab') : $tabLayout; $active = (static::$loaded[__CLASS__ . '::startTabSet'][$selector]['active'] == $id) ? ' active' : ''; return $tabLayout->render(['id' => preg_replace('/^[\.#]/', '', $id), 'active' => $active, 'title' => $title]); } /** * Close the current tab content panel * * @return string HTML to close the pane * * @since 3.1 */ public static function endTab(): string { return LayoutHelper::render('libraries.html.bootstrap.tab.endtab'); } } PK0A#]��e�src/HTML/Helpers/Debug.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extended Utility class for render debug information. * * @since 3.7.0 */ abstract class Debug { /** * xdebug.file_link_format from the php.ini. * * Make this property public to support test. * * @var string * * @since 3.7.0 */ public static $xdebugLinkFormat; /** * Replaces the Joomla! root with "JROOT" to improve readability. * Formats a link with a special value xdebug.file_link_format * from the php.ini file. * * @param string $file The full path to the file. * @param string $line The line number. * * @return string * * @throws \InvalidArgumentException * * @since 3.7.0 */ public static function xdebuglink($file, $line = '') { if (static::$xdebugLinkFormat === null) { static::$xdebugLinkFormat = ini_get('xdebug.file_link_format'); } $link = str_replace(JPATH_ROOT, 'JROOT', Path::clean($file)); $link .= $line ? ':' . $line : ''; if (static::$xdebugLinkFormat) { $href = static::$xdebugLinkFormat; $href = str_replace('%f', $file, $href); $href = str_replace('%l', $line, $href); $html = HTMLHelper::_('link', $href, $link); } else { $html = $link; } return $html; } } PK0A#]C��,,!src/HTML/Helpers/StringHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\HTML\HTMLHelper; use Joomla\String\StringHelper as FrameworkStringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML helper class for rendering manipulated strings. * * @since 1.6 */ abstract class StringHelper { /** * Truncates text blocks over the specified character limit and closes * all open HTML tags. The method will optionally not truncate an individual * word, it will find the first space that is within the limit and * truncate at that point. This method is UTF-8 safe. * * @param string $text The text to truncate. * @param integer $length The maximum length of the text. * @param boolean $noSplit Don't split a word if that is where the cutoff occurs (default: true). * @param boolean $allowHtml Allow HTML tags in the output, and close any open tags (default: true). * * @return string The truncated text. * * @since 1.6 */ public static function truncate($text, $length = 0, $noSplit = true, $allowHtml = true) { // Assume a lone open tag is invalid HTML. if ($length === 1 && $text[0] === '<') { return '...'; } // Check if HTML tags are allowed. if (!$allowHtml) { // Decode entities $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); // Deal with spacing issues in the input. $text = str_replace('>', '> ', $text); $text = str_replace([' ', ' '], ' ', $text); $text = FrameworkStringHelper::trim(preg_replace('#\s+#mui', ' ', $text)); // Strip tags from the input. $text = strip_tags($text); // Remove remaining extra spaces. $text = str_replace(' ', ' ', $text); $text = FrameworkStringHelper::trim(preg_replace('#\s+#mui', ' ', $text)); } // Whether or not allowing HTML, truncate the item text if it is too long. if ($length > 0 && FrameworkStringHelper::strlen($text) > $length) { $tmp = trim(FrameworkStringHelper::substr($text, 0, $length)); if ($tmp[0] === '<' && strpos($tmp, '>') === false) { return '...'; } // $noSplit true means that we do not allow splitting of words. if ($noSplit) { // Find the position of the last space within the allowed length. $offset = FrameworkStringHelper::strrpos($tmp, ' '); $tmp = FrameworkStringHelper::substr($tmp, 0, $offset + 1); // If there are no spaces and the string is longer than the maximum // we need to just use the ellipsis. In that case we are done. if ($offset === false && strlen($text) > $length) { return '...'; } if (FrameworkStringHelper::strlen($tmp) > $length - 3) { $tmp = trim(FrameworkStringHelper::substr($tmp, 0, FrameworkStringHelper::strrpos($tmp, ' '))); } } if ($allowHtml) { // Put all opened tags into an array preg_match_all("#<([a-z][a-z0-9]*)\b.*?(?!/)>#i", $tmp, $result); $openedTags = $result[1]; // Some tags self close so they do not need a separate close tag. $openedTags = array_diff($openedTags, ['img', 'hr', 'br']); $openedTags = array_values($openedTags); // Put all closed tags into an array preg_match_all("#</([a-z][a-z0-9]*)\b(?:[^>]*?)>#iU", $tmp, $result); $closedTags = $result[1]; $numOpened = count($openedTags); // Not all tags are closed so trim the text and finish. if (count($closedTags) !== $numOpened) { // Closing tags need to be in the reverse order of opening tags. $openedTags = array_reverse($openedTags); // Close tags for ($i = 0; $i < $numOpened; $i++) { if (!in_array($openedTags[$i], $closedTags)) { $tmp .= '</' . $openedTags[$i] . '>'; } else { unset($closedTags[array_search($openedTags[$i], $closedTags)]); } } } // Check if we are within a tag if (FrameworkStringHelper::strrpos($tmp, '<') > FrameworkStringHelper::strrpos($tmp, '>')) { $offset = FrameworkStringHelper::strrpos($tmp, '<'); $tmp = FrameworkStringHelper::trim(FrameworkStringHelper::substr($tmp, 0, $offset)); } } if ($tmp === false || strlen($text) > strlen($tmp)) { $text = trim($tmp) . '...'; } } // Clean up any internal spaces created by the processing. $text = str_replace(' </', '</', $text); $text = str_replace(' ...', '...', $text); return $text; } /** * Method to extend the truncate method to more complex situations * * The goal is to get the proper length plain text string with as much of * the html intact as possible with all tags properly closed. * * @param string $html The content of the introtext to be truncated * @param integer $maxLength The maximum number of characters to render * @param boolean $noSplit Don't split a word if that is where the cutoff occurs (default: true). * * @return string The truncated string. If the string is truncated an ellipsis * (...) will be appended. * * @note If a maximum length of 3 or less is selected and the text has more than * that number of characters an ellipsis will be displayed. * This method will not create valid HTML from malformed HTML. * * @since 3.1 */ public static function truncateComplex($html, $maxLength = 0, $noSplit = true) { // Start with some basic rules. $baseLength = strlen($html); // If the original HTML string is shorter than the $maxLength do nothing and return that. if ($baseLength <= $maxLength || $maxLength === 0) { return $html; } // Take care of short simple cases. if ($maxLength <= 3 && $html[0] !== '<' && strpos(substr($html, 0, $maxLength - 1), '<') === false && $baseLength > $maxLength) { return '...'; } // Deal with maximum length of 1 where the string starts with a tag. if ($maxLength === 1 && $html[0] === '<') { $endTagPos = strlen(strstr($html, '>', true)); $tag = substr($html, 1, $endTagPos); $l = $endTagPos + 1; if ($noSplit) { return substr($html, 0, $l) . '</' . $tag . '...'; } // @todo: $character doesn't seem to be used... $character = substr(strip_tags($html), 0, 1); return substr($html, 0, $l) . '</' . $tag . '...'; } // First get the truncated plain text string. This is the rendered text we want to end up with. $ptString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = false); // It's all HTML, just return it. if ($ptString === '') { return $html; } // If the plain text is shorter than the max length the variable will not end in ... // In that case we use the whole string. if (substr($ptString, -3) !== '...') { return $html; } // Regular truncate gives us the ellipsis but we want to go back for text and tags. if ($ptString === '...') { $stripped = substr(strip_tags($html), 0, $maxLength); $ptString = HTMLHelper::_('string.truncate', $stripped, $maxLength, $noSplit, $allowHtml = false); } // We need to trim the ellipsis that truncate adds. $ptString = rtrim($ptString, '.'); // Now deal with more complex truncation. while ($maxLength <= $baseLength) { // Get the truncated string assuming HTML is allowed. $htmlString = HTMLHelper::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = true); if ($htmlString === '...' && strlen($ptString) + 3 > $maxLength) { return $htmlString; } $htmlString = rtrim($htmlString, '.'); // Now get the plain text from the HTML string and trim it. $htmlStringToPtString = HTMLHelper::_('string.truncate', $htmlString, $maxLength, $noSplit, $allowHtml = false); $htmlStringToPtString = rtrim($htmlStringToPtString, '.'); // If the new plain text string matches the original plain text string we are done. if ($ptString === $htmlStringToPtString) { return $htmlString . '...'; } // Get the number of HTML tag characters in the first $maxLength characters $diffLength = strlen($ptString) - strlen($htmlStringToPtString); if ($diffLength <= 0) { return $htmlString . '...'; } // Set new $maxlength that adjusts for the HTML tags $maxLength += $diffLength; } } /** * Abridges text strings over the specified character limit. The * behavior will insert an ellipsis into the text replacing a section * of variable size to ensure the string does not exceed the defined * maximum length. This method is UTF-8 safe. * * For example, it transforms "Really long title" to "Really...title". * * Note that this method does not scan for HTML tags so will potentially break them. * * @param string $text The text to abridge. * @param integer $length The maximum length of the text (default is 50). * @param integer $intro The maximum length of the intro text (default is 30). * * @return string The abridged text. * * @since 1.6 */ public static function abridge($text, $length = 50, $intro = 30) { // Abridge the item text if it is too long. if (FrameworkStringHelper::strlen($text) > $length) { // Determine the remaining text length. $remainder = $length - ($intro + 3); // Extract the beginning and ending text sections. $beg = FrameworkStringHelper::substr($text, 0, $intro); $end = FrameworkStringHelper::substr($text, FrameworkStringHelper::strlen($text) - $remainder); // Build the resulting string. $text = $beg . '...' . $end; } return $text; } } PK0A#]b�{�""src/HTML/Helpers/ListHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseQuery; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for creating different select lists * * @since 1.5 */ abstract class ListHelper { /** * Build the select list to choose an image * * @param string $name The name of the field * @param string $active The selected item * @param string $javascript Alternative javascript * @param string $directory Directory the images are stored in * @param string $extensions Allowed extensions * * @return array Image names * * @since 1.5 */ public static function images($name, $active = null, $javascript = null, $directory = null, $extensions = 'bmp|gif|jpg|png') { if (!$directory) { $directory = '/images/'; } if (!$javascript) { $javascript = "onchange=\"if (document.forms.adminForm." . $name . ".options[selectedIndex].value!='') {document.imagelib.src='..$directory' + document.forms.adminForm." . $name . ".options[selectedIndex].value} else {document.imagelib.src='media/system/images/blank.png'}\""; } $imageFiles = new \DirectoryIterator(JPATH_SITE . '/' . $directory); $images = [HTMLHelper::_('select.option', '', Text::_('JOPTION_SELECT_IMAGE'))]; foreach ($imageFiles as $file) { $fileName = $file->getFilename(); if (!$file->isFile()) { continue; } if (preg_match('#(' . $extensions . ')$#', $fileName)) { $images[] = HTMLHelper::_('select.option', $fileName); } } $images = HTMLHelper::_( 'select.genericlist', $images, $name, [ 'list.attr' => 'size="1" ' . $javascript, 'list.select' => $active, ] ); return $images; } /** * Returns an array of options * * @param DatabaseQuery|string $query SQL with 'ordering' AS value and 'name field' AS text * @param integer $chop The length of the truncated headline * * @return array An array of objects formatted for JHtml list processing * * @since 1.5 */ public static function genericordering($query, $chop = 30) { $db = Factory::getDbo(); $options = []; $db->setQuery($query); $items = $db->loadObjectList(); if (empty($items)) { $options[] = HTMLHelper::_('select.option', 1, Text::_('JLIB_FORM_FIELD_PARAM_INTEGER_FIRST_LABEL')); return $options; } $options[] = HTMLHelper::_('select.option', 0, ' - ' . Text::_('JLIB_FORM_FIELD_PARAM_INTEGER_FIRST_LABEL') . ' - '); for ($i = 0, $n = count($items); $i < $n; $i++) { $items[$i]->text = Text::_($items[$i]->text); if (StringHelper::strlen($items[$i]->text) > $chop) { $text = StringHelper::substr($items[$i]->text, 0, $chop) . '...'; } else { $text = $items[$i]->text; } $options[] = HTMLHelper::_('select.option', $items[$i]->value, $text); } $options[] = HTMLHelper::_('select.option', $items[$i - 1]->value + 1, ' - ' . Text::_('JLIB_FORM_FIELD_PARAM_INTEGER_LAST_LABEL') . ' - '); return $options; } /** * Build the select list for Ordering derived from a query * * @param integer $name The scalar value * @param string $query The query * @param string $attribs HTML tag attributes * @param string $selected The selected item * @param integer $neworder 1 if new and first, -1 if new and last, 0 or null if existing item * @param string $id ID attribute for the resulting <select> element * * @return string HTML markup for the select list * * @since 1.6 */ public static function ordering($name, $query, $attribs = null, $selected = null, $neworder = null, ?string $id = null) { if (empty($attribs)) { $attribs = 'size="1"'; } if (empty($neworder)) { $orders = HTMLHelper::_('list.genericordering', $query); $html = HTMLHelper::_( 'select.genericlist', $orders, $name, ['list.attr' => $attribs, 'list.select' => (int) $selected, 'id' => $id ?? false] ); } else { if ($neworder > 0) { $text = Text::_('JGLOBAL_NEWITEMSLAST_DESC'); } elseif ($neworder <= 0) { $text = Text::_('JGLOBAL_NEWITEMSFIRST_DESC'); } $html = '<input type="hidden" name="' . $name . '" value="' . (int) $selected . '"><span class="readonly">' . $text . '</span>'; } return $html; } /** * Select list of active users * * @param string $name The name of the field * @param string $active The active user * @param integer $nouser If set include an option to select no user * @param string $javascript Custom javascript * @param string $order Specify a field to order by * * @return string The HTML for a list of users list of users * * @since 1.5 */ public static function users($name, $active, $nouser = 0, $javascript = null, $order = 'name') { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('u.id', 'value'), $db->quoteName('u.name', 'text'), ] ) ->from($db->quoteName('#__users', 'u')) ->join('LEFT', $db->quoteName('#__user_usergroup_map', 'm'), $db->quoteName('m.user_id') . ' = ' . $db->quoteName('u.id')) ->where($db->quoteName('u.block') . ' = 0') ->order($order) ->group($db->quoteName('u.id')); $db->setQuery($query); if ($nouser) { $users[] = HTMLHelper::_('select.option', '0', Text::_('JOPTION_NO_USER')); $users = array_merge($users, $db->loadObjectList()); } else { $users = $db->loadObjectList(); } $users = HTMLHelper::_( 'select.genericlist', $users, $name, [ 'list.attr' => 'size="1" ' . $javascript, 'list.select' => $active, ] ); return $users; } /** * Select list of positions - generally used for location of images * * @param string $name Name of the field * @param string $active The active value * @param string $javascript Alternative javascript * @param boolean $none Null if not assigned * @param boolean $center Null if not assigned * @param boolean $left Null if not assigned * @param boolean $right Null if not assigned * @param boolean $id Null if not assigned * * @return array The positions * * @since 1.5 */ public static function positions( $name, $active = null, $javascript = null, $none = true, $center = true, $left = true, $right = true, $id = false ) { $pos = []; if ($none) { $pos[''] = Text::_('JNONE'); } if ($center) { $pos['center'] = Text::_('JGLOBAL_CENTER'); } if ($left) { $pos['left'] = Text::_('JGLOBAL_LEFT'); } if ($right) { $pos['right'] = Text::_('JGLOBAL_RIGHT'); } $positions = HTMLHelper::_( 'select.genericlist', $pos, $name, [ 'id' => $id, 'list.attr' => 'size="1"' . $javascript, 'list.select' => $active, 'option.key' => null, ] ); return $positions; } } PK0A#]�����src/HTML/Helpers/Links.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Layout\FileLayout; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for icons. * * @since 3.2 */ abstract class Links { /** * Method to generate html code for groups of lists of links * * @param array $groupsOfLinks Array of links * * @return string * * @since 3.2 */ public static function linksgroups($groupsOfLinks) { $html = []; if (count($groupsOfLinks) > 0) { $layout = new FileLayout('joomla.links.groupsopen'); $html[] = $layout->render(''); foreach ($groupsOfLinks as $title => $links) { if (isset($links[0]['separategroup'])) { $layout = new FileLayout('joomla.links.groupseparator'); $html[] = $layout->render($title); } $layout = new FileLayout('joomla.links.groupopen'); $htmlHeader = $layout->render($title); $htmlLinks = HTMLHelper::_('links.links', $links); if ($htmlLinks !== '') { $html[] = $htmlHeader; $html[] = $htmlLinks; $layout = new FileLayout('joomla.links.groupclose'); $html[] = $layout->render(''); } } $layout = new FileLayout('joomla.links.groupsclose'); $html[] = $layout->render(''); } return implode($html); } /** * Method to generate html code for a list of links * * @param array $links Array of links * * @return string * * @since 3.2 */ public static function links($links) { $html = []; foreach ($links as $link) { $html[] = HTMLHelper::_('links.link', $link); } return implode($html); } /** * Method to generate html code for a single link * * @param array $link link properties * * @return string * * @since 3.2 */ public static function link($link) { if (isset($link['access'])) { if (is_bool($link['access'])) { if ($link['access'] == false) { return ''; } } else { // Get the user object to verify permissions $user = Factory::getUser(); // Take each pair of permission, context values. for ($i = 0, $n = count($link['access']); $i < $n; $i += 2) { if (!$user->authorise($link['access'][$i], $link['access'][$i + 1])) { return ''; } } } } // Instantiate a new FileLayout instance and render the layout $layout = new FileLayout('joomla.links.link'); return $layout->render($link); } } PK0A#]��OOsrc/HTML/Helpers/JGrid.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\HTML\Helpers; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for creating HTML Grids * * @since 1.6 */ abstract class JGrid { /** * Returns an action on a grid * * @param integer $i The row index * @param string $task The task to fire * @param string|array $prefix An optional task prefix or an array of options * @param string $activeTitle An optional active tooltip to display if $enable is true * @param string $inactiveTitle An optional inactive tooltip to display if $enable is true * @param boolean $tip An optional setting for tooltip * @param string $activeClass An optional active HTML class * @param string $inactiveClass An optional inactive HTML class * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function action( $i, $task, $prefix = '', $activeTitle = '', $inactiveTitle = '', $tip = false, $activeClass = '', $inactiveClass = '', $enabled = true, $translate = true, $checkbox = 'cb', $formId = null ) { $html = []; if (is_array($prefix)) { $options = $prefix; $activeTitle = array_key_exists('active_title', $options) ? $options['active_title'] : $activeTitle; $inactiveTitle = array_key_exists('inactive_title', $options) ? $options['inactive_title'] : $inactiveTitle; $tip = array_key_exists('tip', $options) ? $options['tip'] : $tip; $activeClass = array_key_exists('active_class', $options) ? $options['active_class'] : $activeClass; $inactiveClass = array_key_exists('inactive_class', $options) ? $options['inactive_class'] : $inactiveClass; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } if ($tip) { $title = $enabled ? $activeTitle : $inactiveTitle; $title = $translate ? Text::_($title) : $title; $ariaid = $checkbox . $task . $i . '-desc'; // Don't show empty tooltip. if ($title === '') { $tip = false; } } if ($enabled) { Factory::getDocument()->getWebAssetManager()->useScript('list-view'); $html[] = '<a href="#" class="js-grid-item-action tbody-icon' . ($activeClass === 'publish' ? ' active' : '') . '"'; $html[] = ' data-item-id="' . $checkbox . $i . '" data-item-task="' . $prefix . $task . '" data-item-form-id="' . $formId . '"'; $html[] = $tip ? ' aria-labelledby="' . $ariaid . '"' : ''; $html[] = '>'; $html[] = LayoutHelper::render('joomla.icon.iconclass', ['icon' => $activeClass]); $html[] = '</a>'; $html[] = $tip ? '<div role="tooltip" id="' . $ariaid . '">' . $title . '</div>' : ''; } else { $html[] = '<span class="tbody-icon jgrid"'; $html[] = $tip ? ' aria-labelledby="' . $ariaid . '"' : ''; $html[] = '>'; $html[] = LayoutHelper::render('joomla.icon.iconclass', ['icon' => $inactiveClass]); $html[] = '</span>'; $html[] = $tip ? '<div role="tooltip" id="' . $ariaid . '">' . $title . '</div>' : ''; } return implode($html); } /** * Returns a state on a grid * * @param array $states array of value/state. Each state is an array of the form * (task, text, active title, inactive title, tip (boolean), HTML active class, HTML inactive class) * or ('task'=>task, 'text'=>text, 'active_title'=>active title, * 'inactive_title'=>inactive title, 'tip'=>boolean, 'active_class'=>html active class, * 'inactive_class'=>html inactive class) * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function state($states, $value, $i, $prefix = '', $enabled = true, $translate = true, $checkbox = 'cb', $formId = null) { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $state = ArrayHelper::getValue($states, (int) $value, $states[0]); $task = array_key_exists('task', $state) ? $state['task'] : $state[0]; $text = array_key_exists('text', $state) ? $state['text'] : (array_key_exists(1, $state) ? $state[1] : ''); $activeTitle = array_key_exists('active_title', $state) ? $state['active_title'] : (array_key_exists(2, $state) ? $state[2] : ''); $inactiveTitle = array_key_exists('inactive_title', $state) ? $state['inactive_title'] : (array_key_exists(3, $state) ? $state[3] : ''); $tip = array_key_exists('tip', $state) ? $state['tip'] : (array_key_exists(4, $state) ? $state[4] : false); $activeClass = array_key_exists('active_class', $state) ? $state['active_class'] : (array_key_exists(5, $state) ? $state[5] : ''); $inactiveClass = array_key_exists('inactive_class', $state) ? $state['inactive_class'] : (array_key_exists(6, $state) ? $state[6] : ''); return static::action( $i, $task, $prefix, $activeTitle, $inactiveTitle, $tip, $activeClass, $inactiveClass, $enabled, $translate, $checkbox, $formId ); } /** * Returns a published state on a grid * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $publishUp An optional start publishing date. * @param string $publishDown An optional finish publishing date. * @param string $formId An optional form selector. * * @return string The HTML markup * * @see JHtmlJGrid::state() * @since 1.6 */ public static function published( $value, $i, $prefix = '', $enabled = true, $checkbox = 'cb', $publishUp = null, $publishDown = null, $formId = null ) { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = [ 1 => ['unpublish', 'JPUBLISHED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true, 'publish', 'publish'], 0 => ['publish', 'JUNPUBLISHED', 'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true, 'unpublish', 'unpublish'], 2 => ['unpublish', 'JARCHIVED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true, 'archive', 'archive'], -2 => ['publish', 'JTRASHED', 'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true, 'trash', 'trash'], ]; // Special state for dates if ($publishUp || $publishDown) { $nullDate = Factory::getDbo()->getNullDate(); $nowDate = Factory::getDate()->toUnix(); $tz = Factory::getUser()->getTimezone(); $publishUp = ($publishUp !== null && $publishUp !== $nullDate) ? Factory::getDate($publishUp, 'UTC')->setTimezone($tz) : false; $publishDown = ($publishDown !== null && $publishDown !== $nullDate) ? Factory::getDate($publishDown, 'UTC')->setTimezone($tz) : false; // Create tip text, only we have publish up or down settings $tips = []; if ($publishUp) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_START', HTMLHelper::_('date', $publishUp, Text::_('DATE_FORMAT_LC5'), 'UTC')); } if ($publishDown) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_FINISHED', HTMLHelper::_('date', $publishDown, Text::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br>', $tips); // Add tips and special titles foreach ($states as $key => $state) { // Create special titles for published items if ($key == 1) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_ITEM'; if ($publishUp > $nullDate && $nowDate < $publishUp->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_PENDING_ITEM'; $states[$key][5] = $states[$key][6] = 'pending'; } if ($publishDown > $nullDate && $nowDate > $publishDown->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_EXPIRED_ITEM'; $states[$key][5] = $states[$key][6] = 'expired'; } } // Add tips to titles if ($tip) { $states[$key][1] = Text::_($states[$key][1]); $states[$key][2] = Text::_($states[$key][2]) . '<br>' . $tip; $states[$key][3] = Text::_($states[$key][3]) . '<br>' . $tip; $states[$key][4] = true; } } return static::state($states, $value, $i, ['prefix' => $prefix, 'translate' => !$tip], $enabled, true, $checkbox, $formId); } return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox, $formId); } /** * Returns an isDefault state on a grid * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * @param string $active_class The class for active items. * @param string $inactive_class The class for inactive items. * * @return string The HTML markup * * @see JHtmlJGrid::state() * @since 1.6 */ public static function isdefault($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb', $formId = null, $active_class = 'icon-color-featured icon-star', $inactive_class = 'icon-unfeatured') { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = [ 0 => ['setDefault', '', 'JLIB_HTML_SETDEFAULT_ITEM', '', 1, $inactive_class, $inactive_class], 1 => ['unsetDefault', 'JDEFAULT', 'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1, $active_class, $active_class], ]; return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox, $formId); } /** * Returns an array of standard published state filter options. * * @param array $config An array of configuration options. * This array can contain a list of key/value pairs where values are boolean * and keys can be taken from 'published', 'unpublished', 'archived', 'trash', 'all'. * These pairs determine which values are displayed. * * @return array The array of standard published state filter options * * @since 1.6 */ public static function publishedOptions($config = []) { // Build the active state filter options. $options = []; if (!array_key_exists('published', $config) || $config['published']) { $options[] = HTMLHelper::_('select.option', '1', 'JPUBLISHED'); } if (!array_key_exists('unpublished', $config) || $config['unpublished']) { $options[] = HTMLHelper::_('select.option', '0', 'JUNPUBLISHED'); } if (!array_key_exists('archived', $config) || $config['archived']) { $options[] = HTMLHelper::_('select.option', '2', 'JARCHIVED'); } if (!array_key_exists('trash', $config) || $config['trash']) { $options[] = HTMLHelper::_('select.option', '-2', 'JTRASHED'); } if (!array_key_exists('all', $config) || $config['all']) { $options[] = HTMLHelper::_('select.option', '*', 'JALL'); } return $options; } /** * Returns a checked-out icon * * @param integer $i The row index. * @param string $editorName The name of the editor. * @param string $time The time that the object was checked out. * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled True to enable the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function checkedout($i, $editorName, $time, $prefix = '', $enabled = false, $checkbox = 'cb', $formId = null) { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $text = $editorName . '<br>' . HTMLHelper::_('date', $time, Text::_('DATE_FORMAT_LC')) . '<br>' . HTMLHelper::_('date', $time, 'H:i'); $activeTitle = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKIN'), $text, 0); $inactiveTitle = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKED_OUT'), $text, 0); return static::action( $i, 'checkin', $prefix, html_entity_decode($activeTitle, ENT_QUOTES, 'UTF-8'), html_entity_decode($inactiveTitle, ENT_QUOTES, 'UTF-8'), true, 'checkedout', 'checkedout', $enabled, false, $checkbox, $formId ); } /** * Creates an order-up action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function orderUp($i, $task = 'orderup', $prefix = '', $text = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb', $formId = null) { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'uparrow', 'uparrow_disabled', $enabled, true, $checkbox, $formId); } /** * Creates an order-down action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $formId An optional form selector. * * @return string The HTML markup * * @since 1.6 */ public static function orderDown( $i, $task = 'orderdown', $prefix = '', $text = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb', $formId = null ) { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'downarrow', 'downarrow_disabled', $enabled, true, $checkbox, $formId); } } PK0A#]�ߠɌ�"src/Exception/ExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Exception; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Error\AbstractRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Displays the custom error page when an uncaught exception occurs. * * @since 3.0 */ class ExceptionHandler { /** * Handles an error triggered with the E_USER_DEPRECATED level. * * @param integer $errorNumber The level of the raised error, represented by the E_* constants. * @param string $errorMessage The error message. * @param string $errorFile The file the error was triggered from. * @param integer $errorLine The line number the error was triggered from. * * @return boolean * * @since 4.0.0 */ public static function handleUserDeprecatedErrors(int $errorNumber, string $errorMessage, string $errorFile, int $errorLine): bool { // We only want to handle user deprecation messages, these will be triggered in code if ($errorNumber === E_USER_DEPRECATED) { try { Log::add("$errorMessage - $errorFile - Line $errorLine", Log::WARNING, 'deprecated'); } catch (\Exception $e) { // Silence } // If debug mode is enabled, we want to let PHP continue to handle the error; otherwise, we can bail early if (\defined('JDEBUG') && JDEBUG) { return true; } } // Always return false, this will tell PHP to handle the error internally return false; } /** * Handles exceptions: logs errors and renders error page. * * @param \Exception|\Throwable $error An Exception or Throwable (PHP 7+) object for which to render the error page. * * @return void * * @since 3.10.0 */ public static function handleException(\Throwable $error) { static::logException($error); static::render($error); } /** * Render the error page based on an exception. * * @param \Throwable $error An Exception or Throwable (PHP 7+) object for which to render the error page. * * @return void * * @since 3.0 */ public static function render(\Throwable $error) { try { $app = Factory::getApplication(); // Flag if we are on cli $isCli = $app->isClient('cli'); // If site is offline and it's a 404 error, just go to index (to see offline message, instead of 404) if (!$isCli && $error->getCode() == '404' && $app->get('offline') == 1) { $app->redirect('index.php'); } // Clear all opened Output buffers to prevent misrendering for ($i = 0, $l = ob_get_level(); $i < $l; $i++) { ob_end_clean(); } /* * Try and determine the format to render the error page in * * First we check if a Document instance was registered to Factory and use the type from that if available * If a type doesn't exist for that format, we try to use the format from the application's Input object * Lastly, if all else fails, we default onto the HTML format to at least render something */ if (Factory::$document) { $format = Factory::$document->getType(); } else { $format = $app->getInput()->getString('format', 'html'); } try { $renderer = AbstractRenderer::getRenderer($format); } catch (\InvalidArgumentException $e) { // Default to the HTML renderer $renderer = AbstractRenderer::getRenderer('html'); } // Reset the document object in the factory, this gives us a clean slate and lets everything render properly Factory::$document = $renderer->getDocument(); Factory::getApplication()->loadDocument(Factory::$document); $data = $renderer->render($error); // If nothing was rendered, just use the message from the Exception if (empty($data)) { $data = $error->getMessage(); } if ($isCli) { echo $data; } else { /** @var CMSApplication $app */ // Do not allow cache $app->allowCache(false); $app->setBody($data); } // This return is needed to ensure the test suite does not trigger the non-Exception handling below return; } catch (\Throwable $errorRendererError) { // Pass the error down } /* * To reach this point in the code means there was an error creating the error page. * * Let global handler to handle the error, @see bootstrap.php */ if (isset($errorRendererError)) { /* * Here the thing, at this point we have 2 exceptions: * $errorRendererError - the error caused by error renderer * $error - the main error * * We need to show both exceptions, without loss of trace information, so use a bit of magic to merge them. * * Use exception nesting feature: rethrow the exceptions, an exception thrown in a finally block * will take unhandled exception as previous. * So PHP will add $error Exception as previous to $errorRendererError Exception to keep full error stack. */ try { try { throw $error; } finally { throw $errorRendererError; } } catch (\Throwable $finalError) { throw $finalError; } } else { throw $error; } } /** * Checks if given error belong to PHP exception class (\Throwable for PHP 7+, \Exception for PHP 5-). * * @param mixed $error Any error value. * * @return boolean * * @since 3.10.0 */ protected static function isException($error) { return $error instanceof \Throwable; } /** * Logs exception, catching all possible errors during logging. * * @param \Throwable $error An Exception or Throwable (PHP 7+) object to get error message from. * * @return void * * @since 3.10.0 */ protected static function logException(\Throwable $error) { // Try to log the error, but don't let the logging cause a fatal error try { Log::add( sprintf( 'Uncaught Throwable of type %1$s thrown with message "%2$s". Stack trace: %3$s', \get_class($error), $error->getMessage(), $error->getTraceAsString() ), Log::CRITICAL, 'error' ); } catch (\Throwable $e) { // Logging failed, don't make a stink about it though } } } PK0A#]��`�!�!&src/Log/Logger/FormattedtextLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; use Joomla\CMS\Version; use Joomla\Utilities\IpHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Formatted Text File Log class * * This class is designed to use as a base for building formatted text files for output. By * default it emulates the Syslog style format output. This is a disk based output format. * * @since 1.7.0 */ class FormattedtextLogger extends Logger { /** * The format which each entry follows in the log file. * * All fields must be named in all caps and be within curly brackets eg. {FOOBAR}. * * @var string * @since 1.7.0 */ protected $format = '{DATETIME} {PRIORITY} {CLIENTIP} {CATEGORY} {MESSAGE}'; /** * The parsed fields from the format string. * * @var array * @since 1.7.0 */ protected $fields = []; /** * The full filesystem path for the log file. * * @var string * @since 1.7.0 */ protected $path; /** * If true, all writes will be deferred as long as possible. * NOTE: Deferred logs may never be written if the application encounters a fatal error. * * @var boolean * @since 3.9.0 */ protected $defer = false; /** * If deferring, entries will be stored here prior to writing. * * @var array * @since 3.9.0 */ protected $deferredEntries = []; /** * Constructor. * * @param array &$options Log object options. * * @since 1.7.0 */ public function __construct(array &$options) { // Call the parent constructor. parent::__construct($options); // The name of the text file defaults to 'error.php' if not explicitly given. if (empty($this->options['text_file'])) { $this->options['text_file'] = 'error.php'; } // The name of the text file path defaults to that which is set in configuration if not explicitly given. if (empty($this->options['text_file_path'])) { $this->options['text_file_path'] = Factory::getApplication()->get('log_path', JPATH_ADMINISTRATOR . '/logs'); } // False to treat the log file as a php file. if (empty($this->options['text_file_no_php'])) { $this->options['text_file_no_php'] = false; } // Build the full path to the log file. $this->path = $this->options['text_file_path'] . '/' . $this->options['text_file']; // Use the default entry format unless explicitly set otherwise. if (!empty($this->options['text_entry_format'])) { $this->format = (string) $this->options['text_entry_format']; } // Wait as long as possible before writing logs if (!empty($this->options['defer'])) { $this->defer = (bool) $this->options['defer']; } // Build the fields array based on the format string. $this->parseFields(); } /** * If deferred, write all pending logs. * * @since 3.9.0 */ public function __destruct() { // Nothing to do if (!$this->defer || empty($this->deferredEntries)) { return; } // Initialise the file if not already done. $this->initFile(); // Format all lines and write to file. $lines = array_map([$this, 'formatLine'], $this->deferredEntries); if (!File::append($this->path, implode("\n", $lines) . "\n")) { throw new \RuntimeException('Cannot write to log file.'); } } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 1.7.0 * @throws \RuntimeException */ public function addEntry(LogEntry $entry) { // Store the entry to be written later. if ($this->defer) { $this->deferredEntries[] = $entry; } else { // Write it immediately. // Initialise the file if not already done. $this->initFile(); // Write the new entry to the file. $line = $this->formatLine($entry); $line .= "\n"; if (!File::append($this->path, $line)) { throw new \RuntimeException('Cannot write to log file.'); } } } /** * Format a line for the log file. * * @param LogEntry $entry The log entry to format as a string. * * @return String * * @since 3.9.0 */ protected function formatLine(LogEntry $entry) { // Set some default field values if not already set. if (!isset($entry->clientIP)) { $ip = IpHelper::getIp(); if ($ip !== '') { $entry->clientIP = $ip; } } // If the time field is missing or the date field isn't only the date we need to rework it. if ((\strlen($entry->date) != 10) || !isset($entry->time)) { // Get the date and time strings in GMT. $entry->datetime = $entry->date->toISO8601(); $entry->time = $entry->date->format('H:i:s', false); $entry->date = $entry->date->format('Y-m-d', false); } // Get a list of all the entry keys and make sure they are upper case. $tmp = array_change_key_case(get_object_vars($entry), CASE_UPPER); // Decode the entry priority into an English string. $tmp['PRIORITY'] = $this->priorities[$entry->priority]; // Fill in field data for the line. $line = $this->format; foreach ($this->fields as $field) { $line = str_replace('{' . $field . '}', $tmp[$field] ?? '-', $line); } return $line; } /** * Method to generate the log file header. * * @return string The log file header * * @since 1.7.0 */ protected function generateFileHeader() { $head = []; // Build the log file header. // If the no php flag is not set add the php die statement. if (empty($this->options['text_file_no_php'])) { // Blank line to prevent information disclose: https://bugs.php.net/bug.php?id=60677 $head[] = '#'; $head[] = '#<?php die(\'Forbidden.\'); ?>'; } $head[] = '#Date: ' . gmdate('Y-m-d H:i:s') . ' UTC'; $head[] = '#Software: ' . (new Version())->getLongVersion(); $head[] = ''; // Prepare the fields string $head[] = '#Fields: ' . strtolower(str_replace('}', '', str_replace('{', '', $this->format))); $head[] = ''; return implode("\n", $head); } /** * Method to initialise the log file. This will create the folder path to the file if it doesn't already * exist and also get a new file header if the file doesn't already exist. If the file already exists it * will simply open it for writing. * * @return void * * @since 1.7.0 * @throws \RuntimeException */ protected function initFile() { // We only need to make sure the file exists if (is_file($this->path)) { return; } // Make sure the folder exists in which to create the log file. Folder::create(\dirname($this->path)); // Build the log file header. $head = $this->generateFileHeader(); if (!File::write($this->path, $head)) { throw new \RuntimeException('Cannot write to log file.'); } } /** * Method to parse the format string into an array of fields. * * @return void * * @since 1.7.0 */ protected function parseFields() { $this->fields = []; $matches = []; // Get all of the available fields in the format string. preg_match_all('/{(.*?)}/i', $this->format, $matches); // Build the parsed fields list based on the found fields. foreach ($matches[1] as $match) { $this->fields[] = strtoupper($match); } } } PK0A#]�: ��src/Log/Logger/EchoLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Echo logger class. * * @since 1.7.0 */ class EchoLogger extends Logger { /** * Value to use at the end of an echoed log entry to separate lines. * * @var string * @since 1.7.0 */ protected $line_separator = "\n"; /** * Constructor. * * @param array &$options Log object options. * * @since 3.0.0 */ public function __construct(array &$options) { parent::__construct($options); if (!empty($this->options['line_separator'])) { $this->line_separator = $this->options['line_separator']; } } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 1.7.0 */ public function addEntry(LogEntry $entry) { echo $this->priorities[$entry->priority] . ': ' . $entry->message . (empty($entry->category) ? '' : ' [' . $entry->category . ']') . $this->line_separator; } } PK0A#]���%src/Log/Logger/MessagequeueLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla MessageQueue logger class. * * This class is designed to output logs to a specific MySQL database table. Fields in this * table are based on the Syslog style of log output. This is designed to allow quick and * easy searching. * * @since 1.7.0 */ class MessagequeueLogger extends Logger { /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 1.7.0 */ public function addEntry(LogEntry $entry) { switch ($entry->priority) { case Log::EMERGENCY: case Log::ALERT: case Log::CRITICAL: case Log::ERROR: Factory::getApplication()->enqueueMessage($entry->message, 'error'); break; case Log::WARNING: Factory::getApplication()->enqueueMessage($entry->message, 'warning'); break; case Log::NOTICE: Factory::getApplication()->enqueueMessage($entry->message, 'notice'); break; case Log::INFO: Factory::getApplication()->enqueueMessage($entry->message, 'message'); break; default: // Ignore other priorities. break; } } } PK0A#]f�cx��!src/Log/Logger/InMemoryLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Logger class that keeps all entries in memory * * @since 4.0.0 */ class InMemoryLogger extends Logger { /** * List of collected log entries, grouped by $group * * @var array * @since 4.0.0 */ protected static $logEntries = []; /** * Group name to store the entries * * @var string * @since 4.0.0 */ protected $group = 'default'; /** * Constructor. * * @param array &$options Log object options. * * @since 4.0.0 */ public function __construct(array &$options) { parent::__construct($options); if (!empty($this->options['group'])) { $this->group = $this->options['group']; } } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 4.0.0 */ public function addEntry(LogEntry $entry) { static::$logEntries[$this->group][] = $entry; } /** * Returns a list of collected entries. * * @return array * * @since 4.0.0 */ public function getCollectedEntries() { if (empty(static::$logEntries[$this->group])) { return []; } return static::$logEntries[$this->group]; } } PK0A#]vX�)!src/Log/Logger/DatabaseLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Factory; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! MySQL Database Log class * * This class is designed to output logs to a specific MySQL database table. Fields in this * table are based on the Syslog style of log output. This is designed to allow quick and * easy searching. * * @since 1.7.0 */ class DatabaseLogger extends Logger { /** * The name of the database driver to use for connecting to the database. * * @var string * @since 1.7.0 */ protected $driver = 'mysqli'; /** * The host name (or IP) of the server with which to connect for the logger. * * @var string * @since 1.7.0 */ protected $host = '127.0.0.1'; /** * The database server user to connect as for the logger. * * @var string * @since 1.7.0 */ protected $user = 'root'; /** * The password to use for connecting to the database server. * * @var string * @since 1.7.0 */ protected $password = ''; /** * The name of the database table to use for the logger. * * @var string * @since 1.7.0 */ protected $database = 'logging'; /** * The database table prefix of the database store logging entries. * * @var string * @since 4.3.0 */ protected $prefix; /** * The database table to use for logging entries. * * @var string * @since 1.7.0 */ protected $table = 'jos_'; /** * The database driver object for the logger. * * @var DatabaseDriver * @since 1.7.0 */ protected $db; /** * Constructor. * * @param array &$options Log object options. * * @since 1.7.0 */ public function __construct(array &$options) { // Call the parent constructor. parent::__construct($options); // If both the database object and driver options are empty we want to use the system database connection. if (empty($this->options['db_driver'])) { $this->db = Factory::getDbo(); $this->driver = null; $this->host = null; $this->user = null; $this->password = null; $this->database = null; $this->prefix = null; } else { $this->db = null; $this->driver = (empty($this->options['db_driver'])) ? 'mysqli' : $this->options['db_driver']; $this->host = (empty($this->options['db_host'])) ? '127.0.0.1' : $this->options['db_host']; $this->user = (empty($this->options['db_user'])) ? 'root' : $this->options['db_user']; $this->password = (empty($this->options['db_pass'])) ? '' : $this->options['db_pass']; $this->database = (empty($this->options['db_database'])) ? 'logging' : $this->options['db_database']; $this->prefix = (empty($this->options['db_prefix'])) ? 'jos_' : $this->options['db_prefix']; } // The table name is independent of how we arrived at the connection object. $this->table = (empty($this->options['db_table'])) ? '#__log_entries' : $this->options['db_table']; } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 1.7.0 * @throws \RuntimeException */ public function addEntry(LogEntry $entry) { // Connect to the database if not connected. if (empty($this->db)) { $this->connect(); } // Convert the date. $entry->date = $entry->date->toSql(false, $this->db); $this->db->insertObject($this->table, $entry); } /** * Method to connect to the database server based on object properties. * * @return void * * @since 1.7.0 * @throws \RuntimeException */ protected function connect() { // Build the configuration object to use for DatabaseDriver. $options = [ 'driver' => $this->driver, 'host' => $this->host, 'user' => $this->user, 'password' => $this->password, 'database' => $this->database, 'prefix' => $this->prefix, ]; $this->db = DatabaseDriver::getInstance($options); } } PK0A#]��� QQsrc/Log/Logger/W3cLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! W3C Logging class * * This class is designed to build log files based on the W3C specification. * * @link https://www.w3.org/TR/WD-logfile.html * @since 1.7.0 */ class W3cLogger extends FormattedtextLogger { /** * The format which each entry follows in the log file. * * All fields must be named in all caps and be within curly brackets eg. {FOOBAR}. * * @var string * @since 1.7.0 */ protected $format = '{DATE} {TIME} {PRIORITY} {CLIENTIP} {CATEGORY} {MESSAGE}'; /** * Constructor. * * @param array &$options Log object options. * * @since 1.7.0 */ public function __construct(array &$options) { // The name of the text file defaults to 'error.w3c.php' if not explicitly given. if (empty($options['text_file'])) { $options['text_file'] = 'error.w3c.php'; } // Call the parent constructor. parent::__construct($options); } } PK0A#]��� src/Log/Logger/SyslogLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Log\Log; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Syslog Log class * * This class is designed to call the PHP Syslog function call which is then sent to the * system wide log system. For Linux/Unix based systems this is the syslog subsystem, for * the Windows based implementations this can be found in the Event Log. For Windows, * permissions may prevent PHP from properly outputting messages. * * @since 1.7.0 */ class SyslogLogger extends Logger { /** * Translation array for LogEntry priorities to SysLog priority names. * * @var array * @since 1.7.0 */ protected $priorities = [ Log::EMERGENCY => 'EMERG', Log::ALERT => 'ALERT', Log::CRITICAL => 'CRIT', Log::ERROR => 'ERR', Log::WARNING => 'WARNING', Log::NOTICE => 'NOTICE', Log::INFO => 'INFO', Log::DEBUG => 'DEBUG', ]; /** * Constructor. * * @param array &$options Log object options. * * @since 1.7.0 */ public function __construct(array &$options) { // Call the parent constructor. parent::__construct($options); // Ensure that we have an identity string for the Syslog entries. if (empty($this->options['sys_ident'])) { $this->options['sys_ident'] = 'Joomla Platform'; } // If the option to add the process id to Syslog entries is set use it, otherwise default to true. if (isset($this->options['sys_add_pid'])) { $this->options['sys_add_pid'] = (bool) $this->options['sys_add_pid']; } else { $this->options['sys_add_pid'] = true; } // If the option to also send Syslog entries to STDERR is set use it, otherwise default to false. if (isset($this->options['sys_use_stderr'])) { $this->options['sys_use_stderr'] = (bool) $this->options['sys_use_stderr']; } else { $this->options['sys_use_stderr'] = false; } // Build the Syslog options from our log object options. $sysOptions = 0; if ($this->options['sys_add_pid']) { $sysOptions = $sysOptions | LOG_PID; } if ($this->options['sys_use_stderr']) { $sysOptions = $sysOptions | LOG_PERROR; } // Default logging facility is LOG_USER for Windows compatibility. $sysFacility = LOG_USER; // If we have a facility passed in and we're not on Windows, reset it. if (isset($this->options['sys_facility']) && !IS_WIN) { $sysFacility = $this->options['sys_facility']; } // Open the Syslog connection. openlog((string) $this->options['sys_ident'], $sysOptions, $sysFacility); } /** * Destructor. * * @since 1.7.0 */ public function __destruct() { closelog(); } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 1.7.0 */ public function addEntry(LogEntry $entry) { // Generate the value for the priority based on predefined constants. $priority = \constant(strtoupper('LOG_' . $this->priorities[$entry->priority])); // Send the entry to Syslog. syslog($priority, '[' . $entry->category . '] ' . $entry->message); } } PK0A#]����..!src/Log/Logger/CallbackLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log\Logger; use Joomla\CMS\Log\LogEntry; use Joomla\CMS\Log\Logger; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Callback Log class * * This class allows logging to be handled by a callback function. * This allows unprecedented flexibility in the way logging can be handled. * * @since 3.0.1 */ class CallbackLogger extends Logger { /** * The function to call when an entry is added * * @var callable * @since 3.0.1 */ protected $callback; /** * Constructor. * * @param array &$options Log object options. * * @since 3.0.1 * @throws \RuntimeException */ public function __construct(array &$options) { // Call the parent constructor. parent::__construct($options); // Throw an exception if there is not a valid callback if (!isset($this->options['callback']) || !\is_callable($this->options['callback'])) { throw new \RuntimeException(sprintf('%s created without valid callback function.', \get_class($this))); } $this->callback = $this->options['callback']; } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 3.0.1 * @throws \RuntimeException */ public function addEntry(LogEntry $entry) { // Pass the log entry to the callback function \call_user_func($this->callback, $entry); } } PK0A#]�tj src/Log/LogEntry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log; use Joomla\CMS\Date\Date; use Joomla\CMS\Filesystem\Path; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Log Entry class * * This class is designed to hold log entries for either writing to an engine, or for * supported engines, retrieving lists and building in memory (PHP based) search operations. * * @since 1.7.0 */ #[\AllowDynamicProperties] class LogEntry { /** * Application responsible for log entry. * * @var string * @since 1.7.0 */ public $category; /** * The message context. * * @var array * @since 3.8.0 */ public $context; /** * The date the message was logged. * * @var Date * @since 1.7.0 */ public $date; /** * Message to be logged. * * @var string * @since 1.7.0 */ public $message; /** * The priority of the message to be logged. * * @var string * @since 1.7.0 * @see LogEntry::$priorities */ public $priority = Log::INFO; /** * List of available log priority levels [Based on the Syslog default levels]. * * @var array * @since 1.7.0 */ protected $priorities = [ Log::EMERGENCY, Log::ALERT, Log::CRITICAL, Log::ERROR, Log::WARNING, Log::NOTICE, Log::INFO, Log::DEBUG, ]; /** * Call stack and back trace of the logged call. * @var array * @since 3.1.4 */ public $callStack = []; /** * Constructor * * @param string $message The message to log. * @param int $priority Message priority based on {$this->priorities}. * @param string $category Type of entry * @param string $date Date of entry (defaults to now if not specified or blank) * @param array $context An optional array with additional message context. * * @since 1.7.0 * @change 3.10.7 If the message contains a full path, the root path (JPATH_ROOT) is removed from it * to avoid any full path disclosure. Before 3.10.7, the path was propagated as provided. */ public function __construct($message, $priority = Log::INFO, $category = '', $date = null, array $context = []) { $this->message = Path::removeRoot((string) $message); // Sanitize the priority. if (!\in_array($priority, $this->priorities, true)) { $priority = Log::INFO; } $this->priority = $priority; $this->context = $context; // Sanitize category if it exists. if (!empty($category)) { $this->category = (string) strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $category)); } // Get the current call stack and back trace (without args to save memory). $this->callStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); // Get the date as a Date object. $this->date = new Date($date ?: 'now'); } } PK0A#]I_jn0n0src/Log/Log.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Log Class * * This class hooks into the global log configuration settings to allow for user configured * logging events to be sent to where the user wishes them to be sent. On high load sites * Syslog is probably the best (pure PHP function), then the text file based loggers (CSV, W3c * or plain Formattedtext) and finally MySQL offers the most features (e.g. rapid searching) * but will incur a performance hit due to INSERT being issued. * * @since 1.7.0 */ class Log { /** * All log priorities. * * @var integer * @since 1.7.0 */ public const ALL = 30719; /** * The system is unusable. * * @var integer * @since 1.7.0 */ public const EMERGENCY = 1; /** * Action must be taken immediately. * * @var integer * @since 1.7.0 */ public const ALERT = 2; /** * Critical conditions. * * @var integer * @since 1.7.0 */ public const CRITICAL = 4; /** * Error conditions. * * @var integer * @since 1.7.0 */ public const ERROR = 8; /** * Warning conditions. * * @var integer * @since 1.7.0 */ public const WARNING = 16; /** * Normal, but significant condition. * * @var integer * @since 1.7.0 */ public const NOTICE = 32; /** * Informational message. * * @var integer * @since 1.7.0 */ public const INFO = 64; /** * Debugging message. * * @var integer * @since 1.7.0 */ public const DEBUG = 128; /** * The global Log instance. * * @var Log * @since 1.7.0 */ protected static $instance; /** * Container for Logger configurations. * * @var array * @since 1.7.0 */ protected $configurations = []; /** * Container for Logger objects. * * @var Logger[] * @since 1.7.0 */ protected $loggers = []; /** * Lookup array for loggers. * * @var array * @since 1.7.0 */ protected $lookup = []; /** * The registry of available loggers * * @var LoggerRegistry * @since 4.0.0 */ protected $loggerRegistry; /** * Constructor. * * @since 1.7.0 */ protected function __construct() { $this->loggerRegistry = new LoggerRegistry(); } /** * Method to add an entry to the log. * * @param mixed $entry The LogEntry object to add to the log or the message for a new LogEntry object. * @param integer $priority Message priority. * @param string $category Type of entry * @param string $date Date of entry (defaults to now if not specified or blank) * @param array $context An optional array with additional message context. * * @return void * * @since 1.7.0 */ public static function add($entry, $priority = self::INFO, $category = '', $date = null, array $context = []) { // Automatically instantiate the singleton object if not already done. if (empty(static::$instance)) { static::setInstance(new static()); } // If the entry object isn't a LogEntry object let's make one. if (!($entry instanceof LogEntry)) { $entry = new LogEntry((string) $entry, $priority, $category, $date, $context); } static::$instance->addLogEntry($entry); } /** * Add a logger to the Log instance. Loggers route log entries to the correct files/systems to be logged. * * @param array $options The object configuration array. * @param integer $priorities Message priority * @param array $categories Types of entry * @param boolean $exclude If true, all categories will be logged except those in the $categories array * * @return void * * @since 1.7.0 */ public static function addLogger(array $options, $priorities = self::ALL, $categories = [], $exclude = false) { // Automatically instantiate the singleton object if not already done. if (empty(static::$instance)) { static::setInstance(new static()); } static::$instance->addLoggerInternal($options, $priorities, $categories, $exclude); } /** * Register a logger to the registry * * @param string $key The service key to be registered * @param string $class The class name of the logger * @param boolean $replace Flag indicating the service key may replace an existing definition * * @return void * * @since 4.0.0 */ public function registerLogger(string $key, string $class, bool $replace = false) { // Automatically instantiate the singleton object if not already done. if (empty(static::$instance)) { static::setInstance(new static()); } static::$instance->loggerRegistry->register($key, $class, $replace); } /** * Add a logger to the Log instance. Loggers route log entries to the correct files/systems to be logged. * This method allows you to extend Log completely. * * @param array $options The object configuration array. * @param integer $priorities Message priority * @param array $categories Types of entry * @param boolean $exclude If true, all categories will be logged except those in the $categories array * * @return void * * @since 1.7.0 */ protected function addLoggerInternal(array $options, $priorities = self::ALL, $categories = [], $exclude = false) { // The default logger is the formatted text log file. if (empty($options['logger'])) { $options['logger'] = 'formattedtext'; } $options['logger'] = strtolower($options['logger']); // Special case - if a Closure object is sent as the callback (in case of CallbackLogger) // Closure objects are not serializable so swap it out for a unique id first then back again later if (isset($options['callback'])) { if (is_a($options['callback'], 'closure')) { $callback = $options['callback']; $options['callback'] = spl_object_hash($options['callback']); } elseif (\is_array($options['callback']) && \count($options['callback']) == 2 && \is_object($options['callback'][0])) { $callback = $options['callback']; $options['callback'] = spl_object_hash($options['callback'][0]) . '::' . $options['callback'][1]; } } // Generate a unique signature for the Log instance based on its options. $signature = md5(serialize($options)); // Now that the options array has been serialized, swap the callback back in if (isset($callback)) { $options['callback'] = $callback; } // Register the configuration if it doesn't exist. if (empty($this->configurations[$signature])) { $this->configurations[$signature] = $options; } $this->lookup[$signature] = (object) [ 'priorities' => $priorities, 'categories' => array_map('strtolower', (array) $categories), 'exclude' => (bool) $exclude, ]; } /** * Creates a delegated PSR-3 compatible logger from the current singleton instance. This method always returns a new delegated logger. * * @return DelegatingPsrLogger * * @since 3.8.0 */ public static function createDelegatedLogger() { // Ensure a singleton instance has been created first if (empty(static::$instance)) { static::setInstance(new static()); } return new DelegatingPsrLogger(static::$instance); } /** * Returns a reference to the a Log object, only creating it if it doesn't already exist. * Note: This is principally made available for testing and internal purposes. * * @param Log $instance The logging object instance to be used by the static methods. * * @return void * * @since 1.7.0 */ public static function setInstance($instance) { if (($instance instanceof Log) || $instance === null) { static::$instance = & $instance; } } /** * Method to add an entry to the appropriate loggers. * * @param LogEntry $entry The LogEntry object to send to the loggers. * * @return void * * @since 1.7.0 * @throws \RuntimeException */ protected function addLogEntry(LogEntry $entry) { // Find all the appropriate loggers based on priority and category for the entry. $loggers = $this->findLoggers($entry->priority, $entry->category); foreach ((array) $loggers as $signature) { // Attempt to instantiate the logger object if it doesn't already exist. if (empty($this->loggers[$signature])) { if ($this->loggerRegistry->hasLogger($this->configurations[$signature]['logger'])) { $class = $this->loggerRegistry->getLoggerClass($this->configurations[$signature]['logger']); } else { @trigger_error( sprintf( 'Attempting to automatically resolve loggers to the %s namespace is deprecated as of 4.0 and will be removed in 5.0.' . ' Use the logger registry instead.', __NAMESPACE__ ), E_USER_DEPRECATED ); $class = __NAMESPACE__ . '\\Logger\\' . ucfirst($this->configurations[$signature]['logger']) . 'Logger'; if (!class_exists($class)) { throw new \RuntimeException('Unable to create a Logger instance: ' . $class); } } $this->loggers[$signature] = new $class($this->configurations[$signature]); } // Add the entry to the logger. $this->loggers[$signature]->addEntry(clone $entry); } } /** * Method to find the loggers to use based on priority and category values. * * @param integer $priority Message priority. * @param string $category Type of entry * * @return array The array of loggers to use for the given priority and category values. * * @since 1.7.0 */ protected function findLoggers($priority, $category) { $loggers = []; // Sanitize inputs. $priority = (int) $priority; $category = strtolower((string) $category); // Let's go iterate over the loggers and get all the ones we need. foreach ((array) $this->lookup as $signature => $rules) { // Check to make sure the priority matches the logger. if ($priority & $rules->priorities) { if ($rules->exclude) { // If either there are no set categories or the category (including the empty case) is not in the list of excluded categories, add this logger. if (empty($rules->categories) || !\in_array($category, $rules->categories)) { $loggers[] = $signature; } } else { // If either there are no set categories (meaning all) or the specific category is set, add this logger. if (empty($rules->categories) || \in_array($category, $rules->categories)) { $loggers[] = $signature; } } } } return $loggers; } } PK0A#]�� ��src/Log/Logger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Logger Base Class * * This class is used to be the basis of logger classes to allow for defined functions * to exist regardless of the child class. * * @since 3.0.1 */ abstract class Logger { /** * Options array for the Log instance. * * @var array * @since 3.0.1 */ protected $options = []; /** * Translation array for LogEntry priorities to text strings. * * @var array * @since 3.0.1 */ protected $priorities = [ Log::EMERGENCY => 'EMERGENCY', Log::ALERT => 'ALERT', Log::CRITICAL => 'CRITICAL', Log::ERROR => 'ERROR', Log::WARNING => 'WARNING', Log::NOTICE => 'NOTICE', Log::INFO => 'INFO', Log::DEBUG => 'DEBUG', ]; /** * Constructor. * * @param array &$options Log object options. * * @since 3.0.1 */ public function __construct(array &$options) { // Set the options for the class. $this->options = & $options; } /** * Method to add an entry to the log. * * @param LogEntry $entry The log entry object to add to the log. * * @return void * * @since 3.0.1 * @throws \RuntimeException */ abstract public function addEntry(LogEntry $entry); } PK0A#]Q�����src/Log/DelegatingPsrLogger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log; use Psr\Log\AbstractLogger; use Psr\Log\InvalidArgumentException; use Psr\Log\LogLevel; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Delegating logger which delegates log messages received from the PSR-3 interface to the Joomla! Log object. * * @since 3.8.0 * * @deprecated 4.3 will be become final in 6.0 * Don't extend this class anymore * @internal */ class DelegatingPsrLogger extends AbstractLogger { /** * The Log instance to delegate messages to. * * @var Log * @since 3.8.0 */ protected $logger; /** * Mapping array to map a PSR-3 level to a Joomla priority. * * @var array * @since 3.8.0 */ protected $priorityMap = [ LogLevel::EMERGENCY => Log::EMERGENCY, LogLevel::ALERT => Log::ALERT, LogLevel::CRITICAL => Log::CRITICAL, LogLevel::ERROR => Log::ERROR, LogLevel::WARNING => Log::WARNING, LogLevel::NOTICE => Log::NOTICE, LogLevel::INFO => Log::INFO, LogLevel::DEBUG => Log::DEBUG, ]; /** * Constructor. * * @param Log $logger The Log instance to delegate messages to. * * @since 3.8.0 */ public function __construct(Log $logger) { $this->logger = $logger; } /** * Logs with an arbitrary level. * * @param mixed $level The log level. * @param string $message The log message. * @param array $context Additional message context. * * @return void * * @since 3.8.0 * @throws InvalidArgumentException */ public function log($level, $message, array $context = []) { // Make sure the log level is valid if (!\array_key_exists($level, $this->priorityMap)) { throw new InvalidArgumentException('An invalid log level has been given.'); } // Map the level to Joomla's priority $priority = $this->priorityMap[$level]; $category = null; $date = null; // If a message category is given, map it if (!empty($context['category'])) { $category = $context['category']; } // If a message timestamp is given, map it if (!empty($context['date'])) { $date = $context['date']; } // Joomla's logging API will only process a string or a LogEntry object, if $message is an object without __toString() we can't use it if (!\is_string($message) && !($message instanceof LogEntry)) { if (!\is_object($message) || !method_exists($message, '__toString')) { throw new InvalidArgumentException( 'The message must be a string, a LogEntry object, or an object implementing the __toString() method.' ); } $message = (string) $message; } $this->logger->add($message, $priority, $category, $date, $context); } } PK0A#]�F�� � src/Log/LoggerRegistry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service registry for loggers * * @since 4.0.0 */ final class LoggerRegistry { /** * Array holding the registered services * * @var string[] * @since 4.0.0 */ private $loggerMap = [ 'callback' => Logger\CallbackLogger::class, 'database' => Logger\DatabaseLogger::class, 'echo' => Logger\EchoLogger::class, 'formattedtext' => Logger\FormattedtextLogger::class, 'messagequeue' => Logger\MessagequeueLogger::class, 'syslog' => Logger\SyslogLogger::class, 'w3c' => Logger\W3cLogger::class, 'inmemory' => Logger\InMemoryLogger::class, ]; /** * Get the logger class for a given key * * @param string $key The key to look up * * @return string * * @since 4.0.0 * @throws \InvalidArgumentException */ public function getLoggerClass(string $key): string { if (!$this->hasLogger($key)) { throw new \InvalidArgumentException("The '$key' key is not registered."); } return $this->loggerMap[$key]; } /** * Check if the registry has a logger for the given key * * @param string $key The key to look up * * @return boolean * * @since 4.0.0 */ public function hasLogger(string $key): bool { return isset($this->loggerMap[$key]); } /** * Register a logger * * @param string $key The service key to be registered * @param string $class The class name of the logger * @param boolean $replace Flag indicating the service key may replace an existing definition * * @return void * * @since 4.0.0 */ public function register(string $key, string $class, bool $replace = false) { // If the key exists already and we aren't instructed to replace existing services, bail early if (isset($this->loggerMap[$key]) && !$replace) { throw new \RuntimeException("The '$key' key is already registered."); } // The class must exist if (!class_exists($class)) { throw new \RuntimeException("The '$class' class for key '$key' does not exist."); } $this->loggerMap[$key] = $class; } } PK0A#]��K� � src/Filter/OutputFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filter; use Joomla\CMS\Factory; use Joomla\CMS\Language\Language; use Joomla\Filter\OutputFilter as BaseOutputFilter; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * OutputFilter * * @since 1.7.0 */ class OutputFilter extends BaseOutputFilter { /** * This method processes a string and replaces all instances of & with & in links only. * * @param string $input String to process * * @return string Processed string * * @since 1.7.0 */ public static function linkXHTMLSafe($input) { $regex = 'href="([^"]*(&(amp;){0})[^"]*)*?"'; return preg_replace_callback("#$regex#i", ['\\Joomla\\CMS\\Filter\\OutputFilter', 'ampReplaceCallback'], $input); } /** * This method processes a string and escapes it for use in JavaScript * * @param string $string String to process * * @return string Processed text */ public static function stringJSSafe($string) { $chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); $new_str = ''; foreach ($chars as $chr) { $code = str_pad(dechex(StringHelper::ord($chr)), 4, '0', STR_PAD_LEFT); if (strlen($code) < 5) { $new_str .= '\\u' . $code; } else { $new_str .= '\\u{' . $code . '}'; } } return $new_str; } /** * This method processes a string and replaces all accented UTF-8 characters by unaccented * ASCII-7 "equivalents", whitespaces are replaced by hyphens and the string is lowercase. * * @param string $string String to process * @param string $language Language to transliterate to * * @return string Processed string * * @since 1.7.0 */ public static function stringURLSafe($string, $language = '') { // Remove any '-' from the string since they will be used as concatenaters $str = str_replace('-', ' ', $string); // Transliterate on the language requested (fallback to current language if not specified) $lang = $language == '' || $language == '*' ? Factory::getLanguage() : Language::getInstance($language); $str = $lang->transliterate($str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(StringHelper::strtolower($str)); // Remove any apostrophe. We do it here to ensure it is not replaced by a '-' $str = str_replace("'", '', $str); // Remove any duplicate whitespace, and ensure all characters are alphanumeric $str = preg_replace('/(\s|[^A-Za-z0-9\-])+/', '-', $str); // Trim dashes at beginning and end of alias $str = trim($str, '-'); return $str; } /** * Callback method for replacing & with & in a string * * @param string $m String to process * * @return string Replaced string * * @since 3.5 */ public static function ampReplaceCallback($m) { $rx = '&(?!amp;)'; return preg_replace('#' . $rx . '#', '&', $m[0]); } } PK0A#]��>�J�Jsrc/Filter/InputFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filter; use Joomla\CMS\String\PunycodeHelper; use Joomla\Filter\InputFilter as BaseInputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * InputFilter is a class for filtering input from any data source * * Forked from the php input filter library by: Daniel Morris <dan@rootcube.com> * Original Contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie. * * @since 1.7.0 */ class InputFilter extends BaseInputFilter { /** * An array containing a list of extensions for files that are typically * executable directly in the webserver context, potentially resulting in code executions * * @since 4.0.0 */ public const FORBIDDEN_FILE_EXTENSIONS = [ 'php', 'phps', 'pht', 'phtml', 'php3', 'php4', 'php5', 'php6', 'php7', 'asp', 'php8', 'phar', 'inc', 'pl', 'cgi', 'fcgi', 'java', 'jar', 'py', 'aspx', ]; /** * A flag for Unicode Supplementary Characters (4-byte Unicode character) stripping. * * @var integer * @since 3.5 */ private $stripUSC = 0; /** * A container for InputFilter instances. * * @var InputFilter[] * @since 4.0.0 */ protected static $instances = []; /** * Constructor for inputFilter class. Only first parameter is required. * * @param array $tagsArray List of user-defined tags * @param array $attrArray List of user-defined attributes * @param integer $tagsMethod The constant static::ONLY_ALLOW_DEFINED_TAGS or static::BLOCK_DEFINED_TAGS * @param integer $attrMethod The constant static::ONLY_ALLOW_DEFINED_ATTRIBUTES or static::BLOCK_DEFINED_ATTRIBUTES * @param integer $xssAuto Only auto clean essentials = 0, Allow clean blocked tags/attributes = 1 * @param integer $stripUSC Strip 4-byte unicode characters = 1, no strip = 0 * * @since 1.7.0 */ public function __construct($tagsArray = [], $attrArray = [], $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1, $stripUSC = 0) { parent::__construct($tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto); // Assign member variables $this->stripUSC = $stripUSC; } /** * Returns an input filter object, only creating it if it doesn't already exist. * * @param array $tagsArray List of user-defined tags * @param array $attrArray List of user-defined attributes * @param integer $tagsMethod The constant static::ONLY_ALLOW_DEFINED_TAGS or static::BLOCK_DEFINED_TAGS * @param integer $attrMethod The constant static::ONLY_ALLOW_DEFINED_ATTRIBUTES or static::BLOCK_DEFINED_ATTRIBUTES * @param integer $xssAuto Only auto clean essentials = 0, Allow clean blocked tags/attributes = 1 * @param integer $stripUSC Strip 4-byte unicode characters = 1, no strip = 0 * * @return InputFilter The InputFilter object. * * @since 1.7.0 */ public static function getInstance($tagsArray = [], $attrArray = [], $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1, $stripUSC = 0) { $sig = md5(serialize([$tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto])); if (empty(self::$instances[$sig])) { self::$instances[$sig] = new InputFilter($tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto, $stripUSC); } return self::$instances[$sig]; } /** * Method to be called by another php script. Processes for XSS and * specified bad code. * * @param mixed $source Input string/array-of-string to be 'cleaned' * @param string $type The return type for the variable: * INT: An integer, or an array of integers, * UINT: An unsigned integer, or an array of unsigned integers, * FLOAT: A floating point number, or an array of floating point numbers, * BOOLEAN: A boolean value, * WORD: A string containing A-Z or underscores only (not case sensitive), * ALNUM: A string containing A-Z or 0-9 only (not case sensitive), * CMD: A string containing A-Z, 0-9, underscores, periods or hyphens (not case sensitive), * BASE64: A string containing A-Z, 0-9, forward slashes, plus or equals (not case sensitive), * STRING: A fully decoded and sanitised string (default), * HTML: A sanitised string, * ARRAY: An array, * PATH: A sanitised file path, or an array of sanitised file paths, * TRIM: A string trimmed from normal, non-breaking and multibyte spaces * USERNAME: Do not use (use an application specific filter), * RAW: The raw string is returned with no filtering, * unknown: An unknown filter will act like STRING. If the input is an array it will return an * array of fully decoded and sanitised strings. * * @return mixed 'Cleaned' version of input parameter * * @since 1.7.0 */ public function clean($source, $type = 'string') { // Strip Unicode Supplementary Characters when requested to do so if ($this->stripUSC) { // Alternatively: preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xE2\xAF\x91", $source) but it'd be slower. $source = $this->stripUSC($source); } return parent::clean($source, $type); } /** * Function to punyencode utf8 mail when saving content * * @param string $text The strings to encode * * @return string The punyencoded mail * * @since 3.5 */ public function emailToPunycode($text) { $pattern = '/(("mailto:)+[\w\.\-\+]+\@[^"?]+\.+[^."?]+("|\?))/'; if (preg_match_all($pattern, $text, $matches)) { foreach ($matches[0] as $match) { $match = (string) str_replace(['?', '"'], '', $match); $text = (string) str_replace($match, PunycodeHelper::emailToPunycode($match), $text); } } return $text; } /** * Checks an uploaded for suspicious naming and potential PHP contents which could indicate a hacking attempt. * * The options you can define are: * null_byte Prevent files with a null byte in their name (buffer overflow attack) * forbidden_extensions Do not allow these strings anywhere in the file's extension * php_tag_in_content Do not allow `<?php` tag in content * phar_stub_in_content Do not allow the `__HALT_COMPILER()` phar stub in content * shorttag_in_content Do not allow short tag `<?` in content * shorttag_extensions Which file extensions to scan for short tags in content * fobidden_ext_in_content Do not allow forbidden_extensions anywhere in content * php_ext_content_extensions Which file extensions to scan for .php in content * * This code is an adaptation and improvement of Admin Tools' UploadShield feature, * relicensed and contributed by its author. * * @param array $file An uploaded file descriptor * @param array $options The scanner options (see the code for details) * * @return boolean True of the file is safe * * @since 3.4 */ public static function isSafeFile($file, $options = []) { $defaultOptions = [ // Null byte in file name 'null_byte' => true, // Forbidden string in extension (e.g. php matched .php, .xxx.php, .php.xxx and so on) 'forbidden_extensions' => self::FORBIDDEN_FILE_EXTENSIONS, // <?php tag in file contents 'php_tag_in_content' => true, // <? tag in file contents 'shorttag_in_content' => true, // __HALT_COMPILER() 'phar_stub_in_content' => true, // Which file extensions to scan for short tags 'shorttag_extensions' => [ 'inc', 'phps', 'class', 'php3', 'php4', 'php5', 'php6', 'php7', 'php8', 'txt', 'dat', 'tpl', 'tmpl', ], // Forbidden extensions anywhere in the content 'fobidden_ext_in_content' => true, // Which file extensions to scan for .php in the content 'php_ext_content_extensions' => ['zip', 'rar', 'tar', 'gz', 'tgz', 'bz2', 'tbz', 'jpa'], ]; $options = array_merge($defaultOptions, $options); // Make sure we can scan nested file descriptors $descriptors = $file; if (isset($file['name']) && isset($file['tmp_name'])) { $descriptors = static::decodeFileData( [ $file['name'], $file['type'], $file['tmp_name'], $file['error'], $file['size'], ] ); } // Handle non-nested descriptors (single files) if (isset($descriptors['name'])) { $descriptors = [$descriptors]; } // Scan all descriptors detected foreach ($descriptors as $fileDescriptor) { if (!isset($fileDescriptor['name'])) { // This is a nested descriptor. We have to recurse. if (!static::isSafeFile($fileDescriptor, $options)) { return false; } continue; } $tempNames = $fileDescriptor['tmp_name']; $intendedNames = $fileDescriptor['name']; if (!\is_array($tempNames)) { $tempNames = [$tempNames]; } if (!\is_array($intendedNames)) { $intendedNames = [$intendedNames]; } $len = \count($tempNames); for ($i = 0; $i < $len; $i++) { $tempName = array_shift($tempNames); $intendedName = array_shift($intendedNames); // 1. Null byte check if ($options['null_byte']) { if (strstr($intendedName, "\x00")) { return false; } } // 2. PHP-in-extension check (.php, .php.xxx[.yyy[.zzz[...]]], .xxx[.yyy[.zzz[...]]].php) if (!empty($options['forbidden_extensions'])) { $explodedName = explode('.', $intendedName); $explodedName = array_reverse($explodedName); array_pop($explodedName); $explodedName = array_map('strtolower', $explodedName); /* * DO NOT USE array_intersect HERE! array_intersect expects the two arrays to * be set, i.e. they should have unique values. */ foreach ($options['forbidden_extensions'] as $ext) { if (\in_array($ext, $explodedName)) { return false; } } } // 3. File contents scanner (PHP tag in file contents) if ( $options['php_tag_in_content'] || $options['shorttag_in_content'] || $options['phar_stub_in_content'] || ($options['fobidden_ext_in_content'] && !empty($options['forbidden_extensions'])) ) { $fp = strlen($tempName) ? @fopen($tempName, 'r') : false; if ($fp !== false) { $data = ''; while (!feof($fp)) { $data .= @fread($fp, 131072); if ($options['php_tag_in_content'] && stripos($data, '<?php') !== false) { return false; } if ($options['phar_stub_in_content'] && stripos($data, '__HALT_COMPILER()') !== false) { return false; } if ($options['shorttag_in_content']) { $suspiciousExtensions = $options['shorttag_extensions']; if (empty($suspiciousExtensions)) { $suspiciousExtensions = [ 'inc', 'phps', 'class', 'php3', 'php4', 'txt', 'dat', 'tpl', 'tmpl', ]; } /* * DO NOT USE array_intersect HERE! array_intersect expects the two arrays to * be set, i.e. they should have unique values. */ $collide = false; foreach ($suspiciousExtensions as $ext) { if (\in_array($ext, $explodedName)) { $collide = true; break; } } if ($collide) { // These are suspicious text files which may have the short tag (<?) in them if (strstr($data, '<?')) { return false; } } } if ($options['fobidden_ext_in_content'] && !empty($options['forbidden_extensions'])) { $suspiciousExtensions = $options['php_ext_content_extensions']; if (empty($suspiciousExtensions)) { $suspiciousExtensions = [ 'zip', 'rar', 'tar', 'gz', 'tgz', 'bz2', 'tbz', 'jpa', ]; } /* * DO NOT USE array_intersect HERE! array_intersect expects the two arrays to * be set, i.e. they should have unique values. */ $collide = false; foreach ($suspiciousExtensions as $ext) { if (\in_array($ext, $explodedName)) { $collide = true; break; } } if ($collide) { /* * These are suspicious text files which may have an executable * file extension in them */ foreach ($options['forbidden_extensions'] as $ext) { if (strstr($data, '.' . $ext)) { return false; } } } } /* * This makes sure that we don't accidentally skip a <?php tag if it's across * a read boundary, even on multibyte strings */ $data = substr($data, -10); } fclose($fp); } } } } return true; } /** * Method to decode a file data array. * * @param array $data The data array to decode. * * @return array * * @since 3.4 */ protected static function decodeFileData(array $data) { $result = []; if (\is_array($data[0])) { foreach ($data[0] as $k => $v) { $result[$k] = static::decodeFileData([$data[0][$k], $data[1][$k], $data[2][$k], $data[3][$k], $data[4][$k]]); } return $result; } return ['name' => $data[0], 'type' => $data[1], 'tmp_name' => $data[2], 'error' => $data[3], 'size' => $data[4]]; } /** * Try to convert to plaintext * * @param string $source The source string. * * @return string Plaintext string * * @since 3.5 */ protected function decode($source) { static $ttr = []; if (!\count($ttr)) { // Entity decode $trans_tbl = get_html_translation_table(HTML_ENTITIES, ENT_COMPAT, 'ISO-8859-1'); foreach ($trans_tbl as $k => $v) { $ttr[$v] = mb_convert_encoding($k, 'UTF-8', 'ISO-8859-1'); } } $source = strtr($source, $ttr); // Convert decimal $source = preg_replace_callback( '/&#(\d+);/m', function ($m) { return mb_convert_encoding(\chr($m[1]), 'UTF-8', 'ISO-8859-1'); }, $source ); // Convert hex $source = preg_replace_callback( '/&#x([a-f0-9]+);/mi', function ($m) { return mb_convert_encoding(\chr(\hexdec($m[1])), 'UTF-8', 'ISO-8859-1'); }, $source ); return $source; } /** * Recursively strip Unicode Supplementary Characters from the source. Not: objects cannot be filtered. * * @param mixed $source The data to filter * * @return mixed The filtered result * * @since 3.5 */ protected function stripUSC($source) { if (\is_object($source)) { return $source; } if (\is_array($source)) { $filteredArray = []; foreach ($source as $k => $v) { $filteredArray[$k] = $this->stripUSC($v); } return $filteredArray; } return preg_replace('/[\xF0-\xF7].../s', "\xE2\xAF\x91", $source); } } PK0A#]�̈�C�C,src/Application/AdministratorApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\Web\WebClient; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Input\Input; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Router; use Joomla\CMS\Session\Session; use Joomla\CMS\Uri\Uri; use Joomla\DI\Container; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Administrator Application class * * @since 3.2 */ class AdministratorApplication extends CMSApplication { use MultiFactorAuthenticationHandler; /** * List of allowed components for guests and users which do not have the core.login.admin privilege. * * By default we allow two core components: * * - com_login Absolutely necessary to let users log into the backend of the site. Do NOT remove! * - com_ajax Handle AJAX requests or other administrative callbacks without logging in. Required for * passwordless authentication using WebAuthn. * * @var array */ protected $allowedUnprivilegedOptions = [ 'com_login', 'com_ajax', ]; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's input * object. If the argument is a JInput object that object will become the * application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config * object. If the argument is a Registry object that object will become the * application's config object, otherwise a default config object is created. * @param WebClient $client An optional argument to provide dependency injection for the application's * client object. If the argument is a WebClient object that object will become the * application's client object, otherwise a default client object is created. * @param Container $container Dependency injection container. * * @since 3.2 */ public function __construct(Input $input = null, Registry $config = null, WebClient $client = null, Container $container = null) { // Register the application name $this->name = 'administrator'; // Register the client ID $this->clientId = 1; // Execute the parent constructor parent::__construct($input, $config, $client, $container); // Set the root in the URI based on the application name Uri::root(null, rtrim(\dirname(Uri::base(true)), '/\\')); } /** * Dispatch the application * * @param string $component The component which is being rendered. * * @return void * * @since 3.2 */ public function dispatch($component = null) { if ($component === null) { $component = $this->findOption(); } // Load the document to the API $this->loadDocument(); // Set up the params $document = Factory::getDocument(); // Register the document object with Factory Factory::$document = $document; switch ($document->getType()) { case 'html': // Get the template $template = $this->getTemplate(true); $clientId = $this->getClientId(); // Store the template and its params to the config $this->set('theme', $template->template); $this->set('themeParams', $template->params); // Add Asset registry files $wr = $document->getWebAssetManager()->getRegistry(); if ($component) { $wr->addExtensionRegistryFile($component); } if (!empty($template->parent)) { $wr->addTemplateRegistryFile($template->parent, $clientId); } $wr->addTemplateRegistryFile($template->template, $clientId); break; default: break; } $document->setTitle($this->get('sitename') . ' - ' . Text::_('JADMINISTRATION')); $document->setDescription($this->get('MetaDesc')); $document->setGenerator('Joomla! - Open Source Content Management'); $contents = ComponentHelper::renderComponent($component); $document->setBuffer($contents, 'component'); // Trigger the onAfterDispatch event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterDispatch'); } /** * Method to run the Web application routines. * * @return void * * @since 3.2 */ protected function doExecute() { // Get the language from the (login) form or user state $login_lang = ($this->input->get('option') === 'com_login') ? $this->input->get('lang') : ''; $options = ['language' => $login_lang ?: $this->getUserState('application.lang')]; // Initialise the application $this->initialiseApp($options); // Mark afterInitialise in the profiler. JDEBUG ? $this->profiler->mark('afterInitialise') : null; // Route the application $this->route(); // Mark afterRoute in the profiler. JDEBUG ? $this->profiler->mark('afterRoute') : null; /* * Check if the user is required to reset their password * * Before $this->route(); "option" and "view" can't be safely read using: * $this->input->getCmd('option'); or $this->input->getCmd('view'); * ex: due of the sef urls */ $this->checkUserRequireReset('com_users', 'user', 'edit', 'com_users/user.edit,com_users/user.save,com_users/user.apply,com_login/logout'); // Dispatch the application $this->dispatch(); // Mark afterDispatch in the profiler. JDEBUG ? $this->profiler->mark('afterDispatch') : null; } /** * Return a reference to the Router object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return Router * * @since 3.2 * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: * Factory::getContainer()->get(AdministratorRouter::class); */ public static function getRouter($name = 'administrator', array $options = []) { return parent::getRouter($name, $options); } /** * Gets the name of the current template. * * @param boolean $params True to return the template parameters * * @return string|\stdClass The name of the template if the params argument is false. The template object if the params argument is true. * * @since 3.2 * @throws \InvalidArgumentException */ public function getTemplate($params = false) { if (\is_object($this->template)) { if ($params) { return $this->template; } return $this->template->template; } $adminStyle = $this->getIdentity() ? (int) $this->getIdentity()->getParam('admin_style') : 0; $template = $this->bootComponent('templates')->getMVCFactory() ->createModel('Style', 'Administrator')->getAdminTemplate($adminStyle); $template->template = InputFilter::getInstance()->clean($template->template, 'cmd'); $template->params = new Registry($template->params); // Fallback template if ( !is_file(JPATH_THEMES . '/' . $template->template . '/index.php') && !is_file(JPATH_THEMES . '/' . $template->parent . '/index.php') ) { $this->getLogger()->error(Text::_('JERROR_ALERTNOTEMPLATE'), ['category' => 'system']); $template->params = new Registry(); $template->template = 'atum'; // Check, the data were found and if template really exists if (!is_file(JPATH_THEMES . '/' . $template->template . '/index.php')) { throw new \InvalidArgumentException(Text::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $template->template)); } } // Cache the result $this->template = $template; // Pass the parent template to the state $this->set('themeInherits', $template->parent); if ($params) { return $template; } return $template->template; } /** * Initialise the application. * * @param array $options An optional associative array of configuration settings. * * @return void * * @since 3.2 */ protected function initialiseApp($options = []) { $user = Factory::getUser(); // If the user is a guest we populate it with the guest user group. if ($user->guest) { $guestUsergroup = ComponentHelper::getParams('com_users')->get('guest_usergroup', 1); $user->groups = [$guestUsergroup]; } // If a language was specified it has priority, otherwise use user or default language settings if (empty($options['language'])) { $lang = $user->getParam('admin_language'); // Make sure that the user's language exists if ($lang && LanguageHelper::exists($lang)) { $options['language'] = $lang; } else { $params = ComponentHelper::getParams('com_languages'); $options['language'] = $params->get('administrator', $this->get('language', 'en-GB')); } } // One last check to make sure we have something if (!LanguageHelper::exists($options['language'])) { $lang = $this->get('language', 'en-GB'); if (LanguageHelper::exists($lang)) { $options['language'] = $lang; } else { // As a last ditch fail to english $options['language'] = 'en-GB'; } } // Finish initialisation parent::initialiseApp($options); } /** * Login authentication function * * @param array $credentials Array('username' => string, 'password' => string) * @param array $options Array('remember' => boolean) * * @return boolean True on success. * * @since 3.2 */ public function login($credentials, $options = []) { // The minimum group $options['group'] = 'Public Backend'; // Make sure users are not auto-registered $options['autoregister'] = false; // Set the application login entry point if (!\array_key_exists('entry_url', $options)) { $options['entry_url'] = Uri::base() . 'index.php?option=com_users&task=login'; } // Set the access control action to check. $options['action'] = 'core.login.admin'; $result = parent::login($credentials, $options); if (!($result instanceof \Exception)) { $lang = $this->input->getCmd('lang', ''); $lang = preg_replace('/[^A-Z-]/i', '', $lang); if ($lang) { $this->setUserState('application.lang', $lang); } $this->bootComponent('messages')->getMVCFactory() ->createModel('Messages', 'Administrator')->purge($this->getIdentity() ? $this->getIdentity()->id : 0); } return $result; } /** * Purge the jos_messages table of old messages * * @return void * * @since 3.2 * * @deprecated 4.3 will be removed in 6.0 * Purge the messages through the messages model * Example: * Factory::getApplication()->bootComponent('messages')->getMVCFactory() * ->createModel('Messages', 'Administrator')->purge(Factory::getApplication()->getIdentity()->id); */ public static function purgeMessages() { Factory::getApplication()->bootComponent('messages')->getMVCFactory() ->createModel('Messages', 'Administrator')->purge(Factory::getUser()->id); } /** * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the application response buffer. * * @return void * * @since 3.2 */ protected function render() { // Get the \JInput object $input = $this->input; $component = $input->getCmd('option', 'com_login'); $file = $input->getCmd('tmpl', 'index'); if ($component === 'com_login') { $file = 'login'; } $this->set('themeFile', $file . '.php'); // Safety check for when configuration.php root_user is in use. $rootUser = $this->get('root_user'); if (property_exists('\JConfig', 'root_user')) { if (Factory::getUser()->get('username') === $rootUser || Factory::getUser()->id === (string) $rootUser) { $this->enqueueMessage( Text::sprintf( 'JWARNING_REMOVE_ROOT_USER', 'index.php?option=com_config&task=application.removeroot&' . Session::getFormToken() . '=1' ), 'warning' ); } elseif (Factory::getUser()->authorise('core.admin')) { // Show this message to superusers too $this->enqueueMessage( Text::sprintf( 'JWARNING_REMOVE_ROOT_USER_ADMIN', $rootUser, 'index.php?option=com_config&task=application.removeroot&' . Session::getFormToken() . '=1' ), 'warning' ); } } parent::render(); } /** * Route the application. * * Routing is the process of examining the request environment to determine which * component should receive the request. The component optional parameters * are then set in the request object to be processed when the application is being * dispatched. * * @return void * * @since 3.2 */ protected function route() { $uri = Uri::getInstance(); if ($this->get('force_ssl') >= 1 && strtolower($uri->getScheme()) !== 'https') { // Forward to https $uri->setScheme('https'); $this->redirect((string) $uri, 301); } $this->isHandlingMultiFactorAuthentication(); // Trigger the onAfterRoute event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterRoute'); } /** * Return the application option string [main component]. * * @return string The component to access. * * @since 4.0.0 */ public function findOption(): string { /** @var self $app */ $app = Factory::getApplication(); $option = strtolower($app->getInput()->get('option', '')); $user = $app->getIdentity(); /** * Special handling for guest users and authenticated users without the Backend Login privilege. * * If the component they are trying to access is in the $this->allowedUnprivilegedOptions array we allow the * request to go through. Otherwise we force com_login to be loaded, letting the user (re)try authenticating * with a user account that has the Backend Login privilege. */ if ($user->get('guest') || !$user->authorise('core.login.admin')) { $option = in_array($option, $this->allowedUnprivilegedOptions) ? $option : 'com_login'; } /** * If no component is defined in the request we will try to load com_cpanel, the administrator Control Panel * component. This allows the /administrator URL to display something meaningful after logging in instead of an * error. */ if (empty($option)) { $option = 'com_cpanel'; } /** * Force the option to the input object. This is necessary because we might have force-changed the component in * the two if-blocks above. */ $app->getInput()->set('option', $option); return $option; } } PK0A#]�hȨ�"src/Application/CLI/ColorStyle.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class defining ANSI-color styles for command line output * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ final class ColorStyle { /** * Known colors * * @var array * @since 4.0.0 */ private static $knownColors = [ 'black' => 0, 'red' => 1, 'green' => 2, 'yellow' => 3, 'blue' => 4, 'magenta' => 5, 'cyan' => 6, 'white' => 7, ]; /** * Known styles * * @var array * @since 4.0.0 */ private static $knownOptions = [ 'bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, ]; /** * Foreground base value * * @var integer * @since 4.0.0 */ private static $fgBase = 30; /** * Background base value * * @var integer * @since 4.0.0 */ private static $bgBase = 40; /** * Foreground color * * @var integer * @since 4.0.0 */ private $fgColor = 0; /** * Background color * * @var integer * @since 4.0.0 */ private $bgColor = 0; /** * Array of style options * * @var array * @since 4.0.0 */ private $options = []; /** * Constructor * * @param string $fg Foreground color. * @param string $bg Background color. * @param array $options Style options. * * @since 4.0.0 * @throws \InvalidArgumentException */ public function __construct(string $fg = '', string $bg = '', array $options = []) { if ($fg) { if (\array_key_exists($fg, static::$knownColors) == false) { throw new \InvalidArgumentException( sprintf( 'Invalid foreground color "%1$s" [%2$s]', $fg, implode(', ', $this->getKnownColors()) ) ); } $this->fgColor = static::$fgBase + static::$knownColors[$fg]; } if ($bg) { if (\array_key_exists($bg, static::$knownColors) == false) { throw new \InvalidArgumentException( sprintf( 'Invalid background color "%1$s" [%2$s]', $bg, implode(', ', $this->getKnownColors()) ) ); } $this->bgColor = static::$bgBase + static::$knownColors[$bg]; } foreach ($options as $option) { if (\array_key_exists($option, static::$knownOptions) == false) { throw new \InvalidArgumentException( sprintf( 'Invalid option "%1$s" [%2$s]', $option, implode(', ', $this->getKnownOptions()) ) ); } $this->options[] = $option; } } /** * Convert to a string. * * @return string * * @since 4.0.0 */ public function __toString() { return $this->getStyle(); } /** * Create a color style from a parameter string. * * Example: fg=red;bg=blue;options=bold,blink * * @param string $string The parameter string. * * @return $this * * @since 4.0.0 * @throws \RuntimeException */ public static function fromString(string $string): self { $fg = ''; $bg = ''; $options = []; $parts = explode(';', $string); foreach ($parts as $part) { $subParts = explode('=', $part); if (\count($subParts) < 2) { continue; } switch ($subParts[0]) { case 'fg': $fg = $subParts[1]; break; case 'bg': $bg = $subParts[1]; break; case 'options': $options = explode(',', $subParts[1]); break; default: throw new \RuntimeException('Invalid option: ' . $subParts[0]); } } return new self($fg, $bg, $options); } /** * Get the translated color code. * * @return string * * @since 4.0.0 */ public function getStyle(): string { $values = []; if ($this->fgColor) { $values[] = $this->fgColor; } if ($this->bgColor) { $values[] = $this->bgColor; } foreach ($this->options as $option) { $values[] = static::$knownOptions[$option]; } return implode(';', $values); } /** * Get the known colors. * * @return string[] * * @since 4.0.0 */ public function getKnownColors(): array { return array_keys(static::$knownColors); } /** * Get the known options. * * @return string[] * * @since 4.0.0 */ public function getKnownOptions(): array { return array_keys(static::$knownOptions); } } PK0A#]����$$ src/Application/CLI/CliInput.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class CliInput * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ class CliInput { /** * Get a value from standard input. * * @return string The input string from standard input. * * @codeCoverageIgnore * @since 4.0.0 */ public function in() { return rtrim(fread(STDIN, 8192), "\n\r"); } } PK0A#]H�0 ii%src/Application/CLI/Output/Stdout.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI\Output; use Joomla\CMS\Application\CLI\CliOutput; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Output handler for writing command line output to the stdout interface * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ class Stdout extends CliOutput { /** * Write a string to standard output * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return $this * * @codeCoverageIgnore * @since 4.0.0 */ public function out($text = '', $nl = true) { fwrite(STDOUT, $this->getProcessor()->process($text) . ($nl ? "\n" : null)); return $this; } } PK0A#]�c��ii"src/Application/CLI/Output/Xml.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI\Output; use Joomla\CMS\Application\CLI\CliOutput; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Output handler for writing command line output to the stdout interface * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ class Xml extends CliOutput { /** * Write a string to standard output. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return $this * * @since 4.0.0 * @throws \RuntimeException * @codeCoverageIgnore */ public function out($text = '', $nl = true) { fwrite(STDOUT, $text . ($nl ? "\n" : null)); return $this; } } PK0A#]C� H%%7src/Application/CLI/Output/Processor/ColorProcessor.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI\Output\Processor; use Joomla\CMS\Application\CLI\ColorStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Command line output processor supporting ANSI-colored output * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ class ColorProcessor implements ProcessorInterface { /** * Flag to remove color codes from the output * * @var boolean * @since 4.0.0 */ public $noColors = false; /** * Regex to match tags * * @var string * @since 4.0.0 */ protected $tagFilter = '/<([a-z=;]+)>(.*?)<\/\\1>/s'; /** * Regex used for removing color codes * * @var string * @since 4.0.0 */ protected static $stripFilter = '/<[\/]?[a-z=;]+>/'; /** * Array of ColorStyle objects * * @var ColorStyle[] * @since 4.0.0 */ protected $styles = []; /** * Class constructor * * @param boolean $noColors Defines non-colored mode on construct * * @since 4.0.0 */ public function __construct($noColors = null) { if ($noColors === null) { /* * By default windows cmd.exe and PowerShell does not support ANSI-colored output * if the variable is not set explicitly colors should be disabled on Windows */ $noColors = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); } $this->noColors = $noColors; $this->addPredefinedStyles(); } /** * Add a style. * * @param string $name The style name. * @param ColorStyle $style The color style. * * @return $this * * @since 4.0.0 */ public function addStyle($name, ColorStyle $style) { $this->styles[$name] = $style; return $this; } /** * Strip color tags from a string. * * @param string $string The string. * * @return string * * @since 4.0.0 */ public static function stripColors($string) { return preg_replace(static::$stripFilter, '', $string); } /** * Process a string. * * @param string $string The string to process. * * @return string * * @since 4.0.0 */ public function process($string) { preg_match_all($this->tagFilter, $string, $matches); if (!$matches) { return $string; } foreach ($matches[0] as $i => $m) { if (\array_key_exists($matches[1][$i], $this->styles)) { $string = $this->replaceColors($string, $matches[1][$i], $matches[2][$i], $this->styles[$matches[1][$i]]); } elseif (strpos($matches[1][$i], '=')) { // Custom format $string = $this->replaceColors($string, $matches[1][$i], $matches[2][$i], ColorStyle::fromString($matches[1][$i])); } } return $string; } /** * Replace color tags in a string. * * @param string $text The original text. * @param string $tag The matched tag. * @param string $match The match. * @param ColorStyle $style The color style to apply. * * @return mixed * * @since 4.0.0 */ private function replaceColors($text, $tag, $match, ColorStyle $style) { $replace = $this->noColors ? $match : "\033[" . $style . 'm' . $match . "\033[0m"; return str_replace('<' . $tag . '>' . $match . '</' . $tag . '>', $replace, $text); } /** * Adds predefined color styles to the ColorProcessor object * * @return $this * * @since 4.0.0 */ private function addPredefinedStyles() { $this->addStyle( 'info', new ColorStyle('green', '', ['bold']) ); $this->addStyle( 'comment', new ColorStyle('yellow', '', ['bold']) ); $this->addStyle( 'question', new ColorStyle('black', 'cyan') ); $this->addStyle( 'error', new ColorStyle('white', 'red') ); return $this; } } PK0A#]O��}88;src/Application/CLI/Output/Processor/ProcessorInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI\Output\Processor; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a command line output processor * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ interface ProcessorInterface { /** * Process the provided output into a string. * * @param string $output The string to process. * * @return string * * @since 4.0.0 */ public function process($output); } PK0A#]��V�PP!src/Application/CLI/CliOutput.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\CLI; use Joomla\CMS\Application\CLI\Output\Processor\ProcessorInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class defining a command line output handler * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ abstract class CliOutput { /** * Output processing object * * @var ProcessorInterface * @since 4.0.0 */ protected $processor; /** * Constructor * * @param ProcessorInterface $processor The output processor. * * @since 4.0.0 */ public function __construct(ProcessorInterface $processor = null) { $this->setProcessor($processor ?: new Output\Processor\ColorProcessor()); } /** * Set a processor * * @param ProcessorInterface $processor The output processor. * * @return $this * * @since 4.0.0 */ public function setProcessor(ProcessorInterface $processor) { $this->processor = $processor; return $this; } /** * Get a processor * * @return ProcessorInterface * * @since 4.0.0 * @throws \RuntimeException */ public function getProcessor() { if ($this->processor) { return $this->processor; } throw new \RuntimeException('A ProcessorInterface object has not been set.'); } /** * Write a string to an output handler. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return $this * * @since 4.0.0 * @codeCoverageIgnore */ abstract public function out($text = '', $nl = true); } PK0A#]����O�O4src/Application/MultiFactorAuthenticationHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Date\Date; use Joomla\CMS\Encrypt\Aes; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\Table\User as UserTable; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\User; use Joomla\Component\Users\Administrator\Helper\Mfa as MfaHelper; use Joomla\Database\DatabaseInterface; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Implements the code required for integrating with Joomla's Multi-factor Authentication. * * Please keep in mind that Joomla's MFA, like any MFA method, is designed to be user-interactive. * Moreover, it's meant to be used in an HTML- and JavaScript-aware execution environment i.e. a web * browser, web view or similar. * * If your application is designed to work non-interactively (e.g. a JSON API application) or * outside and HTML- and JavaScript-aware execution environments (e.g. CLI) you MUST NOT use this * trait. Authentication should be either implicit (e.g. CLI) or using sufficiently secure non- * interactive methods (tokens, certificates, ...). * * Regarding the Joomla CMS itself, only the SiteApplication (frontend) and AdministratorApplication * (backend) applications use this trait because of this reason. The CLI application is implicitly * authorised at the highest level, whereas the ApiApplication encourages the use of tokens for * authentication. * * @since 4.2.0 */ trait MultiFactorAuthenticationHandler { /** * Handle the redirection to the Multi-factor Authentication captive login or setup page. * * @return boolean True if we are currently handling a Multi-factor Authentication captive page. * @throws \Exception * @since 4.2.0 */ protected function isHandlingMultiFactorAuthentication(): bool { // Multi-factor Authentication checks take place only for logged in users. try { $user = $this->getIdentity() ?? null; } catch (\Exception $e) { return false; } if (!($user instanceof User) || $user->guest) { return false; } // If there is no need for a redirection I must not proceed if (!$this->needsMultiFactorAuthenticationRedirection()) { return false; } /** * Automatically migrate from legacy MFA, if needed. * * We prefer to do a user-by-user migration instead of migrating everybody on Joomla update * for practical reasons. On a site with hundreds or thousands of users the migration could * take several minutes, causing Joomla Update to time out. * * Instead, every time we are in a captive Multi-factor Authentication page (captive MFA login * or captive forced MFA setup) we spend a few milliseconds to check if a migration is * necessary. If it's necessary, we perform it. * * The captive pages don't load any content or modules, therefore the few extra milliseconds * we spend here are not a big deal. A failed all-users migration which would stop Joomla * Update dead in its tracks would, however, be a big deal (broken sites). Moreover, a * migration that has to be initiated by the site owner would also be a big deal — if they * did not know they need to do it none of their users who had previously enabled MFA would * now have it enabled! * * To paraphrase Otto von Bismarck: programming, like politics, is the art of the possible, * the attainable -- the art of the next best. */ $this->migrateFromLegacyMFA(); // We only kick in when the user has actually set up MFA or must definitely enable MFA. $userOptions = ComponentHelper::getParams('com_users'); $neverMFAUserGroups = $userOptions->get('neverMFAUserGroups', []); $forceMFAUserGroups = $userOptions->get('forceMFAUserGroups', []); $isMFADisallowed = count( array_intersect( is_array($neverMFAUserGroups) ? $neverMFAUserGroups : [], $user->getAuthorisedGroups() ) ) >= 1; $isMFAMandatory = count( array_intersect( is_array($forceMFAUserGroups) ? $forceMFAUserGroups : [], $user->getAuthorisedGroups() ) ) >= 1; $isMFADisallowed = $isMFADisallowed && !$isMFAMandatory; $isMFAPending = $this->isMultiFactorAuthenticationPending(); $session = $this->getSession(); $isNonHtml = $this->input->getCmd('format', 'html') !== 'html'; // Prevent non-interactive (non-HTML) content from being loaded until MFA is validated. if ($isMFAPending && $isNonHtml) { throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403); } if ($isMFAPending && !$isMFADisallowed) { /** * Saves the current URL as the return URL if all of the following conditions apply * - It is not a URL to com_users' MFA feature itself * - A return URL does not already exist, is imperfect or external to the site * * If no return URL has been set up and the current URL is com_users' MFA feature * we will save the home page as the redirect target. */ $returnUrl = $session->get('com_users.return_url', ''); if (empty($returnUrl) || !Uri::isInternal($returnUrl)) { $returnUrl = $this->isMultiFactorAuthenticationPage() ? Uri::base() : Uri::getInstance()->toString(['scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment']); $session->set('com_users.return_url', $returnUrl); } // Redirect $this->redirect(Route::_('index.php?option=com_users&view=captive', false), 307); } // If we're here someone just logged in but does not have MFA set up. Just flag him as logged in and continue. $session->set('com_users.mfa_checked', 1); // If the user is in a group that requires MFA we will redirect them to the setup page. if (!$isMFAPending && $isMFAMandatory) { // First unset the flag to make sure the redirection will apply until they conform to the mandatory MFA $session->set('com_users.mfa_checked', 0); // Now set a flag which forces rechecking MFA for this user $session->set('com_users.mandatory_mfa_setup', 1); // Then redirect them to the setup page if (!$this->isMultiFactorAuthenticationPage()) { $url = Route::_('index.php?option=com_users&view=methods', false); $this->redirect($url, 307); } } // Do I need to redirect the user to the MFA setup page after they have fully logged in? $hasRejectedMultiFactorAuthenticationSetup = $this->hasRejectedMultiFactorAuthenticationSetup() && !$isMFAMandatory; if ( !$isMFAPending && !$isMFADisallowed && ($userOptions->get('mfaredirectonlogin', 0) == 1) && !$user->guest && !$hasRejectedMultiFactorAuthenticationSetup && !empty(MfaHelper::getMfaMethods()) ) { $this->redirect( $userOptions->get('mfaredirecturl', '') ?: Route::_('index.php?option=com_users&view=methods&layout=firsttime', false) ); } return true; } /** * Does the current user need to complete MFA authentication before being allowed to access the site? * * @return boolean * @throws \Exception * @since 4.2.0 */ private function isMultiFactorAuthenticationPending(): bool { $user = $this->getIdentity(); if (empty($user) || $user->guest) { return false; } // Get the user's MFA records $records = MfaHelper::getUserMfaRecords($user->id); // No MFA Methods? Then we obviously don't need to display a Captive login page. if (count($records) < 1) { return false; } // Let's get a list of all currently active MFA Methods $mfaMethods = MfaHelper::getMfaMethods(); // If no MFA Method is active we can't really display a Captive login page. if (empty($mfaMethods)) { return false; } // Get a list of just the Method names $methodNames = []; foreach ($mfaMethods as $mfaMethod) { $methodNames[] = $mfaMethod['name']; } // Filter the records based on currently active MFA Methods foreach ($records as $record) { if (in_array($record->method, $methodNames)) { // We found an active Method. Show the Captive page. return true; } } // No viable MFA Method found. We won't show the Captive page. return false; } /** * Check whether we'll need to do a redirection to the Multi-factor Authentication captive page. * * @return boolean * @since 4.2.0 */ private function needsMultiFactorAuthenticationRedirection(): bool { $isAdmin = $this->isClient('administrator'); /** * We only kick in if the session flag is not set AND the user is not flagged for monitoring of their MFA status * * In case a user belongs to a group which requires MFA to be always enabled and they logged in without having * MFA enabled we have the recheck flag. This prevents the user from enabling and immediately disabling MFA, * circumventing the requirement for MFA. */ $session = $this->getSession(); $isMFAComplete = $session->get('com_users.mfa_checked', 0) != 0; $isMFASetupMandatory = $session->get('com_users.mandatory_mfa_setup', 0) != 0; if ($isMFAComplete && !$isMFASetupMandatory) { return false; } // Make sure we are logged in try { $user = $this->getIdentity(); } catch (\Exception $e) { // This would happen if we are in CLI or under an old Joomla! version. Either case is not supported. return false; } // The plugin only needs to kick in when you have logged in if (empty($user) || $user->guest) { return false; } // If we are in the administrator section we only kick in when the user has backend access privileges if ($isAdmin && !$user->authorise('core.login.admin')) { // @todo How exactly did you end up here if you didn't have the core.login.admin privilege to begin with?! return false; } $option = strtolower($this->input->getCmd('option', '')); $task = strtolower($this->input->getCmd('task', '')); // Allow the frontend user to log out (in case they forgot their MFA code or something) if (!$isAdmin && ($option == 'com_users') && in_array($task, ['user.logout', 'user.menulogout'])) { return false; } // Allow the backend user to log out (in case they forgot their MFA code or something) if ($isAdmin && ($option == 'com_login') && ($task == 'logout')) { return false; } // Allow the Joomla update finalisation to run if ($isAdmin && $option === 'com_joomlaupdate' && in_array($task, ['update.finalise', 'update.cleanup', 'update.finaliseconfirm'])) { return false; } // Do not redirect if we are already in a MFA management or captive page $onlyCaptive = $this->isMultiFactorAuthenticationPending() && !$isMFASetupMandatory; if ($this->isMultiFactorAuthenticationPage($onlyCaptive)) { return false; } return true; } /** * Is this a page concerning the Multi-factor Authentication feature? * * @param bool $onlyCaptive Should I only check for the MFA captive page? * * @return boolean * @since 4.2.0 */ public function isMultiFactorAuthenticationPage(bool $onlyCaptive = false): bool { $option = $this->input->get('option'); $task = $this->input->get('task'); $view = $this->input->get('view'); if ($option !== 'com_users') { return false; } $allowedViews = ['captive', 'method', 'methods', 'callback']; $allowedTasks = [ 'captive.display', 'captive.captive', 'captive.validate', 'methods.display', ]; if (!$onlyCaptive) { $allowedTasks = array_merge( $allowedTasks, [ 'method.display', 'method.add', 'method.edit', 'method.regenerateBackupCodes', 'method.delete', 'method.save', 'methods.disable', 'methods.doNotShowThisAgain', ] ); } return in_array($view, $allowedViews) || in_array($task, $allowedTasks); } /** * Does the user have a "don't show this again" flag? * * @return boolean * @since 4.2.0 */ private function hasRejectedMultiFactorAuthenticationSetup(): bool { $user = $this->getIdentity(); $profileKey = 'mfa.dontshow'; /** @var DatabaseInterface $db */ $db = Factory::getContainer()->get(DatabaseInterface::class); $query = $db->getQuery(true) ->select($db->quoteName('profile_value')) ->from($db->quoteName('#__user_profiles')) ->where($db->quoteName('user_id') . ' = :userId') ->where($db->quoteName('profile_key') . ' = :profileKey') ->bind(':userId', $user->id, ParameterType::INTEGER) ->bind(':profileKey', $profileKey); try { $result = $db->setQuery($query)->loadResult(); } catch (\Exception $e) { $result = 1; } return $result == 1; } /** * Automatically migrates a user's legacy MFA records into the new Captive MFA format. * * @return void * @since 4.2.0 */ private function migrateFromLegacyMFA(): void { $user = $this->getIdentity(); if (!($user instanceof User) || $user->guest || $user->id <= 0) { return; } /** @var DatabaseInterface $db */ $db = Factory::getContainer()->get(DatabaseInterface::class); $userTable = new UserTable($db); if (!$userTable->load($user->id) || empty($userTable->otpKey)) { return; } [$otpMethod, $otpKey] = explode(':', $userTable->otpKey, 2); $secret = $this->get('secret'); $otpKey = $this->decryptLegacyTFAString($secret, $otpKey); $otep = $this->decryptLegacyTFAString($secret, $userTable->otep); $config = @json_decode($otpKey, true); $hasConverted = true; if (!empty($config)) { switch ($otpMethod) { case 'totp': $this->getLanguage()->load('plg_multifactorauth_totp', JPATH_ADMINISTRATOR); Factory::getApplication()->bootComponent('com_users')->getMVCFactory()->createTable('Mfa', 'Administrator')->save( [ 'user_id' => $user->id, 'title' => Text::_('PLG_MULTIFACTORAUTH_TOTP_METHOD_TITLE'), 'method' => 'totp', 'default' => 0, 'created_on' => Date::getInstance()->toSql(), 'last_used' => null, 'tries' => 0, 'try_count' => null, 'options' => ['key' => $config['code']], ] ); break; case 'yubikey': $this->getLanguage()->load('plg_multifactorauth_yubikey', JPATH_ADMINISTRATOR); Factory::getApplication()->bootComponent('com_users')->getMVCFactory()->createTable('Mfa', 'Administrator')->save( [ 'user_id' => $user->id, 'title' => sprintf("%s %s", Text::_('PLG_MULTIFACTORAUTH_YUBIKEY_METHOD_TITLE'), $config['yubikey']), 'method' => 'yubikey', 'default' => 0, 'created_on' => Date::getInstance()->toSql(), 'last_used' => null, 'tries' => 0, 'try_count' => null, 'options' => ['id' => $config['yubikey']], ] ); break; default: $hasConverted = false; break; } } // Convert the emergency codes if ($hasConverted && !empty(@json_decode($otep, true))) { // Delete any other record with the same user_id and Method. $method = 'emergencycodes'; $userId = $user->id; $query = $db->getQuery(true) ->delete($db->quoteName('#__user_mfa')) ->where($db->quoteName('user_id') . ' = :user_id') ->where($db->quoteName('method') . ' = :method') ->bind(':user_id', $userId, ParameterType::INTEGER) ->bind(':method', $method); $db->setQuery($query)->execute(); // Migrate data Factory::getApplication()->bootComponent('com_users')->getMVCFactory()->createTable('Mfa', 'Administrator')->save( [ 'user_id' => $user->id, 'title' => Text::_('COM_USERS_USER_BACKUPCODES'), 'method' => 'backupcodes', 'default' => 0, 'created_on' => Date::getInstance()->toSql(), 'last_used' => null, 'tries' => 0, 'try_count' => null, 'options' => @json_decode($otep, true), ] ); } // Remove the legacy MFA $update = (object) [ 'id' => $user->id, 'otpKey' => '', 'otep' => '', ]; $db->updateObject('#__users', $update, ['id']); } /** * Tries to decrypt the legacy MFA configuration. * * @param string $secret Site's secret key * @param string $stringToDecrypt Base64-encoded and encrypted, JSON-encoded information * * @return string Decrypted, but JSON-encoded, information * * @see https://github.com/joomla/joomla-cms/pull/12497 * @since 4.2.0 */ private function decryptLegacyTFAString(string $secret, string $stringToDecrypt): string { // Is this already decrypted? try { $decrypted = @json_decode($stringToDecrypt, true); } catch (\Exception $e) { $decrypted = null; } if (!empty($decrypted)) { return $stringToDecrypt; } // No, we need to decrypt the string $aes = new Aes($secret, 256); $decrypted = $aes->decryptString($stringToDecrypt); if (!is_string($decrypted) || empty($decrypted)) { $aes->setPassword($secret, true); $decrypted = $aes->decryptString($stringToDecrypt); } if (!is_string($decrypted) || empty($decrypted)) { return ''; } // Remove the null padding added during encryption return rtrim($decrypted, "\0"); } } PK0A#]��`k~~'src/Application/EventAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherInterface; use Joomla\Event\Event; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining application that can trigger Joomla 3.x style events * * @since 4.0.0 * @deprecated 4.3 will be removed in 6.0 * This interface will be removed without replacement as the Joomla 3.x compatibility layer will be removed * @todo Move to combat plugin */ interface EventAwareInterface extends DispatcherAwareInterface { /** * Get the event dispatcher. * * @return DispatcherInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ public function getDispatcher(); /** * Calls all handlers associated with an event group. * * This is a legacy method, implementing old-style (Joomla! 3.x) plugin calls. It's best to go directly through the * Dispatcher and handle the returned EventInterface object instead of going through this method. This method is * deprecated and will be removed in Joomla! 5.x. * * This method will only return the 'result' argument of the event * * @param string $eventName The event name. * @param array|Event $args An array of arguments or an Event object (optional). * * @return array An array of results from each function call. Note this will be an empty array if no dispatcher is set. * * @since 4.0.0 * @throws \InvalidArgumentException * * @deprecated 4.0 will be removed in 6.0 * Use the Dispatcher method instead * Example: Factory::getApplication()->getDispatcher()->dispatch($eventName, $event); */ public function triggerEvent($eventName, $args = []); } PK0A#]��([[.src/Application/CMSWebApplicationInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\SessionAwareWebApplicationInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\Menu\AbstractMenu; use Joomla\CMS\Router\Router; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a Joomla! CMS Application class for web applications. * * @since 4.0.0 */ interface CMSWebApplicationInterface extends SessionAwareWebApplicationInterface, CMSApplicationInterface { /** * Method to get the application document object. * * @return Document The document object * * @since 4.0.0 */ public function getDocument(); /** * Get the menu object. * * @param string $name The application name for the menu * @param array $options An array of options to initialise the menu with * * @return AbstractMenu|null An AbstractMenu object or null if not set. * * @since 4.0.0 */ public function getMenu($name = null, $options = []); /** * Returns the application Router object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return Router * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: Factory::getContainer()->get($name); */ public static function getRouter($name = null, array $options = []); /** * Gets a user state. * * @param string $key The path of the state. * @param mixed $default Optional default value, returned if the internal value is null. * * @return mixed The user state or null. * * @since 4.0.0 */ public function getUserState($key, $default = null); /** * Gets the value of a user state variable. * * @param string $key The key of the user state variable. * @param string $request The name of the variable passed in a request. * @param string $default The default value for the variable if not found. Optional. * @param string $type Filter for the variable, for valid values see {@link InputFilter::clean()}. Optional. * * @return mixed The request user state. * * @since 4.0.0 */ public function getUserStateFromRequest($key, $request, $default = null, $type = 'none'); /** * Sets the value of a user state variable. * * @param string $key The path of the state. * @param mixed $value The value of the variable. * * @return mixed The previous state, if one existed. Null otherwise. * * @since 4.0.0 */ public function setUserState($key, $value); } PK0A#]æZ+#src/Application/BaseApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\AbstractApplication; use Joomla\CMS\Input\Input; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform Base Application Class * * @property-read Input $input The application input object * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0 * Application classes should directly be based on \Joomla\Application\AbstractApplication * don't use this class anymore */ abstract class BaseApplication extends AbstractApplication implements DispatcherAwareInterface { use DispatcherAwareTrait; use EventAware; use IdentityAware; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's * input object. If the argument is a \JInput object that object will become * the application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's * config object. If the argument is a Registry object that object will become * the application's config object, otherwise a default config object is created. * * @since 3.0.0 */ public function __construct(Input $input = null, Registry $config = null) { $this->input = $input instanceof Input ? $input : new Input(); $this->config = $config instanceof Registry ? $config : new Registry(); $this->initialise(); } } PK0A#]n(�%�s�s#src/Application/SiteApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\Web\WebClient; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Cache\Controller\OutputController; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Input\Input; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Pathway\Pathway; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Route; use Joomla\CMS\Router\SiteRouter; use Joomla\CMS\Uri\Uri; use Joomla\DI\Container; use Joomla\Registry\Registry; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Site Application class * * @since 3.2 */ final class SiteApplication extends CMSApplication { use CacheControllerFactoryAwareTrait; use MultiFactorAuthenticationHandler; /** * Option to filter by language * * @var boolean * @since 4.0.0 */ protected $language_filter = false; /** * Option to detect language by the browser * * @var boolean * @since 4.0.0 */ protected $detect_browser = false; /** * The registered URL parameters. * * @var object * @since 4.3.0 */ public $registeredurlparams; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's input * object. If the argument is a JInput object that object will become the * application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config * object. If the argument is a Registry object that object will become the * application's config object, otherwise a default config object is created. * @param WebClient $client An optional argument to provide dependency injection for the application's client * object. If the argument is a WebClient object that object will become the * application's client object, otherwise a default client object is created. * @param Container $container Dependency injection container. * * @since 3.2 */ public function __construct(Input $input = null, Registry $config = null, WebClient $client = null, Container $container = null) { // Register the application name $this->name = 'site'; // Register the client ID $this->clientId = 0; // Execute the parent constructor parent::__construct($input, $config, $client, $container); } /** * Check if the user can access the application * * @param integer $itemid The item ID to check authorisation for * * @return void * * @since 3.2 * * @throws \Exception When you are not authorised to view the home page menu item */ protected function authorise($itemid) { $menus = $this->getMenu(); $user = Factory::getUser(); if (!$menus->authorise($itemid)) { if ($user->get('id') == 0) { // Set the data $this->setUserState('users.login.form.data', ['return' => Uri::getInstance()->toString()]); $url = Route::_('index.php?option=com_users&view=login', false); $this->enqueueMessage(Text::_('JGLOBAL_YOU_MUST_LOGIN_FIRST'), 'error'); $this->redirect($url); } else { // Get the home page menu item $home_item = $menus->getDefault($this->getLanguage()->getTag()); // If we are already in the homepage raise an exception if ($menus->getActive()->id == $home_item->id) { throw new \Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403); } // Otherwise redirect to the homepage and show an error $this->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error'); $this->redirect(Route::_('index.php?Itemid=' . $home_item->id, false)); } } } /** * Dispatch the application * * @param string $component The component which is being rendered. * * @return void * * @since 3.2 */ public function dispatch($component = null) { // Get the component if not set. if (!$component) { $component = $this->input->getCmd('option', null); } // Load the document to the API $this->loadDocument(); // Set up the params $document = $this->getDocument(); $params = $this->getParams(); // Register the document object with Factory Factory::$document = $document; switch ($document->getType()) { case 'html': // Set up the language LanguageHelper::getLanguages('lang_code'); // Set metadata $document->setMetaData('rights', $this->get('MetaRights')); // Get the template $template = $this->getTemplate(true); // Store the template and its params to the config $this->set('theme', $template->template); $this->set('themeParams', $template->params); // Add Asset registry files $wr = $document->getWebAssetManager()->getRegistry(); if ($component) { $wr->addExtensionRegistryFile($component); } if ($template->parent) { $wr->addTemplateRegistryFile($template->parent, $this->getClientId()); } $wr->addTemplateRegistryFile($template->template, $this->getClientId()); break; case 'feed': $document->setBase(htmlspecialchars(Uri::current())); break; } $document->setTitle($params->get('page_title')); $document->setDescription($params->get('page_description')); // Add version number or not based on global configuration if ($this->get('MetaVersion', 0)) { $document->setGenerator('Joomla! - Open Source Content Management - Version ' . JVERSION); } else { $document->setGenerator('Joomla! - Open Source Content Management'); } $contents = ComponentHelper::renderComponent($component); $document->setBuffer($contents, 'component'); // Trigger the onAfterDispatch event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterDispatch'); } /** * Method to run the Web application routines. * * @return void * * @since 3.2 */ protected function doExecute() { // Initialise the application $this->initialiseApp(); // Mark afterInitialise in the profiler. JDEBUG ? $this->profiler->mark('afterInitialise') : null; // Route the application $this->route(); // Mark afterRoute in the profiler. JDEBUG ? $this->profiler->mark('afterRoute') : null; if (!$this->isHandlingMultiFactorAuthentication()) { /* * Check if the user is required to reset their password * * Before $this->route(); "option" and "view" can't be safely read using: * $this->input->getCmd('option'); or $this->input->getCmd('view'); * ex: due of the sef urls */ $this->checkUserRequireReset('com_users', 'profile', 'edit', 'com_users/profile.save,com_users/profile.apply,com_users/user.logout'); } // Dispatch the application $this->dispatch(); // Mark afterDispatch in the profiler. JDEBUG ? $this->profiler->mark('afterDispatch') : null; } /** * Return the current state of the detect browser option. * * @return boolean * * @since 3.2 */ public function getDetectBrowser() { return $this->detect_browser; } /** * Return the current state of the language filter. * * @return boolean * * @since 3.2 */ public function getLanguageFilter() { return $this->language_filter; } /** * Get the application parameters * * @param string $option The component option * * @return Registry The parameters object * * @since 3.2 */ public function getParams($option = null) { static $params = []; $hash = '__default'; if (!empty($option)) { $hash = $option; } if (!isset($params[$hash])) { // Get component parameters if (!$option) { $option = $this->input->getCmd('option', null); } // Get new instance of component global parameters $params[$hash] = clone ComponentHelper::getParams($option); // Get menu parameters $menus = $this->getMenu(); $menu = $menus->getActive(); // Get language $lang_code = $this->getLanguage()->getTag(); $languages = LanguageHelper::getLanguages('lang_code'); $title = $this->get('sitename'); if (isset($languages[$lang_code]) && $languages[$lang_code]->metadesc) { $description = $languages[$lang_code]->metadesc; } else { $description = $this->get('MetaDesc'); } $rights = $this->get('MetaRights'); $robots = $this->get('robots'); // Retrieve com_menu global settings $temp = clone ComponentHelper::getParams('com_menus'); // Lets cascade the parameters if we have menu item parameters if (\is_object($menu)) { // Get show_page_heading from com_menu global settings $params[$hash]->def('show_page_heading', $temp->get('show_page_heading')); $params[$hash]->merge($menu->getParams()); $title = $menu->title; } else { // Merge com_menu global settings $params[$hash]->merge($temp); // If supplied, use page title $title = $temp->get('page_title', $title); } $params[$hash]->def('page_title', $title); $params[$hash]->def('page_description', $description); $params[$hash]->def('page_rights', $rights); $params[$hash]->def('robots', $robots); } return $params[$hash]; } /** * Return a reference to the Pathway object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return Pathway A Pathway object * * @since 3.2 */ public function getPathway($name = 'site', $options = []) { return parent::getPathway($name, $options); } /** * Return a reference to the Router object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return \Joomla\CMS\Router\Router * * @since 3.2 * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: Factory::getContainer()->get(SiteRouter::class); */ public static function getRouter($name = 'site', array $options = []) { return parent::getRouter($name, $options); } /** * Gets the name of the current template. * * @param boolean $params True to return the template parameters * * @return string|\stdClass The name of the template if the params argument is false. The template object if the params argument is true. * * @since 3.2 * @throws \InvalidArgumentException */ public function getTemplate($params = false) { if (\is_object($this->template)) { if ($this->template->parent) { if (!is_file(JPATH_THEMES . '/' . $this->template->template . '/index.php')) { if (!is_file(JPATH_THEMES . '/' . $this->template->parent . '/index.php')) { throw new \InvalidArgumentException(Text::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $this->template->template)); } } } elseif (!is_file(JPATH_THEMES . '/' . $this->template->template . '/index.php')) { throw new \InvalidArgumentException(Text::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $this->template->template)); } if ($params) { return $this->template; } return $this->template->template; } // Get the id of the active menu item $menu = $this->getMenu(); $item = $menu->getActive(); if (!$item) { $item = $menu->getItem($this->input->getInt('Itemid', null)); } $id = 0; if (\is_object($item)) { // Valid item retrieved $id = $item->template_style_id; } $tid = $this->input->getUint('templateStyle', 0); if (is_numeric($tid) && (int) $tid > 0) { $id = (int) $tid; } /** @var OutputController $cache */ $cache = $this->getCacheControllerFactory()->createCacheController('output', ['defaultgroup' => 'com_templates']); if ($this->getLanguageFilter()) { $tag = $this->getLanguage()->getTag(); } else { $tag = ''; } $cacheId = 'templates0' . $tag; if ($cache->contains($cacheId)) { $templates = $cache->get($cacheId); } else { $templates = $this->bootComponent('templates')->getMVCFactory() ->createModel('Style', 'Administrator')->getSiteTemplates(); foreach ($templates as &$template) { // Create home element if ($template->home == 1 && !isset($template_home) || $this->getLanguageFilter() && $template->home == $tag) { $template_home = clone $template; } $template->params = new Registry($template->params); } // Unset the $template reference to the last $templates[n] item cycled in the foreach above to avoid editing it later unset($template); // Add home element, after loop to avoid double execution if (isset($template_home)) { $template_home->params = new Registry($template_home->params); $templates[0] = $template_home; } $cache->store($templates, $cacheId); } if (isset($templates[$id])) { $template = $templates[$id]; } else { $template = $templates[0]; } // Allows for overriding the active template from the request $template_override = $this->input->getCmd('template', ''); // Only set template override if it is a valid template (= it exists and is enabled) if (!empty($template_override)) { if (is_file(JPATH_THEMES . '/' . $template_override . '/index.php')) { foreach ($templates as $tmpl) { if ($tmpl->template === $template_override) { $template = $tmpl; break; } } } } // Need to filter the default value as well $template->template = InputFilter::getInstance()->clean($template->template, 'cmd'); // Fallback template if (!empty($template->parent)) { if (!is_file(JPATH_THEMES . '/' . $template->template . '/index.php')) { if (!is_file(JPATH_THEMES . '/' . $template->parent . '/index.php')) { $this->enqueueMessage(Text::_('JERROR_ALERTNOTEMPLATE'), 'error'); // Try to find data for 'cassiopeia' template $original_tmpl = $template->template; foreach ($templates as $tmpl) { if ($tmpl->template === 'cassiopeia') { $template = $tmpl; break; } } // Check, the data were found and if template really exists if (!is_file(JPATH_THEMES . '/' . $template->template . '/index.php')) { throw new \InvalidArgumentException(Text::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $original_tmpl)); } } } } elseif (!is_file(JPATH_THEMES . '/' . $template->template . '/index.php')) { $this->enqueueMessage(Text::_('JERROR_ALERTNOTEMPLATE'), 'error'); // Try to find data for 'cassiopeia' template $original_tmpl = $template->template; foreach ($templates as $tmpl) { if ($tmpl->template === 'cassiopeia') { $template = $tmpl; break; } } // Check, the data were found and if template really exists if (!is_file(JPATH_THEMES . '/' . $template->template . '/index.php')) { throw new \InvalidArgumentException(Text::sprintf('JERROR_COULD_NOT_FIND_TEMPLATE', $original_tmpl)); } } // Cache the result $this->template = $template; if ($params) { return $template; } return $template->template; } /** * Initialise the application. * * @param array $options An optional associative array of configuration settings. * * @return void * * @since 3.2 */ protected function initialiseApp($options = []) { $user = Factory::getUser(); // If the user is a guest we populate it with the guest user group. if ($user->guest) { $guestUsergroup = ComponentHelper::getParams('com_users')->get('guest_usergroup', 1); $user->groups = [$guestUsergroup]; } if ($plugin = PluginHelper::getPlugin('system', 'languagefilter')) { $pluginParams = new Registry($plugin->params); $this->setLanguageFilter(true); $this->setDetectBrowser($pluginParams->get('detect_browser', 1) == 1); } if (empty($options['language'])) { // Detect the specified language $lang = $this->input->getString('language', null); // Make sure that the user's language exists if ($lang && LanguageHelper::exists($lang)) { $options['language'] = $lang; } } if (empty($options['language']) && $this->getLanguageFilter()) { // Detect cookie language $lang = $this->input->cookie->get(md5($this->get('secret') . 'language'), null, 'string'); // Make sure that the user's language exists if ($lang && LanguageHelper::exists($lang)) { $options['language'] = $lang; } } if (empty($options['language'])) { // Detect user language $lang = $user->getParam('language'); // Make sure that the user's language exists if ($lang && LanguageHelper::exists($lang)) { $options['language'] = $lang; } } if (empty($options['language']) && $this->getDetectBrowser()) { // Detect browser language $lang = LanguageHelper::detectLanguage(); // Make sure that the user's language exists if ($lang && LanguageHelper::exists($lang)) { $options['language'] = $lang; } } if (empty($options['language'])) { // Detect default language $params = ComponentHelper::getParams('com_languages'); $options['language'] = $params->get('site', $this->get('language', 'en-GB')); } // One last check to make sure we have something if (!LanguageHelper::exists($options['language'])) { $lang = $this->config->get('language', 'en-GB'); if (LanguageHelper::exists($lang)) { $options['language'] = $lang; } else { // As a last ditch fail to english $options['language'] = 'en-GB'; } } // Finish initialisation parent::initialiseApp($options); } /** * Load the library language files for the application * * @return void * * @since 3.6.3 */ protected function loadLibraryLanguage() { /* * Try the lib_joomla file in the current language (without allowing the loading of the file in the default language) * Fallback to the default language if necessary */ $this->getLanguage()->load('lib_joomla', JPATH_SITE) || $this->getLanguage()->load('lib_joomla', JPATH_ADMINISTRATOR); } /** * Login authentication function * * @param array $credentials Array('username' => string, 'password' => string) * @param array $options Array('remember' => boolean) * * @return boolean True on success. * * @since 3.2 */ public function login($credentials, $options = []) { // Set the application login entry point if (!\array_key_exists('entry_url', $options)) { $options['entry_url'] = Uri::base() . 'index.php?option=com_users&task=user.login'; } // Set the access control action to check. $options['action'] = 'core.login.site'; return parent::login($credentials, $options); } /** * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the application response buffer. * * @return void * * @since 3.2 */ protected function render() { switch ($this->document->getType()) { case 'feed': // No special processing for feeds break; case 'html': default: $template = $this->getTemplate(true); $file = $this->input->get('tmpl', 'index'); if ($file === 'offline' && !$this->get('offline')) { $this->set('themeFile', 'index.php'); } if ($this->get('offline') && !Factory::getUser()->authorise('core.login.offline')) { $this->setUserState('users.login.form.data', ['return' => Uri::getInstance()->toString()]); $this->set('themeFile', 'offline.php'); $this->setHeader('Status', '503 Service Temporarily Unavailable', 'true'); } if (!is_dir(JPATH_THEMES . '/' . $template->template) && !$this->get('offline')) { $this->set('themeFile', 'component.php'); } // Ensure themeFile is set by now if ($this->get('themeFile') == '') { $this->set('themeFile', $file . '.php'); } // Pass the parent template to the state $this->set('themeInherits', $template->parent); break; } parent::render(); } /** * Route the application. * * Routing is the process of examining the request environment to determine which * component should receive the request. The component optional parameters * are then set in the request object to be processed when the application is being * dispatched. * * @return void * * @since 3.2 */ protected function route() { // Get the full request URI. $uri = clone Uri::getInstance(); // It is not possible to inject the SiteRouter as it requires a SiteApplication // and we would end in an infinite loop $result = $this->getContainer()->get(SiteRouter::class)->parse($uri, true); $active = $this->getMenu()->getActive(); if ( $active !== null && $active->type === 'alias' && $active->getParams()->get('alias_redirect') && \in_array($this->input->getMethod(), ['GET', 'HEAD'], true) ) { $item = $this->getMenu()->getItem($active->getParams()->get('aliasoptions')); if ($item !== null) { $oldUri = clone Uri::getInstance(); if ($oldUri->getVar('Itemid') == $active->id) { $oldUri->setVar('Itemid', $item->id); } $base = Uri::base(true); $oldPath = StringHelper::strtolower(substr($oldUri->getPath(), \strlen($base) + 1)); $activePathPrefix = StringHelper::strtolower($active->route); $position = strpos($oldPath, $activePathPrefix); if ($position !== false) { $oldUri->setPath($base . '/' . substr_replace($oldPath, $item->route, $position, \strlen($activePathPrefix))); $this->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true); $this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true); $this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false); $this->sendHeaders(); $this->redirect((string) $oldUri, 301); } } } foreach ($result as $key => $value) { $this->input->def($key, $value); } // Trigger the onAfterRoute event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterRoute'); $Itemid = $this->input->getInt('Itemid', null); $this->authorise($Itemid); } /** * Set the current state of the detect browser option. * * @param boolean $state The new state of the detect browser option * * @return boolean The previous state * * @since 3.2 */ public function setDetectBrowser($state = false) { $old = $this->getDetectBrowser(); $this->detect_browser = $state; return $old; } /** * Set the current state of the language filter. * * @param boolean $state The new state of the language filter * * @return boolean The previous state * * @since 3.2 */ public function setLanguageFilter($state = false) { $old = $this->getLanguageFilter(); $this->language_filter = $state; return $old; } /** * Overrides the default template that would be used * * @param \stdClass|string $template The template name or definition * @param mixed $styleParams The template style parameters * * @return void * * @since 3.2 */ public function setTemplate($template, $styleParams = null) { if (is_object($template)) { $templateName = empty($template->template) ? '' : $template->template; $templateInheritable = empty($template->inheritable) ? 0 : $template->inheritable; $templateParent = empty($template->parent) ? '' : $template->parent; $templateParams = empty($template->params) ? $styleParams : $template->params; } else { $templateName = $template; $templateInheritable = 0; $templateParent = ''; $templateParams = $styleParams; } if (is_dir(JPATH_THEMES . '/' . $templateName)) { $this->template = new \stdClass(); $this->template->template = $templateName; if ($templateParams instanceof Registry) { $this->template->params = $templateParams; } else { $this->template->params = new Registry($templateParams); } $this->template->inheritable = $templateInheritable; $this->template->parent = $templateParent; // Store the template and its params to the config $this->set('theme', $this->template->template); $this->set('themeParams', $this->template->params); } } } PK0A#]aj'Z�p�p%src/Application/DaemonApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Input\Cli; use Joomla\CMS\Log\Log; use Joomla\Event\DispatcherInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to turn CliApplication applications into daemons. It requires CLI and PCNTL support built into PHP. * * @link https://www.php.net/manual/en/book.pcntl.php * @link https://www.php.net/manual/en/features.commandline.php * @since 1.7.0 */ abstract class DaemonApplication extends CliApplication { /** * @var array The available POSIX signals to be caught by default. * @link https://www.php.net/manual/pcntl.constants.php * @since 1.7.0 */ protected static $signals = [ 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGIOT', 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGSTKFLT', 'SIGCLD', 'SIGCHLD', 'SIGCONT', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGXCPU', 'SIGXFSZ', 'SIGVTALRM', 'SIGPROF', 'SIGWINCH', 'SIGPOLL', 'SIGIO', 'SIGPWR', 'SIGSYS', 'SIGBABY', 'SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK', ]; /** * @var boolean True if the daemon is in the process of exiting. * @since 1.7.0 */ protected $exiting = false; /** * @var integer The parent process id. * @since 3.0.0 */ protected $parentId = 0; /** * @var integer The process id of the daemon. * @since 1.7.0 */ protected $processId = 0; /** * @var boolean True if the daemon is currently running. * @since 1.7.0 */ protected $running = false; /** * Class constructor. * * @param Cli $input An optional argument to provide dependency injection for the application's * input object. If the argument is a JInputCli object that object will become * the application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's * config object. If the argument is a Registry object that object will become * the application's config object, otherwise a default config object is created. * @param DispatcherInterface $dispatcher An optional argument to provide dependency injection for the application's * event dispatcher. If the argument is a DispatcherInterface object that object will become * the application's event dispatcher, if it is null then the default event dispatcher * will be created based on the application's loadDispatcher() method. * * @since 1.7.0 */ public function __construct(Cli $input = null, Registry $config = null, DispatcherInterface $dispatcher = null) { // Verify that the process control extension for PHP is available. if (!\defined('SIGHUP')) { Log::add('The PCNTL extension for PHP is not available.', Log::ERROR); throw new \RuntimeException('The PCNTL extension for PHP is not available.'); } // Verify that POSIX support for PHP is available. if (!\function_exists('posix_getpid')) { Log::add('The POSIX extension for PHP is not available.', Log::ERROR); throw new \RuntimeException('The POSIX extension for PHP is not available.'); } // Call the parent constructor. parent::__construct($input, $config, null, null, $dispatcher); // Set some system limits. if (\function_exists('set_time_limit')) { set_time_limit($this->config->get('max_execution_time', 0)); } if ($this->config->get('max_memory_limit') !== null) { ini_set('memory_limit', $this->config->get('max_memory_limit', '256M')); } // Flush content immediately. ob_implicit_flush(); } /** * Method to handle POSIX signals. * * @param integer $signal The received POSIX signal. * * @return void * * @since 1.7.0 * @see pcntl_signal() * @throws \RuntimeException */ public static function signal($signal) { // Log all signals sent to the daemon. Log::add('Received signal: ' . $signal, Log::DEBUG); // Let's make sure we have an application instance. if (!is_subclass_of(static::$instance, CliApplication::class)) { Log::add('Cannot find the application instance.', Log::EMERGENCY); throw new \RuntimeException('Cannot find the application instance.'); } // Fire the onReceiveSignal event. static::$instance->triggerEvent('onReceiveSignal', [$signal]); switch ($signal) { case SIGINT: case SIGTERM: // Handle shutdown tasks if (static::$instance->running && static::$instance->isActive()) { static::$instance->shutdown(); } else { static::$instance->close(); } break; case SIGHUP: // Handle restart tasks if (static::$instance->running && static::$instance->isActive()) { static::$instance->shutdown(true); } else { static::$instance->close(); } break; case SIGCHLD: // A child process has died while (static::$instance->pcntlWait($signal, WNOHANG || WUNTRACED) > 0) { usleep(1000); } break; case SIGCLD: while (static::$instance->pcntlWait($signal, WNOHANG) > 0) { $signal = static::$instance->pcntlChildExitStatus($signal); } break; default: break; } } /** * Check to see if the daemon is active. This does not assume that $this daemon is active, but * only if an instance of the application is active as a daemon. * * @return boolean True if daemon is active. * * @since 1.7.0 */ public function isActive() { // Get the process id file location for the application. $pidFile = $this->config->get('application_pid_file'); // If the process id file doesn't exist then the daemon is obviously not running. if (!is_file($pidFile)) { return false; } // Read the contents of the process id file as an integer. $fp = fopen($pidFile, 'r'); $pid = fread($fp, filesize($pidFile)); $pid = (int) $pid; fclose($fp); // Check to make sure that the process id exists as a positive integer. if (!$pid) { return false; } // Check to make sure the process is active by pinging it and ensure it responds. if (!posix_kill($pid, 0)) { // No response so remove the process id file and log the situation. @ unlink($pidFile); Log::add('The process found based on PID file was unresponsive.', Log::WARNING); return false; } return true; } /** * Load an object or array into the application configuration object. * * @param mixed $data Either an array or object to be loaded into the configuration object. * * @return DaemonApplication Instance of $this to allow chaining. * * @since 1.7.0 */ public function loadConfiguration($data) { /* * Setup some application metadata options. This is useful if we ever want to write out startup scripts * or just have some sort of information available to share about things. */ // The application author name. This string is used in generating startup scripts and has // a maximum of 50 characters. $tmp = (string) $this->config->get('author_name', 'Joomla Platform'); $this->config->set('author_name', (\strlen($tmp) > 50) ? substr($tmp, 0, 50) : $tmp); // The application author email. This string is used in generating startup scripts. $tmp = (string) $this->config->get('author_email', 'admin@joomla.org'); $this->config->set('author_email', filter_var($tmp, FILTER_VALIDATE_EMAIL)); // The application name. This string is used in generating startup scripts. $tmp = (string) $this->config->get('application_name', 'DaemonApplication'); $this->config->set('application_name', (string) preg_replace('/[^A-Z0-9_-]/i', '', $tmp)); // The application description. This string is used in generating startup scripts. $tmp = (string) $this->config->get('application_description', 'A generic Joomla Platform application.'); $this->config->set('application_description', filter_var($tmp, FILTER_SANITIZE_STRING)); /* * Setup the application path options. This defines the default executable name, executable directory, * and also the path to the daemon process id file. */ // The application executable daemon. This string is used in generating startup scripts. $tmp = (string) $this->config->get('application_executable', basename($this->input->executable)); $this->config->set('application_executable', $tmp); // The home directory of the daemon. $tmp = (string) $this->config->get('application_directory', \dirname($this->input->executable)); $this->config->set('application_directory', $tmp); // The pid file location. This defaults to a path inside the /tmp directory. $name = $this->config->get('application_name'); $tmp = (string) $this->config->get('application_pid_file', strtolower('/tmp/' . $name . '/' . $name . '.pid')); $this->config->set('application_pid_file', $tmp); /* * Setup the application identity options. It is important to remember if the default of 0 is set for * either UID or GID then changing that setting will not be attempted as there is no real way to "change" * the identity of a process from some user to root. */ // The user id under which to run the daemon. $tmp = (int) $this->config->get('application_uid', 0); $options = ['options' => ['min_range' => 0, 'max_range' => 65000]]; $this->config->set('application_uid', filter_var($tmp, FILTER_VALIDATE_INT, $options)); // The group id under which to run the daemon. $tmp = (int) $this->config->get('application_gid', 0); $options = ['options' => ['min_range' => 0, 'max_range' => 65000]]; $this->config->set('application_gid', filter_var($tmp, FILTER_VALIDATE_INT, $options)); // Option to kill the daemon if it cannot switch to the chosen identity. $tmp = (bool) $this->config->get('application_require_identity', 1); $this->config->set('application_require_identity', $tmp); /* * Setup the application runtime options. By default our execution time limit is infinite obviously * because a daemon should be constantly running unless told otherwise. The default limit for memory * usage is 256M, which admittedly is a little high, but remember it is a "limit" and PHP's memory * management leaves a bit to be desired :-) */ // The maximum execution time of the application in seconds. Zero is infinite. $tmp = $this->config->get('max_execution_time'); if ($tmp !== null) { $this->config->set('max_execution_time', (int) $tmp); } // The maximum amount of memory the application can use. $tmp = $this->config->get('max_memory_limit', '256M'); if ($tmp !== null) { $this->config->set('max_memory_limit', (string) $tmp); } return $this; } /** * Execute the daemon. * * @return void * * @since 1.7.0 */ public function execute() { // Trigger the onBeforeExecute event $this->triggerEvent('onBeforeExecute'); // Enable basic garbage collection. gc_enable(); Log::add('Starting ' . $this->name, Log::INFO); // Set off the process for becoming a daemon. if ($this->daemonize()) { // Declare ticks to start signal monitoring. When you declare ticks, PCNTL will monitor // incoming signals after each tick and call the relevant signal handler automatically. declare(ticks=1); // Start the main execution loop. while (true) { // Perform basic garbage collection. $this->gc(); // Don't completely overload the CPU. usleep(1000); // Execute the main application logic. $this->doExecute(); } } else { // We were not able to daemonize the application so log the failure and die gracefully. Log::add('Starting ' . $this->name . ' failed', Log::INFO); } // Trigger the onAfterExecute event. $this->triggerEvent('onAfterExecute'); } /** * Restart daemon process. * * @return void * * @since 1.7.0 */ public function restart() { Log::add('Stopping ' . $this->name, Log::INFO); $this->shutdown(true); } /** * Stop daemon process. * * @return void * * @since 1.7.0 */ public function stop() { Log::add('Stopping ' . $this->name, Log::INFO); $this->shutdown(); } /** * Method to change the identity of the daemon process and resources. * * @return boolean True if identity successfully changed * * @since 1.7.0 * @see posix_setuid() */ protected function changeIdentity() { // Get the group and user ids to set for the daemon. $uid = (int) $this->config->get('application_uid', 0); $gid = (int) $this->config->get('application_gid', 0); // Get the application process id file path. $file = $this->config->get('application_pid_file'); // Change the user id for the process id file if necessary. if ($uid && (fileowner($file) != $uid) && (!@ chown($file, $uid))) { Log::add('Unable to change user ownership of the process id file.', Log::ERROR); return false; } // Change the group id for the process id file if necessary. if ($gid && (filegroup($file) != $gid) && (!@ chgrp($file, $gid))) { Log::add('Unable to change group ownership of the process id file.', Log::ERROR); return false; } // Set the correct home directory for the process. if ($uid && ($info = posix_getpwuid($uid)) && is_dir($info['dir'])) { system('export HOME="' . $info['dir'] . '"'); } // Change the user id for the process necessary. if ($uid && (posix_getuid() != $uid) && (!@ posix_setuid($uid))) { Log::add('Unable to change user ownership of the process.', Log::ERROR); return false; } // Change the group id for the process necessary. if ($gid && (posix_getgid() != $gid) && (!@ posix_setgid($gid))) { Log::add('Unable to change group ownership of the process.', Log::ERROR); return false; } // Get the user and group information based on uid and gid. $user = posix_getpwuid($uid); $group = posix_getgrgid($gid); Log::add('Changed daemon identity to ' . $user['name'] . ':' . $group['name'], Log::INFO); return true; } /** * Method to put the application into the background. * * @return boolean * * @since 1.7.0 * @throws \RuntimeException */ protected function daemonize() { // Is there already an active daemon running? if ($this->isActive()) { Log::add($this->name . ' daemon is still running. Exiting the application.', Log::EMERGENCY); return false; } // Reset Process Information $this->processId = 0; $this->running = false; // Detach process! try { // Check if we should run in the foreground. if (!$this->input->get('f')) { // Detach from the terminal. $this->detach(); } else { // Setup running values. $this->exiting = false; $this->running = true; // Set the process id. $this->processId = (int) posix_getpid(); $this->parentId = $this->processId; } } catch (\RuntimeException $e) { Log::add('Unable to fork.', Log::EMERGENCY); return false; } // Verify the process id is valid. if ($this->processId < 1) { Log::add('The process id is invalid; the fork failed.', Log::EMERGENCY); return false; } // Clear the umask. @ umask(0); // Write out the process id file for concurrency management. if (!$this->writeProcessIdFile()) { Log::add('Unable to write the pid file at: ' . $this->config->get('application_pid_file'), Log::EMERGENCY); return false; } // Attempt to change the identity of user running the process. if (!$this->changeIdentity()) { // If the identity change was required then we need to return false. if ($this->config->get('application_require_identity')) { Log::add('Unable to change process owner.', Log::CRITICAL); return false; } else { Log::add('Unable to change process owner.', Log::WARNING); } } // Setup the signal handlers for the daemon. if (!$this->setupSignalHandlers()) { return false; } // Change the current working directory to the application working directory. @ chdir($this->config->get('application_directory')); return true; } /** * This is truly where the magic happens. This is where we fork the process and kill the parent * process, which is essentially what turns the application into a daemon. * * @return void * * @since 3.0.0 * @throws \RuntimeException */ protected function detach() { Log::add('Detaching the ' . $this->name . ' daemon.', Log::DEBUG); // Attempt to fork the process. $pid = $this->fork(); // If the pid is positive then we successfully forked, and can close this application. if ($pid) { // Add the log entry for debugging purposes and exit gracefully. Log::add('Ending ' . $this->name . ' parent process', Log::DEBUG); $this->close(); } else { // We are in the forked child process. // Setup some protected values. $this->exiting = false; $this->running = true; // Set the parent to self. $this->parentId = $this->processId; } } /** * Method to fork the process. * * @return integer The child process id to the parent process, zero to the child process. * * @since 1.7.0 * @throws \RuntimeException */ protected function fork() { // Attempt to fork the process. $pid = $this->pcntlFork(); // If the fork failed, throw an exception. if ($pid === -1) { throw new \RuntimeException('The process could not be forked.'); } elseif ($pid === 0) { // Update the process id for the child. $this->processId = (int) posix_getpid(); } else { // Log the fork in the parent. // Log the fork. Log::add('Process forked ' . $pid, Log::DEBUG); } // Trigger the onFork event. $this->postFork(); return $pid; } /** * Method to perform basic garbage collection and memory management in the sense of clearing the * stat cache. We will probably call this method pretty regularly in our main loop. * * @return void * * @since 1.7.0 */ protected function gc() { // Perform generic garbage collection. gc_collect_cycles(); // Clear the stat cache so it doesn't blow up memory. clearstatcache(); } /** * Method to attach the DaemonApplication signal handler to the known signals. Applications * can override these handlers by using the pcntl_signal() function and attaching a different * callback method. * * @return boolean * * @since 1.7.0 * @see pcntl_signal() */ protected function setupSignalHandlers() { // We add the error suppression for the loop because on some platforms some constants are not defined. foreach (self::$signals as $signal) { // Ignore signals that are not defined. if (!\defined($signal) || !\is_int(\constant($signal)) || (\constant($signal) === 0)) { // Define the signal to avoid notices. Log::add('Signal "' . $signal . '" not defined. Defining it as null.', Log::DEBUG); \define($signal, null); // Don't listen for signal. continue; } // Attach the signal handler for the signal. if (!$this->pcntlSignal(\constant($signal), ['DaemonApplication', 'signal'])) { Log::add(sprintf('Unable to reroute signal handler: %s', $signal), Log::EMERGENCY); return false; } } return true; } /** * Method to shut down the daemon and optionally restart it. * * @param boolean $restart True to restart the daemon on exit. * * @return void * * @since 1.7.0 */ protected function shutdown($restart = false) { // If we are already exiting, chill. if ($this->exiting) { return; } else { // If not, now we are. $this->exiting = true; } // If we aren't already daemonized then just kill the application. if (!$this->running && !$this->isActive()) { Log::add('Process was not daemonized yet, just halting current process', Log::INFO); $this->close(); } // Only read the pid for the parent file. if ($this->parentId == $this->processId) { // Read the contents of the process id file as an integer. $fp = fopen($this->config->get('application_pid_file'), 'r'); $pid = fread($fp, filesize($this->config->get('application_pid_file'))); $pid = (int) $pid; fclose($fp); // Remove the process id file. @ unlink($this->config->get('application_pid_file')); // If we are supposed to restart the daemon we need to execute the same command. if ($restart) { $this->close(exec(implode(' ', $GLOBALS['argv']) . ' > /dev/null &')); } else { // If we are not supposed to restart the daemon let's just kill -9. passthru('kill -9 ' . $pid); $this->close(); } } } /** * Method to write the process id file out to disk. * * @return boolean * * @since 1.7.0 */ protected function writeProcessIdFile() { // Verify the process id is valid. if ($this->processId < 1) { Log::add('The process id is invalid.', Log::EMERGENCY); return false; } // Get the application process id file path. $file = $this->config->get('application_pid_file'); if (empty($file)) { Log::add('The process id file path is empty.', Log::ERROR); return false; } // Make sure that the folder where we are writing the process id file exists. $folder = \dirname($file); if (!is_dir($folder) && !Folder::create($folder)) { Log::add('Unable to create directory: ' . $folder, Log::ERROR); return false; } // Write the process id file out to disk. if (!file_put_contents($file, $this->processId)) { Log::add('Unable to write process id file: ' . $file, Log::ERROR); return false; } // Make sure the permissions for the process id file are accurate. if (!chmod($file, 0644)) { Log::add('Unable to adjust permissions for the process id file: ' . $file, Log::ERROR); return false; } return true; } /** * Method to handle post-fork triggering of the onFork event. * * @return void * * @since 3.0.0 */ protected function postFork() { // Trigger the onFork event. $this->triggerEvent('onFork'); } /** * Method to return the exit code of a terminated child process. * * @param integer $status The status parameter is the status parameter supplied to a successful call to pcntl_waitpid(). * * @return integer The child process exit code. * * @see pcntl_wexitstatus() * @since 1.7.3 */ protected function pcntlChildExitStatus($status) { return pcntl_wexitstatus($status); } /** * Method to return the exit code of a terminated child process. * * @return integer On success, the PID of the child process is returned in the parent's thread * of execution, and a 0 is returned in the child's thread of execution. On * failure, a -1 will be returned in the parent's context, no child process * will be created, and a PHP error is raised. * * @see pcntl_fork() * @since 1.7.3 */ protected function pcntlFork() { return pcntl_fork(); } /** * Method to install a signal handler. * * @param integer $signal The signal number. * @param callable $handler The signal handler which may be the name of a user created function, * or method, or either of the two global constants SIG_IGN or SIG_DFL. * @param boolean $restart Specifies whether system call restarting should be used when this * signal arrives. * * @return boolean True on success. * * @see pcntl_signal() * @since 1.7.3 */ protected function pcntlSignal($signal, $handler, $restart = true) { return pcntl_signal($signal, $handler, $restart); } /** * Method to wait on or return the status of a forked child. * * @param integer &$status Status information. * @param integer $options If wait3 is available on your system (mostly BSD-style systems), * you can provide the optional options parameter. * * @return integer The process ID of the child which exited, -1 on error or zero if WNOHANG * was provided as an option (on wait3-available systems) and no child was available. * * @see pcntl_wait() * @since 1.7.3 */ protected function pcntlWait(&$status, $options = 0) { return pcntl_wait($status, $options); } } PK0A#]���++!src/Application/IdentityAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\User\User; use Joomla\CMS\User\UserFactoryAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for application classes which are identity (user) aware * * @since 4.0.0 */ trait IdentityAware { use UserFactoryAwareTrait; /** * The application identity object. * * @var User * @since 4.0.0 */ protected $identity; /** * Get the application identity. * * @return User * * @since 4.0.0 */ public function getIdentity() { return $this->identity; } /** * Allows the application to load a custom or default identity. * * @param User $identity An optional identity object. If omitted, a null user object is created. * * @return $this * * @since 4.0.0 */ public function loadIdentity(User $identity = null) { $this->identity = $identity ?: $this->getUserFactory()->loadUserById(0); return $this; } } PK0A#]?h�,��,src/Application/ExtensionNamespaceMapper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for application classes which ensures the namespace mapper exists and includes it. * * @since 4.0.0 */ trait ExtensionNamespaceMapper { /** * Allows the application to load a custom or default identity. * * @return void * * @since 4.0.0 */ public function createExtensionNamespaceMap() { \JLoader::register('JNamespacePsr4Map', JPATH_LIBRARIES . '/namespacemap.php'); $extensionPsr4Loader = new \JNamespacePsr4Map(); $extensionPsr4Loader->load(); } } PK0A#]��2̵N�N&src/Application/ConsoleApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\Console; use Joomla\CMS\Extension\ExtensionManagerTrait; use Joomla\CMS\Factory; use Joomla\CMS\Language\Language; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Router; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Version; use Joomla\Console\Application; use Joomla\Database\DatabaseAwareTrait; use Joomla\DI\Container; use Joomla\DI\ContainerAwareTrait; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Input\Input; use Joomla\Registry\Registry; use Joomla\Session\SessionInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The Joomla! CMS Console Application * * @since 4.0.0 */ class ConsoleApplication extends Application implements DispatcherAwareInterface, CMSApplicationInterface { use DispatcherAwareTrait; use EventAware; use IdentityAware; use ContainerAwareTrait; use ExtensionManagerTrait; use ExtensionNamespaceMapper; use DatabaseAwareTrait; /** * The input. * * @var Input * @since 4.0.0 */ protected $input = null; /** * The name of the application. * * @var string * @since 4.0.0 */ protected $name = null; /** * The application language object. * * @var Language * @since 4.0.0 */ protected $language; /** * The application message queue. * * @var array * @since 4.0.0 */ private $messages = []; /** * The application session object. * * @var SessionInterface * @since 4.0.0 */ private $session; /** * Class constructor. * * @param Registry $config An optional argument to provide dependency injection for the application's config object. If the * argument is a Registry object that object will become the application's config object, * otherwise a default config object is created. * @param DispatcherInterface $dispatcher An optional argument to provide dependency injection for the application's event dispatcher. If the * argument is a DispatcherInterface object that object will become the application's event dispatcher, * if it is null then the default event dispatcher will be created based on the application's * loadDispatcher() method. * @param Container $container Dependency injection container. * @param Language $language The language object provisioned for the application. * @param InputInterface|null $input An optional argument to provide dependency injection for the application's input object. If the * argument is an InputInterface object that object will become the application's input object, * otherwise a default input object is created. * @param OutputInterface|null $output An optional argument to provide dependency injection for the application's output object. If the * argument is an OutputInterface object that object will become the application's output object, * otherwise a default output object is created. * * @since 4.0.0 */ public function __construct( Registry $config, DispatcherInterface $dispatcher, Container $container, Language $language, ?InputInterface $input = null, ?OutputInterface $output = null ) { // Close the application if it is not executed from the command line. if (!\defined('STDOUT') || !\defined('STDIN') || !isset($_SERVER['argv'])) { $this->close(); } // Set up a Input object for Controllers etc to use $this->input = new \Joomla\CMS\Input\Cli(); $this->language = $language; parent::__construct($input, $output, $config); $this->setVersion(JVERSION); // Register the client name as cli $this->name = 'cli'; $this->setContainer($container); $this->setDispatcher($dispatcher); // Set the execution datetime and timestamp; $this->set('execution.datetime', gmdate('Y-m-d H:i:s')); $this->set('execution.timestamp', time()); $this->set('execution.microtimestamp', microtime(true)); // Set the current directory. $this->set('cwd', getcwd()); // Set up the environment $this->input->set('format', 'cli'); } /** * Magic method to access properties of the application. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * This is a B/C proxy for deprecated read accesses, use getInput() method instead * Example: * $app->getInput(); */ public function __get($name) { switch ($name) { case 'input': @trigger_error( 'Accessing the input property of the application is deprecated, use the getInput() method instead.', E_USER_DEPRECATED ); return $this->getInput(); default: $trace = debug_backtrace(); trigger_error( sprintf( 'Undefined property via __get(): %1$s in %2$s on line %3$s', $name, $trace[0]['file'], $trace[0]['line'] ), E_USER_NOTICE ); } } /** * Method to run the application routines. * * @return integer The exit code for the application * * @since 4.0.0 * @throws \Throwable */ protected function doExecute(): int { $exitCode = parent::doExecute(); $style = new SymfonyStyle($this->getConsoleInput(), $this->getConsoleOutput()); $methodMap = [ self::MSG_ALERT => 'error', self::MSG_CRITICAL => 'caution', self::MSG_DEBUG => 'comment', self::MSG_EMERGENCY => 'caution', self::MSG_ERROR => 'error', self::MSG_INFO => 'note', self::MSG_NOTICE => 'note', self::MSG_WARNING => 'warning', ]; // Output any enqueued messages before the app exits foreach ($this->getMessageQueue() as $type => $messages) { $method = $methodMap[$type] ?? 'comment'; $style->$method($messages); } return $exitCode; } /** * Execute the application. * * @return void * * @since 4.0.0 * @throws \Throwable */ public function execute() { // Load extension namespaces $this->createExtensionNamespaceMap(); /** * Address issues with instantiating WebApplication descendants under CLI. * * IMPORTANT! This code must be always be executed **before** the first use of * PluginHelper::importPlugin(). Some plugins will attempt to register an MVCFactory for a * component in their service provider. This will in turn try to get the SiteRouter service * for the component which tries to get an instance of SiteApplication which will fail with * a RuntimeException if the populateHttpHost() method has not already executed. */ $this->populateHttpHost(); // Import CMS plugin groups to be able to subscribe to events PluginHelper::importPlugin('system'); PluginHelper::importPlugin('console'); parent::execute(); } /** * Enqueue a system message. * * @param string $msg The message to enqueue. * @param string $type The message type. * * @return void * * @since 4.0.0 */ public function enqueueMessage($msg, $type = self::MSG_INFO) { if (!array_key_exists($type, $this->messages)) { $this->messages[$type] = []; } $this->messages[$type][] = $msg; } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName(): string { return $this->name; } /** * Get the commands which should be registered by default to the application. * * @return \Joomla\Console\Command\AbstractCommand[] * * @since 4.0.0 */ protected function getDefaultCommands(): array { return array_merge( parent::getDefaultCommands(), [ new Console\CleanCacheCommand(), new Console\CheckUpdatesCommand(), new Console\RemoveOldFilesCommand(), new Console\AddUserCommand($this->getDatabase()), new Console\AddUserToGroupCommand($this->getDatabase()), new Console\RemoveUserFromGroupCommand($this->getDatabase()), new Console\DeleteUserCommand($this->getDatabase()), new Console\ChangeUserPasswordCommand(), new Console\ListUserCommand($this->getDatabase()), ] ); } /** * Retrieve the application configuration object. * * @return Registry * * @since 4.0.0 */ public function getConfig() { return $this->config; } /** * Method to get the application input object. * * @return Input * * @since 4.0.0 */ public function getInput(): Input { return $this->input; } /** * Method to get the application language object. * * @return Language The language object * * @since 4.0.0 */ public function getLanguage() { return $this->language; } /** * Get the system message queue. * * @return array The system message queue. * * @since 4.0.0 */ public function getMessageQueue() { return $this->messages; } /** * Method to get the application session object. * * @return SessionInterface The session object * * @since 4.0.0 */ public function getSession() { return $this->session; } /** * Check the client interface by name. * * @param string $identifier String identifier for the application interface * * @return boolean True if this application is of the given type client interface. * * @since 4.0.0 */ public function isClient($identifier) { return $this->getName() === $identifier; } /** * Flag if the application instance is a CLI or web based application. * * Helper function, you should use the native PHP functions to detect if it is a CLI application. * * @return boolean * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement. CLI will be handled by the joomla/console package instead */ public function isCli() { return true; } /** * Sets the session for the application to use, if required. * * @param SessionInterface $session A session object. * * @return $this * * @since 4.0.0 */ public function setSession(SessionInterface $session): self { $this->session = $session; return $this; } /** * Flush the media version to refresh versionable assets * * @return void * * @since 4.0.0 */ public function flushAssets() { (new Version())->refreshMediaVersion(); } /** * Get the long version string for the application. * * Overrides the parent method due to conflicting use of the getName method between the console application and * the CMS application interface. * * @return string * * @since 4.0.0 */ public function getLongVersion(): string { return sprintf('Joomla! <info>%s</info> (debug: %s)', (new Version())->getShortVersion(), (\defined('JDEBUG') && JDEBUG ? 'Yes' : 'No')); } /** * Set the name of the application. * * @param string $name The new application name. * * @return void * * @since 4.0.0 * @throws \RuntimeException because the application name cannot be changed */ public function setName(string $name): void { throw new \RuntimeException('The console application name cannot be changed'); } /** * Returns the application Router object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return Router * * @since 4.0.6 * * @throws \InvalidArgumentException * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: Factory::getContainer()->get(ApiRouter::class); */ public static function getRouter($name = null, array $options = []) { if (empty($name)) { throw new \InvalidArgumentException('A router name must be set in console application.'); } $options['mode'] = Factory::getApplication()->get('sef'); return Router::getInstance($name, $options); } /** * Populates the HTTP_HOST and REQUEST_URI from the URL provided in the --live-site parameter. * * If the URL provided is empty or invalid we will use the URL * https://joomla.invalid/set/by/console/application just so that the CLI application doesn't * crash when a WebApplication descendant is instantiated in it. * * This is a practical workaround for using any service depending on a WebApplication * descendant under CLI. * * Practical example: using a component's MVCFactory which instantiates the SiteRouter * service for that component which in turn relies on an instance of SiteApplication. * * @return void * @since 4.2.1 * @see https://github.com/joomla/joomla-cms/issues/38518 */ protected function populateHttpHost() { // First check for the --live-site command line option. $input = $this->getConsoleInput(); $liveSite = ''; if ($input->hasParameterOption(['--live-site', false])) { $liveSite = $input->getParameterOption(['--live-site'], ''); } // Fallback to the $live_site global configuration option in configuration.php $liveSite = $liveSite ?: $this->get('live_site', 'https://joomla.invalid/set/by/console/application'); /** * Try to use the live site URL we were given. If all else fails, fall back to * https://joomla.invalid/set/by/console/application. */ try { $uri = Uri::getInstance($liveSite); } catch (\RuntimeException $e) { $uri = Uri::getInstance('https://joomla.invalid/set/by/console/application'); } /** * Yes, this is icky but it is the only way to trick WebApplication into compliance. * * @see \Joomla\Application\AbstractWebApplication::detectRequestUri */ $_SERVER['HTTP_HOST'] = $uri->toString(['host', 'port']); $_SERVER['REQUEST_URI'] = $uri->getPath(); $_SERVER['HTTPS'] = $uri->getScheme() === 'https' ? 'on' : 'off'; } /** * Builds the default input definition. * * @return InputDefinition * * @since 4.2.1 */ protected function getDefaultInputDefinition(): InputDefinition { return new InputDefinition( [ new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), new InputOption( '--live-site', null, InputOption::VALUE_OPTIONAL, 'The URL to your site, e.g. https://www.example.com' ), new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display the help information'), new InputOption( '--quiet', '-q', InputOption::VALUE_NONE, 'Flag indicating that all output should be silenced' ), new InputOption( '--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug' ), new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Displays the application version'), new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), new InputOption( '--no-interaction', '-n', InputOption::VALUE_NONE, 'Flag to disable interacting with the user' ), ] ); } /** * Gets a user state. * * @param string $key The path of the state. * @param mixed $default Optional default value, returned if the internal value is null. * * @return mixed The user state or null. * * @since 4.4.0 */ public function getUserState($key, $default = null) { $registry = $this->getSession()->get('registry'); if ($registry !== null) { return $registry->get($key, $default); } return $default; } /** * Gets the value of a user state variable. * * @param string $key The key of the user state variable. * @param string $request The name of the variable passed in a request. * @param string $default The default value for the variable if not found. Optional. * @param string $type Filter for the variable, for valid values see {@link InputFilter::clean()}. Optional. * * @return mixed The request user state. * * @since 4.4.0 */ public function getUserStateFromRequest($key, $request, $default = null, $type = 'none') { $cur_state = $this->getUserState($key, $default); $new_state = $this->input->get($request, null, $type); if ($new_state === null) { return $cur_state; } // Save the new value only if it was set in this request. $this->setUserState($key, $new_state); return $new_state; } } PK0A#]����+src/Application/Exception/NotAcceptable.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining a not acceptable class * * @since 4.0.0 */ class NotAcceptable extends \RuntimeException { } PK0A#]bva#I-I-"src/Application/CliApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\AbstractApplication; use Joomla\CMS\Application\CLI\CliInput; use Joomla\CMS\Application\CLI\CliOutput; use Joomla\CMS\Application\CLI\Output\Stdout; use Joomla\CMS\Extension\ExtensionManagerTrait; use Joomla\CMS\Factory; use Joomla\CMS\Language\Language; use Joomla\DI\Container; use Joomla\DI\ContainerAwareTrait; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Input\Input; use Joomla\Registry\Registry; use Joomla\Session\SessionInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla! command line application. * * @since 2.5.0 * * @deprecated 4.0 will be removed in 6.0 * Use the ConsoleApplication instead */ abstract class CliApplication extends AbstractApplication implements DispatcherAwareInterface, CMSApplicationInterface { use DispatcherAwareTrait; use EventAware; use IdentityAware; use ContainerAwareTrait; use ExtensionManagerTrait; use ExtensionNamespaceMapper; /** * Output object * * @var CliOutput * @since 4.0.0 */ protected $output; /** * The input. * * @var \Joomla\Input\Input * @since 4.0.0 */ protected $input = null; /** * CLI Input object * * @var CliInput * @since 4.0.0 */ protected $cliInput; /** * The application language object. * * @var Language * @since 4.0.0 */ protected $language; /** * The application message queue. * * @var array * @since 4.0.0 */ protected $messages = []; /** * The application instance. * * @var CliApplication * @since 1.7.0 */ protected static $instance; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's * input object. If the argument is a JInputCli object that object will become * the application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's * config object. If the argument is a Registry object that object will become * the application's config object, otherwise a default config object is created. * @param CliOutput $output The output handler. * @param CliInput $cliInput The CLI input handler. * @param DispatcherInterface $dispatcher An optional argument to provide dependency injection for the application's * event dispatcher. If the argument is a DispatcherInterface object that object will become * the application's event dispatcher, if it is null then the default event dispatcher * will be created based on the application's loadDispatcher() method. * @param Container $container Dependency injection container. * * @since 1.7.0 */ public function __construct( Input $input = null, Registry $config = null, CliOutput $output = null, CliInput $cliInput = null, DispatcherInterface $dispatcher = null, Container $container = null ) { // Close the application if we are not executed from the command line. if (!\defined('STDOUT') || !\defined('STDIN') || !isset($_SERVER['argv'])) { $this->close(); } $container = $container ?: Factory::getContainer(); $this->setContainer($container); $this->setDispatcher($dispatcher ?: $container->get(\Joomla\Event\DispatcherInterface::class)); if (!$container->has('session')) { $container->alias('session', 'session.cli') ->alias('JSession', 'session.cli') ->alias(\Joomla\CMS\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\SessionInterface::class, 'session.cli'); } $this->input = new \Joomla\CMS\Input\Cli(); $this->language = Factory::getLanguage(); $this->output = $output ?: new Stdout(); $this->cliInput = $cliInput ?: new CliInput(); parent::__construct($config); // Set the current directory. $this->set('cwd', getcwd()); // Set up the environment $this->input->set('format', 'cli'); } /** * Magic method to access properties of the application. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * This is a B/C proxy for deprecated read accesses * Example: Factory::getApplication()->getInput(); */ public function __get($name) { switch ($name) { case 'input': @trigger_error( 'Accessing the input property of the application is deprecated, use the getInput() method instead.', E_USER_DEPRECATED ); return $this->getInput(); default: $trace = debug_backtrace(); trigger_error( sprintf( 'Undefined property via __get(): %1$s in %2$s on line %3$s', $name, $trace[0]['file'], $trace[0]['line'] ), E_USER_NOTICE ); } } /** * Method to get the application input object. * * @return Input * * @since 4.0.0 */ public function getInput(): Input { return $this->input; } /** * Method to get the application language object. * * @return Language The language object * * @since 4.0.0 */ public function getLanguage() { return $this->language; } /** * Returns a reference to the global CliApplication object, only creating it if it doesn't already exist. * * This method must be invoked as: $cli = CliApplication::getInstance(); * * @param string $name The name (optional) of the Application Cli class to instantiate. * * @return CliApplication * * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Load the app through the container or via the Factory * Example: Factory::getContainer()->get(CliApplication::class) * * @throws \RuntimeException */ public static function getInstance($name = null) { // Only create the object if it doesn't exist. if (empty(static::$instance)) { if (!class_exists($name)) { throw new \RuntimeException(sprintf('Unable to load application: %s', $name), 500); } static::$instance = new $name(); } return static::$instance; } /** * Execute the application. * * @return void * * @since 1.7.0 */ public function execute() { $this->createExtensionNamespaceMap(); // Trigger the onBeforeExecute event $this->triggerEvent('onBeforeExecute'); // Perform application routines. $this->doExecute(); // Trigger the onAfterExecute event. $this->triggerEvent('onAfterExecute'); } /** * Get an output object. * * @return CliOutput * * @since 4.0.0 */ public function getOutput() { return $this->output; } /** * Get a CLI input object. * * @return CliInput * * @since 4.0.0 */ public function getCliInput() { return $this->cliInput; } /** * Write a string to standard output. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return $this * * @since 4.0.0 */ public function out($text = '', $nl = true) { $this->getOutput()->out($text, $nl); return $this; } /** * Get a value from standard input. * * @return string The input string from standard input. * * @codeCoverageIgnore * @since 4.0.0 */ public function in() { return $this->getCliInput()->in(); } /** * Set an output object. * * @param CliOutput $output CliOutput object * * @return $this * * @since 3.3 */ public function setOutput(CliOutput $output) { $this->output = $output; return $this; } /** * Enqueue a system message. * * @param string $msg The message to enqueue. * @param string $type The message type. * * @return void * * @since 4.0.0 */ public function enqueueMessage($msg, $type = self::MSG_INFO) { if (!\array_key_exists($type, $this->messages)) { $this->messages[$type] = []; } $this->messages[$type][] = $msg; } /** * Get the system message queue. * * @return array The system message queue. * * @since 4.0.0 */ public function getMessageQueue() { return $this->messages; } /** * Check the client interface by name. * * @param string $identifier String identifier for the application interface * * @return boolean True if this application is of the given type client interface. * * @since 4.0.0 */ public function isClient($identifier) { return $identifier === 'cli'; } /** * Method to get the application session object. * * @return SessionInterface The session object * * @since 4.0.0 */ public function getSession() { return $this->container->get(SessionInterface::class); } /** * Retrieve the application configuration object. * * @return Registry * * @since 4.0.0 */ public function getConfig() { return $this->config; } /** * Flag if the application instance is a CLI or web based application. * * Helper function, you should use the native PHP functions to detect if it is a CLI application. * * @return boolean * * @since 4.0.0 * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacements */ public function isCli() { return true; } } PK0A#]�+g}�5�5"src/Application/ApiApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\Web\WebClient; use Joomla\CMS\Access\Exception\AuthenticationFailed; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\ApiRouter; use Joomla\CMS\Router\Exception\RouteNotFoundException; use Joomla\CMS\Uri\Uri; use Joomla\DI\Container; use Joomla\Input\Json as JInputJson; use Joomla\Registry\Registry; use Negotiation\Accept; use Negotiation\Exception\InvalidArgument; use Negotiation\Negotiator; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! API Application class * * @since 4.0.0 */ final class ApiApplication extends CMSApplication { /** * Maps extension types to their * * @var array * @since 4.0.0 */ protected $formatMapper = []; /** * The authentication plugin type * * @var string * @since 4.0.0 */ protected $authenticationPluginType = 'api-authentication'; /** * Class constructor. * * @param JInputJson $input An optional argument to provide dependency injection for the application's input * object. If the argument is a JInput object that object will become the * application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config * object. If the argument is a Registry object that object will become the * application's config object, otherwise a default config object is created. * @param WebClient $client An optional argument to provide dependency injection for the application's client * object. If the argument is a WebClient object that object will become the * application's client object, otherwise a default client object is created. * @param Container $container Dependency injection container. * * @since 4.0.0 */ public function __construct(JInputJson $input = null, Registry $config = null, WebClient $client = null, Container $container = null) { // Register the application name $this->name = 'api'; // Register the client ID $this->clientId = 3; // Execute the parent constructor parent::__construct($input, $config, $client, $container); $this->addFormatMap('application/json', 'json'); $this->addFormatMap('application/vnd.api+json', 'jsonapi'); // Set the root in the URI based on the application name Uri::root(null, str_ireplace('/' . $this->getName(), '', Uri::base(true))); } /** * Method to run the application routines. * * Most likely you will want to instantiate a controller and execute it, or perform some sort of task directly. * * @return void * * @since 4.0.0 */ protected function doExecute() { // Initialise the application $this->initialiseApp(); // Mark afterInitialise in the profiler. JDEBUG ? $this->profiler->mark('afterInitialise') : null; // Route the application $this->route(); // Mark afterApiRoute in the profiler. JDEBUG ? $this->profiler->mark('afterApiRoute') : null; // Dispatch the application $this->dispatch(); // Mark afterDispatch in the profiler. JDEBUG ? $this->profiler->mark('afterDispatch') : null; } /** * Adds a mapping from a content type to the format stored. Note the format type cannot be overwritten. * * @param string $contentHeader The content header * @param string $format The content type format * * @return void * * @since 4.0.0 */ public function addFormatMap($contentHeader, $format) { if (!\array_key_exists($contentHeader, $this->formatMapper)) { $this->formatMapper[$contentHeader] = $format; } } /** * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the application response buffer. * * @return void * * @since 4.0.0 * * @note Rendering should be overridden to get rid of the theme files. */ protected function render() { // Render the document $this->setBody($this->document->render($this->allowCache())); } /** * Method to send the application response to the client. All headers will be sent prior to the main application output data. * * @param array $options An optional argument to enable CORS. (Temporary) * * @return void * * @since 4.0.0 */ protected function respond($options = []) { // Set the Joomla! API signature $this->setHeader('X-Powered-By', 'JoomlaAPI/1.0', true); $forceCORS = (int) $this->get('cors'); if ($forceCORS) { /** * Enable CORS (Cross-origin resource sharing) * Obtain allowed CORS origin from Global Settings. * Set to * (=all) if not set. */ $allowedOrigin = $this->get('cors_allow_origin', '*'); $this->setHeader('Access-Control-Allow-Origin', $allowedOrigin, true); $this->setHeader('Access-Control-Allow-Headers', 'Authorization'); if ($this->input->server->getString('HTTP_ORIGIN', null) !== null) { $this->setHeader('Access-Control-Allow-Origin', $this->input->server->getString('HTTP_ORIGIN'), true); $this->setHeader('Access-Control-Allow-Credentials', 'true', true); } } // Parent function can be overridden later on for debugging. parent::respond(); } /** * Gets the name of the current template. * * @param boolean $params True to return the template parameters * * @return string|\stdClass * * @since 4.0.0 */ public function getTemplate($params = false) { // The API application should not need to use a template if ($params) { $template = new \stdClass(); $template->template = 'system'; $template->params = new Registry(); $template->inheritable = 0; $template->parent = ''; return $template; } return 'system'; } /** * Route the application. * * Routing is the process of examining the request environment to determine which * component should receive the request. The component optional parameters * are then set in the request object to be processed when the application is being * dispatched. * * @return void * * @since 4.0.0 */ protected function route() { $router = $this->getContainer()->get(ApiRouter::class); // Trigger the onBeforeApiRoute event. PluginHelper::importPlugin('webservices'); $this->triggerEvent('onBeforeApiRoute', [&$router, $this]); $caught404 = false; $method = $this->input->getMethod(); try { $this->handlePreflight($method, $router); $route = $router->parseApiRoute($method); } catch (RouteNotFoundException $e) { $caught404 = true; } /** * Now we have an API perform content negotiation to ensure we have a valid header. Assume if the route doesn't * tell us otherwise it uses the plain JSON API */ $priorities = ['application/vnd.api+json']; if (!$caught404 && \array_key_exists('format', $route['vars'])) { $priorities = $route['vars']['format']; } $negotiator = new Negotiator(); try { $mediaType = $negotiator->getBest($this->input->server->getString('HTTP_ACCEPT'), $priorities); } catch (InvalidArgument $e) { $mediaType = null; } // If we can't find a match bail with a 406 - Not Acceptable if ($mediaType === null) { throw new Exception\NotAcceptable('Could not match accept header', 406); } /** @var $mediaType Accept */ $format = $mediaType->getValue(); if (\array_key_exists($mediaType->getValue(), $this->formatMapper)) { $format = $this->formatMapper[$mediaType->getValue()]; } $this->input->set('format', $format); if ($caught404) { throw $e; } $this->input->set('controller', $route['controller']); $this->input->set('task', $route['task']); foreach ($route['vars'] as $key => $value) { // We inject the format directly above based on the negotiated format. We do not want the array of possible // formats provided by the plugin! if ($key === 'format') { continue; } // We inject the component key into the option parameter in global input for b/c with the other applications if ($key === 'component') { $this->input->set('option', $route['vars'][$key]); continue; } if ($this->input->getMethod() === 'POST') { $this->input->post->set($key, $value); } else { $this->input->set($key, $value); } } $this->triggerEvent('onAfterApiRoute', [$this]); if (!isset($route['vars']['public']) || $route['vars']['public'] === false) { if (!$this->login(['username' => ''], ['silent' => true, 'action' => 'core.login.api'])) { throw new AuthenticationFailed(); } } } /** * Handles preflight requests. * * @param String $method The REST verb * * @param ApiRouter $router The API Routing object * * @return void * * @since 4.0.0 */ protected function handlePreflight($method, $router) { /** * If not an OPTIONS request or CORS is not enabled, * there's nothing useful to do here. */ if ($method !== 'OPTIONS' || !(int) $this->get('cors')) { return; } // Extract routes matching current route from all known routes. $matchingRoutes = $router->getMatchingRoutes(); // Extract exposed methods from matching routes. $matchingRoutesMethods = array_unique( array_reduce( $matchingRoutes, function ($carry, $route) { return array_merge($carry, $route->getMethods()); }, [] ) ); /** * Obtain allowed CORS origin from Global Settings. * Set to * (=all) if not set. */ $allowedOrigin = $this->get('cors_allow_origin', '*'); /** * Obtain allowed CORS headers from Global Settings. * Set to sensible default if not set. */ $allowedHeaders = $this->get('cors_allow_headers', 'Content-Type,X-Joomla-Token'); /** * Obtain allowed CORS methods from Global Settings. * Set to methods exposed by current route if not set. */ $allowedMethods = $this->get('cors_allow_methods', implode(',', $matchingRoutesMethods)); // No use to go through the regular route handling hassle, // so let's simply output the headers and exit. $this->setHeader('status', '204'); $this->setHeader('Access-Control-Allow-Origin', $allowedOrigin); $this->setHeader('Access-Control-Allow-Headers', $allowedHeaders); $this->setHeader('Access-Control-Allow-Methods', $allowedMethods); $this->sendHeaders(); $this->close(); } /** * Returns the application Router object. * * @return ApiRouter * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: * Factory::getContainer()->get(ApiRouter::class); * */ public function getApiRouter() { return $this->getContainer()->get(ApiRouter::class); } /** * Dispatch the application * * @param string $component The component which is being rendered. * * @return void * * @since 4.0.0 */ public function dispatch($component = null) { // Get the component if not set. if (!$component) { $component = $this->input->get('option', null); } // Load the document to the API $this->loadDocument(); // Set up the params $document = Factory::getDocument(); // Register the document object with Factory Factory::$document = $document; $contents = ComponentHelper::renderComponent($component); $document->setBuffer($contents, 'component'); // Trigger the onAfterDispatch event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterDispatch'); } } PK0A#]��=EMMsrc/Application/EventAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\Event\CoreEventAware; use Joomla\Event\DispatcherInterface; use Joomla\Event\Event; use Psr\Log\LoggerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for application classes which dispatch events * * @since 4.0.0 */ trait EventAware { use CoreEventAware; /** * Get the event dispatcher. * * @return DispatcherInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ abstract public function getDispatcher(); /** * Get the logger. * * @return LoggerInterface * * @since 4.0.0 */ abstract public function getLogger(); /** * Registers a handler to a particular event group. * * @param string $event The event name. * @param callable $handler The handler, a function or an instance of an event object. * * @return $this * * @since 4.0.0 */ public function registerEvent($event, callable $handler) { try { $this->getDispatcher()->addListener($event, $handler); } catch (\UnexpectedValueException $e) { // No dispatcher is registered, don't throw an error (mimics old behavior) } return $this; } /** * Calls all handlers associated with an event group. * * This is a legacy method, implementing old-style (Joomla! 3.x) plugin calls. It's best to go directly through the * Dispatcher and handle the returned EventInterface object instead of going through this method. This method is * deprecated and will be removed in Joomla! 5.x. * * This method will only return the 'result' argument of the event * * @param string $eventName The event name. * @param array|Event $args An array of arguments or an Event object (optional). * * @return array An array of results from each function call. Note this will be an empty array if no dispatcher is set. * * @since 4.0.0 * @throws \InvalidArgumentException * * @deprecated 4.0 will be removed in 6.0 * Use the Dispatcher method instead * Example: Factory::getApplication()->getDispatcher()->dispatch($eventName, $event); * */ public function triggerEvent($eventName, $args = []) { try { $dispatcher = $this->getDispatcher(); } catch (\UnexpectedValueException $exception) { $this->getLogger()->error(sprintf('Dispatcher not set in %s, cannot trigger events.', \get_class($this))); return []; } if ($args instanceof Event) { $event = $args; } elseif (\is_array($args)) { $className = self::getEventClassByEventName($eventName); $event = new $className($eventName, $args); } else { throw new \InvalidArgumentException('The arguments must either be an event or an array'); } $result = $dispatcher->dispatch($eventName, $event); // @todo - There are still test cases where the result isn't defined, temporarily leave the isset check in place return !isset($result['result']) || \is_null($result['result']) ? [] : $result['result']; } } PK0A#]�A��"src/Application/CMSApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\SessionAwareWebApplicationTrait; use Joomla\Application\Web\WebClient; use Joomla\CMS\Authentication\Authentication; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Event\ErrorEvent; use Joomla\CMS\Exception\ExceptionHandler; use Joomla\CMS\Extension\ExtensionManagerTrait; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Input\Input; use Joomla\CMS\Language\LanguageFactoryInterface; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Menu\AbstractMenu; use Joomla\CMS\Menu\MenuFactoryInterface; use Joomla\CMS\Pathway\Pathway; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Profiler\Profiler; use Joomla\CMS\Router\Route; use Joomla\CMS\Router\Router; use Joomla\CMS\Session\MetadataManager; use Joomla\CMS\Session\Session; use Joomla\CMS\Uri\Uri; use Joomla\DI\Container; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; use Joomla\Registry\Registry; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! CMS Application class * * @since 3.2 */ abstract class CMSApplication extends WebApplication implements ContainerAwareInterface, CMSWebApplicationInterface { use ContainerAwareTrait; use ExtensionManagerTrait; use ExtensionNamespaceMapper; use SessionAwareWebApplicationTrait; /** * Array of options for the \JDocument object * * @var array * @since 3.2 */ protected $docOptions = []; /** * Application instances container. * * @var CmsApplication[] * @since 3.2 */ protected static $instances = []; /** * The scope of the application. * * @var string * @since 3.2 */ public $scope = null; /** * The client identifier. * * @var integer * @since 4.0.0 */ protected $clientId = null; /** * The application message queue. * * @var array * @since 4.0.0 */ protected $messageQueue = []; /** * The name of the application. * * @var string * @since 4.0.0 */ protected $name = null; /** * The profiler instance * * @var Profiler * @since 3.2 */ protected $profiler = null; /** * Currently active template * * @var object * @since 3.2 */ protected $template = null; /** * The pathway object * * @var Pathway * @since 4.0.0 */ protected $pathway = null; /** * The authentication plugin type * * @var string * @since 4.0.0 */ protected $authenticationPluginType = 'authentication'; /** * Menu instances container. * * @var AbstractMenu[] * @since 4.2.0 */ protected $menus = []; /** * The menu factory * * @var MenuFactoryInterface * * @since 4.2.0 */ private $menuFactory; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's input * object. If the argument is a JInput object that object will become the * application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config * object. If the argument is a Registry object that object will become the * application's config object, otherwise a default config object is created. * @param WebClient $client An optional argument to provide dependency injection for the application's client * object. If the argument is a WebClient object that object will become the * application's client object, otherwise a default client object is created. * @param Container $container Dependency injection container. * * @since 3.2 */ public function __construct(Input $input = null, Registry $config = null, WebClient $client = null, Container $container = null) { $container = $container ?: new Container(); $this->setContainer($container); parent::__construct($input, $config, $client); // If JDEBUG is defined, load the profiler instance if (\defined('JDEBUG') && JDEBUG) { $this->profiler = Profiler::getInstance('Application'); } // Enable sessions by default. if ($this->config->get('session') === null) { $this->config->set('session', true); } // Set the session default name. if ($this->config->get('session_name') === null) { $this->config->set('session_name', $this->getName()); } } /** * Checks the user session. * * If the session record doesn't exist, initialise it. * If session is new, create session variables * * @return void * * @since 3.2 * @throws \RuntimeException */ public function checkSession() { $this->getContainer()->get(MetadataManager::class)->createOrUpdateRecord($this->getSession(), $this->getIdentity()); } /** * Enqueue a system message. * * @param string $msg The message to enqueue. * @param string $type The message type. Default is message. * * @return void * * @since 3.2 */ public function enqueueMessage($msg, $type = self::MSG_INFO) { // Don't add empty messages. if ($msg === null || trim($msg) === '') { return; } $inputFilter = InputFilter::getInstance( [], [], InputFilter::ONLY_BLOCK_DEFINED_TAGS, InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES ); // Build the message array and apply the HTML InputFilter with the default blacklist to the message $message = [ 'message' => $inputFilter->clean($msg, 'html'), 'type' => $inputFilter->clean(strtolower($type), 'cmd'), ]; // For empty queue, if messages exists in the session, enqueue them first. $messages = $this->getMessageQueue(); if (!\in_array($message, $this->messageQueue)) { // Enqueue the message. $this->messageQueue[] = $message; } } /** * Ensure several core system input variables are not arrays. * * @return void * * @since 3.9 */ private function sanityCheckSystemVariables() { $input = $this->input; // Get invalid input variables $invalidInputVariables = array_filter( ['option', 'view', 'format', 'lang', 'Itemid', 'template', 'templateStyle', 'task'], function ($systemVariable) use ($input) { return $input->exists($systemVariable) && is_array($input->getRaw($systemVariable)); } ); // Unset invalid system variables foreach ($invalidInputVariables as $systemVariable) { $input->set($systemVariable, null); } // Stop when there are invalid variables if ($invalidInputVariables) { throw new \RuntimeException('Invalid input, aborting application.'); } } /** * Execute the application. * * @return void * * @since 3.2 */ public function execute() { try { $this->sanityCheckSystemVariables(); $this->setupLogging(); $this->createExtensionNamespaceMap(); // Perform application routines. $this->doExecute(); // If we have an application document object, render it. if ($this->document instanceof \Joomla\CMS\Document\Document) { // Render the application output. $this->render(); } // If gzip compression is enabled in configuration and the server is compliant, compress the output. if ($this->get('gzip') && !ini_get('zlib.output_compression') && ini_get('output_handler') !== 'ob_gzhandler') { $this->compress(); // Trigger the onAfterCompress event. $this->triggerEvent('onAfterCompress'); } } catch (\Throwable $throwable) { /** @var ErrorEvent $event */ $event = AbstractEvent::create( 'onError', [ 'subject' => $throwable, 'eventClass' => ErrorEvent::class, 'application' => $this, ] ); // Trigger the onError event. $this->triggerEvent('onError', $event); ExceptionHandler::handleException($event->getError()); } // Trigger the onBeforeRespond event. $this->getDispatcher()->dispatch('onBeforeRespond'); // Send the application response. $this->respond(); // Trigger the onAfterRespond event. $this->getDispatcher()->dispatch('onAfterRespond'); } /** * Check if the user is required to reset their password. * * If the user is required to reset their password will be redirected to the page that manage the password reset. * * @param string $option The option that manage the password reset * @param string $view The view that manage the password reset * @param string $layout The layout of the view that manage the password reset * @param string $tasks Permitted tasks * * @return void * * @throws \Exception */ protected function checkUserRequireReset($option, $view, $layout, $tasks) { if (Factory::getUser()->get('requireReset', 0)) { $redirect = false; /* * By default user profile edit page is used. * That page allows you to change more than just the password and might not be the desired behavior. * This allows a developer to override the page that manage the password reset. * (can be configured using the file: configuration.php, or if extended, through the global configuration form) */ $name = $this->getName(); if ($this->get($name . '_reset_password_override', 0)) { $option = $this->get($name . '_reset_password_option', ''); $view = $this->get($name . '_reset_password_view', ''); $layout = $this->get($name . '_reset_password_layout', ''); $tasks = $this->get($name . '_reset_password_tasks', ''); } $task = $this->input->getCmd('task', ''); // Check task or option/view/layout if (!empty($task)) { $tasks = explode(',', $tasks); // Check full task version "option/task" if (array_search($this->input->getCmd('option', '') . '/' . $task, $tasks) === false) { // Check short task version, must be on the same option of the view if ($this->input->getCmd('option', '') !== $option || array_search($task, $tasks) === false) { // Not permitted task $redirect = true; } } } else { if ( $this->input->getCmd('option', '') !== $option || $this->input->getCmd('view', '') !== $view || $this->input->getCmd('layout', '') !== $layout ) { // Requested a different option/view/layout $redirect = true; } } if ($redirect) { // Redirect to the profile edit page $this->enqueueMessage(Text::_('JGLOBAL_PASSWORD_RESET_REQUIRED'), 'notice'); $url = Route::_('index.php?option=' . $option . '&view=' . $view . '&layout=' . $layout, false); // In the administrator we need a different URL if (strtolower($name) === 'administrator') { $user = Factory::getApplication()->getIdentity(); $url = Route::_('index.php?option=' . $option . '&task=' . $view . '.' . $layout . '&id=' . $user->id, false); } $this->redirect($url); } } } /** * Gets a configuration value. * * @param string $varname The name of the value to get. * @param string $default Default value to return * * @return mixed The user state. * * @since 3.2 * * @deprecated 3.2 will be removed in 6.0 * Use get() instead * Example: Factory::getApplication()->get($varname, $default); */ public function getCfg($varname, $default = null) { try { Log::add( sprintf('%s() is deprecated and will be removed in 6.0. Use Factory->getApplication()->get() instead.', __METHOD__), Log::WARNING, 'deprecated' ); } catch (\RuntimeException $exception) { // Informational log only } return $this->get($varname, $default); } /** * Gets the client id of the current running application. * * @return integer A client identifier. * * @since 3.2 */ public function getClientId() { return $this->clientId; } /** * Returns a reference to the global CmsApplication object, only creating it if it doesn't already exist. * * This method must be invoked as: $web = CmsApplication::getInstance(); * * @param string $name The name (optional) of the CmsApplication class to instantiate. * @param string $prefix The class name prefix of the object. * @param Container $container An optional dependency injection container to inject into the application. * * @return CmsApplication * * @since 3.2 * @throws \RuntimeException * @deprecated 4.0 will be removed in 6.0 * Use the application service from the DI container instead * Example: Factory::getContainer()->get($name); */ public static function getInstance($name = null, $prefix = '\JApplication', Container $container = null) { if (empty(static::$instances[$name])) { // Create a CmsApplication object. $classname = $prefix . ucfirst($name); if (!$container) { $container = Factory::getContainer(); } if ($container->has($classname)) { static::$instances[$name] = $container->get($classname); } elseif (class_exists($classname)) { // @todo This creates an implicit hard requirement on the ApplicationCms constructor static::$instances[$name] = new $classname(null, null, null, $container); } else { throw new \RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_APPLICATION_LOAD', $name), 500); } static::$instances[$name]->loadIdentity(Factory::getUser()); } return static::$instances[$name]; } /** * Returns the application \JMenu object. * * @param string $name The name of the application/client. * @param array $options An optional associative array of configuration settings. * * @return AbstractMenu * * @since 3.2 */ public function getMenu($name = null, $options = []) { if (!isset($name)) { $name = $this->getName(); } // Inject this application object into the \JMenu tree if one isn't already specified if (!isset($options['app'])) { $options['app'] = $this; } if (array_key_exists($name, $this->menus)) { return $this->menus[$name]; } if ($this->menuFactory === null) { @trigger_error('Menu factory must be set in 5.0', E_USER_DEPRECATED); $this->menuFactory = $this->getContainer()->get(MenuFactoryInterface::class); } $this->menus[$name] = $this->menuFactory->createMenu($name, $options); // Make sure the abstract menu has the instance too, is needed for BC and will be removed with version 5 AbstractMenu::$instances[$name] = $this->menus[$name]; return $this->menus[$name]; } /** * Get the system message queue. * * @param boolean $clear Clear the messages currently attached to the application object * * @return array The system message queue. * * @since 3.2 */ public function getMessageQueue($clear = false) { // For empty queue, if messages exists in the session, enqueue them. if (!\count($this->messageQueue)) { $sessionQueue = $this->getSession()->get('application.queue', []); if ($sessionQueue) { $this->messageQueue = $sessionQueue; $this->getSession()->set('application.queue', []); } } $messageQueue = $this->messageQueue; if ($clear) { $this->messageQueue = []; } return $messageQueue; } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 3.2 */ public function getName() { return $this->name; } /** * Returns the application Pathway object. * * @return Pathway * * @since 3.2 */ public function getPathway() { if (!$this->pathway) { $resourceName = ucfirst($this->getName()) . 'Pathway'; if (!$this->getContainer()->has($resourceName)) { throw new \RuntimeException( Text::sprintf('JLIB_APPLICATION_ERROR_PATHWAY_LOAD', $this->getName()), 500 ); } $this->pathway = $this->getContainer()->get($resourceName); } return $this->pathway; } /** * Returns the application Router object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return Router * * @since 3.2 * * @deprecated 4.3 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: Factory::getContainer()->get($name); */ public static function getRouter($name = null, array $options = []) { $app = Factory::getApplication(); if (!isset($name)) { $name = $app->getName(); } $options['mode'] = $app->get('sef'); return Router::getInstance($name, $options); } /** * Gets the name of the current template. * * @param boolean $params An optional associative array of configuration settings * * @return string|\stdClass The name of the template if the params argument is false. The template object if the params argument is true. * * @since 3.2 */ public function getTemplate($params = false) { if ($params) { $template = new \stdClass(); $template->template = 'system'; $template->params = new Registry(); $template->inheritable = 0; $template->parent = ''; return $template; } return 'system'; } /** * Gets a user state. * * @param string $key The path of the state. * @param mixed $default Optional default value, returned if the internal value is null. * * @return mixed The user state or null. * * @since 3.2 */ public function getUserState($key, $default = null) { $registry = $this->getSession()->get('registry'); if ($registry !== null) { return $registry->get($key, $default); } return $default; } /** * Gets the value of a user state variable. * * @param string $key The key of the user state variable. * @param string $request The name of the variable passed in a request. * @param string $default The default value for the variable if not found. Optional. * @param string $type Filter for the variable, for valid values see {@link InputFilter::clean()}. Optional. * * @return mixed The request user state. * * @since 3.2 */ public function getUserStateFromRequest($key, $request, $default = null, $type = 'none') { $cur_state = $this->getUserState($key, $default); $new_state = $this->input->get($request, null, $type); if ($new_state === null) { return $cur_state; } // Save the new value only if it was set in this request. $this->setUserState($key, $new_state); return $new_state; } /** * Initialise the application. * * @param array $options An optional associative array of configuration settings. * * @return void * * @since 3.2 */ protected function initialiseApp($options = []) { // Check that we were given a language in the array (since by default may be blank). if (isset($options['language'])) { $this->set('language', $options['language']); } // Build our language object $lang = $this->getContainer()->get(LanguageFactoryInterface::class)->createLanguage($this->get('language'), $this->get('debug_lang')); // Load the language to the API $this->loadLanguage($lang); // Register the language object with Factory Factory::$language = $this->getLanguage(); // Load the library language files $this->loadLibraryLanguage(); // Set user specific editor. $user = Factory::getUser(); $editor = $user->getParam('editor', $this->get('editor')); if (!PluginHelper::isEnabled('editors', $editor)) { $editor = $this->get('editor'); if (!PluginHelper::isEnabled('editors', $editor)) { $editor = 'none'; } } $this->set('editor', $editor); // Load the behaviour plugins PluginHelper::importPlugin('behaviour'); // Trigger the onAfterInitialise event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterInitialise'); } /** * Checks if HTTPS is forced in the client configuration. * * @param integer $clientId An optional client id (defaults to current application client). * * @return boolean True if is forced for the client, false otherwise. * * @since 3.7.3 */ public function isHttpsForced($clientId = null) { $clientId = (int) ($clientId !== null ? $clientId : $this->getClientId()); $forceSsl = (int) $this->get('force_ssl'); if ($clientId === 0 && $forceSsl === 2) { return true; } if ($clientId === 1 && $forceSsl >= 1) { return true; } return false; } /** * Check the client interface by name. * * @param string $identifier String identifier for the application interface * * @return boolean True if this application is of the given type client interface. * * @since 3.7.0 */ public function isClient($identifier) { return $this->getName() === $identifier; } /** * Load the library language files for the application * * @return void * * @since 3.6.3 */ protected function loadLibraryLanguage() { $this->getLanguage()->load('lib_joomla', JPATH_ADMINISTRATOR); } /** * Login authentication function. * * Username and encoded password are passed the onUserLogin event which * is responsible for the user validation. A successful validation updates * the current session record with the user's details. * * Username and encoded password are sent as credentials (along with other * possibilities) to each observer (authentication plugin) for user * validation. Successful validation will update the current session with * the user details. * * @param array $credentials Array('username' => string, 'password' => string) * @param array $options Array('remember' => boolean) * * @return boolean|\Exception True on success, false if failed or silent handling is configured, or a \Exception object on authentication error. * * @since 3.2 */ public function login($credentials, $options = []) { // Get the global Authentication object. $authenticate = Authentication::getInstance($this->authenticationPluginType); $response = $authenticate->authenticate($credentials, $options); // Import the user plugin group. PluginHelper::importPlugin('user'); if ($response->status === Authentication::STATUS_SUCCESS) { /* * Validate that the user should be able to login (different to being authenticated). * This permits authentication plugins blocking the user. */ $authorisations = $authenticate->authorise($response, $options); $denied_states = Authentication::STATUS_EXPIRED | Authentication::STATUS_DENIED; foreach ($authorisations as $authorisation) { if ((int) $authorisation->status & $denied_states) { // Trigger onUserAuthorisationFailure Event. $this->triggerEvent('onUserAuthorisationFailure', [(array) $authorisation]); // If silent is set, just return false. if (isset($options['silent']) && $options['silent']) { return false; } // Return the error. switch ($authorisation->status) { case Authentication::STATUS_EXPIRED: Factory::getApplication()->enqueueMessage(Text::_('JLIB_LOGIN_EXPIRED'), 'error'); return false; case Authentication::STATUS_DENIED: Factory::getApplication()->enqueueMessage(Text::_('JLIB_LOGIN_DENIED'), 'error'); return false; default: Factory::getApplication()->enqueueMessage(Text::_('JLIB_LOGIN_AUTHORISATION'), 'error'); return false; } } } // OK, the credentials are authenticated and user is authorised. Let's fire the onLogin event. $results = $this->triggerEvent('onUserLogin', [(array) $response, $options]); /* * If any of the user plugins did not successfully complete the login routine * then the whole method fails. * * Any errors raised should be done in the plugin as this provides the ability * to provide much more information about why the routine may have failed. */ $user = Factory::getUser(); if ($response->type === 'Cookie') { $user->set('cookieLogin', true); } if (\in_array(false, $results, true) == false) { $options['user'] = $user; $options['responseType'] = $response->type; // The user is successfully logged in. Run the after login events $this->triggerEvent('onUserAfterLogin', [$options]); return true; } } // Trigger onUserLoginFailure Event. $this->triggerEvent('onUserLoginFailure', [(array) $response]); // If silent is set, just return false. if (isset($options['silent']) && $options['silent']) { return false; } // If status is success, any error will have been raised by the user plugin if ($response->status !== Authentication::STATUS_SUCCESS) { $this->getLogger()->warning($response->error_message, ['category' => 'jerror']); } return false; } /** * Logout authentication function. * * Passed the current user information to the onUserLogout event and reverts the current * session record back to 'anonymous' parameters. * If any of the authentication plugins did not successfully complete * the logout routine then the whole method fails. Any errors raised * should be done in the plugin as this provides the ability to give * much more information about why the routine may have failed. * * @param integer $userid The user to load - Can be an integer or string - If string, it is converted to ID automatically * @param array $options Array('clientid' => array of client id's) * * @return boolean True on success * * @since 3.2 */ public function logout($userid = null, $options = []) { // Get a user object from the Application. $user = Factory::getUser($userid); // Build the credentials array. $parameters = [ 'username' => $user->get('username'), 'id' => $user->get('id'), ]; // Set clientid in the options array if it hasn't been set already and shared sessions are not enabled. if (!$this->get('shared_session', '0') && !isset($options['clientid'])) { $options['clientid'] = $this->getClientId(); } // Import the user plugin group. PluginHelper::importPlugin('user'); // OK, the credentials are built. Lets fire the onLogout event. $results = $this->triggerEvent('onUserLogout', [$parameters, $options]); // Check if any of the plugins failed. If none did, success. if (!\in_array(false, $results, true)) { $options['username'] = $user->get('username'); $this->triggerEvent('onUserAfterLogout', [$options]); return true; } // Trigger onUserLogoutFailure Event. $this->triggerEvent('onUserLogoutFailure', [$parameters]); return false; } /** * Redirect to another URL. * * If the headers have not been sent the redirect will be accomplished using a "301 Moved Permanently" * or "303 See Other" code in the header pointing to the new location. If the headers have already been * sent this will be accomplished using a JavaScript statement. * * @param string $url The URL to redirect to. Can only be http/https URL * @param integer $status The HTTP 1.1 status code to be provided. 303 is assumed by default. * * @return void * * @since 3.2 */ public function redirect($url, $status = 303) { // Persist messages if they exist. if (\count($this->messageQueue)) { $this->getSession()->set('application.queue', $this->messageQueue); } // Hand over processing to the parent now parent::redirect($url, $status); } /** * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the application response buffer. * * @return void * * @since 3.2 */ protected function render() { // Setup the document options. $this->docOptions['template'] = $this->get('theme'); $this->docOptions['file'] = $this->get('themeFile', 'index.php'); $this->docOptions['params'] = $this->get('themeParams'); $this->docOptions['csp_nonce'] = $this->get('csp_nonce'); $this->docOptions['templateInherits'] = $this->get('themeInherits'); if ($this->get('themes.base')) { $this->docOptions['directory'] = $this->get('themes.base'); } else { // Fall back to constants. $this->docOptions['directory'] = \defined('JPATH_THEMES') ? JPATH_THEMES : (\defined('JPATH_BASE') ? JPATH_BASE : __DIR__) . '/themes'; } // Parse the document. $this->document->parse($this->docOptions); // Trigger the onBeforeRender event. PluginHelper::importPlugin('system'); $this->triggerEvent('onBeforeRender'); $caching = false; if ($this->isClient('site') && $this->get('caching') && $this->get('caching', 2) == 2 && !Factory::getUser()->get('id')) { $caching = true; } // Render the document. $data = $this->document->render($caching, $this->docOptions); // Set the application output data. $this->setBody($data); // Trigger the onAfterRender event. $this->triggerEvent('onAfterRender'); // Mark afterRender in the profiler. JDEBUG ? $this->profiler->mark('afterRender') : null; } /** * Route the application. * * Routing is the process of examining the request environment to determine which * component should receive the request. The component optional parameters * are then set in the request object to be processed when the application is being * dispatched. * * @return void * * @since 3.2 * * @deprecated 4.0 will be removed in 6.0 * Implement the route functionality in the extending class, this here will be removed without replacement */ protected function route() { // Get the full request URI. $uri = clone Uri::getInstance(); $router = static::getRouter(); $result = $router->parse($uri, true); $active = $this->getMenu()->getActive(); if ( $active !== null && $active->type === 'alias' && $active->getParams()->get('alias_redirect') && \in_array($this->input->getMethod(), ['GET', 'HEAD'], true) ) { $item = $this->getMenu()->getItem($active->getParams()->get('aliasoptions')); if ($item !== null) { $oldUri = clone Uri::getInstance(); if ($oldUri->getVar('Itemid') == $active->id) { $oldUri->setVar('Itemid', $item->id); } $base = Uri::base(true); $oldPath = StringHelper::strtolower(substr($oldUri->getPath(), \strlen($base) + 1)); $activePathPrefix = StringHelper::strtolower($active->route); $position = strpos($oldPath, $activePathPrefix); if ($position !== false) { $oldUri->setPath($base . '/' . substr_replace($oldPath, $item->route, $position, \strlen($activePathPrefix))); $this->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true); $this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true); $this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false); $this->sendHeaders(); $this->redirect((string) $oldUri, 301); } } } foreach ($result as $key => $value) { $this->input->def($key, $value); } // Trigger the onAfterRoute event. PluginHelper::importPlugin('system'); $this->triggerEvent('onAfterRoute'); } /** * Sets the value of a user state variable. * * @param string $key The path of the state. * @param mixed $value The value of the variable. * * @return mixed The previous state, if one existed. Null otherwise. * * @since 3.2 */ public function setUserState($key, $value) { $session = $this->getSession(); $registry = $session->get('registry'); if ($registry !== null) { return $registry->set($key, $value); } return null; } /** * Sends all headers prior to returning the string * * @param boolean $compress If true, compress the data * * @return string * * @since 3.2 */ public function toString($compress = false) { // Don't compress something if the server is going to do it anyway. Waste of time. if ($compress && !ini_get('zlib.output_compression') && ini_get('output_handler') !== 'ob_gzhandler') { $this->compress(); } if ($this->allowCache() === false) { $this->setHeader('Cache-Control', 'no-cache', false); } $this->sendHeaders(); return $this->getBody(); } /** * Method to determine a hash for anti-spoofing variable names * * @param boolean $forceNew If true, force a new token to be created * * @return string Hashed var name * * @since 4.0.0 */ public function getFormToken($forceNew = false) { /** @var Session $session */ $session = $this->getSession(); return $session->getFormToken($forceNew); } /** * Checks for a form token in the request. * * Use in conjunction with getFormToken. * * @param string $method The request method in which to look for the token key. * * @return boolean True if found and valid, false otherwise. * * @since 4.0.0 */ public function checkToken($method = 'post') { /** @var Session $session */ $session = $this->getSession(); return $session->checkToken($method); } /** * Flag if the application instance is a CLI or web based application. * * Helper function, you should use the native PHP functions to detect if it is a CLI application. * * @return boolean * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacements */ public function isCli() { return false; } /** * No longer used * * @return boolean * * @since 4.0.0 * * @throws \Exception * * @deprecated 4.2 will be removed in 6.0 * Will be removed without replacements */ protected function isTwoFactorAuthenticationRequired(): bool { return false; } /** * No longer used * * @return boolean * * @since 4.0.0 * * @throws \Exception * * @deprecated 4.2 will be removed in 6.0 * Will be removed without replacements */ private function hasUserConfiguredTwoFactorAuthentication(): bool { return false; } /** * Setup logging functionality. * * @return void * * @since 4.0.0 */ private function setupLogging(): void { // Add InMemory logger that will collect all log entries to allow to display them later by extensions if ($this->get('debug')) { Log::addLogger(['logger' => 'inmemory']); } // Log the deprecated API. if ($this->get('log_deprecated')) { Log::addLogger(['text_file' => 'deprecated.php'], Log::ALL, ['deprecated']); } // We only log errors unless Site Debug is enabled $logLevels = Log::ERROR | Log::CRITICAL | Log::ALERT | Log::EMERGENCY; if ($this->get('debug')) { $logLevels = Log::ALL; } Log::addLogger(['text_file' => 'joomla_core_errors.php'], $logLevels, ['system']); // Log everything (except deprecated APIs, these are logged separately with the option above). if ($this->get('log_everything')) { Log::addLogger(['text_file' => 'everything.php'], Log::ALL, ['deprecated', 'deprecation-notes', 'databasequery'], true); } if ($this->get('log_categories')) { $priority = 0; foreach ($this->get('log_priorities', ['all']) as $p) { $const = '\\Joomla\\CMS\\Log\\Log::' . strtoupper($p); if (defined($const)) { $priority |= constant($const); } } // Split into an array at any character other than alphabet, numbers, _, ., or - $categories = preg_split('/[^\w.-]+/', $this->get('log_categories', ''), -1, PREG_SPLIT_NO_EMPTY); $mode = (bool) $this->get('log_category_mode', false); if (!$categories) { return; } Log::addLogger(['text_file' => 'custom-logging.php'], $priority, $categories, $mode); } } /** * Sets the internal menu factory. * * @param MenuFactoryInterface $menuFactory The menu factory * * @return void * * @since 4.2.0 */ public function setMenuFactory(MenuFactoryInterface $menuFactory): void { $this->menuFactory = $menuFactory; } } PK0A#]�i��9�9"src/Application/WebApplication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\AbstractWebApplication; use Joomla\Application\Web\WebClient; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory; use Joomla\CMS\Input\Input; use Joomla\CMS\Language\Language; use Joomla\CMS\Session\Session; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\User; use Joomla\CMS\Version; use Joomla\Registry\Registry; use Joomla\Session\SessionEvent; use Psr\Http\Message\ResponseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla! Web application. * * @since 2.5.0 */ abstract class WebApplication extends AbstractWebApplication { use EventAware; use IdentityAware; /** * The application component title. * * @var string * @since 4.3.0 */ public $JComponentTitle; /** * The item associations * * @var integer * @since 4.3.0 * * @deprecated 4.4.0 will be removed in 6.0 as this property is not used anymore */ public $item_associations; /** * The application document object. * * @var Document * @since 1.7.3 */ protected $document; /** * The application language object. * * @var Language * @since 1.7.3 */ protected $language; /** * The application instance. * * @var static * @since 1.7.3 */ protected static $instance; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's * input object. If the argument is a JInput object that object will become * the application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's * config object. If the argument is a Registry object that object will become * the application's config object, otherwise a default config object is created. * @param WebClient $client An optional argument to provide dependency injection for the application's * client object. If the argument is a WebClient object that object will become * the application's client object, otherwise a default client object is created. * @param ResponseInterface $response An optional argument to provide dependency injection for the application's * response object. If the argument is a ResponseInterface object that object * will become the application's response object, otherwise a default response * object is created. * * @since 1.7.3 */ public function __construct(Input $input = null, Registry $config = null, WebClient $client = null, ResponseInterface $response = null) { // Ensure we have a CMS Input object otherwise the DI for \Joomla\CMS\Session\Storage\JoomlaStorage fails $input = $input ?: new Input(); parent::__construct($input, $config, $client, $response); // Set the execution datetime and timestamp; $this->set('execution.datetime', gmdate('Y-m-d H:i:s')); $this->set('execution.timestamp', time()); // Set the system URIs. $this->loadSystemUris(); } /** * Returns a reference to the global WebApplication object, only creating it if it doesn't already exist. * * This method must be invoked as: $web = WebApplication::getInstance(); * * @param string $name The name (optional) of the WebApplication class to instantiate. * * @return WebApplication * * @since 1.7.3 * @throws \RuntimeException * * @deprecated 4.0 will be removed in 6.0 * Use the application service in the DI container instead * Example: \Joomla\CMS\Factory::getContainer()->get($name) */ public static function getInstance($name = null) { // Only create the object if it doesn't exist. if (empty(static::$instance)) { if (!is_subclass_of($name, '\\Joomla\\CMS\\Application\\WebApplication')) { throw new \RuntimeException(sprintf('Unable to load application: %s', $name), 500); } static::$instance = new $name(); } return static::$instance; } /** * Execute the application. * * @return void * * @since 1.7.3 */ public function execute() { // Trigger the onBeforeExecute event. $this->triggerEvent('onBeforeExecute'); // Perform application routines. $this->doExecute(); // Trigger the onAfterExecute event. $this->triggerEvent('onAfterExecute'); // If we have an application document object, render it. if ($this->document instanceof Document) { // Trigger the onBeforeRender event. $this->triggerEvent('onBeforeRender'); // Render the application output. $this->render(); // Trigger the onAfterRender event. $this->triggerEvent('onAfterRender'); } // If gzip compression is enabled in configuration and the server is compliant, compress the output. if ($this->get('gzip') && !ini_get('zlib.output_compression') && (ini_get('output_handler') !== 'ob_gzhandler')) { $this->compress(); } // Trigger the onBeforeRespond event. $this->triggerEvent('onBeforeRespond'); // Send the application response. $this->respond(); // Trigger the onAfterRespond event. $this->triggerEvent('onAfterRespond'); } /** * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the application response buffer. * * @return void * * @since 1.7.3 */ protected function render() { // Setup the document options. $options = [ 'template' => $this->get('theme'), 'file' => $this->get('themeFile', 'index.php'), 'params' => $this->get('themeParams'), 'templateInherits' => $this->get('themeInherits'), ]; if ($this->get('themes.base')) { $options['directory'] = $this->get('themes.base'); } else { // Fall back to constants. $options['directory'] = \defined('JPATH_THEMES') ? JPATH_THEMES : (\defined('JPATH_BASE') ? JPATH_BASE : __DIR__) . '/themes'; } // Parse the document. $this->document->parse($options); // Render the document. $data = $this->document->render($this->get('cache_enabled'), $options); // Set the application output data. $this->setBody($data); } /** * Method to get the application document object. * * @return Document The document object * * @since 1.7.3 */ public function getDocument() { return $this->document; } /** * Method to get the application language object. * * @return Language The language object * * @since 1.7.3 */ public function getLanguage() { return $this->language; } /** * Flush the media version to refresh versionable assets * * @return void * * @since 3.2 */ public function flushAssets() { (new Version())->refreshMediaVersion(); } /** * Allows the application to load a custom or default document. * * The logic and options for creating this object are adequately generic for default cases * but for many applications it will make sense to override this method and create a document, * if required, based on more specific needs. * * @param Document $document An optional document object. If omitted, the factory document is created. * * @return WebApplication This method is chainable. * * @since 1.7.3 */ public function loadDocument(Document $document = null) { $this->document = $document ?? Factory::getDocument(); return $this; } /** * Allows the application to load a custom or default language. * * The logic and options for creating this object are adequately generic for default cases * but for many applications it will make sense to override this method and create a language, * if required, based on more specific needs. * * @param Language $language An optional language object. If omitted, the factory language is created. * * @return WebApplication This method is chainable. * * @since 1.7.3 */ public function loadLanguage(Language $language = null) { $this->language = $language ?? Factory::getLanguage(); return $this; } /** * Allows the application to load a custom or default session. * * The logic and options for creating this object are adequately generic for default cases * but for many applications it will make sense to override this method and create a session, * if required, based on more specific needs. * * @param Session $session An optional session object. If omitted, the session is created. * * @return WebApplication This method is chainable. * * @since 1.7.3 * * @deprecated 4.3 will be removed in 6.0 * The session should be injected as a service. */ public function loadSession(Session $session = null) { $this->getLogger()->warning(__METHOD__ . '() is deprecated. Inject the session as a service instead.', ['category' => 'deprecated']); return $this; } /** * After the session has been started we need to populate it with some default values. * * @param SessionEvent $event Session event being triggered * * @return void * * @since 3.0.1 */ public function afterSessionStart(SessionEvent $event) { $session = $event->getSession(); if ($session->isNew()) { $session->set('registry', new Registry()); $session->set('user', new User()); } // Ensure the identity is loaded if (!$this->getIdentity()) { $this->loadIdentity($session->get('user')); } } /** * Method to load the system URI strings for the application. * * @param string $requestUri An optional request URI to use instead of detecting one from the * server environment variables. * * @return void * * @since 1.7.3 */ protected function loadSystemUris($requestUri = null) { // Set the request URI. if (!empty($requestUri)) { $this->set('uri.request', $requestUri); } else { $this->set('uri.request', $this->detectRequestUri()); } // Check to see if an explicit base URI has been set. $siteUri = trim($this->get('site_uri', '')); if ($siteUri !== '') { $uri = Uri::getInstance($siteUri); $path = $uri->toString(['path']); } else { // No explicit base URI was set so we need to detect it. // Start with the requested URI. $uri = Uri::getInstance($this->get('uri.request')); // If we are working from a CGI SAPI with the 'cgi.fix_pathinfo' directive disabled we use PHP_SELF. if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($_SERVER['REQUEST_URI'])) { // We aren't expecting PATH_INFO within PHP_SELF so this should work. $path = \dirname($_SERVER['PHP_SELF']); } else { // Pretty much everything else should be handled with SCRIPT_NAME. $path = \dirname($_SERVER['SCRIPT_NAME']); } } $host = $uri->toString(['scheme', 'user', 'pass', 'host', 'port']); // Check if the path includes "index.php". if (strpos($path, 'index.php') !== false) { // Remove the index.php portion of the path. $path = substr_replace($path, '', strpos($path, 'index.php'), 9); } $path = rtrim($path, '/\\'); // Set the base URI both as just a path and as the full URI. $this->set('uri.base.full', $host . $path . '/'); $this->set('uri.base.host', $host); $this->set('uri.base.path', $path . '/'); // Set the extended (non-base) part of the request URI as the route. if (stripos($this->get('uri.request'), $this->get('uri.base.full')) === 0) { $this->set('uri.route', substr_replace($this->get('uri.request'), '', 0, \strlen($this->get('uri.base.full')))); } // Get an explicitly set media URI is present. $mediaURI = trim($this->get('media_uri', '')); if ($mediaURI) { if (strpos($mediaURI, '://') !== false) { $this->set('uri.media.full', $mediaURI); $this->set('uri.media.path', $mediaURI); } else { // Normalise slashes. $mediaURI = trim($mediaURI, '/\\'); $mediaURI = !empty($mediaURI) ? '/' . $mediaURI . '/' : '/'; $this->set('uri.media.full', $this->get('uri.base.host') . $mediaURI); $this->set('uri.media.path', $mediaURI); } } else { // No explicit media URI was set, build it dynamically from the base uri. $this->set('uri.media.full', $this->get('uri.base.full') . 'media/'); $this->set('uri.media.path', $this->get('uri.base.path') . 'media/'); } } /** * Retrieve the application configuration object. * * @return Registry * * @since 4.0.0 */ public function getConfig() { return $this->config; } } PK0A#](��AA%src/Application/ApplicationHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\OutputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Application helper functions * * @since 1.5 */ class ApplicationHelper { /** * Client information array * * @var array * @since 1.6 */ protected static $_clients = []; /** * Return the name of the request component [main component] * * @param string $default The default option * * @return string Option (e.g. com_something) * * @since 1.6 */ public static function getComponentName($default = null) { static $option; if ($option) { return $option; } $input = Factory::getApplication()->getInput(); $option = strtolower($input->get('option', '')); if (empty($option)) { $option = $default; } $input->set('option', $option); return $option; } /** * Provides a secure hash based on a seed * * @param string $seed Seed string. * * @return string A secure hash * * @since 3.2 */ public static function getHash($seed) { return md5(Factory::getApplication()->get('secret') . $seed); } /** * This method transliterates a string into a URL * safe string or returns a URL safe UTF-8 string * based on the global configuration * * @param string $string String to process * @param string $language Language to transliterate to if unicode slugs are disabled * * @return string Processed string * * @since 3.2 */ public static function stringURLSafe($string, $language = '') { if (Factory::getApplication()->get('unicodeslugs') == 1) { $output = OutputFilter::stringUrlUnicodeSlug($string); } else { if ($language === '*' || $language === '') { $languageParams = ComponentHelper::getParams('com_languages'); $language = $languageParams->get('site'); } $output = OutputFilter::stringURLSafe($string, $language); } return $output; } /** * Gets information on a specific client id. This method will be useful in * future versions when we start mapping applications in the database. * * This method will return a client information array if called * with no arguments which can be used to add custom application information. * * @param integer|string|null $id A client identifier * @param boolean $byName If true, find the client by its name * * @return \stdClass|\stdClass[]|null Object describing the client, array containing all the clients or null if $id not known * * @since 1.5 */ public static function getClientInfo($id = null, $byName = false) { // Only create the array if it is empty if (empty(self::$_clients)) { $obj = new \stdClass(); // Site Client $obj->id = 0; $obj->name = 'site'; $obj->path = JPATH_SITE; self::$_clients[0] = clone $obj; // Administrator Client $obj->id = 1; $obj->name = 'administrator'; $obj->path = JPATH_ADMINISTRATOR; self::$_clients[1] = clone $obj; // Installation Client $obj->id = 2; $obj->name = 'installation'; $obj->path = JPATH_INSTALLATION; self::$_clients[2] = clone $obj; // API Client $obj->id = 3; $obj->name = 'api'; $obj->path = JPATH_API; self::$_clients[3] = clone $obj; // CLI Client $obj->id = 4; $obj->name = 'cli'; $obj->path = JPATH_CLI; self::$_clients[4] = clone $obj; } // If no client id has been passed return the whole array if ($id === null) { return self::$_clients; } // Are we looking for client information by id or by name? if (!$byName) { if (isset(self::$_clients[$id])) { return self::$_clients[$id]; } } else { foreach (self::$_clients as $client) { if ($client->name == strtolower($id)) { return $client; } } } return null; } /** * Adds information for a client. * * @param mixed $client A client identifier either an array or object * * @return boolean True if the information is added. False on error * * @since 1.6 */ public static function addClientInfo($client) { if (\is_array($client)) { $client = (object) $client; } if (!\is_object($client)) { return false; } $info = self::getClientInfo(); if (!isset($client->id)) { $client->id = \count($info); } self::$_clients[$client->id] = clone $client; return true; } } PK0A#]������+src/Application/CMSApplicationInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Application; use Joomla\Application\ConfigurationAwareApplicationInterface; use Joomla\CMS\Extension\ExtensionManagerInterface; use Joomla\CMS\Language\Language; use Joomla\CMS\User\User; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a Joomla! CMS Application class * * @since 4.0.0 * @note In Joomla 5 this interface will no longer extend EventAwareInterface * @property-read Input $input {@deprecated 4.0 will be removed in 6.0} The Joomla Input property. Deprecated in favour of getInput() */ interface CMSApplicationInterface extends ExtensionManagerInterface, ConfigurationAwareApplicationInterface, EventAwareInterface { /** * Constant defining an enqueued emergency message * * @var string * @since 4.0.0 */ public const MSG_EMERGENCY = 'emergency'; /** * Constant defining an enqueued alert message * * @var string * @since 4.0.0 */ public const MSG_ALERT = 'alert'; /** * Constant defining an enqueued critical message * * @var string * @since 4.0.0 */ public const MSG_CRITICAL = 'critical'; /** * Constant defining an enqueued error message * * @var string * @since 4.0.0 */ public const MSG_ERROR = 'error'; /** * Constant defining an enqueued warning message * * @var string * @since 4.0.0 */ public const MSG_WARNING = 'warning'; /** * Constant defining an enqueued notice message * * @var string * @since 4.0.0 */ public const MSG_NOTICE = 'notice'; /** * Constant defining an enqueued info message * * @var string * @since 4.0.0 */ public const MSG_INFO = 'info'; /** * Constant defining an enqueued debug message * * @var string * @since 4.0.0 */ public const MSG_DEBUG = 'debug'; /** * Enqueue a system message. * * @param string $msg The message to enqueue. * @param string $type The message type. * * @return void * * @since 4.0.0 */ public function enqueueMessage($msg, $type = self::MSG_INFO); /** * Get the system message queue. * * @return array The system message queue. * * @since 4.0.0 */ public function getMessageQueue(); /** * Check the client interface by name. * * @param string $identifier String identifier for the application interface * * @return boolean True if this application is of the given type client interface. * * @since 4.0.0 */ public function isClient($identifier); /** * Flag if the application instance is a CLI or web based application. * * Helper function, you should use the native PHP functions to detect if it is a CLI application. * * @return boolean * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement. CLI will be handled by the joomla/console package instead */ public function isCli(); /** * Get the application identity. * * @return User|null A User object or null if not set. * * @since 4.0.0 */ public function getIdentity(); /** * Method to get the application input object. * * @return Input * * @since 4.0.0 */ public function getInput(): Input; /** * Method to get the application language object. * * @return Language The language object * * @since 4.0.0 */ public function getLanguage(); /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName(); /** * Allows the application to load a custom or default identity. * * @param User $identity An optional identity object. If omitted, the factory user is created. * * @return $this * * @since 4.0.0 */ public function loadIdentity(User $identity = null); } PK0A#]�dOOsrc/Response/JsonResponse.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Response; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JSON Response class. * * This class serves to provide the Joomla Platform with a common interface to access * response variables for e.g. Ajax requests. * * @since 3.1 */ class JsonResponse { /** * Determines whether the request was successful * * @var boolean * * @since 3.1 */ public $success = true; /** * The main response message * * @var string * * @since 3.1 */ public $message = null; /** * Array of messages gathered in the Application object * * @var array * * @since 3.1 */ public $messages = null; /** * The response data * * @var mixed * * @since 3.1 */ public $data = null; /** * Constructor * * @param mixed $response The Response data * @param string $message The main response message * @param boolean $error True, if the success flag shall be set to false, defaults to false * @param boolean $ignoreMessages True, if the message queue shouldn't be included, defaults to false * * @since 3.1 */ public function __construct($response = null, $message = null, $error = false, $ignoreMessages = false) { $this->message = $message; // Get the message queue if requested and available $app = Factory::getApplication(); if (!$ignoreMessages && $app !== null && \is_callable([$app, 'getMessageQueue'])) { $messages = $app->getMessageQueue(); $lists = []; // Build the sorted messages list if (\is_array($messages) && \count($messages)) { foreach ($messages as $message) { if (isset($message['type']) && isset($message['message'])) { $lists[$message['type']][] = $message['message']; } } } // If messages exist add them to the output if (count($lists)) { $this->messages = $lists; } } // Check if we are dealing with an error if ($response instanceof \Throwable) { // Prepare the error response $this->success = false; $this->message = $response->getMessage(); } else { // Prepare the response data $this->success = !$error; $this->data = $response; } } /** * Magic toString method for sending the response in JSON format * * @return string The response in JSON format * * @since 3.1 */ public function __toString() { return json_encode($this); } } PK0A#]_�[�ff#src/Mail/MailerFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create mailer objects. * * @since 4.4.0 */ interface MailerFactoryInterface { /** * Method to get an instance of a mailer. If the passed settings are null, * then the mailer does use the internal configuration. * * @param ?Registry $settings The configuration * * @return MailerInterface * * @since 4.4.0 */ public function createMailer(?Registry $settings = null): MailerInterface; } PK0A#]ڲ��V�Vsrc/Mail/Mail.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Mail\Exception\MailDisabledException; use PHPMailer\PHPMailer\Exception as phpmailerException; use PHPMailer\PHPMailer\PHPMailer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Email Class. Provides a common interface to send email from the Joomla! Platform * * @since 1.7.0 */ class Mail extends PHPMailer implements MailerInterface { /** * Mail instances container. * * @var Mail[] * @since 1.7.3 * * @deprecated 4.4.0 will be removed in 6.0 * See getInstance() for more details */ public static $instances = []; /** * Charset of the message. * * @var string * @since 1.7.0 */ public $CharSet = 'utf-8'; /** * Constructor * * @param boolean $exceptions Flag if Exceptions should be thrown * * @since 1.7.0 */ public function __construct($exceptions = true) { parent::__construct($exceptions); // PHPMailer has an issue using the relative path for its language files $this->setLanguage('en_gb', __DIR__ . '/language/'); // Configure a callback function to handle errors when $this->debug() is called $this->Debugoutput = function ($message, $level) { Log::add(sprintf('Error in Mail API: %s', $message), Log::ERROR, 'mail'); }; // If debug mode is enabled then set SMTPDebug to the maximum level if (\defined('JDEBUG') && JDEBUG) { $this->SMTPDebug = 4; } // Don't disclose the PHPMailer version $this->XMailer = ' '; /** * Which validator to use by default when validating email addresses. * Validation patterns supported: * `auto` Pick best pattern automatically; * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; * `pcre` Use old PCRE implementation; * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. * `noregex` Don't use a regex: super fast, really dumb. * * The default used by phpmailer is `php` but this does not support dotless domains so instead we use `html5` * * @see PHPMailer::validateAddress() * * @var string|callable */ PHPMailer::$validator = 'html5'; } /** * Returns the global email object, only creating it if it doesn't already exist. * * NOTE: If you need an instance to use that does not have the global configuration * values, use an id string that is not 'Joomla'. * * @param string $id The id string for the Mail instance [optional] * @param boolean $exceptions Flag if Exceptions should be thrown [optional] * * @return Mail The global Mail object * * @since 4.4.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use the mailer service in the DI container and create a mailer from there * Example: * Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer(); */ public static function getInstance($id = 'Joomla', $exceptions = true) { if (empty(static::$instances[$id])) { $config = clone Factory::getConfig(); $config->set('throw_exceptions', $exceptions); static::$instances[$id] = Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer($config); } return static::$instances[$id]; } /** * Send the mail * * @return boolean Boolean true if successful, false if exception throwing is disabled. * * @since 1.7.0 * * @throws MailDisabledException if the mail function is disabled * @throws phpmailerException if sending failed */ public function Send() { if (!Factory::getApplication()->get('mailonline', 1)) { throw new MailDisabledException( MailDisabledException::REASON_USER_DISABLED, Text::_('JLIB_MAIL_FUNCTION_OFFLINE'), 500 ); } if ($this->Mailer === 'mail' && !\function_exists('mail')) { throw new MailDisabledException( MailDisabledException::REASON_MAIL_FUNCTION_NOT_AVAILABLE, Text::_('JLIB_MAIL_FUNCTION_DISABLED'), 500 ); } try { $result = parent::send(); } catch (phpmailerException $e) { // If auto TLS is disabled just let this bubble up if (!$this->SMTPAutoTLS) { throw $e; } $result = false; } /* * If sending failed and auto TLS is enabled, retry sending with the feature disabled * * See https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting#opportunistic-tls for more info */ if (!$result && $this->SMTPAutoTLS) { $this->SMTPAutoTLS = false; try { $result = parent::send(); } finally { // Reset the value for any future emails $this->SMTPAutoTLS = true; } } return $result; } /** * Set the email sender * * @param mixed $from email address and Name of sender * <code>array([0] => email Address, [1] => Name)</code> * or as a string * @param mixed $name Either a string or array of strings [name(s)] * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure. * * @since 1.7.0 * * @throws \UnexpectedValueException if the sender is not a valid address * @throws phpmailerException if setting the sender failed and exception throwing is enabled */ public function setSender($from, $name = '') { if (\is_array($from)) { // If $from is an array we assume it has an address and a name if (isset($from[2])) { // If it is an array with entries, use them $result = $this->setFrom(MailHelper::cleanLine($from[0]), MailHelper::cleanLine($from[1]), (bool) $from[2]); } else { $result = $this->setFrom(MailHelper::cleanLine($from[0]), MailHelper::cleanLine($from[1])); } } elseif (\is_string($from)) { // If it is a string we assume it is just the address $result = $this->setFrom(MailHelper::cleanLine($from), $name); } else { // If it is neither, we log a message and throw an exception Log::add(Text::sprintf('JLIB_MAIL_INVALID_EMAIL_SENDER', $from), Log::WARNING, 'jerror'); throw new \UnexpectedValueException(sprintf('Invalid email sender: %s', $from)); } if ($result === false) { return false; } return $this; } /** * Set the email subject * * @param string $subject Subject of the email * * @return Mail Returns this object for chaining. * * @since 1.7.0 */ public function setSubject($subject) { $this->Subject = MailHelper::cleanSubject($subject); return $this; } /** * Set the email body * * @param string $content Body of the email * * @return Mail Returns this object for chaining. * * @since 1.7.0 */ public function setBody($content) { /* * Filter the Body * @todo: Check for XSS */ $this->Body = MailHelper::cleanText($content); return $this; } /** * Add recipients to the email. * * @param mixed $recipient Either a string or array of strings [email address(es)] * @param mixed $name Either a string or array of strings [name(s)] * @param string $method The parent method's name. * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure. * * @since 1.7.0 * * @throws \InvalidArgumentException if the argument array counts do not match * @throws phpmailerException if setting the address failed and exception throwing is enabled */ protected function add($recipient, $name = '', $method = 'addAddress') { $method = lcfirst($method); // If the recipient is an array, add each recipient... otherwise just add the one if (\is_array($recipient)) { if (\is_array($name)) { $combined = array_combine($recipient, $name); if ($combined === false) { throw new \InvalidArgumentException("The number of elements for each array isn't equal."); } foreach ($combined as $recipientEmail => $recipientName) { $recipientEmail = MailHelper::cleanLine($recipientEmail); $recipientName = MailHelper::cleanLine($recipientName); // Check for boolean false return if exception handling is disabled if (\call_user_func([parent::class, $method], $recipientEmail, $recipientName) === false) { return false; } } } else { $name = MailHelper::cleanLine($name); foreach ($recipient as $to) { $to = MailHelper::cleanLine($to); // Check for boolean false return if exception handling is disabled if (\call_user_func([parent::class, $method], $to, $name) === false) { return false; } } } } else { $recipient = MailHelper::cleanLine($recipient); // Check for boolean false return if exception handling is disabled if (\call_user_func([parent::class, $method], $recipient, $name) === false) { return false; } } return $this; } /** * Add recipients to the email * * @param mixed $recipient Either a string or array of strings [email address(es)] * @param mixed $name Either a string or array of strings [name(s)] * * @return Mail|boolean Returns this object for chaining on success or false on failure when exception throwing is disabled. * * @since 1.7.0 * * @throws phpmailerException if exception throwing is enabled */ public function addRecipient($recipient, $name = '') { return $this->add($recipient, $name, 'addAddress'); } /** * Add carbon copy recipients to the email * * @param mixed $cc Either a string or array of strings [email address(es)] * @param mixed $name Either a string or array of strings [name(s)] * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure when exception throwing is enabled. * * @since 1.7.0 * * @throws phpmailerException if exception throwing is enabled */ public function addCc($cc, $name = '') { // If the carbon copy recipient is an array, add each recipient... otherwise just add the one if (isset($cc)) { return $this->add($cc, $name, 'addCC'); } return $this; } /** * Add blind carbon copy recipients to the email * * @param mixed $bcc Either a string or array of strings [email address(es)] * @param mixed $name Either a string or array of strings [name(s)] * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure when exception throwing is disabled. * * @since 1.7.0 * * @throws phpmailerException if exception throwing is enabled */ public function addBcc($bcc, $name = '') { // If the blind carbon copy recipient is an array, add each recipient... otherwise just add the one if (isset($bcc)) { return $this->add($bcc, $name, 'addBCC'); } return $this; } /** * Add file attachment to the email * * @param mixed $path Either a string or array of strings [filenames] * @param mixed $name Either a string or array of strings [names]. N.B. if this is an array it must contain the same * number of elements as the array of paths supplied. * @param mixed $encoding The encoding of the attachment * @param mixed $type The mime type * @param string $disposition The disposition of the attachment * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure when exception throwing is disabled. * * @since 3.0.1 * @throws \InvalidArgumentException if the argument array counts do not match * @throws phpmailerException if setting the attachment failed and exception throwing is enabled */ public function addAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream', $disposition = 'attachment') { // If the file attachments is an array, add each file... otherwise just add the one if (isset($path)) { $result = true; if (\is_array($path)) { if (!empty($name) && \count($path) != \count($name)) { throw new \InvalidArgumentException('The number of attachments must be equal with the number of name'); } foreach ($path as $key => $file) { if (!empty($name)) { $result = parent::addAttachment($file, $name[$key], $encoding, $type); } else { if (!empty($name)) { $result = parent::addAttachment($file, $name[$key], $encoding, $type, $disposition); } else { $result = parent::addAttachment($file, $name, $encoding, $type, $disposition); } } } // Check for boolean false return if exception handling is disabled if ($result === false) { return false; } } else { $result = parent::addAttachment($path, $name, $encoding, $type); } // Check for boolean false return if exception handling is disabled if ($result === false) { return false; } } return $this; } /** * Unset all file attachments from the email * * @return Mail Returns this object for chaining. * * @since 3.0.1 */ public function clearAttachments() { parent::clearAttachments(); return $this; } /** * Unset file attachments specified by array index. * * @param integer $index The numerical index of the attachment to remove * * @return Mail Returns this object for chaining. * * @since 3.0.1 */ public function removeAttachment($index = 0) { if (isset($this->attachment[$index])) { unset($this->attachment[$index]); } return $this; } /** * Add Reply to email address(es) to the email * * @param mixed $replyto Either a string or array of strings [email address(es)] * @param mixed $name Either a string or array of strings [name(s)] * * @return Mail|boolean Returns this object for chaining on success or boolean false on failure when exception throwing is disabled. * * @since 1.7.0 * * @throws phpmailerException if exception throwing is enabled */ public function addReplyTo($replyto, $name = '') { return $this->add($replyto, $name, 'addReplyTo'); } /** * Sets message type to HTML * * @param boolean $ishtml Boolean true or false. * * @return Mail Returns this object for chaining. * * @since 3.1.4 */ public function isHtml($ishtml = true) { parent::isHTML($ishtml); return $this; } /** * Send messages using $Sendmail. * * This overrides the parent class to remove the restriction on the executable's name containing the word "sendmail" * * @return void * * @since 1.7.0 */ public function isSendmail() { // Prefer the Joomla configured sendmail path and default to the configured PHP path otherwise $sendmail = Factory::getApplication()->get('sendmail', ini_get('sendmail_path')); // And if we still don't have a path, then use the system default for Linux if (empty($sendmail)) { $sendmail = '/usr/sbin/sendmail'; } $this->Sendmail = $sendmail; $this->Mailer = 'sendmail'; } /** * Use sendmail for sending the email * * @param string $sendmail Path to sendmail [optional] * * @return boolean True on success * * @since 1.7.0 */ public function useSendmail($sendmail = null) { $this->Sendmail = $sendmail; if (!empty($this->Sendmail)) { $this->isSendmail(); return true; } else { $this->isMail(); return false; } } /** * Use SMTP for sending the email * * @param string $auth SMTP Authentication [optional] * @param string $host SMTP Host [optional] * @param string $user SMTP Username [optional] * @param string $pass SMTP Password [optional] * @param string $secure Use secure methods * @param integer $port The SMTP port * * @return boolean True on success * * @since 1.7.0 */ public function useSmtp($auth = null, $host = null, $user = null, $pass = null, $secure = null, $port = 25) { $this->SMTPAuth = $auth; $this->Host = $host; $this->Username = $user; $this->Password = $pass; $this->Port = $port; if ($secure === 'ssl' || $secure === 'tls') { $this->SMTPSecure = $secure; } if ( ($this->SMTPAuth !== null && $this->Host !== null && $this->Username !== null && $this->Password !== null) || ($this->SMTPAuth === null && $this->Host !== null) ) { $this->isSMTP(); return true; } else { $this->isMail(); return false; } } /** * Function to send an email * * @param string $from From email address * @param string $fromName From name * @param mixed $recipient Recipient email address(es) * @param string $subject email subject * @param string $body Message body * @param boolean $mode false = plain text, true = HTML * @param mixed $cc CC email address(es) * @param mixed $bcc BCC email address(es) * @param mixed $attachment Attachment file name(s) * @param mixed $replyTo Reply to email address(es) * @param mixed $replyToName Reply to name(s) * * @return boolean True on success, false on failure when exception throwing is disabled. * * @since 1.7.0 * * @throws MailDisabledException if the mail function is disabled * @throws phpmailerException if exception throwing is enabled */ public function sendMail( $from, $fromName, $recipient, $subject, $body, $mode = false, $cc = null, $bcc = null, $attachment = null, $replyTo = null, $replyToName = null ) { // Create config object $app = Factory::getApplication(); $this->setSubject($subject); $this->setBody($body); // Are we sending the email as HTML? $this->isHtml($mode); /* * Do not send the message if adding any of the below items fails */ if ($this->addRecipient($recipient) === false) { return false; } if ($this->addCc($cc) === false) { return false; } if ($this->addBcc($bcc) === false) { return false; } if ($this->addAttachment($attachment) === false) { return false; } // Take care of reply email addresses if (\is_array($replyTo)) { $numReplyTo = \count($replyTo); for ($i = 0; $i < $numReplyTo; $i++) { if ($this->addReplyTo($replyTo[$i], $replyToName[$i]) === false) { return false; } } } elseif (isset($replyTo)) { if ($this->addReplyTo($replyTo, $replyToName) === false) { return false; } } elseif ($app->get('replyto')) { $this->addReplyTo($app->get('replyto'), $app->get('replytoname')); } // Add sender to replyTo only if no replyTo received $autoReplyTo = empty($this->ReplyTo); if ($this->setSender([$from, $fromName, $autoReplyTo]) === false) { return false; } return $this->Send(); } } PK0A#]Wŋ�(src/Mail/MailerFactoryAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a mailer factory. * * @since 4.4.0 */ interface MailerFactoryAwareInterface { /** * Set the mailer factory to use. * * @param ?MailerFactoryInterface $mailerFactory The mailer factory to use. * * @return void * * @since 4.4.0 */ public function setMailerFactory(?MailerFactoryInterface $mailerFactory = null): void; } PK0A#]h"����src/Mail/MailerFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; use Exception; use Joomla\CMS\Log\Log; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating mailer objects. * * @since 4.4.0 */ class MailerFactory implements MailerFactoryInterface { /** * The default configuration. * * @var Registry * @since 4.4.0 */ private $defaultConfiguration; /** * The MailerFactory constructor. * * @param Registry $defaultConfiguration The default configuration */ public function __construct(Registry $defaultConfiguration) { $this->defaultConfiguration = $defaultConfiguration; } /** * Method to get an instance of a mailer. If the passed settings are null, * then the mailer does use the internal configuration. * * @param ?Registry $settings The configuration * * @return MailerInterface * * @since 4.4.0 */ public function createMailer(?Registry $settings = null): MailerInterface { $configuration = new Registry($this->defaultConfiguration); if ($settings) { $configuration->merge($settings); } $mailer = new Mail((bool) $configuration->get('throw_exceptions', true)); $smtpauth = $configuration->get('smtpauth') == 0 ? null : 1; $smtpuser = $configuration->get('smtpuser'); $smtppass = $configuration->get('smtppass'); $smtphost = $configuration->get('smtphost'); $smtpsecure = $configuration->get('smtpsecure'); $smtpport = $configuration->get('smtpport'); $mailfrom = $configuration->get('mailfrom'); $fromname = $configuration->get('fromname'); $mailType = $configuration->get('mailer'); // Clean the email address $mailfrom = MailHelper::cleanLine($mailfrom); // Set default sender without Reply-to if the mailfrom is a valid address if (MailHelper::isEmailAddress($mailfrom)) { // Wrap in try/catch to catch Exception if it is throwing them try { // Check for a false return value if exception throwing is disabled if ($mailer->setFrom($mailfrom, MailHelper::cleanLine($fromname), false) === false) { Log::add(__METHOD__ . '() could not set the sender data.', Log::WARNING, 'mail'); } } catch (\Exception $e) { Log::add(__METHOD__ . '() could not set the sender data.', Log::WARNING, 'mail'); } } // Default mailer is to use PHP's mail function switch ($mailType) { case 'smtp': $mailer->useSmtp($smtpauth, $smtphost, $smtpuser, $smtppass, $smtpsecure, $smtpport); break; case 'sendmail': $mailer->isSendmail(); break; default: $mailer->isMail(); break; } return $mailer; } } PK0A#]�%�E��*src/Mail/language/phpmailer.lang-en_gb.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\CMS\Language\Text; $PHPMAILER_LANG['authenticate'] = Text::_('PHPMAILER_AUTHENTICATE'); $PHPMAILER_LANG['connect_host'] = Text::_('PHPMAILER_CONNECT_HOST'); $PHPMAILER_LANG['data_not_accepted'] = Text::_('PHPMAILER_DATA_NOT_ACCEPTED'); $PHPMAILER_LANG['empty_message'] = Text::_('PHPMAILER_EMPTY_MESSAGE'); $PHPMAILER_LANG['encoding'] = Text::_('PHPMAILER_ENCODING'); $PHPMAILER_LANG['execute'] = Text::_('PHPMAILER_EXECUTE'); $PHPMAILER_LANG['file_access'] = Text::_('PHPMAILER_FILE_ACCESS'); $PHPMAILER_LANG['file_open'] = Text::_('PHPMAILER_FILE_OPEN'); $PHPMAILER_LANG['from_failed'] = Text::_('PHPMAILER_FROM_FAILED'); $PHPMAILER_LANG['instantiate'] = Text::_('PHPMAILER_INSTANTIATE'); $PHPMAILER_LANG['invalid_address'] = Text::_('PHPMAILER_INVALID_ADDRESS'); $PHPMAILER_LANG['mailer_not_supported'] = Text::_('PHPMAILER_MAILER_IS_NOT_SUPPORTED'); $PHPMAILER_LANG['provide_address'] = Text::_('PHPMAILER_PROVIDE_ADDRESS'); $PHPMAILER_LANG['recipients_failed'] = Text::_('PHPMAILER_RECIPIENTS_FAILED'); $PHPMAILER_LANG['signing'] = Text::_('PHPMAILER_SIGNING_ERROR'); $PHPMAILER_LANG['smtp_connect_failed'] = Text::_('PHPMAILER_SMTP_CONNECT_FAILED'); $PHPMAILER_LANG['smtp_error'] = Text::_('PHPMAILER_SMTP_ERROR'); $PHPMAILER_LANG['variable_set'] = Text::_('PHPMAILER_VARIABLE_SET'); $PHPMAILER_LANG['extension_missing'] = Text::_('PHPMAILER_EXTENSION_MISSING'); PK0A#]E���E�Esrc/Mail/MailTemplate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Mail\Exception\MailDisabledException; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; use PHPMailer\PHPMailer\Exception as phpmailerException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Email Templating Class * * @since 4.0.0 */ class MailTemplate { /** * Mailer object to send the actual mail. * * @var \Joomla\CMS\Mail\Mail * @since 4.0.0 */ protected $mailer; /** * Identifier of the mail template. * * @var string * @since 4.0.0 */ protected $template_id; /** * Language of the mail template. * * @var string */ protected $language; /** * * @var string[] * @since 4.0.0 */ protected $data = []; /** * * @var string[] * @since 4.4.7 */ protected $unsafe_tags = []; /** * * @var string[] * @since 4.0.0 */ protected $attachments = []; /** * List of recipients of the email * * @var \stdClass[] * @since 4.0.0 */ protected $recipients = []; /** * Reply To of the email * * @var \stdClass * @since 4.0.0 */ protected $replyto; /** * Constructor for the mail templating class * * @param string $templateId Id of the mail template. * @param string $language Language of the template to use. * @param Mail $mailer Mail object to send the mail with. * * @since 4.0.0 */ public function __construct($templateId, $language, Mail $mailer = null) { $this->template_id = $templateId; $this->language = $language; if ($mailer) { $this->mailer = $mailer; } else { $this->mailer = Factory::getMailer(); } } /** * Add an attachment to the mail * * @param string $name Filename of the attachment * @param string $file Either a filepath or filecontent * * @return void * * @since 4.0.0 */ public function addAttachment($name, $file) { $attachment = new \stdClass(); $attachment->name = $name; $attachment->file = $file; $this->attachments[] = $attachment; } /** * Adds recipients for this mail * * @param string $mail Mail address of the recipient * @param string $name Name of the recipient * @param string $type How should the recipient receive the mail? ('to', 'cc', 'bcc') * * @return void * * @since 4.0.0 */ public function addRecipient($mail, $name = null, $type = 'to') { $recipient = new \stdClass(); $recipient->mail = $mail; $recipient->name = $name ?? $mail; $recipient->type = $type; $this->recipients[] = $recipient; } /** * Set reply to for this mail * * @param string $mail Mail address to reply to * @param string $name Name * * @return void * * @since 4.0.0 */ public function setReplyTo($mail, $name = '') { $reply = new \stdClass(); $reply->mail = $mail; $reply->name = $name; $this->replyto = $reply; } /** * Add data to replace in the template * * @param array $data Associative array of strings to replace * * @return void * * @since 4.0.0 */ public function addTemplateData($data) { $this->data = array_merge($this->data, $data); } /** * Mark tags as unsafe to ensure escaping in HTML mails * * @param array $tags Tag names * * @return void * * @since 4.4.7 */ public function addUnsafeTags($tags) { $this->unsafe_tags = array_merge($this->unsafe_tags, array_map('strtoupper', $tags)); } /** * Render and send the mail * * @return boolean True on success * * @since 4.0.0 * @throws \Exception * @throws MailDisabledException * @throws phpmailerException */ public function send() { $config = ComponentHelper::getParams('com_mails'); $mail = self::getTemplate($this->template_id, $this->language); // If the Mail Template was not found in the db, we cannot send an email. if ($mail === null) { return false; } /** @var Registry $params */ $params = $mail->params; $app = Factory::getApplication(); $replyTo = $app->get('replyto', ''); $replyToName = $app->get('replytoname', ''); if ((int) $config->get('alternative_mailconfig', 0) === 1 && (int) $params->get('alternative_mailconfig', 0) === 1) { if ($this->mailer->Mailer === 'smtp' || $params->get('mailer') === 'smtp') { $smtpauth = ($params->get('smtpauth', $app->get('smtpauth')) == 0) ? null : 1; $smtpuser = $params->get('smtpuser', $app->get('smtpuser')); $smtppass = $params->get('smtppass', $app->get('smtppass')); $smtphost = $params->get('smtphost', $app->get('smtphost')); $smtpsecure = $params->get('smtpsecure', $app->get('smtpsecure')); $smtpport = $params->get('smtpport', $app->get('smtpport')); $this->mailer->useSmtp($smtpauth, $smtphost, $smtpuser, $smtppass, $smtpsecure, $smtpport); } if ($params->get('mailer') === 'sendmail') { $this->mailer->isSendmail(); } $mailfrom = $params->get('mailfrom', $app->get('mailfrom')); $fromname = $params->get('fromname', $app->get('fromname')); if (MailHelper::isEmailAddress($mailfrom)) { $this->mailer->setFrom(MailHelper::cleanLine($mailfrom), MailHelper::cleanLine($fromname), false); } $replyTo = $params->get('replyto', $replyTo); $replyToName = $params->get('replytoname', $replyToName); } $app->triggerEvent('onMailBeforeRendering', [$this->template_id, &$this]); $subject = $this->replaceTags(Text::_($mail->subject), $this->data); $this->mailer->setSubject($subject); $mailStyle = $config->get('mail_style', 'plaintext'); $plainBody = $this->replaceTags(Text::_($mail->body), $this->data); $htmlBody = $this->replaceTags(Text::_($mail->htmlbody), $this->data, true); if ($mailStyle === 'plaintext' || $mailStyle === 'both') { // If the Plain template is empty try to convert the HTML template to a Plain text if (!$plainBody) { $plainBody = strip_tags(str_replace(['<br>', '<br />', '<br/>'], "\n", $htmlBody)); } $this->mailer->setBody($plainBody); // Set alt body, use $mailer->Body directly because it was filtered by $mailer->setBody() if ($mailStyle === 'both') { $this->mailer->AltBody = $this->mailer->Body; } } if ($mailStyle === 'html' || $mailStyle === 'both') { $this->mailer->isHtml(true); // If HTML body is empty try to convert the Plain template to html if (!$htmlBody) { $htmlBody = nl2br($this->replaceTags(Text::_($mail->body), $this->data, true), false); } $htmlBody = MailHelper::convertRelativeToAbsoluteUrls($htmlBody); $this->mailer->setBody($htmlBody); } if ($config->get('copy_mails') && $params->get('copyto')) { $this->mailer->addBcc($params->get('copyto')); } foreach ($this->recipients as $recipient) { switch ($recipient->type) { case 'cc': $this->mailer->addCc($recipient->mail, $recipient->name); break; case 'bcc': $this->mailer->addBcc($recipient->mail, $recipient->name); break; case 'to': default: $this->mailer->addAddress($recipient->mail, $recipient->name); } } if ($this->replyto) { $this->mailer->addReplyTo($this->replyto->mail, $this->replyto->name); } elseif ($replyTo) { $this->mailer->addReplyTo($replyTo, $replyToName); } if (trim($config->get('attachment_folder', ''))) { $folderPath = rtrim(Path::check(JPATH_ROOT . '/' . $config->get('attachment_folder')), \DIRECTORY_SEPARATOR); if ($folderPath && $folderPath !== Path::clean(JPATH_ROOT) && is_dir($folderPath)) { foreach ((array) json_decode($mail->attachments) as $attachment) { $filePath = Path::check($folderPath . '/' . $attachment->file); if (is_file($filePath)) { $this->mailer->addAttachment($filePath, $this->getAttachmentName($filePath, $attachment->name)); } } } } foreach ($this->attachments as $attachment) { if (is_file($attachment->file)) { $this->mailer->addAttachment($attachment->file, $this->getAttachmentName($attachment->file, $attachment->name)); } else { $this->mailer->addStringAttachment($attachment->file, $attachment->name); } } return $this->mailer->Send(); } /** * Replace tags with their values recursively * * @param string $text The template to process * @param array $tags An associative array to replace in the template * @param bool $isHtml Is the text an HTML text and requires escaping * * @return string Rendered mail template * * @since 4.0.0 */ protected function replaceTags($text, $tags, $isHtml = false) { foreach ($tags as $key => $value) { // If the value is NULL, replace with an empty string. NULL itself throws notices if (\is_null($value)) { $value = ''; } if (\is_array($value)) { $matches = []; $pregKey = preg_quote(strtoupper($key), '/'); if (preg_match_all('/{' . $pregKey . '}(.*?){\/' . $pregKey . '}/s', $text, $matches)) { foreach ($matches[0] as $i => $match) { $replacement = ''; foreach ($value as $name => $subvalue) { if (\is_array($subvalue) && $name == $matches[1][$i]) { $subvalue = implode("\n", $subvalue); // Escape if necessary if ($isHtml && \in_array(strtoupper($key), $this->unsafe_tags, true)) { $subvalue = htmlspecialchars($subvalue, ENT_QUOTES, 'UTF-8'); } $replacement .= implode("\n", $subvalue); } elseif (\is_array($subvalue)) { $replacement .= $this->replaceTags($matches[1][$i], $subvalue, $isHtml); } elseif (\is_string($subvalue) && $name == $matches[1][$i]) { // Escape if necessary if ($isHtml && \in_array(strtoupper($key), $this->unsafe_tags, true)) { $subvalue = htmlspecialchars($subvalue, ENT_QUOTES, 'UTF-8'); } $replacement .= $subvalue; } } $text = str_replace($match, $replacement, $text); } } } else { // Escape if necessary if ($isHtml && \in_array(strtoupper($key), $this->unsafe_tags, true)) { $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } $text = str_replace('{' . strtoupper($key) . '}', $value, $text); } } return $text; } /** * Get a specific mail template * * @param string $key Template identifier * @param string $language Language code of the template * * @return object|null An object with the data of the mail, or null if the template not found in the db. * * @since 4.0.0 */ public static function getTemplate($key, $language) { $db = Factory::getDbo(); $query = $db->getQuery(true); $query->select('*') ->from($db->quoteName('#__mail_templates')) ->where($db->quoteName('template_id') . ' = :key') ->whereIn($db->quoteName('language'), ['', $language], ParameterType::STRING) ->order($db->quoteName('language') . ' DESC') ->bind(':key', $key); $db->setQuery($query); $mail = $db->loadObject(); if ($mail) { $mail->params = new Registry($mail->params); } return $mail; } /** * Insert a new mail template into the system * * @param string $key Mail template key * @param string $subject A default subject (normally a translatable string) * @param string $body A default body (normally a translatable string) * @param array $tags Associative array of tags to replace * @param string $htmlbody A default htmlbody (normally a translatable string) * * @return boolean True on success, false on failure * * @since 4.0.0 */ public static function createTemplate($key, $subject, $body, $tags, $htmlbody = '') { $db = Factory::getDbo(); $template = new \stdClass(); $template->template_id = $key; $template->language = ''; $template->subject = $subject; $template->body = $body; $template->htmlbody = $htmlbody; $template->extension = explode('.', $key, 2)[0] ?? ''; $template->attachments = ''; $params = new \stdClass(); $params->tags = (array) $tags; $template->params = json_encode($params); return $db->insertObject('#__mail_templates', $template); } /** * Update an existing mail template * * @param string $key Mail template key * @param string $subject A default subject (normally a translatable string) * @param string $body A default body (normally a translatable string) * @param array $tags Associative array of tags to replace * @param string $htmlbody A default htmlbody (normally a translatable string) * * @return boolean True on success, false on failure * * @since 4.0.0 */ public static function updateTemplate($key, $subject, $body, $tags, $htmlbody = '') { $db = Factory::getDbo(); $template = new \stdClass(); $template->template_id = $key; $template->language = ''; $template->subject = $subject; $template->body = $body; $template->htmlbody = $htmlbody; $params = new \stdClass(); $params->tags = (array) $tags; $template->params = json_encode($params); return $db->updateObject('#__mail_templates', $template, ['template_id', 'language']); } /** * Method to delete a mail template * * @param string $key The key of the mail template * * @return boolean True on success, false on failure * * @since 4.0.0 */ public static function deleteTemplate($key) { $db = Factory::getDbo(); $query = $db->getQuery(true); $query->delete($db->quoteName('#__mail_templates')) ->where($db->quoteName('template_id') . ' = :key') ->bind(':key', $key); $db->setQuery($query); return $db->execute(); } /** * Check and if necessary fix the file name of an attachment so that the attached file * has the same extension as the source file, and not a different file extension * * @param string $file Path to the file to be attached * @param string $name The file name to be used for the attachment * * @return string The corrected file name for the attachment * * @since 4.0.0 */ protected function getAttachmentName(string $file, string $name): string { // If no name is given, do not process it further if (!trim($name)) { return ''; } // Replace any placeholders. $name = $this->replaceTags($name, $this->data); // Get the file extension. $ext = File::getExt($file); // Strip off extension from $name and append extension of $file, if any return File::stripExt($name) . ($ext ? '.' . $ext : ''); } } PK0A#]��T�__src/Mail/MailerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Provides a common interface to send emails with. * * @since 4.4.0 */ interface MailerInterface { /** * Send the mail. Throws an exception when something goes wrong. * * @return void * * @since 4.4.0 * * @throws \RuntimeException */ public function send(); /** * Set the email sender. * * @param string $fromEmail The Email address of the sender * @param string $name The name of the sender * * @return void * * @since 4.4.0 * * @throws \UnexpectedValueException if the sender is not a valid address */ public function setSender(string $fromEmail, string $name = ''); /** * Set the email subject. * * @param string $subject Subject of the email * * @return void * * @since 4.4.0 */ public function setSubject(string $subject); /** * Set the email body. * * @param string $content Body of the email * * @return void * * @since 4.4.0 */ public function setBody(string $content); /** * Add a recipient to the email. * * @param string $recipientEmail The email of the recipient * @param string $name The name of the recipient * * @return void * * @since 4.4.0 * * @throws \UnexpectedValueException if the recipient is not a valid address */ public function addRecipient(string $recipientEmail, string $name = ''); /** * Add a carbon copy recipient to the email. * * @param string $ccEmail The email of the CC recipient * @param string $name The name of the CC recipient * * @return void * * @since 4.4.0 * * @throws \UnexpectedValueException if the CC is not a valid address */ public function addCc(string $ccEmail, string $name = ''); /** * Add a blind carbon copy recipient to the email. * * @param string $bccEmail The email of the BCC recipient * @param string $name The name of the BCC recipient * * @return void * * @since 4.4.0 * * @throws \UnexpectedValueException if the BCC is not a valid address */ public function addBcc(string $bccEmail, string $name = ''); /** * Add file attachment to the email. * * @param string $data The data of the attachment * @param string $name The name of the attachment * @param string $encoding The encoding of the attachment * @param string $type The mime type of the attachment * * @return void * * @since 4.4.0 */ public function addAttachment(string $data, string $name = '', string $encoding = 'base64', string $type = 'application/octet-stream'); /** * Add Reply to email address to the email * * @param string $replyToEmail The email of the reply address * @param string $name The name of the reply address * * @return void * * @since 4.4.0 * * @throws \UnexpectedValueException if the replay to is not a valid address */ public function addReplyTo(string $replyToEmail, string $name = ''); } PK0A#]&LG�!!,src/Mail/Exception/MailDisabledException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an error for disabled mail functionality. * * @since 4.0.0 */ final class MailDisabledException extends \RuntimeException { /** * Send Mail option is disabled by the user. * * @var string * @since 4.0.0 */ public const REASON_USER_DISABLED = 'user_disabled'; /** * Mail() function is not available on the system. * * @var string * @since 4.0.0 */ public const REASON_MAIL_FUNCTION_NOT_AVAILABLE = 'mail_function_not_available'; /** * Reason mail is disabled. * * @var string * @since 4.0.0 */ private $reason; /** * Constructor. * * @param string $reason The reason why mail is disabled. * @param string $message The Exception message to throw. * @param integer $code The Exception code. * @param \Throwable $previous The previous exception used for the exception chaining. * * @since 4.0.0 */ public function __construct(string $reason, string $message = '', int $code = 0, \Throwable $previous = null) { parent::__construct($message, $code, $previous); $this->reason = $reason; } /** * Method to return the reason why mail is disabled. * * @return string * * @since 4.0.0 */ public function getReason(): string { return $this->reason; } } PK0A#]�$����src/Mail/MailHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; use Joomla\CMS\Router\Route; use Joomla\CMS\String\PunycodeHelper; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Email helper class, provides static methods to perform various tasks relevant * to the Joomla email routines. * * @todo: Test these methods as the regex work is first run and not tested thoroughly * * @since 1.7.0 */ abstract class MailHelper { /** * Cleans single line inputs. * * @param string $value String to be cleaned. * * @return string Cleaned string. * * @since 1.7.0 */ public static function cleanLine($value) { $value = PunycodeHelper::emailToPunycode($value); return trim(preg_replace('/(%0A|%0D|\n+|\r+)/i', '', $value)); } /** * Cleans multi-line inputs. * * @param string $value Multi-line string to be cleaned. * * @return string Cleaned multi-line string. * * @since 1.7.0 */ public static function cleanText($value) { return trim(preg_replace('/(%0A|%0D|\n+|\r+)(content-type:|to:|cc:|bcc:)/i', '', $value)); } /** * Cleans any injected headers from the email body. * * @param string $body email body string. * * @return string Cleaned email body string. * * @since 1.7.0 */ public static function cleanBody($body) { // Strip all email headers from a string return preg_replace("/((From:|To:|Cc:|Bcc:|Subject:|Content-type:) ([\S]+))/", '', $body); } /** * Cleans any injected headers from the subject string. * * @param string $subject email subject string. * * @return string Cleaned email subject string. * * @since 1.7.0 */ public static function cleanSubject($subject) { return preg_replace("/((From:|To:|Cc:|Bcc:|Content-type:) ([\S]+))/", '', $subject); } /** * Verifies that an email address does not have any extra headers injected into it. * * @param string $address email address. * * @return mixed email address string or boolean false if injected headers are present. * * @since 1.7.0 */ public static function cleanAddress($address) { if (preg_match("[\s;,]", $address)) { return false; } return $address; } /** * Verifies that the string is in a proper email address format. * * @param string $email String to be verified. * * @return boolean True if string has the correct format; false otherwise. * * @since 1.7.0 */ public static function isEmailAddress($email) { // Split the email into a local and domain $atIndex = strrpos($email, '@'); $domain = substr($email, $atIndex + 1); $local = substr($email, 0, $atIndex); // Check Length of domain $domainLen = \strlen($domain); if ($domainLen < 1 || $domainLen > 255) { return false; } /* * Check the local address * We're a bit more conservative about what constitutes a "legal" address, that is, a-zA-Z0-9.!#$%&'*+/=?^_`{|}~- * The first and last character in local cannot be a period ('.') * Also, period should not appear 2 or more times consecutively */ $allowed = "a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-"; $regex = "/^[$allowed][\.$allowed]{0,63}$/"; if (!preg_match($regex, $local) || substr($local, -1) === '.' || $local[0] === '.' || preg_match('/\.\./', $local)) { return false; } // No problem if the domain looks like an IP address, ish $regex = '/^[0-9\.]+$/'; if (preg_match($regex, $domain)) { return true; } // Check Lengths $localLen = \strlen($local); if ($localLen < 1 || $localLen > 64) { return false; } // Check the domain $domain_array = explode('.', $domain); $regex = '/^[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/'; foreach ($domain_array as $domain) { // Convert domain to punycode $domain = PunycodeHelper::toPunycode($domain); // Must be something if (!$domain) { return false; } // Check for invalid characters if (!preg_match($regex, $domain)) { return false; } // Check for a dash at the beginning of the domain if (strpos($domain, '-') === 0) { return false; } // Check for a dash at the end of the domain $length = \strlen($domain) - 1; if (strpos($domain, '-', $length) === $length) { return false; } } return true; } /** * Convert relative (links, images sources) to absolute urls so that content is accessible in email * * @param string $content The content need to convert * * @return string The converted content which the relative urls are converted to absolute urls * * @since 4.1.0 */ public static function convertRelativeToAbsoluteUrls($content) { $siteUrl = Uri::root(); // Replace none SEF URLs by absolute SEF URLs if (strpos($content, 'href="index.php?') !== false) { preg_match_all('#href="index.php\?([^"]+)"#m', $content, $matches); foreach ($matches[1] as $urlQueryString) { $content = str_replace( 'href="index.php?' . $urlQueryString . '"', 'href="' . Route::link('site', 'index.php?' . $urlQueryString, Route::TLS_IGNORE, true) . '"', $content ); } self::checkContent($content); } // Replace relative links, image sources with absolute Urls $protocols = '[a-zA-Z0-9\-]+:'; $attributes = ['href=', 'src=', 'poster=']; foreach ($attributes as $attribute) { if (strpos($content, $attribute) !== false) { $regex = '#\s' . $attribute . '"(?!/|' . $protocols . '|\#|\')([^"]*)"#m'; $content = preg_replace($regex, ' ' . $attribute . '"' . $siteUrl . '$1"', $content); self::checkContent($content); } } return $content; } /** * Check the content after regular expression function call. * * @param string $content Content to be checked. * * @return void * * @throws \RuntimeException If there is an error in previous regular expression function call. * @since 4.1.0 */ private static function checkContent($content) { if ($content !== null) { return; } switch (preg_last_error()) { case PREG_BACKTRACK_LIMIT_ERROR: $message = 'PHP regular expression limit reached (pcre.backtrack_limit)'; break; case PREG_RECURSION_LIMIT_ERROR: $message = 'PHP regular expression limit reached (pcre.recursion_limit)'; break; case PREG_BAD_UTF8_ERROR: $message = 'Bad UTF8 passed to PCRE function'; break; default: $message = 'Unknown PCRE error calling PCRE function'; } throw new \RuntimeException($message); } } PK0A#]�:;��$src/Mail/MailerFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Mail; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a MailerFactoryInterface aware class. * * @since 4.4.0 */ trait MailerFactoryAwareTrait { /** * MailerFactoryInterface * * @var MailerFactoryInterface * @since 4.4.0 */ private $mailerFactory; /** * Get the MailerFactoryInterface. * * @return MailerFactoryInterface * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the MailerFactory has not been set. */ protected function getMailerFactory(): MailerFactoryInterface { if ($this->mailerFactory) { return $this->mailerFactory; } throw new \UnexpectedValueException('MailerFactory not set in ' . __CLASS__); } /** * Set the mailer factory to use. * * @param ?MailerFactoryInterface $mailerFactory The mailer factory to use. * * @return void * * @since 4.4.0 */ public function setMailerFactory(?MailerFactoryInterface $mailerFactory = null): void { $this->mailerFactory = $mailerFactory; } } PK0A#]��0 0 (src/Versioning/VersionableModelTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Versioning; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a Versionable Model Class. * * @since 3.10.0 */ trait VersionableModelTrait { /** * Method to load a row for editing from the version history table. * * @param integer $versionId Key to the version history table. * @param Table $table Content table object being loaded. * * @return boolean False on failure or error, true otherwise. * * @since 4.0.0 */ public function loadHistory($versionId, Table &$table) { // Only attempt to check the row in if it exists, otherwise do an early exit. if (!$versionId) { return false; } // Get an instance of the row to checkout. $historyTable = Table::getInstance('Contenthistory'); if (!$historyTable->load($versionId)) { $this->setError($historyTable->getError()); return false; } $typeAlias = explode('.', $historyTable->item_id); array_pop($typeAlias); $rowArray = ArrayHelper::fromObject(json_decode($historyTable->version_data)); $key = $table->getKeyName(); if (implode('.', $typeAlias) != $this->typeAlias) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_HISTORY_ID_MISMATCH')); if (isset($rowArray[$key])) { $table->checkIn($rowArray[$key]); } return false; } $this->setState('save_date', $historyTable->save_date); $this->setState('version_note', $historyTable->version_note); /** * Load data from current version before replacing it with data from history to avoid error * if there are some required keys missing in the history data */ if (isset($rowArray[$key])) { $table->load($rowArray[$key]); } return $table->bind($rowArray); } } PK0A#]-���NN,src/Versioning/VersionableTableInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Versioning; use Joomla\CMS\Table\TableInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a versionable Table class * * @since 3.10.0 */ interface VersionableTableInterface extends TableInterface { /** * Get the type alias for the history table * * The type alias generally is the internal component name with the * content type. Ex.: com_content.article * * @return string The alias as described above * * @since 3.10.0 */ public function getTypeAlias(); } PK0A#]����}}-src/Versioning/VersionableControllerTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Versioning; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a Versionable Controller Class. * * @since 3.10.0 */ trait VersionableControllerTrait { /** * Method to load a row from version history * * @return boolean True if the record can be loaded, False if it cannot. * * @since 4.0.0 */ public function loadhistory() { $model = $this->getModel(); $table = $model->getTable(); $historyId = $this->input->getInt('version_id', null); if (!$model->loadhistory($historyId, $table)) { $this->setMessage($model->getError(), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } $recordId = $table->$key; // To avoid data collisions the urlVar may be different from the primary key. $urlVar = empty($this->urlVar) ? $key : $this->urlVar; // Access check. if (!$this->allowEdit([$key => $recordId], $key)) { $this->setMessage(Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); $table->checkIn(); return false; } $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); if (!$table->check() || !$table->store()) { $this->setMessage($table->getError(), 'error'); return false; } $this->setMessage( Text::sprintf( 'JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note') ) ); // Invoke the postSave method to allow for the child class to access the model. $this->postSaveHook($model); return true; } } PK0A#]�Ak��src/Versioning/Versioning.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Versioning; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Table; use Joomla\CMS\Workflow\WorkflowServiceInterface; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handle the versioning of content items * * @since 4.0.0 */ class Versioning { /** * Method to get a list of available versions of this item. * * @param string $typeAlias Typealias of the component * @param integer $id ID of the content item to get * * @return object[] A list of history items * * @since 4.0.0 */ public static function get($typeAlias, $id) { $db = Factory::getDbo(); $itemid = $typeAlias . '.' . $id; $query = $db->getQuery(true); $query->select($db->quoteName('h.version_note') . ',' . $db->quoteName('h.save_date') . ',' . $db->quoteName('u.name')) ->from($db->quoteName('#__history', 'h')) ->leftJoin($db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('h.editor_user_id')) ->where($db->quoteName('item_id') . ' = :item_id') ->bind(':item_id', $itemid, ParameterType::STRING) ->order($db->quoteName('save_date') . ' DESC '); $db->setQuery($query); return $db->loadObjectList(); } /** * Method to delete the history for an item. * * @param string $typeAlias Typealias of the component * @param integer $id ID of the content item to delete * * @return boolean true on success, otherwise false. * * @since 4.0.0 */ public static function delete($typeAlias, $id) { $db = Factory::getDbo(); $itemid = $typeAlias . '.' . $id; $query = $db->getQuery(true); $query->delete($db->quoteName('#__history')) ->where($db->quoteName('item_id') . ' = :item_id') ->bind(':item_id', $itemid, ParameterType::STRING); $db->setQuery($query); return $db->execute(); } /** * Method to save a version snapshot to the content history table. * * @param string $typeAlias Typealias of the content type * @param integer $id ID of the content item * @param mixed $data Array or object of data that can be * en- and decoded into JSON * @param string $note Note for the version to store * * @return boolean True on success, otherwise false. * * @since 4.0.0 */ public static function store($typeAlias, $id, $data, $note = '') { $typeTable = Table::getInstance('Contenttype', 'JTable'); $typeTable->load(['type_alias' => $typeAlias]); $historyTable = Table::getInstance('Contenthistory', 'JTable'); $historyTable->item_id = $typeAlias . '.' . $id; $aliasParts = explode('.', $typeAlias); // Don't store unless we have a non-zero item id if (!$historyTable->item_id) { return true; } // We should allow workflow items interact with the versioning $component = Factory::getApplication()->bootComponent($aliasParts[0]); if ($component instanceof WorkflowServiceInterface && $component->isWorkflowActive($typeAlias)) { PluginHelper::importPlugin('workflow'); // Pre-processing by observers $event = AbstractEvent::create( 'onContentVersioningPrepareTable', [ 'subject' => $historyTable, 'extension' => $typeAlias, ] ); Factory::getApplication()->getDispatcher()->dispatch('onContentVersioningPrepareTable', $event); } $historyTable->version_data = json_encode($data); $historyTable->version_note = $note; // Don't save if hash already exists and same version note $historyTable->sha1_hash = $historyTable->getSha1($data, $typeTable); if ($historyRow = $historyTable->getHashMatch()) { if (!$note || ($historyRow->version_note === $note)) { return true; } else { // Update existing row to set version note $historyTable->version_id = $historyRow->version_id; } } $result = $historyTable->store(); // Load history_limit config from extension. $context = $aliasParts[1] ?? ''; $maxVersionsContext = ComponentHelper::getParams($aliasParts[0])->get('history_limit_' . $context, 0); if ($maxVersionsContext) { $historyTable->deleteOldVersions($maxVersionsContext); } elseif ($maxVersions = ComponentHelper::getParams($aliasParts[0])->get('history_limit', 0)) { $historyTable->deleteOldVersions($maxVersions); } return $result; } } PK0A#]ex�`%%src/Plugin/PluginHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Plugin; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Plugin helper class * * @since 1.5 */ abstract class PluginHelper { /** * A persistent cache of the loaded plugins. * * @var array|null * * @since 1.7 */ protected static $plugins = null; /** * Get the path to a layout from a Plugin * * @param string $type Plugin type * @param string $name Plugin name * @param string $layout Layout name * * @return string Layout path * * @since 3.0 */ public static function getLayoutPath($type, $name, $layout = 'default') { $app = Factory::getApplication(); if ($app->isClient('site') || $app->isClient('administrator')) { $templateObj = $app->getTemplate(true); } else { $templateObj = (object) [ 'template' => '', 'parent' => '', ]; } $defaultLayout = $layout; $template = $templateObj->template; if (strpos($layout, ':') !== false) { // Get the template and file name from the string $temp = explode(':', $layout); $template = $temp[0] === '_' ? $templateObj->template : $temp[0]; $layout = $temp[1]; $defaultLayout = $temp[1] ?: 'default'; } // Build the template and base path for the layout $layoutPaths = []; if ($template) { $layoutPaths[] = JPATH_THEMES . '/' . $template . '/html/plg_' . $type . '_' . $name . '/' . $layout . '.php'; } if ($templateObj->parent) { $layoutPaths[] = JPATH_THEMES . '/' . $templateObj->parent . '/html/plg_' . $type . '_' . $name . '/' . $layout . '.php'; } $layoutPaths[] = JPATH_PLUGINS . '/' . $type . '/' . $name . '/tmpl/' . $defaultLayout . '.php'; $layoutPaths[] = JPATH_PLUGINS . '/' . $type . '/' . $name . '/tmpl/default.php'; foreach ($layoutPaths as $path) { if (is_file($path)) { return $path; } } return end($layoutPaths); } /** * Get the plugin data of a specific type if no specific plugin is specified * otherwise only the specific plugin data is returned. * * @param string $type The plugin type, relates to the subdirectory in the plugins directory. * @param string $plugin The plugin name. * * @return mixed An array of plugin data objects, or a plugin data object. * * @since 1.5 */ public static function getPlugin($type, $plugin = null) { $result = []; $plugins = static::load(); // Find the correct plugin(s) to return. if (!$plugin) { foreach ($plugins as $p) { // Is this the right plugin? if ($p->type === $type) { $result[] = $p; } } } else { foreach ($plugins as $p) { // Is this plugin in the right group? if ($p->type === $type && $p->name === $plugin) { $result = $p; break; } } } return $result; } /** * Checks if a plugin is enabled. * * @param string $type The plugin type, relates to the subdirectory in the plugins directory. * @param string $plugin The plugin name. * * @return boolean * * @since 1.5 */ public static function isEnabled($type, $plugin = null) { $result = static::getPlugin($type, $plugin); return !empty($result); } /** * Loads all the plugin files for a particular type if no specific plugin is specified * otherwise only the specific plugin is loaded. * * @param string $type The plugin type, relates to the subdirectory in the plugins directory. * @param string $plugin The plugin name. * @param boolean $autocreate Autocreate the plugin. * @param DispatcherInterface $dispatcher Optionally allows the plugin to use a different dispatcher. * * @return boolean True on success. * * @since 1.5 */ public static function importPlugin($type, $plugin = null, $autocreate = true, DispatcherInterface $dispatcher = null) { static $loaded = []; // Check for the default args, if so we can optimise cheaply $defaults = false; if ($plugin === null && $autocreate === true && $dispatcher === null) { $defaults = true; } // Ensure we have a dispatcher now so we can correctly track the loaded plugins $dispatcher = $dispatcher ?: Factory::getApplication()->getDispatcher(); // Get the dispatcher's hash to allow plugins to be registered to unique dispatchers $dispatcherHash = spl_object_hash($dispatcher); if (!isset($loaded[$dispatcherHash])) { $loaded[$dispatcherHash] = []; } if (!$defaults || !isset($loaded[$dispatcherHash][$type])) { $results = null; // Load the plugins from the database. $plugins = static::load(); // Get the specified plugin(s). for ($i = 0, $t = \count($plugins); $i < $t; $i++) { if ($plugins[$i]->type === $type && ($plugin === null || $plugins[$i]->name === $plugin)) { static::import($plugins[$i], $autocreate, $dispatcher); $results = true; } } // Bail out early if we're not using default args if (!$defaults) { return $results; } $loaded[$dispatcherHash][$type] = $results; } return $loaded[$dispatcherHash][$type]; } /** * Loads the plugin file. * * @param object $plugin The plugin. * @param boolean $autocreate True to autocreate. * @param DispatcherInterface $dispatcher Optionally allows the plugin to use a different dispatcher. * * @return void * * @since 3.2 */ protected static function import($plugin, $autocreate = true, DispatcherInterface $dispatcher = null) { static $plugins = []; // Get the dispatcher's hash to allow paths to be tracked against unique dispatchers $hash = spl_object_hash($dispatcher) . $plugin->type . $plugin->name; if (\array_key_exists($hash, $plugins)) { return; } $plugins[$hash] = true; $plugin = Factory::getApplication()->bootPlugin($plugin->name, $plugin->type); if ($dispatcher && $plugin instanceof DispatcherAwareInterface) { $plugin->setDispatcher($dispatcher); } if (!$autocreate) { return; } $plugin->registerListeners(); } /** * Loads the published plugins. * * @return array An array of published plugins * * @since 3.2 */ protected static function load() { if (static::$plugins !== null) { return static::$plugins; } $levels = Factory::getUser()->getAuthorisedViewLevels(); /** @var \Joomla\CMS\Cache\Controller\CallbackController $cache */ $cache = Factory::getCache('com_plugins', 'callback'); $loader = function () use ($levels) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( $db->quoteName( [ 'folder', 'element', 'params', 'extension_id', ], [ 'type', 'name', 'params', 'id', ] ) ) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('enabled') . ' = 1', $db->quoteName('type') . ' = ' . $db->quote('plugin'), $db->quoteName('state') . ' IN (0,1)', ] ) ->whereIn($db->quoteName('access'), $levels) ->order($db->quoteName('ordering')); $db->setQuery($query); return $db->loadObjectList(); }; try { static::$plugins = $cache->get($loader, [], md5(implode(',', $levels)), false); } catch (CacheExceptionInterface $cacheException) { static::$plugins = $loader(); } return static::$plugins; } } PK0A#] <��\2\2src/Plugin/CMSPlugin.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Plugin; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Extension\PluginInterface; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageAwareInterface; use Joomla\CMS\Language\LanguageAwareTrait; use Joomla\Event\AbstractEvent; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Event\EventInterface; use Joomla\Event\SubscriberInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Plugin Class * * @since 1.5 */ abstract class CMSPlugin implements DispatcherAwareInterface, PluginInterface, LanguageAwareInterface { use DispatcherAwareTrait; use LanguageAwareTrait; /** * A Registry object holding the parameters for the plugin * * @var Registry * @since 1.5 */ public $params = null; /** * The name of the plugin * * @var string * @since 1.5 */ protected $_name = null; /** * The plugin type * * @var string * @since 1.5 */ protected $_type = null; /** * Affects constructor behavior. If true, language files will be loaded automatically. * * @var boolean * @since 3.1 */ protected $autoloadLanguage = false; /** * Should I try to detect and register legacy event listeners, i.e. methods which accept unwrapped arguments? While * this maintains a great degree of backwards compatibility to Joomla! 3.x-style plugins it is much slower. You are * advised to implement your plugins using proper Listeners, methods accepting an AbstractEvent as their sole * parameter, for best performance. Also bear in mind that Joomla! 5.x onwards will only allow proper listeners, * removing support for legacy Listeners. * * @var boolean * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Implement your plugin methods accepting an AbstractEvent object * Example: * onEventTriggerName(AbstractEvent $event) { * $context = $event->getArgument(...); * } */ protected $allowLegacyListeners = true; /** * The application object * * @var CMSApplicationInterface * * @since 4.2.0 */ private $application; /** * Constructor * * @param DispatcherInterface &$subject The object to observe * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'group', 'params', 'language' * (this list is not meant to be comprehensive). * * @since 1.5 */ public function __construct(&$subject, $config = []) { // Get the parameters. if (isset($config['params'])) { if ($config['params'] instanceof Registry) { $this->params = $config['params']; } else { $this->params = new Registry($config['params']); } } // Get the plugin name. if (isset($config['name'])) { $this->_name = $config['name']; } // Get the plugin type. if (isset($config['type'])) { $this->_type = $config['type']; } // Load the language files if needed. if ($this->autoloadLanguage) { $this->loadLanguage(); } if (property_exists($this, 'app')) { @trigger_error('The application should be injected through setApplication() and requested through getApplication().', E_USER_DEPRECATED); $reflection = new \ReflectionClass($this); $appProperty = $reflection->getProperty('app'); if ($appProperty->isPrivate() === false && \is_null($this->app)) { $this->app = Factory::getApplication(); } } if (property_exists($this, 'db')) { @trigger_error('The database should be injected through the DatabaseAwareInterface and trait.', E_USER_DEPRECATED); $reflection = new \ReflectionClass($this); $dbProperty = $reflection->getProperty('db'); if ($dbProperty->isPrivate() === false && \is_null($this->db)) { $this->db = Factory::getDbo(); } } // Set the dispatcher we are to register our listeners with $this->setDispatcher($subject); } /** * Loads the plugin language file * * @param string $extension The extension for which a language file should be loaded * @param string $basePath The basepath to use * * @return boolean True, if the file has successfully loaded. * * @since 1.5 */ public function loadLanguage($extension = '', $basePath = JPATH_ADMINISTRATOR) { if (empty($extension)) { $extension = 'Plg_' . $this->_type . '_' . $this->_name; } $extension = strtolower($extension); $lang = $this->getApplication() ? $this->getApplication()->getLanguage() : Factory::getLanguage(); // If language already loaded, don't load it again. if ($lang->getPaths($extension)) { return true; } return $lang->load($extension, $basePath) || $lang->load($extension, JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name); } /** * Registers legacy Listeners to the Dispatcher, emulating how plugins worked under Joomla! 3.x and below. * * By default, this method will look for all public methods whose name starts with "on". It will register * lambda functions (closures) which try to unwrap the arguments of the dispatched Event into method call * arguments and call your on<Something> method. The result will be passed back to the Event into its 'result' * argument. * * This method additionally supports Joomla\Event\SubscriberInterface and plugins implementing this will be * registered to the dispatcher as a subscriber. * * @return void * * @since 4.0.0 */ public function registerListeners() { // Plugins which are SubscriberInterface implementations are handled without legacy layer support if ($this instanceof SubscriberInterface) { $this->getDispatcher()->addSubscriber($this); return; } $reflectedObject = new \ReflectionObject($this); $methods = $reflectedObject->getMethods(\ReflectionMethod::IS_PUBLIC); /** @var \ReflectionMethod $method */ foreach ($methods as $method) { if (substr($method->name, 0, 2) !== 'on') { continue; } // Save time if I'm not to detect legacy listeners if (!$this->allowLegacyListeners) { $this->registerListener($method->name); continue; } /** @var \ReflectionParameter[] $parameters */ $parameters = $method->getParameters(); // If the parameter count is not 1 it is by definition a legacy listener if (\count($parameters) !== 1) { $this->registerLegacyListener($method->name); continue; } /** @var \ReflectionParameter $param */ $param = array_shift($parameters); $paramName = $param->getName(); // No type hint / type hint class not an event or parameter name is not "event"? It's a legacy listener. if ($paramName !== 'event' || !$this->parameterImplementsEventInterface($param)) { $this->registerLegacyListener($method->name); continue; } // Everything checks out, this is a proper listener. $this->registerListener($method->name); } } /** * Registers a legacy event listener, i.e. a method which accepts individual arguments instead of an AbstractEvent * in its arguments. This provides backwards compatibility to Joomla! 3.x-style plugins. * * This method will register lambda functions (closures) which try to unwrap the arguments of the dispatched Event * into old style method arguments and call your on<Something> method with them. The result will be passed back to * the Event, as an element into an array argument called 'result'. * * @param string $methodName The method name to register * * @return void * * @since 4.0.0 */ final protected function registerLegacyListener(string $methodName) { $this->getDispatcher()->addListener( $methodName, function (AbstractEvent $event) use ($methodName) { // Get the event arguments $arguments = $event->getArguments(); // Extract any old results; they must not be part of the method call. $allResults = []; if (isset($arguments['result'])) { $allResults = $arguments['result']; unset($arguments['result']); } // Convert to indexed array for unpacking. $arguments = \array_values($arguments); $result = $this->{$methodName}(...$arguments); // Ignore null results if ($result === null) { return; } if ($event instanceof ResultAwareInterface) { $event->addResult($result); } elseif (!$event instanceof AbstractImmutableEvent) { // Restore the old results and add the new result from our method call $allResults[] = $result; $event['result'] = $allResults; } } ); } /** * Registers a proper event listener, i.e. a method which accepts an AbstractEvent as its sole argument. This is the * preferred way to implement plugins in Joomla! 4.x and will be the only possible method with Joomla! 5.x onwards. * * @param string $methodName The method name to register * * @return void * * @since 4.0.0 */ final protected function registerListener(string $methodName) { $this->getDispatcher()->addListener($methodName, [$this, $methodName]); } /** * Checks if parameter is typehinted to accept \Joomla\Event\EventInterface. * * @param \ReflectionParameter $parameter * * @return boolean * * @since 4.0.0 */ private function parameterImplementsEventInterface(\ReflectionParameter $parameter): bool { $reflectionType = $parameter->getType(); // Parameter is not typehinted. if ($reflectionType === null) { return false; } // Parameter is nullable. if ($reflectionType->allowsNull()) { return false; } // Handle standard typehints. if ($reflectionType instanceof \ReflectionNamedType) { return \is_a($reflectionType->getName(), EventInterface::class, true); } // Handle PHP 8 union types. if ($reflectionType instanceof \ReflectionUnionType) { foreach ($reflectionType->getTypes() as $type) { if (!\is_a($type->getName(), EventInterface::class, true)) { return false; } } return true; } return false; } /** * Returns the internal application or null when not set. * * @return CMSApplicationInterface|null * * @since 4.2.0 */ protected function getApplication(): ?CMSApplicationInterface { return $this->application; } /** * Sets the application to use. * * @param CMSApplicationInterface $application The application * * @return void * * @since 4.2.0 */ public function setApplication(CMSApplicationInterface $application): void { $this->application = $application; if ($application->getLanguage()) { $this->setLanguage($application->getLanguage()); } } } PK0A#]����U>U>src/Date/Date.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Date; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Date is a class that stores a date and provides logic to manipulate * and render that date in a variety of formats. * * @method Date|bool add(\DateInterval $interval) Adds an amount of days, months, years, hours, minutes and seconds to a Date object. * @method Date|bool sub(\DateInterval $interval) Subtracts an amount of days, months, years, hours, minutes and seconds from a Date object. * @method Date|bool modify(string $modify) Alter the timestamp of this object by incre/decre-menting in a format accepted by strtotime(). * * @property-read string $daysinmonth t - Number of days in the given month. * @property-read string $dayofweek N - ISO-8601 numeric representation of the day of the week. * @property-read string $dayofyear z - The day of the year (starting from 0). * @property-read boolean $isleapyear L - Whether it's a leap year. * @property-read string $day d - Day of the month, 2 digits with leading zeros. * @property-read string $hour H - 24-hour format of an hour with leading zeros. * @property-read string $minute i - Minutes with leading zeros. * @property-read string $second s - Seconds with leading zeros. * @property-read string $microsecond u - Microseconds with leading zeros. * @property-read string $month m - Numeric representation of a month, with leading zeros. * @property-read string $ordinal S - English ordinal suffix for the day of the month, 2 characters. * @property-read string $week W - ISO-8601 week number of year, weeks starting on Monday. * @property-read string $year Y - A full numeric representation of a year, 4 digits. * * @since 1.7.0 */ class Date extends \DateTime { public const DAY_ABBR = "\x021\x03"; public const DAY_NAME = "\x022\x03"; public const MONTH_ABBR = "\x023\x03"; public const MONTH_NAME = "\x024\x03"; /** * The format string to be applied when using the __toString() magic method. * * @var string * @since 1.7.0 */ public static $format = 'Y-m-d H:i:s'; /** * Placeholder for a \DateTimeZone object with GMT as the time zone. * * @var object * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement */ protected static $gmt; /** * Placeholder for a \DateTimeZone object with the default server * time zone as the time zone. * * @var object * @since 1.7.0 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement */ protected static $stz; /** * The \DateTimeZone object for usage in rending dates as strings. * * @var \DateTimeZone * @since 3.0.0 */ protected $tz; /** * Constructor. * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param mixed $tz Time zone to be used for the date. Might be a string or a DateTimeZone object. * * @since 1.7.0 */ public function __construct($date = 'now', $tz = null) { // Create the base GMT and server time zone objects. if (empty(self::$gmt) || empty(self::$stz)) { // @TODO: This code block stays here only for B/C, can be removed in 5.0 self::$gmt = new \DateTimeZone('GMT'); self::$stz = new \DateTimeZone(@date_default_timezone_get()); } // If the time zone object is not set, attempt to build it. if (!($tz instanceof \DateTimeZone)) { if (\is_string($tz)) { $tz = new \DateTimeZone($tz); } else { $tz = new \DateTimeZone('UTC'); } } // Backup active time zone $activeTZ = date_default_timezone_get(); // Force UTC timezone for correct time handling date_default_timezone_set('UTC'); // If the date is numeric assume a unix timestamp and convert it. $date = is_numeric($date) ? date('c', $date) : $date; // Call the DateTime constructor. parent::__construct($date, $tz); // Restore previously active timezone date_default_timezone_set($activeTZ); // Set the timezone object for access later. $this->tz = $tz; } /** * Magic method to access properties of the date given by class to the format method. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. * * @since 1.7.0 */ public function __get($name) { $value = null; switch ($name) { case 'daysinmonth': $value = $this->format('t', true); break; case 'dayofweek': $value = $this->format('N', true); break; case 'dayofyear': $value = $this->format('z', true); break; case 'isleapyear': $value = (bool) $this->format('L', true); break; case 'day': $value = $this->format('d', true); break; case 'hour': $value = $this->format('H', true); break; case 'minute': $value = $this->format('i', true); break; case 'second': $value = $this->format('s', true); break; case 'month': $value = $this->format('m', true); break; case 'ordinal': $value = $this->format('S', true); break; case 'week': $value = $this->format('W', true); break; case 'year': $value = $this->format('Y', true); break; default: $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE ); } return $value; } /** * Magic method to render the date object in the format specified in the public * static member Date::$format. * * @return string The date as a formatted string. * * @since 1.7.0 */ public function __toString() { return (string) parent::format(self::$format); } /** * Proxy for new Date(). * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param mixed $tz Time zone to be used for the date. * * @return Date * * @since 1.7.3 */ public static function getInstance($date = 'now', $tz = null) { return new static($date, $tz); } /** * Translates day of week number to a string. * * @param integer $day The numeric day of the week. * @param boolean $abbr Return the abbreviated day string? * * @return string The day of the week. * * @since 1.7.0 */ public function dayToString($day, $abbr = false) { switch ($day) { case 0: return $abbr ? Text::_('SUN') : Text::_('SUNDAY'); case 1: return $abbr ? Text::_('MON') : Text::_('MONDAY'); case 2: return $abbr ? Text::_('TUE') : Text::_('TUESDAY'); case 3: return $abbr ? Text::_('WED') : Text::_('WEDNESDAY'); case 4: return $abbr ? Text::_('THU') : Text::_('THURSDAY'); case 5: return $abbr ? Text::_('FRI') : Text::_('FRIDAY'); case 6: return $abbr ? Text::_('SAT') : Text::_('SATURDAY'); } } /** * Gets the date as a formatted string in a local calendar. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. * * @since 1.7.0 */ public function calendar($format, $local = false, $translate = true) { return $this->format($format, $local, $translate); } /** * Gets the date as a formatted string. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. * * @since 1.7.0 */ #[\ReturnTypeWillChange] public function format($format, $local = false, $translate = true) { if ($translate) { // Do string replacements for date format options that can be translated. $format = preg_replace('/(^|[^\\\])D/', "\\1" . self::DAY_ABBR, $format); $format = preg_replace('/(^|[^\\\])l/', "\\1" . self::DAY_NAME, $format); $format = preg_replace('/(^|[^\\\])M/', "\\1" . self::MONTH_ABBR, $format); $format = preg_replace('/(^|[^\\\])F/', "\\1" . self::MONTH_NAME, $format); } // If the returned time should not be local use UTC. if ($local == false) { parent::setTimezone(new \DateTimeZone('UTC')); } // Format the date. $return = parent::format($format); if ($translate) { // Manually modify the month and day strings in the formatted time. if (strpos($return, self::DAY_ABBR) !== false) { $return = str_replace(self::DAY_ABBR, $this->dayToString(parent::format('w'), true), $return); } if (strpos($return, self::DAY_NAME) !== false) { $return = str_replace(self::DAY_NAME, $this->dayToString(parent::format('w')), $return); } if (strpos($return, self::MONTH_ABBR) !== false) { $return = str_replace(self::MONTH_ABBR, $this->monthToString(parent::format('n'), true), $return); } if (strpos($return, self::MONTH_NAME) !== false) { $return = str_replace(self::MONTH_NAME, $this->monthToString(parent::format('n')), $return); } } if ($local == false && $this->tz !== null) { parent::setTimezone($this->tz); } return $return; } /** * Get the time offset from GMT in hours or seconds. * * @param boolean $hours True to return the value in hours. * * @return float The time offset from GMT either in hours or in seconds. * * @since 1.7.0 */ public function getOffsetFromGmt($hours = false) { return (float) $hours ? ($this->tz->getOffset($this) / 3600) : $this->tz->getOffset($this); } /** * Translates month number to a string. * * @param integer $month The numeric month of the year. * @param boolean $abbr If true, return the abbreviated month string * * @return string The month of the year. * * @since 1.7.0 */ public function monthToString($month, $abbr = false) { switch ($month) { case 1: return $abbr ? Text::_('JANUARY_SHORT') : Text::_('JANUARY'); case 2: return $abbr ? Text::_('FEBRUARY_SHORT') : Text::_('FEBRUARY'); case 3: return $abbr ? Text::_('MARCH_SHORT') : Text::_('MARCH'); case 4: return $abbr ? Text::_('APRIL_SHORT') : Text::_('APRIL'); case 5: return $abbr ? Text::_('MAY_SHORT') : Text::_('MAY'); case 6: return $abbr ? Text::_('JUNE_SHORT') : Text::_('JUNE'); case 7: return $abbr ? Text::_('JULY_SHORT') : Text::_('JULY'); case 8: return $abbr ? Text::_('AUGUST_SHORT') : Text::_('AUGUST'); case 9: return $abbr ? Text::_('SEPTEMBER_SHORT') : Text::_('SEPTEMBER'); case 10: return $abbr ? Text::_('OCTOBER_SHORT') : Text::_('OCTOBER'); case 11: return $abbr ? Text::_('NOVEMBER_SHORT') : Text::_('NOVEMBER'); case 12: return $abbr ? Text::_('DECEMBER_SHORT') : Text::_('DECEMBER'); } } /** * Method to wrap the setTimezone() function and set the internal time zone object. * * @param \DateTimeZone $tz The new \DateTimeZone object. * * @return Date * * @since 1.7.0 * @note This method can't be type hinted due to a PHP bug: https://bugs.php.net/bug.php?id=61483 */ #[\ReturnTypeWillChange] public function setTimezone($tz) { $this->tz = $tz; return parent::setTimezone($tz); } /** * Gets the date as an ISO 8601 string. IETF RFC 3339 defines the ISO 8601 format * and it can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in ISO 8601 format. * * @link http://www.ietf.org/rfc/rfc3339.txt * @since 1.7.0 */ public function toISO8601($local = false) { return $this->format(\DateTimeInterface::RFC3339, $local, false); } /** * Gets the date as an SQL datetime string. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param DatabaseDriver $db The database driver or null to use Factory::getDbo() * * @return string The date string in SQL datetime format. * * @link http://dev.mysql.com/doc/refman/5.0/en/datetime.html * @since 2.5.0 */ public function toSql($local = false, DatabaseDriver $db = null) { if ($db === null) { $db = Factory::getDbo(); } return $this->format($db->getDateFormat(), $local, false); } /** * Gets the date as an RFC 822 string. IETF RFC 2822 supercedes RFC 822 and its definition * can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in RFC 822 format. * * @link http://www.ietf.org/rfc/rfc2822.txt * @since 1.7.0 */ public function toRFC822($local = false) { return $this->format(\DateTimeInterface::RFC2822, $local, false); } /** * Gets the date as UNIX time stamp. * * @return integer The date as a UNIX timestamp. * * @since 1.7.0 */ public function toUnix() { return (int) parent::format('U'); } } PK0A#]f��ڪ � src/Feed/FeedEntry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; use Joomla\CMS\Date\Date; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to encapsulate a feed entry for the Joomla Platform. * * @property FeedPerson $author Person responsible for feed entry content. * @property array $categories Categories to which the feed entry belongs. * @property string $content The content of the feed entry. * @property array $contributors People who contributed to the feed entry content. * @property string $copyright Information about rights, e.g. copyrights, held in and over the feed entry. * @property array $links Links associated with the feed entry. * @property Date $publishedDate The publication date for the feed entry. * @property Feed $source The feed from which the entry is sourced. * @property string $title A human readable title for the feed entry. * @property Date $updatedDate The last time the content of the feed entry changed. * @property string $uri Universal, permanent identifier for the feed entry. * * @since 3.1.4 */ class FeedEntry { /** * @var array The entry properties. * @since 3.1.4 */ protected $properties = [ 'uri' => '', 'title' => '', 'updatedDate' => '', 'content' => '', 'categories' => [], 'contributors' => [], 'links' => [], ]; /** * Magic method to return values for feed entry properties. * * @param string $name The name of the property. * * @return mixed * * @since 3.1.4 */ public function __get($name) { return $this->properties[$name] ?? null; } /** * Magic method to set values for feed properties. * * @param string $name The name of the property. * @param mixed $value The value to set for the property. * * @return void * * @since 3.1.4 */ public function __set($name, $value) { // Ensure that setting a date always sets a Date instance. if ((($name === 'updatedDate') || ($name === 'publishedDate')) && !($value instanceof Date)) { $value = new Date($value); } // Validate that any authors that are set are instances of JFeedPerson or null. if (($name === 'author') && (!($value instanceof FeedPerson) || ($value === null))) { throw new \InvalidArgumentException( sprintf( '%1$s "author" must be an instance of Joomla\\CMS\\Feed\\FeedPerson. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } // Validate that any sources that are set are instances of JFeed or null. if (($name === 'source') && (!($value instanceof Feed) || ($value === null))) { throw new \InvalidArgumentException( sprintf( '%1$s "source" must be an instance of Joomla\\CMS\\Feed\\Feed. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } // Disallow setting categories, contributors, or links directly. if (\in_array($name, ['categories', 'contributors', 'links'])) { throw new \InvalidArgumentException( sprintf( 'Cannot directly set %1$s property "%2$s".', \get_class($this), $name ) ); } $this->properties[$name] = $value; } /** * Method to add a category to the feed entry object. * * @param string $name The name of the category to add. * @param string $uri The optional URI for the category to add. * * @return FeedEntry * * @since 3.1.4 */ public function addCategory($name, $uri = '') { $this->properties['categories'][$name] = $uri; return $this; } /** * Method to add a contributor to the feed entry object. * * @param string $name The full name of the person to add. * @param string $email The email address of the person to add. * @param string $uri The optional URI for the person to add. * @param string $type The optional type of person to add. * * @return FeedEntry * * @since 3.1.4 */ public function addContributor($name, $email, $uri = null, $type = null) { $contributor = new FeedPerson($name, $email, $uri, $type); // If the new contributor already exists then there is nothing to do, so just return. foreach ($this->properties['contributors'] as $c) { if ($c == $contributor) { return $this; } } // Add the new contributor. $this->properties['contributors'][] = $contributor; return $this; } /** * Method to add a link to the feed entry object. * * @param FeedLink $link The link object to add. * * @return FeedEntry * * @since 3.1.4 */ public function addLink(FeedLink $link) { // If the new link already exists then there is nothing to do, so just return. foreach ($this->properties['links'] as $l) { if ($l == $link) { return $this; } } // Add the new link. $this->properties['links'][] = $link; return $this; } /** * Method to remove a category from the feed entry object. * * @param string $name The name of the category to remove. * * @return FeedEntry * * @since 3.1.4 */ public function removeCategory($name) { unset($this->properties['categories'][$name]); return $this; } /** * Method to remove a contributor from the feed entry object. * * @param FeedPerson $contributor The person object to remove. * * @return FeedEntry * * @since 3.1.4 */ public function removeContributor(FeedPerson $contributor) { // If the contributor exists remove it. foreach ($this->properties['contributors'] as $k => $c) { if ($c == $contributor) { unset($this->properties['contributors'][$k]); $this->properties['contributors'] = array_values($this->properties['contributors']); return $this; } } return $this; } /** * Method to remove a link from the feed entry object. * * @param FeedLink $link The link object to remove. * * @return FeedEntry * * @since 3.1.4 */ public function removeLink(FeedLink $link) { // If the link exists remove it. foreach ($this->properties['links'] as $k => $l) { if ($l == $link) { unset($this->properties['links'][$k]); $this->properties['links'] = array_values($this->properties['links']); return $this; } } return $this; } /** * Shortcut method to set the author for the feed entry object. * * @param string $name The full name of the person to set. * @param string $email The email address of the person to set. * @param string $uri The optional URI for the person to set. * @param string $type The optional type of person to set. * * @return FeedEntry * * @since 3.1.4 */ public function setAuthor($name, $email, $uri = null, $type = null) { $author = new FeedPerson($name, $email, $uri, $type); $this->properties['author'] = $author; return $this; } } PK0A#]���� � src/Feed/FeedLink.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Feed Link class. * * @since 3.1.4 */ class FeedLink { /** * The URI to the linked resource. * * @var string * @since 3.1.4 */ public $uri; /** * The relationship between the feed and the linked resource. * * @var string * @since 3.1.4 */ public $relation; /** * The resource type. * * @var string * @since 3.1.4 */ public $type; /** * The language of the resource found at the given URI. * * @var string * @since 3.1.4 */ public $language; /** * The title of the resource. * * @var string * @since 3.1.4 */ public $title; /** * The length of the resource in bytes. * * @var integer * @since 3.1.4 */ public $length; /** * The link of the image. * * @var integer * @since 4.4.0 */ public $link; /** * The description of the image. * * @var integer * @since 4.4.0 */ public $description; /** * The height of the image. * * @var integer * @since 4.4.0 */ public $height; /** * The width of the image. * * @var integer * @since 4.4.0 */ public $width; /** * Constructor. * * @param string $uri The URI to the linked resource. * @param string $relation The relationship between the feed and the linked resource. * @param string $type The resource type. * @param string $language The language of the resource found at the given URI. * @param string $title The title of the resource. * @param integer $length The length of the resource in bytes. * * @since 3.1.4 * @throws \InvalidArgumentException */ public function __construct($uri = null, $relation = null, $type = null, $language = null, $title = null, $length = null) { $this->uri = $uri; $this->relation = $relation; $this->type = $type; $this->language = $language; $this->title = $title; // Validate the length input. if (isset($length) && !is_numeric($length)) { throw new \InvalidArgumentException('Length must be numeric.'); } $this->length = (int) $length; } } PK0A#] �[�5�5src/Feed/Parser/RssParser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed\Parser; use Joomla\CMS\Feed\Feed; use Joomla\CMS\Feed\FeedEntry; use Joomla\CMS\Feed\FeedLink; use Joomla\CMS\Feed\FeedParser; use Joomla\CMS\Feed\FeedPerson; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RSS Feed Parser class. * * @link http://cyber.law.harvard.edu/rss/rss.html * @since 3.1.4 */ class RssParser extends FeedParser { /** * @var string The feed element name for the entry elements. * @since 3.1.4 */ protected $entryElementName = 'item'; /** * @var string The feed format version. * @since 3.1.4 */ protected $version; /** * Method to handle the `<category>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleCategory(Feed $feed, \SimpleXMLElement $el) { // Get the data from the element. $domain = (string) $el['domain']; $category = $this->inputFilter->clean((string) $el, 'html'); $feed->addCategory($category, $domain); } /** * Method to handle the `<cloud>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleCloud(Feed $feed, \SimpleXMLElement $el) { $cloud = new \stdClass(); $cloud->domain = (string) $el['domain']; $cloud->port = (string) $el['port']; $cloud->path = (string) $el['path']; $cloud->protocol = (string) $el['protocol']; $cloud->registerProcedure = (string) $el['registerProcedure']; $feed->cloud = $cloud; } /** * Method to handle the `<copyright>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleCopyright(Feed $feed, \SimpleXMLElement $el) { $feed->copyright = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<description>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleDescription(Feed $feed, \SimpleXMLElement $el) { $feed->description = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<generator>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleGenerator(Feed $feed, \SimpleXMLElement $el) { $feed->generator = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<image>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleImage(Feed $feed, \SimpleXMLElement $el) { // Create a feed link object for the image. $image = new FeedLink( (string) $el->url, null, 'logo', null, $this->inputFilter->clean((string) $el->title, 'html') ); // Populate extra fields if they exist. $image->link = (string) filter_var($el->link, FILTER_VALIDATE_URL); $image->description = $this->inputFilter->clean((string) $el->description, 'html'); $image->height = (string) $el->height; $image->width = (string) $el->width; $feed->image = $image; } /** * Method to handle the `<language>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleLanguage(Feed $feed, \SimpleXMLElement $el) { $feed->language = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<lastBuildDate>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleLastBuildDate(Feed $feed, \SimpleXMLElement $el) { $feed->updatedDate = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<link>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleLink(Feed $feed, \SimpleXMLElement $el) { $link = new FeedLink(); $link->uri = (string) $el['href']; $feed->link = $link; } /** * Method to handle the `<managingEditor>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleManagingEditor(Feed $feed, \SimpleXMLElement $el) { $feed->author = $this->processPerson((string) $el); } /** * Method to handle the `<skipDays>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleSkipDays(Feed $feed, \SimpleXMLElement $el) { // Initialise the array. $days = []; // Add all of the day values from the feed to the array. foreach ($el->day as $day) { $days[] = (string) $day; } $feed->skipDays = $days; } /** * Method to handle the `<skipHours>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleSkipHours(Feed $feed, \SimpleXMLElement $el) { // Initialise the array. $hours = []; // Add all of the day values from the feed to the array. foreach ($el->hour as $hour) { $hours[] = (int) $hour; } $feed->skipHours = $hours; } /** * Method to handle the `<pubDate>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handlePubDate(Feed $feed, \SimpleXMLElement $el) { $feed->publishedDate = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<title>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleTitle(Feed $feed, \SimpleXMLElement $el) { $feed->title = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<ttl>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleTtl(Feed $feed, \SimpleXMLElement $el) { $feed->ttl = (int) $this->inputFilter->clean((string) $el, 'int'); } /** * Method to handle the `<webmaster>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleWebmaster(Feed $feed, \SimpleXMLElement $el) { // Get the tag contents and split it over the first space. $tmp = (string) $el; $tmp = explode(' ', $tmp, 2); // This is really cheap parsing. Probably need to create a method to do this more robustly. $name = null; if (isset($tmp[1])) { $name = trim( $this->inputFilter->clean($tmp[1], 'html'), ' ()' ); } $email = trim( filter_var((string) $tmp[0], FILTER_VALIDATE_EMAIL) ); $feed->addContributor($name, $email, null, 'webmaster'); } /** * Method to initialise the feed for parsing. Here we detect the version and advance the stream * reader so that it is ready to parse feed elements. * * @return void * * @since 3.1.4 */ protected function initialise() { // We are on the first XML Element after the xml doc type declaration $this->version = $this->stream->getAttribute('version'); // We want to move forward to the first element after the <channel> element. $this->moveToNextElement('channel'); $this->moveToNextElement(); } /** * Method to handle a `<item>` element for the feed. * * @param FeedEntry $entry The FeedEntry object being built from the parsed feed entry. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function processFeedEntry(FeedEntry $entry, \SimpleXMLElement $el) { $entry->uri = (string) filter_var($el->link, FILTER_VALIDATE_URL); $entry->title = $this->inputFilter->clean((string) $el->title, 'html'); $entry->publishedDate = $this->inputFilter->clean((string) $el->pubDate, 'html'); $entry->updatedDate = $this->inputFilter->clean((string) $el->pubDate, 'html'); $entry->content = $this->inputFilter->clean((string) $el->description, 'html'); $entry->guid = $this->inputFilter->clean((string) $el->guid, 'html'); $entry->isPermaLink = $entry->guid !== '' && (string) $el->guid['isPermaLink'] !== 'false'; $entry->comments = $this->inputFilter->clean((string) $el->comments, 'html'); // Add the feed entry author if available. $author = $this->inputFilter->clean((string) $el->author, 'html'); if (!empty($author)) { $entry->author = $this->processPerson($author); } // Add any categories to the entry. foreach ($el->category as $category) { $entry->addCategory((string) $category, (string) $category['domain']); } // Add any enclosures to the entry. foreach ($el->enclosure as $enclosure) { $link = new FeedLink( (string) $enclosure['url'], null, (string) $enclosure['type'], null, null, (int) $enclosure['length'] ); $entry->addLink($link); } } /** * Method to parse a string with person data and return a FeedPerson object. * * @param string $data The string to parse for a person. * * @return FeedPerson * * @since 3.1.4 */ protected function processPerson($data) { // Create a new person object. $person = new FeedPerson(); // This is really cheap parsing, but so far good enough. :) $data = explode(' ', $data, 2); if (isset($data[1])) { $person->name = trim( $this->inputFilter->clean($data[1], 'html'), ' ()' ); } // Set the email for the person. $person->email = trim( filter_var((string) $data[0], FILTER_VALIDATE_EMAIL) ); return $person; } } PK0A#]�Txx,src/Feed/Parser/NamespaceParserInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed\Parser; use Joomla\CMS\Feed\Feed; use Joomla\CMS\Feed\FeedEntry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Feed Namespace interface. * * @since 3.1.4 */ interface NamespaceParserInterface { /** * Method to handle an element for the feed given that a certain namespace is present. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeed(Feed $feed, \SimpleXMLElement $el); /** * Method to handle the feed entry element for the feed given that a certain namespace is present. * * @param FeedEntry $entry The FeedEntry object being built from the parsed feed entry. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeedEntry(FeedEntry $entry, \SimpleXMLElement $el); } PK0A#]{I��!!&src/Feed/Parser/Rss/MediaRssParser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed\Parser\Rss; use Joomla\CMS\Feed\Feed; use Joomla\CMS\Feed\FeedEntry; use Joomla\CMS\Feed\Parser\NamespaceParserInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RSS Feed Parser Namespace handler for MediaRSS. * * @link https://www.rssboard.org/media-rss * @since 3.1.4 */ class MediaRssParser implements NamespaceParserInterface { /** * Method to handle an element for the feed given that the media namespace is present. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeed(Feed $feed, \SimpleXMLElement $el) { } /** * Method to handle the feed entry element for the feed given that the media namespace is present. * * @param FeedEntry $entry The FeedEntry object being built from the parsed feed entry. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeedEntry(FeedEntry $entry, \SimpleXMLElement $el) { } } PK0A#]��tN44'src/Feed/Parser/Rss/ItunesRssParser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed\Parser\Rss; use Joomla\CMS\Feed\Feed; use Joomla\CMS\Feed\FeedEntry; use Joomla\CMS\Feed\Parser\NamespaceParserInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RSS Feed Parser Namespace handler for iTunes. * * @link https://itunespartner.apple.com/en/podcasts/overview * @since 3.1.4 */ class ItunesRssParser implements NamespaceParserInterface { /** * Method to handle an element for the feed given that the itunes namespace is present. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeed(Feed $feed, \SimpleXMLElement $el) { } /** * Method to handle the feed entry element for the feed given that the itunes namespace is present. * * @param FeedEntry $entry The FeedEntry object being built from the parsed feed entry. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ public function processElementForFeedEntry(FeedEntry $entry, \SimpleXMLElement $el) { } } PK0A#]4�Oϔ�src/Feed/Parser/AtomParser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed\Parser; use Joomla\CMS\Feed\Feed; use Joomla\CMS\Feed\FeedEntry; use Joomla\CMS\Feed\FeedLink; use Joomla\CMS\Feed\FeedParser; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * ATOM Feed Parser class. * * @link http://www.atomenabled.org/developers/syndication/ * @since 3.1.4 */ class AtomParser extends FeedParser { /** * @var string The feed format version. * @since 3.1.4 */ protected $version; /** * Method to handle the `<author>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleAuthor(Feed $feed, \SimpleXMLElement $el) { // Set the author information from the XML element. $feed->setAuthor( $this->inputFilter->clean((string) $el->name, 'html'), filter_var((string) $el->email, FILTER_VALIDATE_EMAIL), filter_var((string) $el->uri, FILTER_VALIDATE_URL) ); } /** * Method to handle the `<contributor>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleContributor(Feed $feed, \SimpleXMLElement $el) { $feed->addContributor( $this->inputFilter->clean((string) $el->name, 'html'), filter_var((string) $el->email, FILTER_VALIDATE_EMAIL), filter_var((string) $el->uri, FILTER_VALIDATE_URL) ); } /** * Method to handle the `<generator>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleGenerator(Feed $feed, \SimpleXMLElement $el) { $feed->generator = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<id>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleId(Feed $feed, \SimpleXMLElement $el) { $feed->uri = (string) $el; } /** * Method to handle the `<link>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleLink(Feed $feed, \SimpleXMLElement $el) { $link = new FeedLink(); $link->uri = (string) $el['href']; $link->language = (string) $el['hreflang']; $link->length = (int) $el['length']; $link->relation = (string) $el['rel']; $link->title = $this->inputFilter->clean((string) $el['title'], 'html'); $link->type = (string) $el['type']; $feed->link = $link; } /** * Method to handle the `<rights>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleRights(Feed $feed, \SimpleXMLElement $el) { $feed->copyright = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<subtitle>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleSubtitle(Feed $feed, \SimpleXMLElement $el) { $feed->description = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<title>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleTitle(Feed $feed, \SimpleXMLElement $el) { $feed->title = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to handle the `<updated>` element for the feed. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function handleUpdated(Feed $feed, \SimpleXMLElement $el) { $feed->updatedDate = $this->inputFilter->clean((string) $el, 'html'); } /** * Method to initialise the feed for parsing. Here we detect the version and advance the stream * reader so that it is ready to parse feed elements. * * @return void * * @since 3.1.4 */ protected function initialise() { // We are on the first XML Element after the xml doc type declaration $this->version = ($this->stream->getAttribute('version') == '0.3') ? '0.3' : '1.0'; $this->moveToNextElement(); } /** * Method to handle a `<entry>` element for the feed. * * @param FeedEntry $entry The FeedEntry object being built from the parsed feed entry. * @param \SimpleXMLElement $el The current XML element object to handle. * * @return void * * @since 3.1.4 */ protected function processFeedEntry(FeedEntry $entry, \SimpleXMLElement $el) { $entry->uri = (string) $el->id; $entry->title = $this->inputFilter->clean((string) $el->title, 'html'); $entry->updatedDate = $this->inputFilter->clean((string) $el->updated, 'html'); $entry->content = $this->inputFilter->clean((string) $el->summary, 'html'); if (!$entry->content) { $entry->content = $this->inputFilter->clean((string) $el->content, 'html'); } if (filter_var($entry->uri, FILTER_VALIDATE_URL) === false && !\is_null($el->link) && $el->link) { $link = $el->link; if ($link->count()) { $link = $this->bestLinkForUri($link); } $uri = (string) $link['href']; if ($uri) { $entry->uri = $uri; } } } /** * If there is more than one <link> in the feed entry, find the most appropriate one and return it. * * @param \SimpleXMLElement $links XML node with links from the feed entry. * * @return \SimpleXMLElement */ private function bestLinkForUri(\SimpleXMLElement $links) { $linkPrefs = ['alternate', 'self', '']; foreach ($linkPrefs as $pref) { foreach ($links as $link) { $rel = (string) $link['rel']; if ($rel === $pref) { return $link; } } } return array_shift($links); } } PK0A#]�q���src/Feed/FeedParser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; use Joomla\CMS\Feed\Parser\NamespaceParserInterface; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Feed Parser class. * * @since 3.1.4 */ abstract class FeedParser { /** * The feed element name for the entry elements. * * @var string * @since 3.1.4 */ protected $entryElementName = 'entry'; /** * Array of NamespaceParserInterface objects * * @var array * @since 3.1.4 */ protected $namespaces = []; /** * The XMLReader stream object for the feed. * * @var \XMLReader * @since 3.1.4 */ protected $stream; /** * The InputFilter * * @var InputFilter * @since 3.9.25 */ protected $inputFilter; /** * Constructor. * * @param \XMLReader $stream The XMLReader stream object for the feed. * @param InputFilter $inputFilter The InputFilter object to be used * * @since 3.1.4 */ public function __construct(\XMLReader $stream, InputFilter $inputFilter = null) { $this->stream = $stream; $this->inputFilter = $inputFilter ?: InputFilter::getInstance([], [], 1, 1); } /** * Method to parse the feed into a JFeed object. * * @return Feed * * @since 3.1.4 */ public function parse() { $feed = new Feed(); // Detect the feed version. $this->initialise(); // Let's get this party started... do { // Expand the element for processing. $el = new \SimpleXMLElement($this->stream->readOuterXml()); // Get the list of namespaces used within this element. $ns = $el->getNamespaces(true); // Get an array of available namespace objects for the element. $namespaces = []; foreach ($ns as $prefix => $uri) { // Ignore the empty namespace prefix. if (empty($prefix)) { continue; } // Get the necessary namespace objects for the element. $namespace = $this->fetchNamespace($prefix); if ($namespace) { $namespaces[] = $namespace; } } // Process the element. $this->processElement($feed, $el, $namespaces); // Skip over this element's children since it has been processed. $this->moveToClosingElement(); } while ($this->moveToNextElement()); return $feed; } /** * Method to register a namespace handler object. * * @param string $prefix The XML namespace prefix for which to register the namespace object. * @param NamespaceParserInterface $namespace The namespace object to register. * * @return FeedParser * * @since 3.1.4 */ public function registerNamespace($prefix, NamespaceParserInterface $namespace) { $this->namespaces[$prefix] = $namespace; return $this; } /** * Method to initialise the feed for parsing. If child parsers need to detect versions or other * such things this is where you'll want to implement that logic. * * @return void * * @since 3.1.4 */ abstract protected function initialise(); /** * Method to parse a specific feed element. * * @param Feed $feed The Feed object being built from the parsed feed. * @param \SimpleXMLElement $el The current XML element object to handle. * @param array $namespaces The array of relevant namespace objects to process for the element. * * @return void * * @since 3.1.4 */ protected function processElement(Feed $feed, \SimpleXMLElement $el, array $namespaces) { // Build the internal method name. $method = 'handle' . ucfirst($el->getName()); // If we are dealing with an item then it is feed entry time. if ($el->getName() == $this->entryElementName) { // Create a new feed entry for the item. $entry = new FeedEntry(); // First call the internal method. $this->processFeedEntry($entry, $el); foreach ($namespaces as $namespace) { if ($namespace instanceof NamespaceParserInterface) { $namespace->processElementForFeedEntry($entry, $el); } } // Add the new entry to the feed. $feed->addEntry($entry); return; } // Otherwise we treat it like any other element. // First call the internal method. if (\is_callable([$this, $method])) { $this->$method($feed, $el); } foreach ($namespaces as $namespace) { if ($namespace instanceof NamespaceParserInterface) { $namespace->processElementForFeed($feed, $el); } } } /** * Method to get a namespace object for a given namespace prefix. * * @param string $prefix The XML prefix for which to fetch the namespace object. * * @return mixed NamespaceParserInterface or false if none exists. * * @since 3.1.4 */ protected function fetchNamespace($prefix) { if (isset($this->namespaces[$prefix])) { return $this->namespaces[$prefix]; } $className = \get_class($this) . ucfirst($prefix); if (class_exists($className)) { $this->namespaces[$prefix] = new $className(); return $this->namespaces[$prefix]; } return false; } /** * Method to move the stream parser to the next XML element node. * * @param string $name The name of the element for which to move the stream forward until is found. * * @return boolean True if the stream parser is on an XML element node. * * @since 3.1.4 */ protected function moveToNextElement($name = null) { // Only keep looking until the end of the stream. while ($this->stream->read()) { // As soon as we get to the next ELEMENT node we are done. if ($this->stream->nodeType == \XMLReader::ELEMENT) { // If we are looking for a specific name make sure we have it. if (isset($name) && ($this->stream->name != $name)) { continue; } return true; } } return false; } /** * Method to move the stream parser to the closing XML node of the current element. * * @return void * * @since 3.1.4 * @throws \RuntimeException If the closing tag cannot be found. */ protected function moveToClosingElement() { // If we are on a self-closing tag then there is nothing to do. if ($this->stream->isEmptyElement) { return; } // Get the name and depth for the current node so that we can match the closing node. $name = $this->stream->name; $depth = $this->stream->depth; // Only keep looking until the end of the stream. while ($this->stream->read()) { // If we have an END_ELEMENT node with the same name and depth as the node we started with we have a bingo. :-) if (($this->stream->name == $name) && ($this->stream->depth == $depth) && ($this->stream->nodeType == \XMLReader::END_ELEMENT)) { return; } } throw new \RuntimeException('Unable to find the closing XML node.'); } } PK0A#]�.����src/Feed/FeedPerson.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Feed Person class. * * @since 3.1.4 */ class FeedPerson { /** * The email address of the person. * * @var string * @since 3.1.4 */ public $email; /** * The full name of the person. * * @var string * @since 3.1.4 */ public $name; /** * The type of person. * * @var string * @since 3.1.4 */ public $type; /** * The URI for the person. * * @var string * @since 3.1.4 */ public $uri; /** * Constructor. * * @param string $name The full name of the person. * @param string $email The email address of the person. * @param string $uri The URI for the person. * @param string $type The type of person. * * @since 3.1.4 */ public function __construct($name = null, $email = null, $uri = null, $type = null) { $this->name = $name; $this->email = $email; $this->uri = $uri; $this->type = $type; } } PK0A#]!�7F��src/Feed/FeedFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; use Joomla\CMS\Http\HttpFactory; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Feed factory class. * * @since 3.1.4 */ class FeedFactory { /** * @var array The list of registered parser classes for feeds. * @since 3.1.4 */ protected $parsers = ['rss' => 'Joomla\\CMS\\Feed\\Parser\\RssParser', 'feed' => 'Joomla\\CMS\\Feed\\Parser\\AtomParser']; /** * Method to load a URI into the feed reader for parsing. * * @param string $uri The URI of the feed to load. Idn uris must be passed already converted to punycode. * * @return Feed * * @since 3.1.4 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function getFeed($uri) { // Create the XMLReader object. $reader = new \XMLReader(); // Open the URI within the stream reader. if (!@$reader->open($uri, null, LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_NOWARNING)) { // Retry with JHttpFactory that allow using CURL and Sockets as alternative method when available // Adding a valid user agent string, otherwise some feed-servers returning an error $options = new Registry(); $options->set('userAgent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0'); try { $response = HttpFactory::getHttp($options)->get($uri); } catch (\RuntimeException $e) { throw new \RuntimeException('Unable to open the feed.', $e->getCode(), $e); } if ($response->code != 200) { throw new \RuntimeException('Unable to open the feed.'); } // Set the value to the XMLReader parser if (!$reader->XML($response->body, null, LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_NOWARNING)) { throw new \RuntimeException('Unable to parse the feed.'); } } try { // Skip ahead to the root node. while ($reader->read()) { if ($reader->nodeType == \XMLReader::ELEMENT) { break; } } } catch (\Exception $e) { throw new \RuntimeException('Error reading feed.', $e->getCode(), $e); } // Setup the appropriate feed parser for the feed. $parser = $this->_fetchFeedParser($reader->name, $reader); return $parser->parse(); } /** * Method to register a FeedParser class for a given root tag name. * * @param string $tagName The root tag name for which to register the parser class. * @param string $className The FeedParser class name to register for a root tag name. * @param boolean $overwrite True to overwrite the parser class if one is already registered. * * @return FeedFactory * * @since 3.1.4 * @throws \InvalidArgumentException */ public function registerParser($tagName, $className, $overwrite = false) { // Verify that the class exists. if (!class_exists($className)) { throw new \InvalidArgumentException('The feed parser class ' . $className . ' does not exist.'); } // Validate that the tag name is valid. if (!preg_match('/\A(?!XML)[a-z][\w0-9-]*/i', $tagName)) { throw new \InvalidArgumentException('The tag name ' . $tagName . ' is not valid.'); } // Register the given parser class for the tag name if nothing registered or the overwrite flag set. if (empty($this->parsers[$tagName]) || (bool) $overwrite) { $this->parsers[(string) $tagName] = (string) $className; } return $this; } /** * Method to get the registered Parsers * * @return array * * @since 4.0.0 */ public function getParsers() { return $this->parsers; } /** * Method to return a new JFeedParser object based on the registered parsers and a given type. * * @param string $type The name of parser to return. * @param \XMLReader $reader The XMLReader instance for the feed. * * @return FeedParser * * @since 3.1.4 * @throws \LogicException */ private function _fetchFeedParser($type, \XMLReader $reader) { // Look for a registered parser for the feed type. if (empty($this->parsers[$type])) { throw new \LogicException('No registered feed parser for type ' . $type . '.'); } return new $this->parsers[$type]($reader); } } PK0A#]J��)�)src/Feed/Feed.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Feed; use Joomla\CMS\Date\Date; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to encapsulate a feed for the Joomla Platform. * * @property FeedPerson $author Person responsible for feed content. * @property array $categories Categories to which the feed belongs. * @property array $contributors People who contributed to the feed content. * @property string $copyright Information about rights, e.g. copyrights, held in and over the feed. * @property string $description A phrase or sentence describing the feed. * @property string $generator A string indicating the program used to generate the feed. * @property FeedLink|null $image FeedLink object containing feed image properties. * @property Date $publishedDate The publication date for the feed content. * @property string $title A human readable title for the feed. * @property Date $updatedDate The last time the content of the feed changed. * @property string $uri Universal, permanent identifier for the feed. * * @since 3.1.4 */ class Feed implements \ArrayAccess, \Countable { /** * @var array The entry properties. * @since 3.1.4 */ protected $properties = [ 'uri' => '', 'title' => '', 'updatedDate' => '', 'description' => '', 'categories' => [], 'contributors' => [], ]; /** * @var array The list of feed entry objects. * @since 3.1.4 */ protected $entries = []; /** * Magic method to return values for feed properties. * * @param string $name The name of the property. * * @return mixed * * @since 3.1.4 */ public function __get($name) { return $this->properties[$name] ?? null; } /** * Magic method to set values for feed properties. * * @param string $name The name of the property. * @param mixed $value The value to set for the property. * * @return void * * @since 3.1.4 */ public function __set($name, $value) { // Ensure that setting a date always sets a Date instance. if ((($name === 'updatedDate') || ($name === 'publishedDate')) && !($value instanceof Date)) { $value = new Date($value); } // Validate that any authors that are set are instances of JFeedPerson or null. if (($name === 'author') && (!($value instanceof FeedPerson) || ($value === null))) { throw new \InvalidArgumentException( sprintf( '%1$s "author" must be an instance of Joomla\\CMS\\Feed\\FeedPerson. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } // Disallow setting categories or contributors directly. if (\in_array($name, ['categories', 'contributors'])) { throw new \InvalidArgumentException( sprintf( 'Cannot directly set %1$s property "%2$s".', \get_class($this), $name ) ); } $this->properties[$name] = $value; } /** * Method to add a category to the feed object. * * @param string $name The name of the category to add. * @param string $uri The optional URI for the category to add. * * @return Feed * * @since 3.1.4 */ public function addCategory($name, $uri = '') { $this->properties['categories'][$name] = $uri; return $this; } /** * Method to add a contributor to the feed object. * * @param string $name The full name of the person to add. * @param string $email The email address of the person to add. * @param string $uri The optional URI for the person to add. * @param string $type The optional type of person to add. * * @return Feed * * @since 3.1.4 */ public function addContributor($name, $email, $uri = null, $type = null) { $contributor = new FeedPerson($name, $email, $uri, $type); // If the new contributor already exists then there is nothing to do, so just return. foreach ($this->properties['contributors'] as $c) { if ($c == $contributor) { return $this; } } // Add the new contributor. $this->properties['contributors'][] = $contributor; return $this; } /** * Method to add an entry to the feed object. * * @param FeedEntry $entry The entry object to add. * * @return Feed * * @since 3.1.4 */ public function addEntry(FeedEntry $entry) { // If the new entry already exists then there is nothing to do, so just return. foreach ($this->entries as $e) { if ($e == $entry) { return $this; } } // Add the new entry. $this->entries[] = $entry; return $this; } /** * Returns a count of the number of entries in the feed. * * This method is here to implement the Countable interface. * You can call it by doing count($feed) rather than $feed->count(); * * @return integer number of entries in the feed. */ #[\ReturnTypeWillChange] public function count() { return \count($this->entries); } /** * Whether or not an offset exists. This method is executed when using isset() or empty() on * objects implementing ArrayAccess. * * @param mixed $offset An offset to check for. * * @return boolean * * @see ArrayAccess::offsetExists() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetExists($offset) { return isset($this->entries[$offset]); } /** * Returns the value at specified offset. * * @param mixed $offset The offset to retrieve. * * @return mixed The value at the offset. * * @see ArrayAccess::offsetGet() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->entries[$offset]; } /** * Assigns a value to the specified offset. * * @param mixed $offset The offset to assign the value to. * @param FeedEntry $value The JFeedEntry to set. * * @return boolean * * @see ArrayAccess::offsetSet() * @since 3.1.4 * @throws \InvalidArgumentException */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if (!($value instanceof FeedEntry)) { throw new \InvalidArgumentException( sprintf( '%1$s entries must be an instance of Joomla\\CMS\\Feed\\FeedPerson. %2$s given.', \get_class($this), \gettype($value) === 'object' ? \get_class($value) : \gettype($value) ) ); } $this->entries[$offset] = $value; return true; } /** * Unsets an offset. * * @param mixed $offset The offset to unset. * * @return void * * @see ArrayAccess::offsetUnset() * @since 3.1.4 */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->entries[$offset]); } /** * Method to remove a category from the feed object. * * @param string $name The name of the category to remove. * * @return Feed * * @since 3.1.4 */ public function removeCategory($name) { unset($this->properties['categories'][$name]); return $this; } /** * Method to remove a contributor from the feed object. * * @param FeedPerson $contributor The person object to remove. * * @return Feed * * @since 3.1.4 */ public function removeContributor(FeedPerson $contributor) { // If the contributor exists remove it. foreach ($this->properties['contributors'] as $k => $c) { if ($c == $contributor) { unset($this->properties['contributors'][$k]); $this->properties['contributors'] = array_values($this->properties['contributors']); return $this; } } return $this; } /** * Method to remove an entry from the feed object. * * @param FeedEntry $entry The entry object to remove. * * @return Feed * * @since 3.1.4 */ public function removeEntry(FeedEntry $entry) { // If the entry exists remove it. foreach ($this->entries as $k => $e) { if ($e == $entry) { unset($this->entries[$k]); $this->entries = array_values($this->entries); return $this; } } return $this; } /** * Shortcut method to set the author for the feed object. * * @param string $name The full name of the person to set. * @param string $email The email address of the person to set. * @param string $uri The optional URI for the person to set. * @param string $type The optional type of person to set. * * @return Feed * * @since 3.1.4 */ public function setAuthor($name, $email, $uri = null, $type = null) { $author = new FeedPerson($name, $email, $uri, $type); $this->properties['author'] = $author; return $this; } /** * Method to reverse the items if display is set to 'oldest first' * * @return Feed * * @since 3.1.4 */ public function reverseItems() { if (\is_array($this->entries) && !empty($this->entries)) { $this->entries = array_reverse($this->entries); } return $this; } } PK0A#]���x{{src/Client/ClientHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Client; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Client helper class * * @since 1.7.0 */ class ClientHelper { /** * Method to return the array of client layer configuration options * * @param string $client Client name, currently only 'ftp' is supported * @param boolean $force Forces re-creation of the login credentials. Set this to * true if login credentials in the session storage have changed * * @return array Client layer configuration options, consisting of at least * these fields: enabled, host, port, user, pass, root * * @since 1.7.0 */ public static function getCredentials($client, $force = false) { static $credentials = []; $client = strtolower($client); if (!isset($credentials[$client]) || $force) { $app = Factory::getApplication(); // Fetch the client layer configuration options for the specific client switch ($client) { case 'ftp': $options = [ 'enabled' => $app->get('ftp_enable'), 'host' => $app->get('ftp_host'), 'port' => $app->get('ftp_port'), 'user' => $app->get('ftp_user'), 'pass' => $app->get('ftp_pass'), 'root' => $app->get('ftp_root'), ]; break; default: $options = ['enabled' => false, 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'root' => '']; break; } // If user and pass are not set in global config lets see if they are in the session if ($options['enabled'] == true && ($options['user'] == '' || $options['pass'] == '')) { $session = Factory::getSession(); $options['user'] = $session->get($client . '.user', null, 'JClientHelper'); $options['pass'] = $session->get($client . '.pass', null, 'JClientHelper'); } // If user or pass are missing, disable this client if ($options['user'] == '' || $options['pass'] == '') { $options['enabled'] = false; } // Save the credentials for later use $credentials[$client] = $options; } return $credentials[$client]; } /** * Method to set client login credentials * * @param string $client Client name, currently only 'ftp' is supported * @param string $user Username * @param string $pass Password * * @return boolean True if the given login credentials have been set and are valid * * @since 1.7.0 */ public static function setCredentials($client, $user, $pass) { $return = false; $client = strtolower($client); // Test if the given credentials are valid switch ($client) { case 'ftp': $app = Factory::getApplication(); $options = ['enabled' => $app->get('ftp_enable'), 'host' => $app->get('ftp_host'), 'port' => $app->get('ftp_port')]; if ($options['enabled']) { $ftp = FtpClient::getInstance($options['host'], $options['port']); // Test the connection and try to log in if ($ftp->isConnected()) { if ($ftp->login($user, $pass)) { $return = true; } $ftp->quit(); } } break; default: break; } if ($return) { // Save valid credentials to the session $session = Factory::getSession(); $session->set($client . '.user', $user, 'JClientHelper'); $session->set($client . '.pass', $pass, 'JClientHelper'); // Force re-creation of the data saved within JClientHelper::getCredentials() self::getCredentials($client, true); } return $return; } /** * Method to determine if client login credentials are present * * @param string $client Client name, currently only 'ftp' is supported * * @return boolean True if login credentials are available * * @since 1.7.0 */ public static function hasCredentials($client) { $return = false; $client = strtolower($client); // Get (unmodified) credentials for this client switch ($client) { case 'ftp': $app = Factory::getApplication(); $options = ['enabled' => $app->get('ftp_enable'), 'user' => $app->get('ftp_user'), 'pass' => $app->get('ftp_pass')]; break; default: $options = ['enabled' => false, 'user' => '', 'pass' => '']; break; } if ($options['enabled'] == false) { // The client is disabled in global config, so let's pretend we are OK $return = true; } elseif ($options['user'] != '' && $options['pass'] != '') { // Login credentials are available in global config $return = true; } else { // Check if login credentials are available in the session $session = Factory::getSession(); $user = $session->get($client . '.user', null, 'JClientHelper'); $pass = $session->get($client . '.pass', null, 'JClientHelper'); if ($user != '' && $pass != '') { $return = true; } } return $return; } /** * Determine whether input fields for client settings need to be shown * * If valid credentials were passed along with the request, they are saved to the session. * This functions returns an exception if invalid credentials have been given or if the * connection to the server failed for some other reason. * * @param string $client The name of the client. * * @return boolean True if credentials are present * * @since 1.7.0 * @throws \InvalidArgumentException if credentials invalid */ public static function setCredentialsFromRequest($client) { // Determine whether FTP credentials have been passed along with the current request $input = Factory::getApplication()->getInput(); $user = $input->post->getString('username', null); $pass = $input->post->getString('password', null); if ($user != '' && $pass != '') { // Add credentials to the session if (!self::setCredentials($client, $user, $pass)) { throw new \InvalidArgumentException('Invalid user credentials'); } $return = false; } else { // Just determine if the FTP input fields need to be shown $return = !self::hasCredentials('ftp'); } return $return; } } PK0A#]M����src/Client/FtpClient.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Client; \defined('JPATH_PLATFORM') or die; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Utility\BufferStreamHandler; /** Error Codes: * - 30 : Unable to connect to host * - 31 : Not connected * - 32 : Unable to send command to server * - 33 : Bad username * - 34 : Bad password * - 35 : Bad response * - 36 : Passive mode failed * - 37 : Data transfer error * - 38 : Local filesystem error */ if (!\defined('CRLF')) { /** * Constant defining a line break * * @var string * @since 1.5 */ \define('CRLF', "\r\n"); } if (!\defined('FTP_AUTOASCII')) { /** * Constant defining whether the FTP connection type will automatically determine ASCII support based on a file extension * * @var integer * @since 1.5 */ \define('FTP_AUTOASCII', -1); } if (!\defined('FTP_BINARY')) { /** * Stub of the native FTP_BINARY constant if PHP is running without the ftp extension enabled * * @var integer * @since 1.5 */ \define('FTP_BINARY', 1); } if (!\defined('FTP_ASCII')) { /** * Stub of the native FTP_ASCII constant if PHP is running without the ftp extension enabled * * @var integer * @since 1.5 */ \define('FTP_ASCII', 0); } if (!\defined('FTP_NATIVE')) { /** * Constant defining whether native FTP support is available on the platform * * @var integer * @since 1.5 */ \define('FTP_NATIVE', \function_exists('ftp_connect') ? 1 : 0); } /** * FTP client class * * @since 1.5 */ class FtpClient { /** * The response code * * @var string * @since 4.3.0 */ public $_responseCode; /** * The response message * * @var string * @since 4.3.0 */ public $_responseMsg; /** * @var resource Socket resource * @since 1.5 */ protected $_conn = null; /** * @var resource Data port connection resource * @since 1.5 */ protected $_dataconn = null; /** * @var array Passive connection information * @since 1.5 */ protected $_pasv = null; /** * @var string Response Message * @since 1.5 */ protected $_response = null; /** * @var integer Timeout limit * @since 1.5 */ protected $_timeout = 15; /** * @var integer Transfer Type * @since 1.5 */ protected $_type = null; /** * @var array Array to hold ascii format file extensions * @since 1.5 */ protected $_autoAscii = [ 'asp', 'bat', 'c', 'cpp', 'csv', 'h', 'htm', 'html', 'shtml', 'ini', 'inc', 'log', 'php', 'php3', 'pl', 'perl', 'sh', 'sql', 'txt', 'xhtml', 'xml', ]; /** * Array to hold native line ending characters * * @var array * @since 1.5 */ protected $_lineEndings = ['UNIX' => "\n", 'WIN' => "\r\n"]; /** * @var array FtpClient instances container. * @since 2.5 */ protected static $instances = []; /** * FtpClient object constructor * * @param array $options Associative array of options to set * * @since 1.5 */ public function __construct(array $options = []) { // If default transfer type is not set, set it to autoascii detect if (!isset($options['type'])) { $options['type'] = FTP_BINARY; } $this->setOptions($options); if (FTP_NATIVE) { BufferStreamHandler::stream_register(); } } /** * FtpClient object destructor * * Closes an existing connection, if we have one * * @since 1.5 */ public function __destruct() { if ($this->_conn) { $this->quit(); } } /** * Returns the global FTP connector object, only creating it * if it doesn't already exist. * * You may optionally specify a username and password in the parameters. If you do so, * you may not login() again with different credentials using the same object. * If you do not use this option, you must quit() the current connection when you * are done, to free it for use by others. * * @param string $host Host to connect to * @param string $port Port to connect to * @param array $options Array with any of these options: type=>[FTP_AUTOASCII|FTP_ASCII|FTP_BINARY], timeout=>(int) * @param string $user Username to use for a connection * @param string $pass Password to use for a connection * * @return FtpClient The FTP Client object. * * @since 1.5 */ public static function getInstance($host = '127.0.0.1', $port = '21', array $options = [], $user = null, $pass = null) { $signature = $user . ':' . $pass . '@' . $host . ':' . $port; // Create a new instance, or set the options of an existing one if (!isset(static::$instances[$signature]) || !\is_object(static::$instances[$signature])) { static::$instances[$signature] = new static($options); } else { static::$instances[$signature]->setOptions($options); } // Connect to the server, and login, if requested if (!static::$instances[$signature]->isConnected()) { $return = static::$instances[$signature]->connect($host, $port); if ($return && $user !== null && $pass !== null) { static::$instances[$signature]->login($user, $pass); } } return static::$instances[$signature]; } /** * Set client options * * @param array $options Associative array of options to set * * @return boolean True if successful * * @since 1.5 */ public function setOptions(array $options) { if (isset($options['type'])) { $this->_type = $options['type']; } if (isset($options['timeout'])) { $this->_timeout = $options['timeout']; } return true; } /** * Method to connect to a FTP server * * @param string $host Host to connect to [Default: 127.0.0.1] * @param int $port Port to connect on [Default: port 21] * * @return boolean True if successful * * @since 3.0.0 */ public function connect($host = '127.0.0.1', $port = 21) { $errno = null; $err = null; // If already connected, return if ($this->_conn) { return true; } // If native FTP support is enabled let's use it... if (FTP_NATIVE) { $this->_conn = @ftp_connect($host, $port, $this->_timeout); if (!$this->_conn) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NO_CONNECT', __METHOD__, $host, $port), Log::WARNING, 'jerror'); return false; } // Set the timeout for this connection ftp_set_option($this->_conn, FTP_TIMEOUT_SEC, $this->_timeout); return true; } // Connect to the FTP server. $this->_conn = @ fsockopen($host, $port, $errno, $err, $this->_timeout); if (!$this->_conn) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NO_CONNECT_SOCKET', __METHOD__, $host, $port, $errno, $err), Log::WARNING, 'jerror'); return false; } // Set the timeout for this connection stream_set_timeout($this->_conn, $this->_timeout, 0); // Check for welcome response code if (!$this->_verifyResponse(220)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE', __METHOD__, $this->_response, 220), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to determine if the object is connected to an FTP server * * @return boolean True if connected * * @since 1.5 */ public function isConnected() { return ($this->_conn); } /** * Method to login to a server once connected * * @param string $user Username to login to the server * @param string $pass Password to login to the server * * @return boolean True if successful * * @since 1.5 */ public function login($user = 'anonymous', $pass = 'jftp@joomla.org') { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_login($this->_conn, $user, $pass) === false) { Log::add('JFtp::login: Unable to login', Log::WARNING, 'jerror'); return false; } return true; } // Send the username if (!$this->_putCmd('USER ' . $user, [331, 503])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_USERNAME', __METHOD__, $this->_response, $user), Log::WARNING, 'jerror'); return false; } // If we are already logged in, continue :) if ($this->_responseCode == 503) { return true; } // Send the password if (!$this->_putCmd('PASS ' . $pass, 230)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_PASSWORD', __METHOD__, $this->_response, str_repeat('*', \strlen($pass))), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to quit and close the connection * * @return boolean True if successful * * @since 1.5 */ public function quit() { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { @ftp_close($this->_conn); return true; } // Logout and close connection @fwrite($this->_conn, "QUIT\r\n"); @fclose($this->_conn); return true; } /** * Method to retrieve the current working directory on the FTP server * * @return string Current working directory * * @since 1.5 */ public function pwd() { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (($ret = @ftp_pwd($this->_conn)) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return $ret; } $match = [null]; // Send print working directory command and verify success if (!$this->_putCmd('PWD', 257)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE', __METHOD__, $this->_response, 257), Log::WARNING, 'jerror'); return false; } // Match just the path preg_match('/"[^"\r\n]*"/', $this->_response, $match); // Return the cleaned path return preg_replace("/\"/", '', $match[0]); } /** * Method to system string from the FTP server * * @return string System identifier string * * @since 1.5 */ public function syst() { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { if (($ret = @ftp_systype($this->_conn)) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } } else { // Send print working directory command and verify success if (!$this->_putCmd('SYST', 215)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE', __METHOD__, $this->_response, 215), Log::WARNING, 'jerror'); return false; } $ret = $this->_response; } // Match the system string to an OS if (strpos(strtoupper($ret), 'MAC') !== false) { $ret = 'MAC'; } elseif (strpos(strtoupper($ret), 'WIN') !== false) { $ret = 'WIN'; } else { $ret = 'UNIX'; } // Return the os type return $ret; } /** * Method to change the current working directory on the FTP server * * @param string $path Path to change into on the server * * @return boolean True if successful * * @since 1.5 */ public function chdir($path) { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { if (@ftp_chdir($this->_conn, $path) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } // Send change directory command and verify success if (!$this->_putCmd('CWD ' . $path, 250)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_PATH_SENT', __METHOD__, $this->_response, 250, $path), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to reinitialise the server, ie. need to login again * * NOTE: This command not available on all servers * * @return boolean True if successful * * @since 1.5 */ public function reinit() { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->_conn, 'REIN') === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } // Send reinitialise command to the server if (!$this->_putCmd('REIN', 220)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE', __METHOD__, $this->_response, 220), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to rename a file/folder on the FTP server * * @param string $from Path to change file/folder from * @param string $to Path to change file/folder to * * @return boolean True if successful * * @since 1.5 */ public function rename($from, $to) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_rename($this->_conn, $from, $to) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } // Send rename from command to the server if (!$this->_putCmd('RNFR ' . $from, 350)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_RENAME_BAD_RESPONSE_FROM', __METHOD__, $this->_response, $from), Log::WARNING, 'jerror'); return false; } // Send rename to command to the server if (!$this->_putCmd('RNTO ' . $to, 250)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_RENAME_BAD_RESPONSE_TO', __METHOD__, $this->_response, $to), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to change mode for a path on the FTP server * * @param string $path Path to change mode on * @param mixed $mode Octal value to change mode to, e.g. '0777', 0777 or 511 (string or integer) * * @return boolean True if successful * * @since 1.5 */ public function chmod($path, $mode) { // If no filename is given, we assume the current directory is the target if ($path == '') { $path = '.'; } // Convert the mode to a string if (\is_int($mode)) { $mode = decoct($mode); } // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->_conn, 'CHMOD ' . $mode . ' ' . $path) === false) { if (!IS_WIN) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); } return false; } return true; } // Send change mode command and verify success [must convert mode from octal] if (!$this->_putCmd('SITE CHMOD ' . $mode . ' ' . $path, [200, 250])) { if (!IS_WIN) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_CHMOD_BAD_RESPONSE', __METHOD__, $this->_response, $path, $mode), Log::WARNING, 'jerror'); } return false; } return true; } /** * Method to delete a path [file/folder] on the FTP server * * @param string $path Path to delete * * @return boolean True if successful * * @since 1.5 */ public function delete($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_delete($this->_conn, $path) === false) { if (@ftp_rmdir($this->_conn, $path) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } } return true; } // Send delete file command and if that doesn't work, try to remove a directory if (!$this->_putCmd('DELE ' . $path, 250)) { if (!$this->_putCmd('RMD ' . $path, 250)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_PATH_SENT', __METHOD__, $this->_response, 250, $path), Log::WARNING, 'jerror'); return false; } } return true; } /** * Method to create a directory on the FTP server * * @param string $path Directory to create * * @return boolean True if successful * * @since 1.5 */ public function mkdir($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_mkdir($this->_conn, $path) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } // Send change directory command and verify success if (!$this->_putCmd('MKD ' . $path, 257)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_PATH_SENT', __METHOD__, $this->_response, 257, $path), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to restart data transfer at a given byte * * @param integer $point Byte to restart transfer at * * @return boolean True if successful * * @since 1.5 */ public function restart($point) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->_conn, 'REST ' . $point) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } // Send restart command and verify success if (!$this->_putCmd('REST ' . $point, 350)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_RESTART_BAD_RESPONSE', __METHOD__, $this->_response, $point), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to create an empty file on the FTP server * * @param string $path Path local file to store on the FTP server * * @return boolean True if successful * * @since 1.5 */ public function create($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } $buffer = fopen('buffer://tmp', 'r'); if (@ftp_fput($this->_conn, $path, $buffer, FTP_ASCII) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); fclose($buffer); return false; } fclose($buffer); return true; } // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (!$this->_putCmd('STOR ' . $path, [150, 125])) { @ fclose($this->_dataconn); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); return false; } // To create a zero byte upload close the data port connection fclose($this->_dataconn); if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to read a file from the FTP server's contents into a buffer * * @param string $remote Path to remote file to read on the FTP server * @param string &$buffer Buffer variable to read file contents into * * @return boolean True if successful * * @since 1.5 */ public function read($remote, &$buffer) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } $tmp = fopen('buffer://tmp', 'br+'); if (@ftp_fget($this->_conn, $tmp, $remote, $mode) === false) { fclose($tmp); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } // Read tmp buffer contents rewind($tmp); $buffer = ''; while (!feof($tmp)) { $buffer .= fread($tmp, 8192); } fclose($tmp); return true; } $this->_mode($mode); // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (!$this->_putCmd('RETR ' . $remote, [150, 125])) { @ fclose($this->_dataconn); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } // Read data from data port connection and add to the buffer $buffer = ''; while (!feof($this->_dataconn)) { $buffer .= fread($this->_dataconn, 4096); } // Close the data port connection fclose($this->_dataconn); // Let's try to cleanup some line endings if it is ascii if ($mode == FTP_ASCII) { $os = 'UNIX'; if (IS_WIN) { $os = 'WIN'; } $buffer = preg_replace('/' . CRLF . '/', $this->_lineEndings[$os], $buffer); } if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to get a file from the FTP server and save it to a local file * * @param string $local Local path to save remote file to * @param string $remote Path to remote file to get on the FTP server * * @return boolean True if successful * * @since 1.5 */ public function get($local, $remote) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (@ftp_get($this->_conn, $local, $remote, $mode) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } $this->_mode($mode); // Check to see if the local file can be opened for writing $fp = fopen($local, 'wb'); if (!$fp) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_LOCAL_FILE_OPEN_WRITING', __METHOD__, $local), Log::WARNING, 'jerror'); return false; } // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (!$this->_putCmd('RETR ' . $remote, [150, 125])) { @ fclose($this->_dataconn); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } // Read data from data port connection and add to the buffer while (!feof($this->_dataconn)) { $buffer = fread($this->_dataconn, 4096); fwrite($fp, $buffer, 4096); } // Close the data port connection and file pointer fclose($this->_dataconn); fclose($fp); if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to store a file to the FTP server * * @param string $local Path to local file to store on the FTP server * @param string $remote FTP path to file to create * * @return boolean True if successful * * @since 1.5 */ public function store($local, $remote = null) { // If remote file is not given, use the filename of the local file in the current // working directory. if ($remote == null) { $remote = basename($local); } // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (@ftp_put($this->_conn, $remote, $local, $mode) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } return true; } $this->_mode($mode); // Check to see if the local file exists and if so open it for reading if (@ file_exists($local)) { $fp = fopen($local, 'rb'); if (!$fp) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_LOCAL_FILE_OPEN_READING', __METHOD__, $local), Log::WARNING, 'jerror'); return false; } } else { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_LOCAL_FILE_FIND', __METHOD__, $local), Log::WARNING, 'jerror'); return false; } // Start passive mode if (!$this->_passive()) { @ fclose($fp); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } // Send store command to the FTP server if (!$this->_putCmd('STOR ' . $remote, [150, 125])) { @ fclose($fp); @ fclose($this->_dataconn); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } // Do actual file transfer, read local file and write to data port connection while (!feof($fp)) { $line = fread($fp, 4096); do { if (($result = @ fwrite($this->_dataconn, $line)) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_DATA_PORT', __METHOD__), Log::WARNING, 'jerror'); return false; } $line = substr($line, $result); } while ($line != ''); } fclose($fp); fclose($this->_dataconn); if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to write a string to the FTP server * * @param string $remote FTP path to file to write to * @param string $buffer Contents to write to the FTP server * * @return boolean True if successful * * @since 1.5 */ public function write($remote, $buffer) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } $tmp = fopen('buffer://tmp', 'br+'); fwrite($tmp, $buffer); rewind($tmp); if (@ftp_fput($this->_conn, $remote, $tmp, $mode) === false) { fclose($tmp); Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } fclose($tmp); return true; } // First we need to set the transfer mode $this->_mode($mode); // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } // Send store command to the FTP server if (!$this->_putCmd('STOR ' . $remote, [150, 125])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); @ fclose($this->_dataconn); return false; } // Write buffer to the data connection port do { if (($result = @ fwrite($this->_dataconn, $buffer)) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_DATA_PORT', __METHOD__), Log::WARNING, 'jerror'); return false; } $buffer = substr($buffer, $result); } while ($buffer != ''); // Close the data connection port [Data transfer complete] fclose($this->_dataconn); // Verify that the server received the transfer if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $remote), Log::WARNING, 'jerror'); return false; } return true; } /** * Method to append a string to the FTP server * * @param string $remote FTP path to file to append to * @param string $buffer Contents to append to the FTP server * * @return boolean True if successful * * @since 3.6.0 */ public function append($remote, $buffer) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), 36); } $tmp = fopen('buffer://tmp', 'bw+'); fwrite($tmp, $buffer); rewind($tmp); $size = $this->size($remote); if ($size === false) { } if (@ftp_fput($this->_conn, $remote, $tmp, $mode, $size) === false) { fclose($tmp); throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), 35); } fclose($tmp); return true; } // First we need to set the transfer mode $this->_mode($mode); // Start passive mode if (!$this->_passive()) { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), 36); } // Send store command to the FTP server if (!$this->_putCmd('APPE ' . $remote, [150, 125])) { @fclose($this->_dataconn); throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $remote), 35); } // Write buffer to the data connection port do { if (($result = @ fwrite($this->_dataconn, $buffer)) === false) { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_DATA_PORT', __METHOD__), 37); } $buffer = substr($buffer, $result); } while ($buffer != ''); // Close the data connection port [Data transfer complete] fclose($this->_dataconn); // Verify that the server received the transfer if (!$this->_verifyResponse(226)) { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $remote), 37); } return true; } /** * Get the size of the remote file. * * @param string $remote FTP path to file whose size to get * * @return mixed number of bytes or false on error * * @since 3.6.0 */ public function size($remote) { if (FTP_NATIVE) { $size = ftp_size($this->_conn, $remote); // In case ftp_size fails, try the SIZE command directly. if ($size === -1) { $response = ftp_raw($this->_conn, 'SIZE ' . $remote); $responseCode = substr($response[0], 0, 3); $responseMessage = substr($response[0], 4); if ($responseCode != '213') { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), 35); } $size = (int) $responseMessage; } return $size; } // Start passive mode if (!$this->_passive()) { throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), 36); } // Send size command to the FTP server if (!$this->_putCmd('SIZE ' . $remote, [213])) { @fclose($this->_dataconn); throw new \RuntimeException(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_PATH_SENT', __METHOD__, $this->_response, 213, $remote), 35); } return (int) substr($this->_responseMsg, 4); } /** * Method to list the filenames of the contents of a directory on the FTP server * * Note: Some servers also return folder names. However, to be sure to list folders on all * servers, you should use listDetails() instead if you also need to deal with folders * * @param string $path Path local file to store on the FTP server * * @return string Directory listing * * @since 1.5 */ public function listNames($path = null) { $data = null; // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (($list = @ftp_nlist($this->_conn, $path)) === false) { // Workaround for empty directories on some servers if ($this->listDetails($path, 'files') === []) { return []; } Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } $list = preg_replace('#^' . preg_quote($path, '#') . '[/\\\\]?#', '', $list); if ($keys = array_merge(array_keys($list, '.'), array_keys($list, '..'))) { foreach ($keys as $key) { unset($list[$key]); } } return $list; } // If a path exists, prepend a space if ($path != null) { $path = ' ' . $path; } // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (!$this->_putCmd('NLST' . $path, [150, 125])) { @ fclose($this->_dataconn); // Workaround for empty directories on some servers if ($this->listDetails($path, 'files') === []) { return []; } Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); return false; } // Read in the file listing. while (!feof($this->_dataconn)) { $data .= fread($this->_dataconn, 4096); } fclose($this->_dataconn); // Everything go okay? if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); return false; } $data = preg_split('/[' . CRLF . ']+/', $data, -1, PREG_SPLIT_NO_EMPTY); $data = preg_replace('#^' . preg_quote(substr($path, 1), '#') . '[/\\\\]?#', '', $data); if ($keys = array_merge(array_keys($data, '.'), array_keys($data, '..'))) { foreach ($keys as $key) { unset($data[$key]); } } return $data; } /** * Method to list the contents of a directory on the FTP server * * @param string $path Path to the local file to be stored on the FTP server * @param string $type Return type [raw|all|folders|files] * * @return mixed If $type is raw: string Directory listing, otherwise array of string with file-names * * @since 1.5 */ public function listDetails($path = null, $type = 'all') { $dir_list = []; $data = null; $regs = null; // @todo: Deal with recurse -- nightmare // For now we will just set it to false $recurse = false; // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->_conn, true) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } if (($contents = @ftp_rawlist($this->_conn, $path)) === false) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_BAD_RESPONSE', __METHOD__), Log::WARNING, 'jerror'); return false; } } else { // Non Native mode // Start passive mode if (!$this->_passive()) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE', __METHOD__), Log::WARNING, 'jerror'); return false; } // If a path exists, prepend a space if ($path != null) { $path = ' ' . $path; } // Request the file listing if (!$this->_putCmd(($recurse == true) ? 'LIST -R' : 'LIST' . $path, [150, 125])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NOT_EXPECTED_RESPONSE_150_125', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); @ fclose($this->_dataconn); return false; } // Read in the file listing. while (!feof($this->_dataconn)) { $data .= fread($this->_dataconn, 4096); } fclose($this->_dataconn); // Everything go okay? if (!$this->_verifyResponse(226)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TRANSFER_FAILED', __METHOD__, $this->_response, $path), Log::WARNING, 'jerror'); return false; } $contents = explode(CRLF, $data); } // If only raw output is requested we are done if ($type === 'raw') { return $data; } // If we received the listing of an empty directory, we are done as well if (empty($contents[0])) { return $dir_list; } // If the server returned the number of results in the first response, let's dump it if (strtolower(substr($contents[0], 0, 6)) === 'total ') { array_shift($contents); if (!isset($contents[0]) || empty($contents[0])) { return $dir_list; } } // Regular expressions for the directory listing parsing. $regexps = [ 'UNIX' => '#([-dl][rwxstST-]+).* ([0-9]*) ([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)' . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{1,2}:[0-9]{2})|[0-9]{4}) (.+)#', 'MAC' => '#([-dl][rwxstST-]+).* ?([0-9 ]*)?([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)' . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{2}:[0-9]{2})|[0-9]{4}) (.+)#', 'WIN' => '#([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)#', ]; // Find out the format of the directory listing by matching one of the regexps $osType = null; foreach ($regexps as $k => $v) { if (@preg_match($v, $contents[0])) { $osType = $k; $regexp = $v; break; } } if (!$osType) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_UNRECOGNISED_FOLDER_LISTING_FORMATJLIB_CLIENT_ERROR_JFTP_LISTDETAILS_UNRECOGNISED', __METHOD__), Log::WARNING, 'jerror'); return false; } // Here is where it is going to get dirty.... if ($osType === 'UNIX' || $osType === 'MAC') { foreach ($contents as $file) { $tmp_array = []; if (@preg_match($regexp, $file, $regs)) { $fType = (int) strpos('-dl', $regs[1][0]); // $tmp_array['line'] = $regs[0]; $tmp_array['type'] = $fType; $tmp_array['rights'] = $regs[1]; // $tmp_array['number'] = $regs[2]; $tmp_array['user'] = $regs[3]; $tmp_array['group'] = $regs[4]; $tmp_array['size'] = $regs[5]; $tmp_array['date'] = @date('m-d', strtotime($regs[6])); $tmp_array['time'] = $regs[7]; $tmp_array['name'] = $regs[9]; } // If we just want files, do not add a folder if ($type === 'files' && $tmp_array['type'] == 1) { continue; } // If we just want folders, do not add a file if ($type === 'folders' && $tmp_array['type'] == 0) { continue; } if (\count($tmp_array) && $tmp_array['name'] != '.' && $tmp_array['name'] != '..') { $dir_list[] = $tmp_array; } } } else { foreach ($contents as $file) { $tmp_array = []; if (@preg_match($regexp, $file, $regs)) { $fType = (int) ($regs[7] === '<DIR>'); $timestamp = strtotime("$regs[3]-$regs[1]-$regs[2] $regs[4]:$regs[5]$regs[6]"); // $tmp_array['line'] = $regs[0]; $tmp_array['type'] = $fType; $tmp_array['rights'] = ''; // $tmp_array['number'] = 0; $tmp_array['user'] = ''; $tmp_array['group'] = ''; $tmp_array['size'] = (int) $regs[7]; $tmp_array['date'] = date('m-d', $timestamp); $tmp_array['time'] = date('H:i', $timestamp); $tmp_array['name'] = $regs[8]; } // If we just want files, do not add a folder if ($type === 'files' && $tmp_array['type'] == 1) { continue; } // If we just want folders, do not add a file if ($type === 'folders' && $tmp_array['type'] == 0) { continue; } if (\count($tmp_array) && $tmp_array['name'] != '.' && $tmp_array['name'] != '..') { $dir_list[] = $tmp_array; } } } return $dir_list; } /** * Send command to the FTP server and validate an expected response code * * @param string $cmd Command to send to the FTP server * @param mixed $expectedResponse Integer response code or array of integer response codes * * @return boolean True if command executed successfully * * @since 1.5 */ protected function _putCmd($cmd, $expectedResponse) { // Make sure we have a connection to the server if (!$this->_conn) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PUTCMD_UNCONNECTED', __METHOD__), Log::WARNING, 'jerror'); return false; } // Send the command to the server if (!fwrite($this->_conn, $cmd . "\r\n")) { Log::add(Text::sprintf('DDD', Text::sprintf('JLIB_CLIENT_ERROR_FTP_PUTCMD_SEND', __METHOD__, $cmd)), Log::WARNING, 'jerror'); } return $this->_verifyResponse($expectedResponse); } /** * Verify the response code from the server and log response if flag is set * * @param mixed $expected Integer response code or array of integer response codes * * @return boolean True if response code from the server is expected * * @since 1.5 */ protected function _verifyResponse($expected) { $parts = null; // Wait for a response from the server, but timeout after the set time limit $endTime = time() + $this->_timeout; $this->_response = ''; do { $this->_response .= fgets($this->_conn, 4096); } while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\1)? [^' . CRLF . ']+' . CRLF . "$/", $this->_response, $parts) && time() < $endTime); // Catch a timeout or bad response if (!isset($parts[1])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TIMEOUT', __METHOD__, $this->_response), Log::WARNING, 'jerror'); return false; } // Separate the code from the message $this->_responseCode = $parts[1]; $this->_responseMsg = $parts[0]; // Did the server respond with the code we wanted? if (\is_array($expected)) { if (\in_array($this->_responseCode, $expected)) { $retval = true; } else { $retval = false; } } else { if ($this->_responseCode == $expected) { $retval = true; } else { $retval = false; } } return $retval; } /** * Set server to passive mode and open a data port connection * * @return boolean True if successful * * @since 1.5 */ protected function _passive() { $match = []; $parts = []; $errno = null; $err = null; // Make sure we have a connection to the server if (!$this->_conn) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_NO_CONNECT', __METHOD__), Log::WARNING, 'jerror'); return false; } // Request a passive connection - this means, we'll talk to you, you don't talk to us. @ fwrite($this->_conn, "PASV\r\n"); // Wait for a response from the server, but timeout after the set time limit $endTime = time() + $this->_timeout; $this->_response = ''; do { $this->_response .= fgets($this->_conn, 4096); } while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\1)? [^' . CRLF . ']+' . CRLF . "$/", $this->_response, $parts) && time() < $endTime); // Catch a timeout or bad response if (!isset($parts[1])) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_TIMEOUT', __METHOD__, $this->_response), Log::WARNING, 'jerror'); return false; } // Separate the code from the message $this->_responseCode = $parts[1]; $this->_responseMsg = $parts[0]; // If it's not 227, we weren't given an IP and port, which means it failed. if ($this->_responseCode != '227') { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE_IP_OBTAIN', __METHOD__, $this->_responseMsg), Log::WARNING, 'jerror'); return false; } // Snatch the IP and port information, or die horribly trying... if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $this->_responseMsg, $match) == 0) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_PASSIVE_IP_VALID', __METHOD__, $this->_responseMsg), Log::WARNING, 'jerror'); return false; } // This is pretty simple - store it for later use ;). $this->_pasv = ['ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]]; // Connect, assuming we've got a connection. $this->_dataconn = @fsockopen($this->_pasv['ip'], $this->_pasv['port'], $errno, $err, $this->_timeout); if (!$this->_dataconn) { Log::add( Text::sprintf('JLIB_CLIENT_ERROR_FTP_NO_CONNECT', __METHOD__, $this->_pasv['ip'], $this->_pasv['port'], $errno, $err), Log::WARNING, 'jerror' ); return false; } // Set the timeout for this connection stream_set_timeout($this->_conn, $this->_timeout, 0); return true; } /** * Method to find out the correct transfer mode for a specific file * * @param string $fileName Name of the file * * @return integer Transfer-mode for this filetype [FTP_ASCII|FTP_BINARY] * * @since 1.5 */ protected function _findMode($fileName) { if ($this->_type == FTP_AUTOASCII) { $dot = strrpos($fileName, '.') + 1; $ext = substr($fileName, $dot); if (\in_array($ext, $this->_autoAscii)) { $mode = FTP_ASCII; } else { $mode = FTP_BINARY; } } elseif ($this->_type == FTP_ASCII) { $mode = FTP_ASCII; } else { $mode = FTP_BINARY; } return $mode; } /** * Set transfer mode * * @param integer $mode Integer representation of data transfer mode [1:Binary|0:Ascii] * Defined constants can also be used [FTP_BINARY|FTP_ASCII] * * @return boolean True if successful * * @since 1.5 */ protected function _mode($mode) { if ($mode == FTP_BINARY) { if (!$this->_putCmd('TYPE I', 200)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_MODE_BINARY', __METHOD__, $this->_response), Log::WARNING, 'jerror'); return false; } } else { if (!$this->_putCmd('TYPE A', 200)) { Log::add(Text::sprintf('JLIB_CLIENT_ERROR_FTP_MODE_ASCII', __METHOD__, $this->_response), Log::WARNING, 'jerror'); return false; } } return true; } } PK0A#]0���3�3(src/Updater/Adapter/ExtensionAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; use Joomla\CMS\Updater\UpdateAdapter; use Joomla\CMS\Updater\Updater; use Joomla\CMS\Version; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extension class for updater * * @since 1.7.0 */ class ExtensionAdapter extends UpdateAdapter { /** * Start element parser callback. * * @param object $parser The parser object. * @param string $name The name of the element. * @param array $attrs The attributes of the element. * * @return void * * @since 1.7.0 */ protected function _startElement($parser, $name, $attrs = []) { $this->stack[] = $name; $tag = $this->_getStackLocation(); // Reset the data if (isset($this->$tag)) { $this->$tag->_data = ''; } switch ($name) { case 'UPDATE': $this->currentUpdate = Table::getInstance('update'); $this->currentUpdate->update_site_id = $this->updateSiteId; $this->currentUpdate->detailsurl = $this->_url; $this->currentUpdate->folder = ''; $this->currentUpdate->client_id = 1; $this->currentUpdate->infourl = ''; break; // Don't do anything case 'UPDATES': break; default: if (\in_array($name, $this->updatecols)) { $name = strtolower($name); $this->currentUpdate->$name = ''; } if ($name === 'TARGETPLATFORM') { $this->currentUpdate->targetplatform = $attrs; } if ($name === 'PHP_MINIMUM') { $this->currentUpdate->php_minimum = ''; } if ($name === 'SUPPORTED_DATABASES') { $this->currentUpdate->supported_databases = $attrs; } break; } } /** * Character Parser Function * * @param object $parser Parser object. * @param object $name The name of the element. * * @return void * * @since 1.7.0 */ protected function _endElement($parser, $name) { array_pop($this->stack); switch ($name) { case 'UPDATE': // Lower case and remove the exclamation mark $product = strtolower(InputFilter::getInstance()->clean(Version::PRODUCT, 'cmd')); // Check that the product matches and that the version matches (optionally a regexp) if ( $product == $this->currentUpdate->targetplatform['NAME'] && preg_match('/^' . $this->currentUpdate->targetplatform['VERSION'] . '/', JVERSION) ) { // Check if PHP version supported via <php_minimum> tag, assume true if tag isn't present if (!isset($this->currentUpdate->php_minimum) || version_compare(PHP_VERSION, $this->currentUpdate->php_minimum, '>=')) { $phpMatch = true; } else { // Notify the user of the potential update $msg = Text::sprintf( 'JLIB_INSTALLER_AVAILABLE_UPDATE_PHP_VERSION', $this->currentUpdate->name, $this->currentUpdate->version, $this->currentUpdate->php_minimum, PHP_VERSION ); Factory::getApplication()->enqueueMessage($msg, 'warning'); $phpMatch = false; } $dbMatch = false; // Check if DB & version is supported via <supported_databases> tag, assume supported if tag isn't present if (isset($this->currentUpdate->supported_databases)) { $db = Factory::getDbo(); $dbType = strtolower($db->getServerType()); $dbVersion = $db->getVersion(); $supportedDbs = $this->currentUpdate->supported_databases; // MySQL and MariaDB use the same database driver but not the same version numbers if ($dbType === 'mysql') { // Check whether we have a MariaDB version string and extract the proper version from it if (stripos($dbVersion, 'mariadb') !== false) { // MariaDB: Strip off any leading '5.5.5-', if present $dbVersion = preg_replace('/^5\.5\.5-/', '', $dbVersion); $dbType = 'mariadb'; } } // $supportedDbs has uppercase keys because they are XML attribute names $dbTypeUcase = strtoupper($dbType); // Do we have an entry for the database? if (\array_key_exists($dbTypeUcase, $supportedDbs)) { $minimumVersion = $supportedDbs[$dbTypeUcase]; $dbMatch = version_compare($dbVersion, $minimumVersion, '>='); if (!$dbMatch) { // Notify the user of the potential update $dbMsg = Text::sprintf( 'JLIB_INSTALLER_AVAILABLE_UPDATE_DB_MINIMUM', $this->currentUpdate->name, $this->currentUpdate->version, Text::_('JLIB_DB_SERVER_TYPE_' . $dbTypeUcase), $dbVersion, $minimumVersion ); Factory::getApplication()->enqueueMessage($dbMsg, 'warning'); } } else { // Notify the user of the potential update $dbMsg = Text::sprintf( 'JLIB_INSTALLER_AVAILABLE_UPDATE_DB_TYPE', $this->currentUpdate->name, $this->currentUpdate->version, Text::_('JLIB_DB_SERVER_TYPE_' . $dbTypeUcase) ); Factory::getApplication()->enqueueMessage($dbMsg, 'warning'); } } else { // Set to true if the <supported_databases> tag is not set $dbMatch = true; } // Check minimum stability $stabilityMatch = true; if (isset($this->currentUpdate->stability) && ($this->currentUpdate->stability < $this->minimum_stability)) { $stabilityMatch = false; } // Some properties aren't valid fields in the update table so unset them to prevent J! from trying to store them unset($this->currentUpdate->targetplatform); if (isset($this->currentUpdate->php_minimum)) { unset($this->currentUpdate->php_minimum); } if (isset($this->currentUpdate->supported_databases)) { unset($this->currentUpdate->supported_databases); } if (isset($this->currentUpdate->stability)) { unset($this->currentUpdate->stability); } // If the PHP version and minimum stability checks pass, consider this version as a possible update if ($phpMatch && $stabilityMatch && $dbMatch) { if (isset($this->latest)) { // We already have a possible update. Check the version. if (version_compare($this->currentUpdate->version, $this->latest->version, '>') == 1) { $this->latest = $this->currentUpdate; } } else { // We don't have any possible updates yet, assume this is an available update. $this->latest = $this->currentUpdate; } } } break; case 'UPDATES': // :D break; } } /** * Character Parser Function * * @param object $parser Parser object. * @param object $data The data. * * @return void * * @note This is public because its called externally. * @since 1.7.0 */ protected function _characterData($parser, $data) { $tag = $this->_getLastTag(); if (\in_array($tag, $this->updatecols)) { $tag = strtolower($tag); $this->currentUpdate->$tag .= $data; } if ($tag === 'PHP_MINIMUM') { $this->currentUpdate->php_minimum = $data; } if ($tag === 'TAG') { $this->currentUpdate->stability = $this->stabilityTagToInteger((string) $data); } } /** * Finds an update. * * @param array $options Update options. * * @return array|boolean Array containing the array of update sites and array of updates. False on failure * * @since 1.7.0 */ public function findUpdate($options) { $response = $this->getUpdateSiteResponse($options); if ($response === false) { return false; } if (\array_key_exists('minimum_stability', $options)) { $this->minimum_stability = $options['minimum_stability']; } $this->xmlParser = xml_parser_create(''); xml_set_object($this->xmlParser, $this); xml_set_element_handler($this->xmlParser, '_startElement', '_endElement'); xml_set_character_data_handler($this->xmlParser, '_characterData'); if (!xml_parse($this->xmlParser, $response->body)) { // If the URL is missing the .xml extension, try appending it and retry loading the update if (!$this->appendExtension && (substr($this->_url, -4) !== '.xml')) { $options['append_extension'] = true; return $this->findUpdate($options); } $app = Factory::getApplication(); $app->getLogger()->warning("Error parsing url: {$this->_url}", ['category' => 'updater']); $app->enqueueMessage(Text::sprintf('JLIB_UPDATER_ERROR_EXTENSION_PARSE_URL', $this->_url), 'warning'); return false; } xml_parser_free($this->xmlParser); if (isset($this->latest)) { if (isset($this->latest->client) && \strlen($this->latest->client)) { /** * The client_id in the update XML manifest can be either an integer (backwards * compatible with Joomla 1.6–3.10) or a string. Backwards compatibility with the * integer key is provided as update servers with the legacy, numeric IDs cause PHP notices * during update retrieval. The proper string key is one of 'site' or 'administrator'. */ $this->latest->client_id = is_numeric($this->latest->client) ? $this->latest->client : ApplicationHelper::getClientInfo($this->latest->client, true)->id; unset($this->latest->client); } $updates = [$this->latest]; } else { $updates = []; } return ['update_sites' => [], 'updates' => $updates]; } /** * Converts a tag to numeric stability representation. If the tag doesn't represent a known stability level (one of * dev, alpha, beta, rc, stable) it is ignored. * * @param string $tag The tag string, e.g. dev, alpha, beta, rc, stable * * @return integer * * @since 3.4 */ protected function stabilityTagToInteger($tag) { $constant = '\\Joomla\\CMS\\Updater\\Updater::STABILITY_' . strtoupper($tag); if (\defined($constant)) { return \constant($constant); } return Updater::STABILITY_STABLE; } } PK0A#]���LL)src/Updater/Adapter/CollectionAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; use Joomla\CMS\Updater\UpdateAdapter; use Joomla\CMS\Version; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Collection Update Adapter Class * * @since 1.7.0 */ class CollectionAdapter extends UpdateAdapter { /** * Root of the tree * * @var object * @since 1.7.0 */ protected $base; /** * Tree of objects * * @var array * @since 1.7.0 */ protected $parent = [0]; /** * Used to control if an item has a child or not * * @var integer * @since 1.7.0 */ protected $pop_parent = 0; /** * A list of discovered update sites * * @var array */ protected $update_sites = []; /** * A list of discovered updates * * @var array */ protected $updates = []; /** * Gets the reference to the current direct parent * * @return string * * @since 1.7.0 */ protected function _getStackLocation() { return implode('->', $this->stack); } /** * Get the parent tag * * @return string parent * * @since 1.7.0 */ protected function _getParent() { return end($this->parent); } /** * Opening an XML element * * @param object $parser Parser object * @param string $name Name of element that is opened * @param array $attrs Array of attributes for the element * * @return void * * @since 1.7.0 */ public function _startElement($parser, $name, $attrs = []) { $this->stack[] = $name; $tag = $this->_getStackLocation(); // Reset the data if (isset($this->$tag)) { $this->$tag->_data = ''; } switch ($name) { case 'CATEGORY': if (isset($attrs['REF'])) { $this->update_sites[] = ['type' => 'collection', 'location' => $attrs['REF'], 'update_site_id' => $this->updateSiteId]; } else { // This item will have children, so prepare to attach them $this->pop_parent = 1; } break; case 'EXTENSION': $update = Table::getInstance('update'); $update->set('update_site_id', $this->updateSiteId); foreach ($this->updatecols as $col) { // Reset the values if it doesn't exist if (!\array_key_exists($col, $attrs)) { $attrs[$col] = ''; if ($col === 'CLIENT') { $attrs[$col] = 'site'; } } } $client = ApplicationHelper::getClientInfo($attrs['CLIENT'], 1); if (isset($client->id)) { $attrs['CLIENT_ID'] = $client->id; } $values = []; // Lower case all of the fields foreach ($attrs as $key => $attr) { $values[strtolower($key)] = $attr; } // Only add the update if it is on the same platform and release as we are $ver = new Version(); // Lower case and remove the exclamation mark $product = strtolower(InputFilter::getInstance()->clean($ver::PRODUCT, 'cmd')); /* * Set defaults, the extension file should clarify in case but it may be only available in one version * This allows an update site to specify a targetplatform * targetplatformversion can be a regexp, so 1.[56] would be valid for an extension that supports 1.5 and 1.6 * Note: Whilst the version is a regexp here, the targetplatform is not (new extension per platform) * Additionally, the version is a regexp here and it may also be in an extension file if the extension is * compatible against multiple versions of the same platform (e.g. a library) */ if (!isset($values['targetplatform'])) { $values['targetplatform'] = $product; } // Set this to ourself as a default if (!isset($values['targetplatformversion'])) { $values['targetplatformversion'] = $ver::MAJOR_VERSION . '.' . $ver::MINOR_VERSION; } // Set this to ourselves as a default // validate that we can install the extension if ($product == $values['targetplatform'] && preg_match('/^' . $values['targetplatformversion'] . '/', JVERSION)) { $update->bind($values); $this->updates[] = $update; } break; } } /** * Closing an XML element * Note: This is a protected function though has to be exposed externally as a callback * * @param object $parser Parser object * @param string $name Name of the element closing * * @return void * * @since 1.7.0 */ protected function _endElement($parser, $name) { array_pop($this->stack); if ($name === 'CATEGORY' && $this->pop_parent) { $this->pop_parent = 0; array_pop($this->parent); } } // Note: we don't care about char data in collection because there should be none /** * Finds an update * * @param array $options Options to use: update_site_id: the unique ID of the update site to look at * * @return array|boolean Update_sites and updates discovered. False on failure * * @since 1.7.0 */ public function findUpdate($options) { $response = $this->getUpdateSiteResponse($options); if ($response === false) { return false; } $this->xmlParser = xml_parser_create(''); xml_set_object($this->xmlParser, $this); xml_set_element_handler($this->xmlParser, '_startElement', '_endElement'); if (!xml_parse($this->xmlParser, $response->body)) { // If the URL is missing the .xml extension, try appending it and retry loading the update if (!$this->appendExtension && (substr($this->_url, -4) !== '.xml')) { $options['append_extension'] = true; return $this->findUpdate($options); } $app = Factory::getApplication(); $app->getLogger()->warning("Error parsing url: {$this->_url}", ['category' => 'updater']); $app->enqueueMessage(Text::sprintf('JLIB_UPDATER_ERROR_COLLECTION_PARSE_URL', $this->_url), 'warning'); return false; } // @todo: Decrement the bad counter if non-zero return ['update_sites' => $this->update_sites, 'updates' => $this->updates]; } } PK0A#]�� ��"�"src/Updater/UpdateAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater; use Joomla\CMS\Adapter\AdapterInstance; use Joomla\CMS\Factory; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Version; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * UpdateAdapter class. * * @since 1.7.0 */ abstract class UpdateAdapter extends AdapterInstance { /** * Resource handle for the XML Parser * * @var resource * @since 3.0.0 */ protected $xmlParser; /** * Element call stack * * @var array * @since 3.0.0 */ protected $stack = ['base']; /** * ID of update site * * @var integer * @since 3.0.0 */ protected $updateSiteId = 0; /** * Columns in the extensions table to be updated * * @var array * @since 3.0.0 */ protected $updatecols = ['NAME', 'ELEMENT', 'TYPE', 'FOLDER', 'CLIENT', 'VERSION', 'DESCRIPTION', 'INFOURL', 'CHANGELOGURL', 'EXTRA_QUERY']; /** * Should we try appending a .xml extension to the update site's URL? * * @var boolean */ protected $appendExtension = false; /** * The name of the update site (used in logging) * * @var string */ protected $updateSiteName = ''; /** * The update site URL from which we will get the update information * * @var string */ protected $_url = ''; /** * The minimum stability required for updates to be taken into account. The possible values are: * 0 dev Development snapshots, nightly builds, pre-release versions and so on * 1 alpha Alpha versions (work in progress, things are likely to be broken) * 2 beta Beta versions (major functionality in place, show-stopper bugs are likely to be present) * 3 rc Release Candidate versions (almost stable, minor bugs might be present) * 4 stable Stable versions (production quality code) * * @var integer * @since 14.1 * * @see Updater */ protected $minimum_stability = Updater::STABILITY_STABLE; /** * Gets the reference to the current direct parent * * @return string * * @since 1.7.0 */ protected function _getStackLocation() { return implode('->', $this->stack); } /** * Gets the reference to the last tag * * @return object * * @since 1.7.0 */ protected function _getLastTag() { return $this->stack[\count($this->stack) - 1]; } /** * Finds an update * * @param array $options Options to use: update_site_id: the unique ID of the update site to look at * * @return array Update_sites and updates discovered * * @since 1.7.0 */ abstract public function findUpdate($options); /** * Toggles the enabled status of an update site. Update sites are disabled before getting the update information * from their URL and enabled afterwards. If the URL fetch fails with a PHP fatal error (e.g. timeout) the faulty * update site will remain disabled the next time we attempt to load the update information. * * @param int $updateSiteId The numeric ID of the update site to enable/disable * @param bool $enabled Enable the site when true, disable it when false * * @return void */ protected function toggleUpdateSite($updateSiteId, $enabled = true) { $updateSiteId = (int) $updateSiteId; $enabled = (bool) $enabled ? 1 : 0; if (empty($updateSiteId)) { return; } $db = $this->parent->getDbo(); $query = $db->getQuery(true) ->update($db->quoteName('#__update_sites')) ->set($db->quoteName('enabled') . ' = :enabled') ->where($db->quoteName('update_site_id') . ' = :id') ->bind(':enabled', $enabled, ParameterType::INTEGER) ->bind(':id', $updateSiteId, ParameterType::INTEGER); $db->setQuery($query); try { $db->execute(); } catch (\RuntimeException $e) { // Do nothing } } /** * Get the name of an update site. This is used in logging. * * @param int $updateSiteId The numeric ID of the update site * * @return string The name of the update site or an empty string if it's not found */ protected function getUpdateSiteName($updateSiteId) { $updateSiteId = (int) $updateSiteId; if (empty($updateSiteId)) { return ''; } $db = $this->parent->getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('name')) ->from($db->quoteName('#__update_sites')) ->where($db->quoteName('update_site_id') . ' = :id') ->bind(':id', $updateSiteId, ParameterType::INTEGER); $db->setQuery($query); $name = ''; try { $name = $db->loadResult(); } catch (\RuntimeException $e) { // Do nothing } return $name; } /** * Try to get the raw HTTP response from the update site, hopefully containing the update XML. * * @param array $options The update options, see findUpdate() in children classes * * @return \Joomla\CMS\Http\Response|bool False if we can't connect to the site, HTTP Response object otherwise * * @throws \Exception */ protected function getUpdateSiteResponse($options = []) { $url = trim($options['location']); $this->_url = &$url; $this->updateSiteId = $options['update_site_id']; if (!isset($options['update_site_name'])) { $options['update_site_name'] = $this->getUpdateSiteName($this->updateSiteId); } $this->updateSiteName = $options['update_site_name']; $this->appendExtension = false; if (\array_key_exists('append_extension', $options)) { $this->appendExtension = $options['append_extension']; } if ($this->appendExtension && (substr($url, -4) !== '.xml')) { if (substr($url, -1) !== '/') { $url .= '/'; } $url .= 'extension.xml'; } // Disable the update site. If the get() below fails with a fatal error (e.g. timeout) the faulty update // site will remain disabled $this->toggleUpdateSite($this->updateSiteId, false); $startTime = microtime(true); $version = new Version(); $httpOption = new Registry(); $httpOption->set('userAgent', $version->getUserAgent('Joomla', true, false)); // JHttp transport throws an exception when there's no response. try { $http = HttpFactory::getHttp($httpOption); $response = $http->get($url, [], 20); } catch (\RuntimeException $e) { $response = null; } // Enable the update site. Since the get() returned the update site should remain enabled $this->toggleUpdateSite($this->updateSiteId, true); // Log the time it took to load this update site's information $endTime = microtime(true); $timeToLoad = sprintf('%0.2f', $endTime - $startTime); Log::add( "Loading information from update site #{$this->updateSiteId} with name " . "\"$this->updateSiteName\" and URL $url took $timeToLoad seconds", Log::INFO, 'updater' ); if ($response === null || $response->code !== 200) { // If the URL is missing the .xml extension, try appending it and retry loading the update if (!$this->appendExtension && (substr($url, -4) !== '.xml')) { $options['append_extension'] = true; return $this->getUpdateSiteResponse($options); } // Log the exact update site name and URL which could not be loaded Log::add('Error opening url: ' . $url . ' for update site: ' . $this->updateSiteName, Log::WARNING, 'updater'); $app = Factory::getApplication(); $app->enqueueMessage( html_entity_decode(Text::sprintf('JLIB_UPDATER_ERROR_OPEN_UPDATE_SITE', $this->updateSiteId, $this->updateSiteName, $url)), 'warning' ); return false; } return $response; } } PK0A#]�0��PAPAsrc/Updater/Update.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Version; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Update class. It is used by Updater::update() to install an update. Use Updater::findUpdates() to find updates for * an extension. * * @since 1.7.0 */ class Update extends CMSObject { /** * Update manifest `<name>` element * * @var string * @since 1.7.0 */ protected $name; /** * Update manifest `<description>` element * * @var string * @since 1.7.0 */ protected $description; /** * Update manifest `<element>` element * * @var string * @since 1.7.0 */ protected $element; /** * Update manifest `<type>` element * * @var string * @since 1.7.0 */ protected $type; /** * Update manifest `<version>` element * * @var string * @since 1.7.0 */ protected $version; /** * Update manifest `<infourl>` element * * @var string * @since 1.7.0 */ protected $infourl; /** * Update manifest `<client>` element * * @var string * @since 1.7.0 */ protected $client; /** * Update manifest `<group>` element * * @var string * @since 1.7.0 */ protected $group; /** * Update manifest `<downloads>` element * * @var string * @since 1.7.0 */ protected $downloads; /** * Update manifest `<downloadsource>` elements * * @var DownloadSource[] * @since 3.8.3 */ protected $downloadSources = []; /** * Update manifest `<tags>` element * * @var string * @since 1.7.0 */ protected $tags; /** * Update manifest `<maintainer>` element * * @var string * @since 1.7.0 */ protected $maintainer; /** * Update manifest `<maintainerurl>` element * * @var string * @since 1.7.0 */ protected $maintainerurl; /** * Update manifest `<category>` element * * @var string * @since 1.7.0 */ protected $category; /** * Update manifest `<relationships>` element * * @var string * @since 1.7.0 */ protected $relationships; /** * Update manifest `<targetplatform>` element * * @var string * @since 1.7.0 */ protected $targetplatform; /** * Extra query for download URLs * * @var string * @since 3.2.0 */ protected $extra_query; /** * Resource handle for the XML Parser * * @var resource * @since 3.0.0 */ protected $xmlParser; /** * Element call stack * * @var array * @since 3.0.0 */ protected $stack = ['base']; /** * Unused state array * * @var array * @since 3.0.0 */ protected $stateStore = []; /** * Object containing the current update data * * @var \stdClass * @since 3.0.0 */ protected $currentUpdate; /** * Object containing the latest update data which meets the PHP and DB version requirements * * @var \stdClass * @since 3.0.0 */ protected $latest; /** * Object containing details if the latest update does not meet the PHP and DB version requirements * * @var \stdClass * @since 4.4.2 */ protected $otherUpdateInfo; /** * The minimum stability required for updates to be taken into account. The possible values are: * 0 dev Development snapshots, nightly builds, pre-release versions and so on * 1 alpha Alpha versions (work in progress, things are likely to be broken) * 2 beta Beta versions (major functionality in place, show-stopper bugs are likely to be present) * 3 rc Release Candidate versions (almost stable, minor bugs might be present) * 4 stable Stable versions (production quality code) * * @var integer * @since 14.1 * * @see Updater */ protected $minimum_stability = Updater::STABILITY_STABLE; /** * Array with compatible versions used by the pre-update check * * @var array * @since 3.10.2 */ protected $compatibleVersions = []; /** * Gets the reference to the current direct parent * * @return string * * @since 1.7.0 */ protected function _getStackLocation() { return implode('->', $this->stack); } /** * Get the last position in stack count * * @return string * * @since 1.7.0 */ protected function _getLastTag() { return $this->stack[\count($this->stack) - 1]; } /** * XML Start Element callback * * @param object $parser Parser object * @param string $name Name of the tag found * @param array $attrs Attributes of the tag * * @return void * * @note This is public because it is called externally * @since 1.7.0 */ public function _startElement($parser, $name, $attrs = []) { $this->stack[] = $name; $tag = $this->_getStackLocation(); // Reset the data if (isset($this->$tag)) { $this->$tag->_data = ''; } switch ($name) { // This is a new update; create a current update case 'UPDATE': $this->currentUpdate = new \stdClass(); break; // Handle the array of download sources case 'DOWNLOADSOURCE': $source = new DownloadSource(); foreach ($attrs as $key => $data) { $key = strtolower($key); $source->$key = $data; } $this->downloadSources[] = $source; break; // Don't do anything case 'UPDATES': break; // For everything else there's...the default! default: $name = strtolower($name); if (!isset($this->currentUpdate->$name)) { $this->currentUpdate->$name = new \stdClass(); } $this->currentUpdate->$name->_data = ''; foreach ($attrs as $key => $data) { $key = strtolower($key); $this->currentUpdate->$name->$key = $data; } break; } } /** * Callback for closing the element * * @param object $parser Parser object * @param string $name Name of element that was closed * * @return void * * @note This is public because it is called externally * @since 1.7.0 */ public function _endElement($parser, $name) { array_pop($this->stack); switch ($name) { // Closing update, find the latest version and check case 'UPDATE': $product = strtolower(InputFilter::getInstance()->clean(Version::PRODUCT, 'cmd')); // Check that the product matches and that the version matches (optionally a regexp) if ( isset($this->currentUpdate->targetplatform->name) && $product == $this->currentUpdate->targetplatform->name && preg_match('/^' . $this->currentUpdate->targetplatform->version . '/', $this->get('jversion.full', JVERSION)) ) { // Collect information on updates which do not meet PHP and DB version requirements $otherUpdateInfo = new \stdClass(); $otherUpdateInfo->version = $this->currentUpdate->version->_data; $phpMatch = false; // Check if PHP version supported via <php_minimum> tag, assume true if tag isn't present if (!isset($this->currentUpdate->php_minimum) || version_compare(PHP_VERSION, $this->currentUpdate->php_minimum->_data, '>=')) { $phpMatch = true; } if (!$phpMatch) { $otherUpdateInfo->php = new \stdClass(); $otherUpdateInfo->php->required = $this->currentUpdate->php_minimum->_data; $otherUpdateInfo->php->used = PHP_VERSION; } $dbMatch = false; // Check if DB & version is supported via <supported_databases> tag, assume supported if tag isn't present if (isset($this->currentUpdate->supported_databases)) { $db = Factory::getDbo(); $dbType = strtolower($db->getServerType()); $dbVersion = $db->getVersion(); $supportedDbs = $this->currentUpdate->supported_databases; // MySQL and MariaDB use the same database driver but not the same version numbers if ($dbType === 'mysql') { // Check whether we have a MariaDB version string and extract the proper version from it if (stripos($dbVersion, 'mariadb') !== false) { // MariaDB: Strip off any leading '5.5.5-', if present $dbVersion = preg_replace('/^5\.5\.5-/', '', $dbVersion); $dbType = 'mariadb'; } } // Do we have an entry for the database? if (isset($supportedDbs->$dbType)) { $minimumVersion = $supportedDbs->$dbType; $dbMatch = version_compare($dbVersion, $minimumVersion, '>='); if (!$dbMatch) { $otherUpdateInfo->db = new \stdClass(); $otherUpdateInfo->db->type = $dbType; $otherUpdateInfo->db->required = $minimumVersion; $otherUpdateInfo->db->used = $dbVersion; } } } else { // Set to true if the <supported_databases> tag is not set $dbMatch = true; } // Check minimum stability $stabilityMatch = true; if (isset($this->currentUpdate->stability) && ($this->currentUpdate->stability < $this->minimum_stability)) { $stabilityMatch = false; } if ($phpMatch && $stabilityMatch && $dbMatch) { if (!empty($this->currentUpdate->downloadurl) && !empty($this->currentUpdate->downloadurl->_data)) { $this->compatibleVersions[] = $this->currentUpdate->version->_data; } if ( !isset($this->latest) || version_compare($this->currentUpdate->version->_data, $this->latest->version->_data, '>') ) { $this->latest = $this->currentUpdate; } } elseif ( !isset($this->otherUpdateInfo) || version_compare($otherUpdateInfo->version, $this->otherUpdateInfo->version, '>') ) { $this->otherUpdateInfo = $otherUpdateInfo; } } break; case 'UPDATES': // If the latest item is set then we transfer it to where we want to if (isset($this->latest)) { // This is an optional tag and therefore we need to be sure that this is gone and only used when set by the update itself unset($this->downloadSources); foreach (get_object_vars($this->latest) as $key => $val) { $this->$key = $val; } unset($this->latest); unset($this->currentUpdate); } elseif (isset($this->currentUpdate)) { // The update might be for an older version of j! unset($this->currentUpdate); } break; } } /** * Character Parser Function * * @param object $parser Parser object. * @param object $data The data. * * @return void * * @note This is public because its called externally. * @since 1.7.0 */ public function _characterData($parser, $data) { $tag = $this->_getLastTag(); // Throw the data for this item together $tag = strtolower($tag); if ($tag === 'tag') { $this->currentUpdate->stability = $this->stabilityTagToInteger((string) $data); return; } if ($tag === 'downloadsource') { // Grab the last source so we can append the URL $source = end($this->downloadSources); $source->url = $data; return; } if (isset($this->currentUpdate->$tag)) { $this->currentUpdate->$tag->_data .= $data; } } /** * Loads an XML file from a URL. * * @param string $url The URL. * @param int $minimumStability The minimum stability required for updating the extension {@see Updater} * * @return boolean True on success * * @since 1.7.0 */ public function loadFromXml($url, $minimumStability = Updater::STABILITY_STABLE) { $version = new Version(); $httpOption = new Registry(); $httpOption->set('userAgent', $version->getUserAgent('Joomla', true, false)); try { $http = HttpFactory::getHttp($httpOption); $response = $http->get($url); } catch (\RuntimeException $e) { $response = null; } if ($response === null || $response->code !== 200) { // @todo: Add a 'mark bad' setting here somehow Log::add(Text::sprintf('JLIB_UPDATER_ERROR_EXTENSION_OPEN_URL', $url), Log::WARNING, 'jerror'); return false; } $this->minimum_stability = $minimumStability; $this->xmlParser = xml_parser_create(''); xml_set_object($this->xmlParser, $this); xml_set_element_handler($this->xmlParser, '_startElement', '_endElement'); xml_set_character_data_handler($this->xmlParser, '_characterData'); if (!xml_parse($this->xmlParser, $response->body)) { Log::add( sprintf( 'XML error: %s at line %d', xml_error_string(xml_get_error_code($this->xmlParser)), xml_get_current_line_number($this->xmlParser) ), Log::WARNING, 'updater' ); return false; } xml_parser_free($this->xmlParser); return true; } /** * Converts a tag to numeric stability representation. If the tag doesn't represent a known stability level (one of * dev, alpha, beta, rc, stable) it is ignored. * * @param string $tag The tag string, e.g. dev, alpha, beta, rc, stable * * @return integer * * @since 3.4 */ protected function stabilityTagToInteger($tag) { $constant = '\\Joomla\\CMS\\Updater\\Updater::STABILITY_' . strtoupper($tag); if (\defined($constant)) { return \constant($constant); } return Updater::STABILITY_STABLE; } } PK0A#]�{m:m:src/Updater/Updater.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater; use Joomla\CMS\Adapter\Adapter; use Joomla\CMS\Factory; use Joomla\CMS\Table\Table; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Updater Class * * @since 1.7.0 */ class Updater extends Adapter { /** * Development snapshots, nightly builds, pre-release versions and so on * * @var integer * @since 3.4 */ public const STABILITY_DEV = 0; /** * Alpha versions (work in progress, things are likely to be broken) * * @var integer * @since 3.4 */ public const STABILITY_ALPHA = 1; /** * Beta versions (major functionality in place, show-stopper bugs are likely to be present) * * @var integer * @since 3.4 */ public const STABILITY_BETA = 2; /** * Release Candidate versions (almost stable, minor bugs might be present) * * @var integer * @since 3.4 */ public const STABILITY_RC = 3; /** * Stable versions (production quality code) * * @var integer * @since 3.4 */ public const STABILITY_STABLE = 4; /** * Updater instance container. * * @var Updater * @since 1.7.3 */ protected static $instance; /** * Constructor * * @param string $basepath Base Path of the adapters * @param string $classprefix Class prefix of adapters * @param string $adapterfolder Name of folder to append to base path * * @since 3.1 */ public function __construct($basepath = __DIR__, $classprefix = '\\Joomla\\CMS\\Updater\\Adapter', $adapterfolder = 'Adapter') { parent::__construct($basepath, $classprefix, $adapterfolder); } /** * Returns a reference to the global Installer object, only creating it * if it doesn't already exist. * * @return Updater An installer object * * @since 1.7.0 */ public static function getInstance() { if (!isset(self::$instance)) { self::$instance = new static(); } return self::$instance; } /** * Finds the update for an extension. Any discovered updates are stored in the #__updates table. * * @param int|array $eid Extension Identifier or list of Extension Identifiers; if zero use all * sites * @param integer $cacheTimeout How many seconds to cache update information; if zero, force reload the * update information * @param integer $minimumStability Minimum stability for the updates; 0=dev, 1=alpha, 2=beta, 3=rc, * 4=stable * @param boolean $includeCurrent Should I include the current version in the results? * * @return boolean True if there are updates * * @since 1.7.0 */ public function findUpdates($eid = 0, $cacheTimeout = 0, $minimumStability = self::STABILITY_STABLE, $includeCurrent = false) { $retval = false; $results = $this->getUpdateSites($eid); if (empty($results)) { return $retval; } $now = time(); $earliestTime = $now - $cacheTimeout; $sitesWithUpdates = []; if ($cacheTimeout > 0) { $sitesWithUpdates = $this->getSitesWithUpdates($earliestTime); } foreach ($results as $result) { /** * If we have already checked for updates within the cache timeout period we will report updates available * only if there are update records matching this update site. Then we skip processing of the update site * since it's already processed within the cache timeout period. */ if ( ($cacheTimeout > 0) && isset($result['last_check_timestamp']) && ($result['last_check_timestamp'] >= $earliestTime) ) { $retval = $retval || \in_array($result['update_site_id'], $sitesWithUpdates); continue; } // Make sure there is no update left over in the database. $db = $this->getDbo(); $query = $db->getQuery(true) ->delete($db->quoteName('#__updates')) ->where($db->quoteName('update_site_id') . ' = :id') ->bind(':id', $result['update_site_id'], ParameterType::INTEGER); $db->setQuery($query); $db->execute(); $updateObjects = $this->getUpdateObjectsForSite($result, $minimumStability, $includeCurrent); if (!empty($updateObjects)) { $retval = true; /** @var \Joomla\CMS\Table\Update $update */ foreach ($updateObjects as $update) { $update->check(); $update->store(); } } // Finally, update the last update check timestamp $this->updateLastCheckTimestamp($result['update_site_id']); } return $retval; } /** * Returns the update site records for an extension with ID $eid. If $eid is zero all enabled update sites records * will be returned. * * @param int $eid The extension ID to fetch. * * @return array * * @since 3.6.0 */ private function getUpdateSites($eid = 0) { $db = $this->getDbo(); $query = $db->getQuery(true); $query->select( [ 'DISTINCT ' . $db->quoteName('a.update_site_id'), $db->quoteName('a.type'), $db->quoteName('a.location'), $db->quoteName('a.last_check_timestamp'), $db->quoteName('a.extra_query'), ] ) ->from($db->quoteName('#__update_sites', 'a')) ->where($db->quoteName('a.enabled') . ' = 1'); if ($eid) { $query->join( 'INNER', $db->quoteName('#__update_sites_extensions', 'b'), $db->quoteName('a.update_site_id') . ' = ' . $db->quoteName('b.update_site_id') ); if (\is_array($eid)) { $query->whereIn($db->quoteName('b.extension_id'), $eid); } elseif ($eid = (int) $eid) { $query->where($db->quoteName('b.extension_id') . ' = :eid') ->bind(':eid', $eid, ParameterType::INTEGER); } } $db->setQuery($query); $result = $db->loadAssocList(); if (!\is_array($result)) { return []; } return $result; } /** * Loads the contents of an update site record $updateSite and returns the update objects * * @param array $updateSite The update site record to process * @param int $minimumStability Minimum stability for the returned update records * @param bool $includeCurrent Should I also include the current version? * * @return array The update records. Empty array if no updates are found. * * @since 3.6.0 */ private function getUpdateObjectsForSite($updateSite, $minimumStability = self::STABILITY_STABLE, $includeCurrent = false) { $retVal = []; $this->setAdapter($updateSite['type']); if (!isset($this->_adapters[$updateSite['type']])) { // Ignore update sites requiring adapters we don't have installed return $retVal; } $updateSite['minimum_stability'] = $minimumStability; // Get the update information from the remote update XML document /** @var UpdateAdapter $adapter */ $adapter = $this->_adapters[ $updateSite['type']]; $update_result = $adapter->findUpdate($updateSite); // Version comparison operator. $operator = $includeCurrent ? 'ge' : 'gt'; if (\is_array($update_result)) { // If we have additional update sites in the remote (collection) update XML document, parse them if (\array_key_exists('update_sites', $update_result) && \count($update_result['update_sites'])) { $thisUrl = trim($updateSite['location']); $thisId = (int) $updateSite['update_site_id']; foreach ($update_result['update_sites'] as $extraUpdateSite) { $extraUrl = trim($extraUpdateSite['location']); $extraId = (int) $extraUpdateSite['update_site_id']; // Do not try to fetch the same update site twice if (($thisId == $extraId) || ($thisUrl == $extraUrl)) { continue; } $extraUpdates = $this->getUpdateObjectsForSite($extraUpdateSite, $minimumStability); if (\count($extraUpdates)) { $retVal = array_merge($retVal, $extraUpdates); } } } if (\array_key_exists('updates', $update_result) && \count($update_result['updates'])) { /** @var \Joomla\CMS\Table\Update $current_update */ foreach ($update_result['updates'] as $current_update) { $current_update->extra_query = $updateSite['extra_query']; /** @var \Joomla\CMS\Table\Update $update */ $update = Table::getInstance('update'); /** @var \Joomla\CMS\Table\Extension $extension */ $extension = Table::getInstance('extension'); $uid = $update ->find( [ 'element' => $current_update->get('element'), 'type' => $current_update->get('type'), 'client_id' => $current_update->get('client_id'), 'folder' => $current_update->get('folder'), ] ); $eid = $extension ->find( [ 'element' => $current_update->get('element'), 'type' => $current_update->get('type'), 'client_id' => $current_update->get('client_id'), 'folder' => $current_update->get('folder'), ] ); if (!$uid) { // Set the extension id if ($eid) { // We have an installed extension, check the update is actually newer $extension->load($eid); $data = json_decode($extension->manifest_cache, true); if (version_compare($current_update->version, $data['version'], $operator) == 1) { $current_update->extension_id = $eid; $retVal[] = $current_update; } } else { // A potentially new extension to be installed $retVal[] = $current_update; } } else { $update->load($uid); // We already have an update in the database lets check whether it has an extension_id if ((int) $update->extension_id === 0 && $eid) { // The current update does not have an extension_id but we found one. Let's use it. $current_update->extension_id = $eid; } // If there is an update, check that the version is newer then replaces if (version_compare($current_update->version, $update->version, $operator) == 1) { $retVal[] = $current_update; } } } } } return $retVal; } /** * Returns the IDs of the update sites with cached updates * * @param int $timestamp Optional. If set, only update sites checked before $timestamp will be taken into * account. * * @return array The IDs of the update sites with cached updates * * @since 3.6.0 */ private function getSitesWithUpdates($timestamp = 0) { $db = Factory::getDbo(); $timestamp = (int) $timestamp; $query = $db->getQuery(true) ->select('DISTINCT ' . $db->quoteName('update_site_id')) ->from($db->quoteName('#__updates')); if ($timestamp) { $subQuery = $db->getQuery(true) ->select($db->quoteName('update_site_id')) ->from($db->quoteName('#__update_sites')) ->where( [ $db->quoteName('last_check_timestamp') . ' IS NULL', $db->quoteName('last_check_timestamp') . ' <= :timestamp', ], 'OR' ); $query->where($db->quoteName('update_site_id') . ' IN (' . $subQuery . ')') ->bind(':timestamp', $timestamp, ParameterType::INTEGER); } $retVal = $db->setQuery($query)->loadColumn(0); if (empty($retVal)) { return []; } return $retVal; } /** * Update the last check timestamp of an update site * * @param int $updateSiteId The update site ID to mark as just checked * * @return void * * @since 3.6.0 */ private function updateLastCheckTimestamp($updateSiteId) { $timestamp = time(); $db = Factory::getDbo(); $updateSiteId = (int) $updateSiteId; $query = $db->getQuery(true) ->update($db->quoteName('#__update_sites')) ->set($db->quoteName('last_check_timestamp') . ' = :timestamp') ->where($db->quoteName('update_site_id') . ' = :id') ->bind(':timestamp', $timestamp, ParameterType::INTEGER) ->bind(':id', $updateSiteId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); } } PK0A#]گS@src/Updater/DownloadSource.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Updater; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing a download source given as part of an update's `<downloads>` element * * @since 3.8.3 */ class DownloadSource { /** * Defines a BZIP2 download package * * @var string * @since 3.8.4 */ public const FORMAT_TAR_BZIP = 'bz2'; /** * Defines a TGZ download package * * @var string * @since 3.8.4 */ public const FORMAT_TAR_GZ = 'gz'; /** * Defines a ZIP download package * * @var string * @since 3.8.3 */ public const FORMAT_ZIP = 'zip'; /** * Defines a full package download type * * @var string * @since 3.8.3 */ public const TYPE_FULL = 'full'; /** * Defines a patch package download type * * @var string * @since 3.8.4 */ public const TYPE_PATCH = 'patch'; /** * Defines an upgrade package download type * * @var string * @since 3.8.4 */ public const TYPE_UPGRADE = 'upgrade'; /** * The download type * * @var string * @since 3.8.3 */ public $type = self::TYPE_FULL; /** * The download file's format * * @var string * @since 3.8.3 */ public $format = self::FORMAT_ZIP; /** * The URL to retrieve the package from * * @var string * @since 3.8.3 */ public $url; } PK1A#]j�]%]%src/Session/MetadataManager.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session; use Joomla\Application\AbstractApplication; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\User\User; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Session\SessionInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Manager for optional session metadata. * * @since 3.8.6 * @internal */ final class MetadataManager { /** * Internal variable indicating a session record exists. * * @var integer * @since 4.0.0 * @note Once PHP 7.1 is the minimum supported version this should become a private constant */ private static $sessionRecordExists = 1; /** * Internal variable indicating a session record does not exist. * * @var integer * @since 4.0.0 * @note Once PHP 7.1 is the minimum supported version this should become a private constant */ private static $sessionRecordDoesNotExist = 0; /** * Internal variable indicating an unknown session record statue. * * @var integer * @since 4.0.0 * @note Once PHP 7.1 is the minimum supported version this should become a private constant */ private static $sessionRecordUnknown = -1; /** * Application object. * * @var AbstractApplication * @since 3.8.6 */ private $app; /** * Database driver. * * @var DatabaseInterface * @since 3.8.6 */ private $db; /** * MetadataManager constructor. * * @param AbstractApplication $app Application object. * @param DatabaseInterface $db Database driver. * * @since 3.8.6 */ public function __construct(AbstractApplication $app, DatabaseInterface $db) { $this->app = $app; $this->db = $db; } /** * Create the metadata record if it does not exist. * * @param SessionInterface $session The session to create the metadata record for. * @param User $user The user to associate with the record. * * @return void * * @since 3.8.6 * @throws \RuntimeException */ public function createRecordIfNonExisting(SessionInterface $session, User $user) { $exists = $this->checkSessionRecordExists($session->getId()); // Only touch the database if the record does not already exist if ($exists !== self::$sessionRecordExists) { return; } $this->createSessionRecord($session, $user); } /** * Create the metadata record if it does not exist. * * @param SessionInterface $session The session to create or update the metadata record for. * @param User $user The user to associate with the record. * * @return void * * @since 4.0.0 * @throws \RuntimeException */ public function createOrUpdateRecord(SessionInterface $session, User $user) { $exists = $this->checkSessionRecordExists($session->getId()); // Do not try to touch the database if we can't determine the record state if ($exists === self::$sessionRecordUnknown) { return; } if ($exists === self::$sessionRecordDoesNotExist) { $this->createSessionRecord($session, $user); return; } $this->updateSessionRecord($session, $user); } /** * Delete records with a timestamp prior to the given time. * * @param integer $time The time records should be deleted if expired before. * * @return void * * @since 3.8.6 */ public function deletePriorTo($time) { $query = $this->db->getQuery(true) ->delete($this->db->quoteName('#__session')) ->where($this->db->quoteName('time') . ' < :time') ->bind(':time', $time, ParameterType::INTEGER); $this->db->setQuery($query); try { $this->db->execute(); } catch (ExecutionFailureException $exception) { // Since garbage collection does not result in a fatal error when run in the session API, we don't allow it here either. } } /** * Check if the session record exists * * @param string $sessionId The session ID to check * * @return integer Status value for record presence * * @since 4.0.0 */ private function checkSessionRecordExists(string $sessionId): int { $query = $this->db->getQuery(true) ->select($this->db->quoteName('session_id')) ->from($this->db->quoteName('#__session')) ->where($this->db->quoteName('session_id') . ' = :session_id') ->bind(':session_id', $sessionId) ->setLimit(1); $this->db->setQuery($query); try { $exists = $this->db->loadResult(); } catch (ExecutionFailureException $e) { return self::$sessionRecordUnknown; } if ($exists) { return self::$sessionRecordExists; } return self::$sessionRecordDoesNotExist; } /** * Create the session record * * @param SessionInterface $session The session to create the metadata record for. * @param User $user The user to associate with the record. * * @return void * * @since 4.0.0 */ private function createSessionRecord(SessionInterface $session, User $user) { $query = $this->db->getQuery(true); $time = $session->isNew() ? time() : $session->get('session.timer.start'); $columns = [ $this->db->quoteName('session_id'), $this->db->quoteName('guest'), $this->db->quoteName('time'), $this->db->quoteName('userid'), $this->db->quoteName('username'), ]; // Add query placeholders $values = [ ':session_id', ':guest', ':time', ':user_id', ':username', ]; // Bind query values $sessionId = $session->getId(); $userIsGuest = $user->guest; $userId = $user->id; $username = $user->username === null ? '' : $user->username; $query->bind(':session_id', $sessionId) ->bind(':guest', $userIsGuest, ParameterType::INTEGER) ->bind(':time', $time) ->bind(':user_id', $userId, ParameterType::INTEGER) ->bind(':username', $username); if ($this->app instanceof CMSApplication && !$this->app->get('shared_session', false)) { $clientId = $this->app->getClientId(); $columns[] = $this->db->quoteName('client_id'); $values[] = ':client_id'; $query->bind(':client_id', $clientId, ParameterType::INTEGER); } $query->insert($this->db->quoteName('#__session')) ->columns($columns) ->values(implode(', ', $values)); $this->db->setQuery($query); try { $this->db->execute(); } catch (ExecutionFailureException $e) { // This failure isn't critical, we can go on without the metadata } } /** * Update the session record * * @param SessionInterface $session The session to update the metadata record for. * @param User $user The user to associate with the record. * * @return void * * @since 4.0.0 */ private function updateSessionRecord(SessionInterface $session, User $user) { $query = $this->db->getQuery(true); $time = time(); $setValues = [ $this->db->quoteName('guest') . ' = :guest', $this->db->quoteName('time') . ' = :time', $this->db->quoteName('userid') . ' = :user_id', $this->db->quoteName('username') . ' = :username', ]; // Bind query values $sessionId = $session->getId(); $userIsGuest = $user->guest; $userId = $user->id; $username = $user->username === null ? '' : $user->username; $query->bind(':session_id', $sessionId) ->bind(':guest', $userIsGuest, ParameterType::INTEGER) ->bind(':time', $time) ->bind(':user_id', $userId, ParameterType::INTEGER) ->bind(':username', $username); if ($this->app instanceof CMSApplication && !$this->app->get('shared_session', false)) { $clientId = $this->app->getClientId(); $setValues[] = $this->db->quoteName('client_id') . ' = :client_id'; $query->bind(':client_id', $clientId, ParameterType::INTEGER); } $query->update($this->db->quoteName('#__session')) ->set($setValues) ->where($this->db->quoteName('session_id') . ' = :session_id'); $this->db->setQuery($query); try { $this->db->execute(); } catch (ExecutionFailureException $e) { // This failure isn't critical, we can go on without the metadata } } } PK1A#]��pӝ�src/Session/SessionFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session; use Joomla\Database\DatabaseInterface; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; use Joomla\Registry\Registry; use Joomla\Session\Handler; use Joomla\Session\HandlerInterface; use Symfony\Component\OptionsResolver\OptionsResolver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory for creating session API objects * * @since 4.0.0 */ class SessionFactory implements ContainerAwareInterface { use ContainerAwareTrait; /** * Create a session handler based on the application configuration. * * @param array $options The options used to instantiate the SessionInterface instance. * * @return HandlerInterface * * @since 4.0.0 */ public function createSessionHandler(array $options): HandlerInterface { $resolver = new OptionsResolver(); $this->configureSessionHandlerOptions($resolver); $options = $resolver->resolve($options); /** @var Registry $config */ $config = $this->getContainer()->get('config'); $handlerType = $config->get('session_handler', 'filesystem'); switch ($handlerType) { case 'apcu': if (!Handler\ApcuHandler::isSupported()) { throw new \RuntimeException('APCu is not supported on this system.'); } return new Handler\ApcuHandler(); case 'database': return new Handler\DatabaseHandler($this->getContainer()->get(DatabaseInterface::class)); case 'filesystem': case 'none': // Try to use a custom configured path, fall back to the path in the PHP runtime configuration $path = $config->get('session_filesystem_path', ini_get('session.save_path')); // If we still have no path, as a last resort fall back to the system's temporary directory if (empty($path)) { $path = sys_get_temp_dir(); } return new Handler\FilesystemHandler($path); case 'memcached': if (!Handler\MemcachedHandler::isSupported()) { throw new \RuntimeException('Memcached is not supported on this system.'); } $host = $config->get('session_memcached_server_host', 'localhost'); $port = $config->get('session_memcached_server_port', 11211); $memcached = new \Memcached($config->get('session_memcached_server_id', 'joomla_cms')); $memcached->addServer($host, $port); ini_set('session.save_path', "$host:$port"); ini_set('session.save_handler', 'memcached'); return new Handler\MemcachedHandler($memcached, ['ttl' => $options['expire']]); case 'redis': if (!Handler\RedisHandler::isSupported()) { throw new \RuntimeException('Redis is not supported on this system.'); } $redis = new \Redis(); $host = $config->get('session_redis_server_host', '127.0.0.1'); // Use default port if connecting over a socket whatever the config value $port = $host[0] === '/' ? 0 : $config->get('session_redis_server_port', 6379); if ($config->get('session_redis_persist', true)) { $redis->pconnect( $host, $port ); } else { $redis->connect( $host, $port ); } if (!empty($config->get('session_redis_server_auth', ''))) { $redis->auth($config->get('session_redis_server_auth', null)); } $db = (int) $config->get('session_redis_server_db', 0); if ($db !== 0) { $redis->select($db); } return new Handler\RedisHandler($redis, ['ttl' => $options['expire']]); case 'wincache': // @TODO Remove WinCache with Joomla 5.0 if (!Handler\WincacheHandler::isSupported()) { throw new \RuntimeException('Wincache is not supported on this system.'); } return new Handler\WincacheHandler(); default: throw new \InvalidArgumentException(sprintf('The "%s" session handler is not recognised.', $handlerType)); } } /** * Resolve the options for the session handler. * * @param OptionsResolver $resolver The options resolver. * * @return void * * @since 4.0.0 */ protected function configureSessionHandlerOptions(OptionsResolver $resolver) { $resolver->setDefaults( [ 'force_ssl' => false, ] ); $resolver->setRequired(['name', 'expire']); $resolver->setAllowedTypes('name', ['string']); $resolver->setAllowedTypes('expire', ['int']); $resolver->setAllowedTypes('force_ssl', ['bool']); } } PK1A#]����5src/Session/EventListener/MetadataManagerListener.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session\EventListener; use Joomla\CMS\Session\MetadataManager; use Joomla\Registry\Registry; use Joomla\Session\SessionEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event listener for session events regarding the session metadata for users. * * @since 4.0.0 */ final class MetadataManagerListener { /** * Session metadata manager. * * @var MetadataManager * @since 4.0.0 */ private $metadataManager; /** * Application configuration. * * @var Registry * @since 4.0.0 */ private $config; /** * Constructor. * * @param MetadataManager $metadataManager Session metadata manager. * @param Registry $config Application configuration. * * @since 4.0.0 */ public function __construct(MetadataManager $metadataManager, Registry $config) { $this->metadataManager = $metadataManager; $this->config = $config; } /** * Listener for the `session.start` event. * * @param SessionEvent $event The session event. * * @return void * * @since 4.0.0 */ public function onAfterSessionStart(SessionEvent $event) { // Whether to track Session Metadata if (!$this->config->get('session_metadata', true) || !$event->getSession()->has('user')) { return; } $user = $event->getSession()->get('user'); // Whether to track Session Metadata for Guest user if (!$this->config->get('session_metadata_for_guest', true) && !$user->id) { return; } $this->metadataManager->createOrUpdateRecord($event->getSession(), $user); } } PK1A#]�/����src/Session/SessionManager.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Manager for interacting with the session handler to perform updates on sessions. * * @since 4.0.0 */ final class SessionManager { /** * Session handler. * * @var \SessionHandlerInterface * @since 4.0.0 */ private $sessionHandler; /** * SessionManager constructor. * * @param \SessionHandlerInterface $sessionHandler Session handler. * * @since 4.0.0 */ public function __construct(\SessionHandlerInterface $sessionHandler) { $this->sessionHandler = $sessionHandler; } /** * Destroys the given session ID. * * @param string $sessionId The session ID to destroy. * * @return boolean * * @since 4.0.0 */ public function destroySession(string $sessionId): bool { return $this->sessionHandler->destroy($sessionId); } /** * Destroys the given session IDs. * * @param string[] $sessionIds The session IDs to destroy. * * @return boolean * * @since 4.0.0 */ public function destroySessions(array $sessionIds): bool { $result = true; foreach ($sessionIds as $sessionId) { if (!$this->destroySession($sessionId)) { $result = false; } } return $result; } } PK1A#]�m�5src/Session/Exception/UnsupportedStorageException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an unsupported session storage object * * @since 3.6.3 */ class UnsupportedStorageException extends \RuntimeException { } PK1A#]!��%t+t+src/Session/Session.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\Event\DispatcherInterface; use Joomla\Session\Session as BaseSession; use Joomla\Session\StorageInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class for managing HTTP sessions * * @since 1.5 */ class Session extends BaseSession { /** * Constructor * * @param StorageInterface $store A StorageInterface implementation. * @param DispatcherInterface $dispatcher DispatcherInterface for the session to use. * @param array $options Optional parameters. Supported keys include: * - name: The session name * - id: The session ID * - expire: The session lifetime in seconds * * @since 1.0 */ public function __construct(StorageInterface $store = null, DispatcherInterface $dispatcher = null, array $options = []) { // Extra hash the name of the session for b/c with Joomla 3.x or the session is never found. if (isset($options['name'])) { $options['name'] = md5($options['name']); } parent::__construct($store, $dispatcher, $options); } /** * Checks for a form token in the request. * * Use in conjunction with HTMLHelper::_('form.token') or JSession::getFormToken. * * @param string $method The request method in which to look for the token key. * * @return boolean True if found and valid, false otherwise. * * @since 2.5.4 */ public static function checkToken($method = 'post') { $app = Factory::getApplication(); $token = static::getFormToken(); // Check from header first if ($token === $app->getInput()->server->get('HTTP_X_CSRF_TOKEN', '', 'alnum')) { return true; } // Then fallback to HTTP query if (!$app->getInput()->$method->get($token, '', 'alnum')) { if ($app->getSession()->isNew()) { // Redirect to login screen. $app->enqueueMessage(Text::_('JLIB_ENVIRONMENT_SESSION_EXPIRED'), 'warning'); $app->redirect(Route::_('index.php')); return true; } return false; } return true; } /** * Method to determine a hash for anti-spoofing variable names * * @param boolean $forceNew If true, force a new token to be created * * @return string Hashed var name * * @since 1.6 */ public static function getFormToken($forceNew = false) { $user = Factory::getUser(); return ApplicationHelper::getHash($user->get('id', 0) . Factory::getApplication()->getSession()->getToken($forceNew)); } /** * Get the available session handlers * * @return array An array of available session handlers * * @since 4.0.0 */ public static function getHandlers(): array { $connectors = []; // Get an iterator and loop through the handler classes. $iterator = new \DirectoryIterator(JPATH_LIBRARIES . '/vendor/joomla/session/src/Handler'); foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for PHP files. if (!$file->isFile() || $file->getExtension() !== 'php') { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', '\\Joomla\\Session\\Handler\\' . $fileName); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } // Sweet! Our class exists, so now we just need to know if it passes its test method. if ($class::isSupported()) { // Connector names should not have file the handler suffix or the file extension. $connectors[] = str_ireplace('Handler.php', '', $fileName); } } return $connectors; } /** * Returns the global session object. * * @return static The Session object. * * @since 1.5 * * @deprecated 4.3 will be removed in 6.0 * Load the session service from the dependency injection container or via $app->getSession() * Example: Factory::getApplication()->getSession(); */ public static function getInstance() { @trigger_error( __METHOD__ . '() is deprecated. Load the session from the dependency injection container or via Factory::getApplication()->getSession().', E_USER_DEPRECATED ); return Factory::getApplication()->getSession(); } /** * Get data from the session store * * @param string $name Name of a variable * @param mixed $default Default value of a variable if not set * * @return mixed Value of a variable * * @since 1.5 */ public function get($name, $default = null) { // Handle B/C by checking if a namespace was passed to the method, will be removed at 5.0 if (\func_num_args() > 2) { $args = \func_get_args(); if (!empty($args[2])) { @trigger_error( 'Passing a namespace as a parameter to ' . __METHOD__ . '() is deprecated. ' . 'The namespace should be prepended to the name instead.', E_USER_DEPRECATED ); $name = $args[2] . '.' . $name; } } if (parent::has($name)) { // Parent is used because of b/c, can be changed in Joomla 5 return parent::get($name, $default); } /* * B/C for retrieving sessions that originated in Joomla 3. * A namespace before Joomla 4 has a prefix of 2 underscores (__). * This is no longer the case in Joomla 4 and will be converted * when saving new values in `self::set()` */ if (strpos($name, '.') !== false && parent::has('__' . $name)) { return parent::get('__' . $name, $default); } // More b/c for retrieving sessions that originated in Joomla 3. This will be removed in Joomla 5 // as no sessions should have this format anymore! if (parent::has('__default.' . $name)) { return parent::get('__default.' . $name, $default); } return $default; } /** * Set data into the session store. * * @param string $name Name of a variable. * @param mixed $value Value of a variable. * * @return mixed Old value of a variable. * * @since 1.5 */ public function set($name, $value = null) { // Handle B/C by checking if a namespace was passed to the method, will be removed at 5.0 if (\func_num_args() > 2) { $args = \func_get_args(); if (!empty($args[2])) { @trigger_error( 'Passing a namespace as a parameter to ' . __METHOD__ . '() is deprecated. ' . 'The namespace should be prepended to the name instead.', E_USER_DEPRECATED ); $name = $args[2] . '.' . $name; } } return parent::set($name, $value); } /** * Check whether data exists in the session store * * @param string $name Name of variable * * @return boolean True if the variable exists * * @since 1.5 */ public function has($name) { // Handle B/C by checking if a namespace was passed to the method, will be removed at 5.0 if (\func_num_args() > 1) { $args = \func_get_args(); if (!empty($args[1])) { @trigger_error( 'Passing a namespace as a parameter to ' . __METHOD__ . '() is deprecated. ' . 'The namespace should be prepended to the name instead.', E_USER_DEPRECATED ); $name = $args[1] . '.' . $name; } } if (parent::has($name)) { return true; } /* * B/C for retrieving sessions that originated in Joomla 3. * A namespace before Joomla 4 has a prefix of 2 underscores (__). * This is no longer the case in Joomla 4 and will be converted * when saving new values in `self::set()` */ if (strpos($name, '.') !== false && parent::has('__' . $name)) { return true; } // More b/c for retrieving sessions that originated in Joomla 3. This will be removed in Joomla 5 // as no sessions should have this format anymore! return parent::has('__default.' . $name); } /** * Clears all variables from the session store * * @return void * * @since 1.5 */ public function clear() { // Handle B/C by checking if parameters were passed to this method; if so proxy to the new remove() method, will be removed at 5.0 if (\func_num_args() >= 1) { $args = \func_get_args(); if (!empty($args[0])) { @trigger_error( 'Using ' . __METHOD__ . '() to remove a single element from the session is deprecated. Use ' . __CLASS__ . '::remove() instead.', E_USER_DEPRECATED ); $name = $args[0]; // Also check for a namespace if (\func_num_args() > 1 && !empty($args[1])) { @trigger_error( 'Passing a namespace as a parameter to ' . __METHOD__ . '() is deprecated. ' . 'The namespace should be prepended to the name instead.', E_USER_DEPRECATED ); $name = $args[1] . '.' . $name; } $this->remove($name); /* * B/C for cleaning sessions that originated in Joomla 3. * A namespace before Joomla 4 has a prefix of 2 underscores (__). * This is no longer the case in Joomla 4 so we clean both variants. */ $this->remove('__' . $name); return; } } parent::clear(); } } PK1A#]OE���%src/Session/Storage/JoomlaStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Session\Storage; use Joomla\CMS\Factory; use Joomla\Input\Input; use Joomla\Registry\Registry; use Joomla\Session\Storage\NativeStorage; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's session dependency * * @since 4.0.0 */ class JoomlaStorage extends NativeStorage { /** * Internal data store for the session data * * @var Registry * @since 4.0.0 */ private $data; /** * Force cookies to be SSL only * * @var boolean * @since 4.0.0 */ private $forceSSL = false; /** * Input object * * @var Input * @since 4.0.0 */ private $input; /** * Constructor * * @param Input $input Input object * @param \SessionHandlerInterface $handler Session save handler * @param array $options Session options * * @since 4.0.0 */ public function __construct(Input $input, \SessionHandlerInterface $handler = null, array $options = []) { // Disable transparent sid support and default use cookies $options += [ 'use_cookies' => 1, 'use_trans_sid' => 0, ]; if (!headers_sent() && !$this->isActive()) { session_cache_limiter('none'); } $this->setOptions($options); $this->setHandler($handler); $this->setCookieParams(); $this->data = new Registry(); $this->input = $input; // Register our function as shutdown method, so we can manipulate it register_shutdown_function([$this, 'close']); } /** * Retrieves all variables from the session store * * @return array * * @since 4.0.0 */ public function all(): array { return $this->data->toArray(); } /** * Clears all variables from the session store * * @return void * * @since 4.0.0 */ public function clear(): void { $session_name = $this->getName(); /* * In order to kill the session altogether, such as to log the user out, the session id * must also be unset. If a cookie is used to propagate the session id (default behavior), * then the session cookie must be deleted. */ if (isset($_COOKIE[$session_name])) { $app = Factory::getApplication(); $cookie_domain = $app->get('cookie_domain', ''); $cookie_path = $app->get('cookie_path', '/'); $cookie = session_get_cookie_params(); setcookie($session_name, '', time() - 42000, $cookie_path, $cookie_domain, $cookie['secure'], true); } $this->data = new Registry(); } /** * Writes session data and ends session * * @return void * * @see session_write_close() * @since 4.0.0 */ public function close(): void { // Before storing data to the session, we serialize and encode the Registry $_SESSION['joomla'] = base64_encode(serialize(clone $this->data)); parent::close(); } /** * Get data from the session store * * @param string $name Name of a variable * @param mixed $default Default value of a variable if not set * * @return mixed Value of a variable * * @since 4.0.0 */ public function get(string $name, $default) { if (!$this->isStarted()) { $this->start(); } return $this->data->get($name, $default); } /** * Check whether data exists in the session store * * @param string $name Name of variable * * @return boolean True if the variable exists * * @since 4.0.0 */ public function has(string $name): bool { if (!$this->isStarted()) { $this->start(); } return $this->data->exists($name); } /** * Unset a variable from the session store * * @param string $name Name of variable * * @return mixed The value from session or NULL if not set * * @since 4.0.0 */ public function remove(string $name) { if (!$this->isStarted()) { $this->start(); } $old = $this->data->get($name); unset($this->data[$name]); return $old; } /** * Set data into the session store * * @param string $name Name of a variable. * @param mixed $value Value of a variable. * * @return mixed Old value of a variable. * * @since 4.0.0 */ public function set(string $name, $value = null) { if (!$this->isStarted()) { $this->start(); } $old = $this->data->get($name); $this->data->set($name, $value); return $old; } /** * Set session cookie parameters * * @return void * * @since 4.0.0 */ protected function setCookieParams(): void { if (headers_sent() || $this->isActive()) { return; } $cookie = session_get_cookie_params(); if ($this->forceSSL) { $cookie['secure'] = true; } $app = Factory::getApplication(); if ($app->get('cookie_domain', '') != '') { $cookie['domain'] = $app->get('cookie_domain'); } if ($app->get('cookie_path', '') != '') { $cookie['path'] = $app->get('cookie_path'); } session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], true); } /** * Sets session options * * @param array $options Session ini directives array(key => value). * * @return $this * * @see http://php.net/session.configuration * @since 4.0.0 */ public function setOptions(array $options): NativeStorage { if (isset($options['force_ssl'])) { $this->forceSSL = (bool) $options['force_ssl']; } return parent::setOptions($options); } /** * Start a session * * @return void * * @since 4.0.0 */ public function start(): void { $session_name = $this->getName(); // Get the cookie object $cookie = $this->input->cookie; if (\is_null($cookie->get($session_name))) { $session_clean = $this->input->getString($session_name); if ($session_clean) { $this->setId($session_clean); $cookie->set($session_name, '', time() - 3600); } } parent::start(); // Try loading data from the session if (isset($_SESSION['joomla']) && !empty($_SESSION['joomla'])) { $this->data = unserialize(base64_decode($_SESSION['joomla'])); } } } PK1A#]�V���'�'src/Uri/Uri.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Uri; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Uri Class * * This class serves two purposes. First it parses a URI and provides a common interface * for the Joomla Platform to access and manipulate a URI. Second it obtains the URI of * the current executing script from the server regardless of server. * * @since 1.7.0 */ class Uri extends \Joomla\Uri\Uri { /** * @var Uri[] An array of Uri instances. * @since 1.7.0 */ protected static $instances = []; /** * @var array The current calculated base url segments. * @since 1.7.0 */ protected static $base = []; /** * @var array The current calculated root url segments. * @since 1.7.0 */ protected static $root = []; /** * @var string The current url. * @since 1.7.0 */ protected static $current; /** * Returns the global Uri object, only creating it if it doesn't already exist. * * @param string $uri The URI to parse. [optional: if null uses script URI] * * @return Uri The URI object. * * @since 1.7.0 */ public static function getInstance($uri = 'SERVER') { if (empty(static::$instances[$uri])) { // Are we obtaining the URI from the server? if ($uri === 'SERVER') { // Determine if the request was over SSL (HTTPS). if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) !== 'off')) { $https = 's://'; } elseif ( (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) !== 'http')) ) { $https = 's://'; } else { $https = '://'; } /* * Since we are assigning the URI from the server variables, we first need * to determine if we are running on apache or IIS. If PHP_SELF and REQUEST_URI * are present, we will assume we are running on apache. */ if (!empty($_SERVER['PHP_SELF']) && !empty($_SERVER['REQUEST_URI'])) { // To build the entire URI we need to prepend the protocol, and the http host // to the URI string. $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } else { /* * Since we do not have REQUEST_URI to work with, we will assume we are * running on IIS and will therefore need to work some magic with the SCRIPT_NAME and * QUERY_STRING environment variables. * * IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable... thanks, MS */ $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME']; // If the query string exists append it to the URI string if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) { $theURI .= '?' . $_SERVER['QUERY_STRING']; } } // Extra cleanup to remove invalid chars in the URL to prevent injections through the Host header $theURI = str_replace(["'", '"', '<', '>'], ['%27', '%22', '%3C', '%3E'], $theURI); } else { // We were given a URI $theURI = $uri; } static::$instances[$uri] = new static($theURI); } return static::$instances[$uri]; } /** * Returns the base URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * * @return string The base URI string * * @since 1.7.0 */ public static function base($pathonly = false) { // Get the base request path. if (empty(static::$base)) { $config = Factory::getContainer()->get('config'); $uri = static::getInstance(); $live_site = ($uri->isSsl()) ? str_replace('http://', 'https://', $config->get('live_site', '')) : $config->get('live_site', ''); if (trim($live_site) != '') { $uri = static::getInstance($live_site); static::$base['prefix'] = $uri->toString(['scheme', 'host', 'port']); static::$base['path'] = rtrim($uri->toString(['path']), '/\\'); if (\defined('JPATH_BASE') && \defined('JPATH_ADMINISTRATOR') && JPATH_BASE == JPATH_ADMINISTRATOR) { static::$base['path'] .= '/administrator'; } if (\defined('JPATH_BASE') && \defined('JPATH_API') && JPATH_BASE == JPATH_API) { static::$base['path'] .= '/api'; } } else { static::$base['prefix'] = $uri->toString(['scheme', 'host', 'port']); if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($_SERVER['REQUEST_URI'])) { // PHP-CGI on Apache with "cgi.fix_pathinfo = 0" // We shouldn't have user-supplied PATH_INFO in PHP_SELF in this case // because PHP will not work with PATH_INFO at all. $script_name = $_SERVER['PHP_SELF']; } else { // Others $script_name = $_SERVER['SCRIPT_NAME']; } // Extra cleanup to remove invalid chars in the URL to prevent injections through broken server implementation $script_name = str_replace(["'", '"', '<', '>'], ['%27', '%22', '%3C', '%3E'], $script_name); static::$base['path'] = rtrim(\dirname($script_name), '/\\'); } } return $pathonly === false ? static::$base['prefix'] . static::$base['path'] . '/' : static::$base['path']; } /** * Returns the root URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * @param string $path The path * * @return string The root URI string. * * @since 1.7.0 */ public static function root($pathonly = false, $path = null) { // Get the scheme if (empty(static::$root)) { $uri = static::getInstance(static::base()); static::$root['prefix'] = $uri->toString(['scheme', 'host', 'port']); static::$root['path'] = rtrim($uri->toString(['path']), '/\\'); } // Get the scheme if (isset($path)) { static::$root['path'] = $path; } return $pathonly === false ? static::$root['prefix'] . static::$root['path'] . '/' : static::$root['path']; } /** * Returns the URL for the request, minus the query. * * @return string * * @since 1.7.0 */ public static function current() { // Get the current URL. if (empty(static::$current)) { $uri = static::getInstance(); static::$current = $uri->toString(['scheme', 'host', 'port', 'path']); } return static::$current; } /** * Method to reset class static members for testing and other various issues. * * @return void * * @since 1.7.0 */ public static function reset() { static::$instances = []; static::$base = []; static::$root = []; static::$current = ''; } /** * Checks if the supplied URL is internal * * @param string $url The URL to check. * * @return boolean True if Internal. * * @since 1.7.0 */ public static function isInternal($url) { $url = str_replace('\\', '/', $url); $uri = static::getInstance($url); $base = $uri->toString(['scheme', 'host', 'port', 'path']); $host = $uri->toString(['scheme', 'host', 'port']); // @see UriTest if ( empty($host) && strpos($uri->path, 'index.php') === 0 || !empty($host) && preg_match('#^' . preg_quote(static::base(), '#') . '#', $base) || !empty($host) && $host === static::getInstance(static::base())->host && strpos($uri->path, 'index.php') !== false || !empty($host) && $base === $host && preg_match('#^' . preg_quote($base, '#') . '#', static::base()) ) { return true; } return false; } /** * Build a query from an array (reverse of the PHP parse_str()). * * @param array $params The array of key => value pairs to return as a query string. * * @return string The resulting query string. * * @see parse_str() * @since 1.7.0 * @note The parent method is protected, this exposes it as public for B/C */ public static function buildQuery(array $params) { return parent::buildQuery($params); } /** * Parse a given URI and populate the class fields. * * @param string $uri The URI string to parse. * * @return boolean True on success. * * @since 1.7.0 * @note The parent method is protected, this exposes it as public for B/C */ public function parse($uri) { return parent::parse($uri); } } PK1A#]�s�=�f�fsrc/Environment/Browser.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Environment; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Browser class, provides capability information about the current web client. * * Browser identification is performed by examining the HTTP_USER_AGENT * environment variable provided by the web server. * * This class has many influences from the lib/Browser.php code in * version 3 of Horde by Chuck Hagenbuch and Jon Parise. * * @since 1.7.0 */ class Browser { /** * @var integer Major version number * @since 3.0.0 */ protected $majorVersion = 0; /** * @var integer Minor version number * @since 3.0.0 */ protected $minorVersion = 0; /** * @var string Browser name. * @since 3.0.0 */ protected $browser = ''; /** * @var string Full user agent string. * @since 3.0.0 */ protected $agent = ''; /** * @var string Lower-case user agent string * @since 3.0.0 */ protected $lowerAgent = ''; /** * @var string HTTP_ACCEPT string. * @since 3.0.0 */ protected $accept = ''; /** * @var array Parsed HTTP_ACCEPT string * @since 3.0.0 */ protected $acceptParsed = []; /** * @var string Platform the browser is running on * @since 3.0.0 */ protected $platform = ''; /** * @var array Known robots. * @since 3.0.0 */ protected $robots = [ 'Googlebot\/', 'Googlebot-Mobile', 'Googlebot-Image', 'Googlebot-News', 'Googlebot-Video', 'AdsBot-Google([^-]|$)', 'AdsBot-Google-Mobile', 'Feedfetcher-Google', 'Mediapartners-Google', 'Mediapartners \(Googlebot\)', 'APIs-Google', 'bingbot', 'Slurp', '[wW]get', 'curl', 'LinkedInBot', 'Python-urllib', 'python-requests', 'libwww', 'httpunit', 'nutch', 'Go-http-client', 'phpcrawl', 'msnbot', 'jyxobot', 'FAST-WebCrawler', 'FAST Enterprise Crawler', 'BIGLOTRON', 'Teoma', 'convera', 'seekbot', 'Gigabot', 'Gigablast', 'exabot', 'ia_archiver', 'GingerCrawler', 'webmon ', 'HTTrack', 'grub.org', 'UsineNouvelleCrawler', 'antibot', 'netresearchserver', 'speedy', 'fluffy', 'bibnum.bnf', 'findlink', 'msrbot', 'panscient', 'yacybot', 'AISearchBot', 'ips-agent', 'tagoobot', 'MJ12bot', 'woriobot', 'yanga', 'buzzbot', 'mlbot', 'YandexBot', 'yandex.com\/bots', 'purebot', 'Linguee Bot', 'CyberPatrol', 'voilabot', 'Baiduspider', 'citeseerxbot', 'spbot', 'twengabot', 'postrank', 'turnitinbot', 'scribdbot', 'page2rss', 'sitebot', 'linkdex', 'Adidxbot', 'blekkobot', 'ezooms', 'dotbot', 'Mail.RU_Bot', 'discobot', 'heritrix', 'findthatfile', 'europarchive.org', 'NerdByNature.Bot', 'sistrix crawler', 'Ahrefs(Bot|SiteAudit)', 'fuelbot', 'CrunchBot', 'centurybot9', 'IndeedBot', 'mappydata', 'woobot', 'ZoominfoBot', 'PrivacyAwareBot', 'Multiviewbot', 'SWIMGBot', 'Grobbot', 'eright', 'Apercite', 'semanticbot', 'Aboundex', 'domaincrawler', 'wbsearchbot', 'summify', 'CCBot', 'edisterbot', 'seznambot', 'ec2linkfinder', 'gslfbot', 'aiHitBot', 'intelium_bot', 'facebookexternalhit', 'Yeti', 'RetrevoPageAnalyzer', 'lb-spider', 'Sogou', 'lssbot', 'careerbot', 'wotbox', 'wocbot', 'ichiro', 'DuckDuckBot', 'lssrocketcrawler', 'drupact', 'webcompanycrawler', 'acoonbot', 'openindexspider', 'gnam gnam spider', 'web-archive-net.com.bot', 'backlinkcrawler', 'coccoc', 'integromedb', 'content crawler spider', 'toplistbot', 'it2media-domain-crawler', 'ip-web-crawler.com', 'siteexplorer.info', 'elisabot', 'proximic', 'changedetection', 'arabot', 'WeSEE:Search', 'niki-bot', 'CrystalSemanticsBot', 'rogerbot', '360Spider', 'psbot', 'InterfaxScanBot', 'CC Metadata Scaper', 'g00g1e.net', 'GrapeshotCrawler', 'urlappendbot', 'brainobot', 'fr-crawler', 'binlar', 'SimpleCrawler', 'Twitterbot', 'cXensebot', 'smtbot', 'bnf.fr_bot', 'A6-Indexer', 'ADmantX', 'Facebot', 'OrangeBot\/', 'memorybot', 'AdvBot', 'MegaIndex', 'SemanticScholarBot', 'ltx71', 'nerdybot', 'xovibot', 'BUbiNG', 'Qwantify', 'archive.org_bot', 'Applebot', 'TweetmemeBot', 'crawler4j', 'findxbot', 'S[eE][mM]rushBot', 'yoozBot', 'lipperhey', 'Y!J', 'Domain Re-Animator Bot', 'AddThis', 'Screaming Frog SEO Spider', 'MetaURI', 'Scrapy', 'Livelap[bB]ot', 'OpenHoseBot', 'CapsuleChecker', 'collection@infegy.com', 'IstellaBot', 'DeuSu\/', 'betaBot', 'Cliqzbot\/', 'MojeekBot\/', 'netEstate NE Crawler', 'SafeSearch microdata crawler', 'Gluten Free Crawler\/', 'Sonic', 'Sysomos', 'Trove', 'deadlinkchecker', 'Slack-ImgProxy', 'Embedly', 'RankActiveLinkBot', 'iskanie', 'SafeDNSBot', 'SkypeUriPreview', 'Veoozbot', 'Slackbot', 'redditbot', 'datagnionbot', 'Google-Adwords-Instant', 'adbeat_bot', 'WhatsApp', 'contxbot', 'pinterest', 'electricmonk', 'GarlikCrawler', 'BingPreview\/', 'vebidoobot', 'FemtosearchBot', 'Yahoo Link Preview', 'MetaJobBot', 'DomainStatsBot', 'mindUpBot', 'Daum\/', 'Jugendschutzprogramm-Crawler', 'Xenu Link Sleuth', 'Pcore-HTTP', 'moatbot', 'KosmioBot', 'pingdom', 'PhantomJS', 'Gowikibot', 'PiplBot', 'Discordbot', 'TelegramBot', 'Jetslide', 'newsharecounts', 'James BOT', 'Barkrowler', 'TinEye', 'SocialRankIOBot', 'trendictionbot', 'Ocarinabot', 'epicbot', 'Primalbot', 'DuckDuckGo-Favicons-Bot', 'GnowitNewsbot', 'Leikibot', 'LinkArchiver', 'YaK\/', 'PaperLiBot', 'Digg Deeper', 'dcrawl', 'Snacktory', 'AndersPinkBot', 'Fyrebot', 'EveryoneSocialBot', 'Mediatoolkitbot', 'Luminator-robots', 'ExtLinksBot', 'SurveyBot', 'NING\/', 'okhttp', 'Nuzzel', 'omgili', 'PocketParser', 'YisouSpider', 'um-LN', 'ToutiaoSpider', 'MuckRack', 'Jamie\'s Spider', 'AHC\/', 'NetcraftSurveyAgent', 'Laserlikebot', 'Apache-HttpClient', 'AppEngine-Google', 'Jetty', 'Upflow', 'Thinklab', 'Traackr.com', 'Twurly', 'Mastodon', 'http_get', 'DnyzBot', 'botify', '007ac9 Crawler', 'BehloolBot', 'BrandVerity', 'check_http', 'BDCbot', 'ZumBot', 'EZID', 'ICC-Crawler', 'ArchiveBot', '^LCC ', 'filterdb.iss.net\/crawler', 'BLP_bbot', 'BomboraBot', 'Buck\/', 'Companybook-Crawler', 'Genieo', 'magpie-crawler', 'MeltwaterNews', 'Moreover', 'newspaper\/', 'ScoutJet', '(^| )sentry\/', 'StorygizeBot', 'UptimeRobot', 'OutclicksBot', 'seoscanners', 'Hatena', 'Google Web Preview', 'MauiBot', 'AlphaBot', 'SBL-BOT', 'IAS crawler', 'adscanner', 'Netvibes', 'acapbot', 'Baidu-YunGuanCe', 'bitlybot', 'blogmuraBot', 'Bot.AraTurka.com', 'bot-pge.chlooe.com', 'BoxcarBot', 'BTWebClient', 'ContextAd Bot', 'Digincore bot', 'Disqus', 'Feedly', 'Fetch\/', 'Fever', 'Flamingo_SearchEngine', 'FlipboardProxy', 'g2reader-bot', 'imrbot', 'K7MLWCBot', 'Kemvibot', 'Landau-Media-Spider', 'linkapediabot', 'vkShare', 'Siteimprove.com', 'BLEXBot\/', 'DareBoost', 'ZuperlistBot\/', 'Miniflux\/', 'Feedspotbot\/', 'Diffbot\/', 'SEOkicks', 'tracemyfile', 'Nimbostratus-Bot', 'zgrab', 'PR-CY.RU', 'AdsTxtCrawler', 'Datafeedwatch', 'Zabbix', 'TangibleeBot', 'google-xrawler', 'axios', 'Amazon CloudFront', 'Pulsepoint', ]; /** * @var boolean Is this a mobile browser? * @since 3.0.0 */ protected $mobile = false; /** * List of viewable image MIME subtypes. * This list of viewable images works for IE and Netscape/Mozilla. * * @var array * @since 3.0.0 */ protected $images = ['jpeg', 'gif', 'png', 'pjpeg', 'x-png', 'bmp']; /** * @var array Browser instances container. * @since 1.7.3 */ protected static $instances = []; /** * Create a browser instance (constructor). * * @param string $userAgent The browser string to parse. * @param string $accept The HTTP_ACCEPT settings to use. * * @since 1.7.0 */ public function __construct($userAgent = null, $accept = null) { $this->match($userAgent, $accept); } /** * Returns the global Browser object, only creating it * if it doesn't already exist. * * @param string $userAgent The browser string to parse. * @param string $accept The HTTP_ACCEPT settings to use. * * @return Browser The Browser object. * * @since 1.7.0 */ public static function getInstance($userAgent = null, $accept = null) { $signature = serialize([$userAgent, $accept]); if (empty(self::$instances[$signature])) { self::$instances[$signature] = new static($userAgent, $accept); } return self::$instances[$signature]; } /** * Parses the user agent string and inititializes the object with * all the known features and quirks for the given browser. * * @param string $userAgent The browser string to parse. * @param string $accept The HTTP_ACCEPT settings to use. * * @return void * * @since 1.7.0 */ public function match($userAgent = null, $accept = null) { // Set our agent string. if (\is_null($userAgent)) { if (isset($_SERVER['HTTP_USER_AGENT'])) { $this->agent = trim($_SERVER['HTTP_USER_AGENT']); } } else { $this->agent = $userAgent; } $this->lowerAgent = strtolower($this->agent); // Set our accept string. if (\is_null($accept)) { if (isset($_SERVER['HTTP_ACCEPT'])) { $this->accept = strtolower(trim($_SERVER['HTTP_ACCEPT'])); } } else { $this->accept = strtolower($accept); } if (!empty($this->agent)) { $this->_setPlatform(); /* * Determine if mobile. Note: Some Handhelds have their screen resolution in the * user agent string, which we can use to look for mobile agents. */ if ( strpos($this->agent, 'MOT-') !== false || strpos($this->lowerAgent, 'j-') !== false || preg_match('/(mobileexplorer|openwave|opera mini|opera mobi|operamini|avantgo|wap|elaine)/i', $this->agent) || preg_match('/(iPhone|iPod|iPad|Android|Mobile|Phone|BlackBerry|Xiino|Palmscape|palmsource)/i', $this->agent) || preg_match('/(Nokia|Ericsson|docomo|digital paths|portalmmm|CriOS[\/ ]([0-9.]+))/i', $this->agent) || preg_match('/(UP|UP.B|UP.L)/', $this->agent) || preg_match('/; (120x160|240x280|240x320|320x320)\)/', $this->agent) ) { $this->mobile = true; } /* * We have to check for Edge as the first browser, because Edge has something like: * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393 */ if (preg_match('|Edge\/([0-9.]+)|', $this->agent, $version)) { $this->setBrowser('edge'); if (strpos($version[1], '.') !== false) { list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } else { $this->majorVersion = $version[1]; $this->minorVersion = 0; } } elseif (preg_match('|Edg\/([0-9.]+)|', $this->agent, $version)) { /** * We have to check for Edge as the first browser, because Edge has something like: * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3738.0 Safari/537.36 Edg/75.0.107.0 */ $this->setBrowser('edg'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } elseif (preg_match('|Opera[\/ ]([0-9.]+)|', $this->agent, $version)) { $this->setBrowser('opera'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); /* * Due to changes in Opera UA, we need to check Version/xx.yy, * but only if version is > 9.80. See: http://dev.opera.com/articles/view/opera-ua-string-changes/ */ if ($this->majorVersion == 9 && $this->minorVersion >= 80) { $this->identifyBrowserVersion(); } } elseif (preg_match('/OPR[\/ ]([0-9.]+)/', $this->agent, $version)) { // Opera 15+ $this->setBrowser('opera'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } elseif ( preg_match('/Chrome[\/ ]([0-9.]+)/i', $this->agent, $version) || preg_match('/CrMo[\/ ]([0-9.]+)/i', $this->agent, $version) || preg_match('/CriOS[\/ ]([0-9.]+)/i', $this->agent, $version) ) { $this->setBrowser('chrome'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } elseif ( strpos($this->lowerAgent, 'elaine/') !== false || strpos($this->lowerAgent, 'palmsource') !== false || strpos($this->lowerAgent, 'digital paths') !== false ) { $this->setBrowser('palm'); } elseif ( preg_match('/MSIE ([0-9.]+)/i', $this->agent, $version) || preg_match('/IE ([0-9.]+)/i', $this->agent, $version) || preg_match('/Internet Explorer[\/ ]([0-9.]+)/i', $this->agent, $version) || preg_match('/Trident\/.*rv:([0-9.]+)/i', $this->agent, $version) ) { $this->setBrowser('msie'); // Special case for IE 11+ if (strpos($version[0], 'Trident') !== false && strpos($version[0], 'rv:') !== false) { preg_match('|rv:([0-9.]+)|', $this->agent, $version); } if (strpos($version[1], '.') !== false) { list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } else { $this->majorVersion = $version[1]; $this->minorVersion = 0; } } elseif (preg_match('|amaya\/([0-9.]+)|', $this->agent, $version)) { $this->setBrowser('amaya'); $this->majorVersion = $version[1]; if (isset($version[2])) { $this->minorVersion = $version[2]; } } elseif (preg_match('|ANTFresco\/([0-9]+)|', $this->agent, $version)) { $this->setBrowser('fresco'); } elseif (strpos($this->lowerAgent, 'avantgo') !== false) { $this->setBrowser('avantgo'); } elseif (preg_match('|[Kk]onqueror\/([0-9]+)|', $this->agent, $version) || preg_match('|Safari/([0-9]+)\.?([0-9]+)?|', $this->agent, $version)) { // Konqueror and Apple's Safari both use the KHTML rendering engine. $this->setBrowser('konqueror'); $this->majorVersion = $version[1]; if (isset($version[2])) { $this->minorVersion = $version[2]; } if (strpos($this->agent, 'Safari') !== false && $this->majorVersion >= 60) { // Safari. $this->setBrowser('safari'); $this->identifyBrowserVersion(); } } elseif (preg_match('|Firefox\/([0-9.]+)|', $this->agent, $version)) { $this->setBrowser('firefox'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } elseif (preg_match('|Lynx\/([0-9]+)|', $this->agent, $version)) { $this->setBrowser('lynx'); } elseif (preg_match('|Links \(([0-9]+)|', $this->agent, $version)) { $this->setBrowser('links'); } elseif (preg_match('|HotJava\/([0-9]+)|', $this->agent, $version)) { $this->setBrowser('hotjava'); } elseif (strpos($this->agent, 'UP/') !== false || strpos($this->agent, 'UP.B') !== false || strpos($this->agent, 'UP.L') !== false) { $this->setBrowser('up'); } elseif (strpos($this->agent, 'Xiino/') !== false) { $this->setBrowser('xiino'); } elseif (strpos($this->agent, 'Palmscape/') !== false) { $this->setBrowser('palmscape'); } elseif (strpos($this->agent, 'Nokia') !== false) { $this->setBrowser('nokia'); } elseif (strpos($this->agent, 'Ericsson') !== false) { $this->setBrowser('ericsson'); } elseif (strpos($this->lowerAgent, 'wap') !== false) { $this->setBrowser('wap'); } elseif (strpos($this->lowerAgent, 'docomo') !== false || strpos($this->lowerAgent, 'portalmmm') !== false) { $this->setBrowser('imode'); } elseif (strpos($this->agent, 'BlackBerry') !== false) { $this->setBrowser('blackberry'); } elseif (strpos($this->agent, 'MOT-') !== false) { $this->setBrowser('motorola'); } elseif (strpos($this->lowerAgent, 'j-') !== false) { $this->setBrowser('mml'); } elseif (preg_match('|Mozilla\/([0-9.]+)|', $this->agent, $version)) { $this->setBrowser('mozilla'); list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); } } } /** * Match the platform of the browser. * * This is a pretty simplistic implementation, but it's intended * to let us tell what line breaks to send, so it's good enough * for its purpose. * * @return void * * @since 1.7.0 */ protected function _setPlatform() { if (strpos($this->lowerAgent, 'wind') !== false) { $this->platform = 'win'; } elseif (strpos($this->lowerAgent, 'mac') !== false) { $this->platform = 'mac'; } else { $this->platform = 'unix'; } } /** * Return the currently matched platform. * * @return string The user's platform. * * @since 1.7.0 */ public function getPlatform() { return $this->platform; } /** * Set browser version, not by engine version * Fallback to use when no other method identify the engine version * * @return void * * @since 1.7.0 */ protected function identifyBrowserVersion() { if (preg_match('|Version[/ ]([0-9.]+)|', $this->agent, $version)) { list($this->majorVersion, $this->minorVersion) = explode('.', $version[1]); return; } // Can't identify browser version $this->majorVersion = 0; $this->minorVersion = 0; } /** * Sets the current browser. * * @param string $browser The browser to set as current. * * @return void * * @since 1.7.0 */ public function setBrowser($browser) { $this->browser = $browser; } /** * Retrieve the current browser. * * @return string The current browser. * * @since 1.7.0 */ public function getBrowser() { return $this->browser; } /** * Retrieve the current browser's major version. * * @return integer The current browser's major version * * @since 1.7.0 */ public function getMajor() { return $this->majorVersion; } /** * Retrieve the current browser's minor version. * * @return integer The current browser's minor version. * * @since 1.7.0 */ public function getMinor() { return $this->minorVersion; } /** * Retrieve the current browser's version. * * @return string The current browser's version. * * @since 1.7.0 */ public function getVersion() { return $this->majorVersion . '.' . $this->minorVersion; } /** * Return the full browser agent string. * * @return string The browser agent string * * @since 1.7.0 */ public function getAgentString() { return $this->agent; } /** * Returns the server protocol in use on the current server. * * @return string The HTTP server protocol version. * * @since 1.7.0 */ public function getHTTPProtocol() { if (isset($_SERVER['SERVER_PROTOCOL'])) { if (($pos = strrpos($_SERVER['SERVER_PROTOCOL'], '/'))) { return substr($_SERVER['SERVER_PROTOCOL'], $pos + 1); } } } /** * Determines if a browser can display a given MIME type. * * Note that image/jpeg and image/pjpeg *appear* to be the same * entity, but Mozilla doesn't seem to want to accept the latter. * For our purposes, we will treat them the same. * * @param string $mimetype The MIME type to check. * * @return boolean True if the browser can display the MIME type. * * @since 1.7.0 */ public function isViewable($mimetype) { $mimetype = strtolower($mimetype); list($type, $subtype) = explode('/', $mimetype); if (!empty($this->accept)) { $wildcard_match = false; if (strpos($this->accept, $mimetype) !== false) { return true; } if (strpos($this->accept, '*/*') !== false) { $wildcard_match = true; if ($type !== 'image') { return true; } } // Deal with Mozilla pjpeg/jpeg issue if ($this->isBrowser('mozilla') && ($mimetype === 'image/pjpeg') && (strpos($this->accept, 'image/jpeg') !== false)) { return true; } if (!$wildcard_match) { return false; } } if ($type !== 'image') { return false; } return \in_array($subtype, $this->images); } /** * Determine if the given browser is the same as the current. * * @param string $browser The browser to check. * * @return boolean Is the given browser the same as the current? * * @since 1.7.0 */ public function isBrowser($browser) { return $this->browser === $browser; } /** * Determines if the browser is a robot or not. * * @return boolean True if browser is a known robot. * * @since 1.7.0 */ public function isRobot() { foreach ($this->robots as $robot) { if (preg_match('/' . $robot . '/', $this->agent)) { return true; } } return false; } /** * Determines if the browser is mobile version or not. * * @return boolean True if browser is a known mobile version. * * @since 1.7.0 */ public function isMobile() { return $this->mobile; } } PK1A#]{�Z _ _src/Microdata/Microdata.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Microdata; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform class for interacting with Microdata semantics. * * @since 3.2 */ class Microdata { /** * Array with all available Types and Properties from the http://schema.org vocabulary * * @var array * @since 3.2 */ protected static $types = null; /** * The Type * * @var string * @since 3.2 */ protected $type = null; /** * The Property * * @var string * @since 3.2 */ protected $property = null; /** * The Human content * * @var string * @since 3.2 */ protected $content = null; /** * The Machine content * * @var string * @since 3.2 */ protected $machineContent = null; /** * The Fallback Type * * @var string * @since 3.2 */ protected $fallbackType = null; /** * The Fallback Property * * @var string * @since 3.2 */ protected $fallbackProperty = null; /** * Used for checking if the library output is enabled or disabled * * @var boolean * @since 3.2 */ protected $enabled = true; /** * Initialize the class and setup the default $Type * * @param string $type Optional, fallback to 'Thing' Type * @param boolean $flag Enable or disable the library output * * @since 3.2 */ public function __construct($type = '', $flag = true) { if ($this->enabled = (bool) $flag) { // Fallback to 'Thing' Type if (!$type) { $type = 'Thing'; } $this->setType($type); } } /** * Load all available Types and Properties from the http://schema.org vocabulary contained in the types.json file * * @return void * * @since 3.2 */ protected static function loadTypes() { // Load the JSON if (!static::$types) { $path = __DIR__ . '/types.json'; static::$types = json_decode(file_get_contents($path), true); } } /** * Reset all params * * @return void * * @since 3.2 */ protected function resetParams() { $this->content = null; $this->machineContent = null; $this->property = null; $this->fallbackProperty = null; $this->fallbackType = null; } /** * Enable or Disable the library output * * @param boolean $flag Enable or disable the library output * * @return Microdata Instance of $this * * @since 3.2 */ public function enable($flag = true) { $this->enabled = (bool) $flag; return $this; } /** * Return 'true' if the library output is enabled * * @return boolean * * @since 3.2 */ public function isEnabled() { return $this->enabled; } /** * Set a new http://schema.org Type * * @param string $type The $Type to be setup * * @return Microdata Instance of $this * * @since 3.2 */ public function setType($type) { if (!$this->enabled) { return $this; } // Sanitize the Type $this->type = static::sanitizeType($type); // If the given $Type isn't available, fallback to 'Thing' Type if (!static::isTypeAvailable($this->type)) { $this->type = 'Thing'; } return $this; } /** * Return the current $Type name * * @return string * * @since 3.2 */ public function getType() { return $this->type; } /** * Setup a $Property * * @param string $property The Property * * @return Microdata Instance of $this * * @since 3.2 */ public function property($property) { if (!$this->enabled) { return $this; } // Sanitize the $Property $property = static::sanitizeProperty($property); // Control if the $Property exists in the given $Type and setup it, otherwise leave it 'NULL' if (static::isPropertyInType($this->type, $property)) { $this->property = $property; } return $this; } /** * Return the current $Property name * * @return string * * @since 3.2 */ public function getProperty() { return $this->property; } /** * Setup a Human content or content for the Machines * * @param string $content The human content or machine content to be used * @param string $machineContent The machine content * * @return Microdata Instance of $this * * @since 3.2 */ public function content($content, $machineContent = null) { $this->content = $content; $this->machineContent = $machineContent; return $this; } /** * Return the current $content * * @return string * * @since 3.2 */ public function getContent() { return $this->content; } /** * Return the current $machineContent * * @return string * * @since 3.3 */ public function getMachineContent() { return $this->machineContent; } /** * Setup a Fallback Type and Property * * @param string $type The Fallback Type * @param string $property The Fallback Property * * @return Microdata Instance of $this * * @since 3.2 */ public function fallback($type, $property) { if (!$this->enabled) { return $this; } // Sanitize the $Type $this->fallbackType = static::sanitizeType($type); // If the given $Type isn't available, fallback to 'Thing' Type if (!static::isTypeAvailable($this->fallbackType)) { $this->fallbackType = 'Thing'; } // Control if the $Property exist in the given $Type and setup it, otherwise leave it 'NULL' if (static::isPropertyInType($this->fallbackType, $property)) { $this->fallbackProperty = $property; } else { $this->fallbackProperty = null; } return $this; } /** * Return the current $fallbackType * * @return string * * @since 3.2 */ public function getFallbackType() { return $this->fallbackType; } /** * Return the current $fallbackProperty * * @return string * * @since 3.2 */ public function getFallbackProperty() { return $this->fallbackProperty; } /** * This function handles the display logic. * It checks if the Type, Property are available, if not check for a Fallback, * then reset all params for the next use and return the HTML. * * @param string $displayType Optional, 'inline', available options ['inline'|'span'|'div'|meta] * @param boolean $emptyOutput Return an empty string if the library output is disabled and there is a $content value * * @return string * * @since 3.2 */ public function display($displayType = '', $emptyOutput = false) { // Initialize the HTML to output $html = ($this->content !== null && !$emptyOutput) ? $this->content : ''; // Control if the library output is enabled, otherwise return the $content or an empty string if (!$this->enabled) { // Reset params $this->resetParams(); return $html; } // If the $property is wrong for the current $Type check if a Fallback is available, otherwise return an empty HTML if ($this->property) { // Process and return the HTML the way the user expects to if ($displayType) { switch ($displayType) { case 'span': $html = static::htmlSpan($html, $this->property); break; case 'div': $html = static::htmlDiv($html, $this->property); break; case 'meta': $html = $this->machineContent ?? $html; $html = static::htmlMeta($html, $this->property); break; default: // Default $displayType = 'inline' $html = static::htmlProperty($this->property); break; } } else { /* * Process and return the HTML in an automatic way, * with the $Property expected Types and display everything in the right way, * check if the $Property is 'normal', 'nested' or must be rendered in a metadata tag */ switch (static::getExpectedDisplayType($this->type, $this->property)) { case 'nested': // Retrieve the expected 'nested' Type of the $Property $nestedType = static::getExpectedTypes($this->type, $this->property); $nestedProperty = ''; // If there is a Fallback Type then probably it could be the expectedType if (\in_array($this->fallbackType, $nestedType)) { $nestedType = $this->fallbackType; if ($this->fallbackProperty) { $nestedProperty = $this->fallbackProperty; } } else { $nestedType = $nestedType[0]; } // Check if a $content is available, otherwise fallback to an 'inline' display type if ($this->content !== null) { if ($nestedProperty) { $html = static::htmlSpan( $this->content, $nestedProperty ); } $html = static::htmlSpan( $html, $this->property, $nestedType, true ); } else { $html = static::htmlProperty($this->property) . ' ' . static::htmlScope($nestedType); if ($nestedProperty) { $html .= ' ' . static::htmlProperty($nestedProperty); } } break; case 'meta': // Check if a $content is available, otherwise fallback to an 'inline' display type if ($this->content !== null) { $html = $this->machineContent ?? $this->content; $html = static::htmlMeta($html, $this->property) . $this->content; } else { $html = static::htmlProperty($this->property); } break; default: /* * Default expected display type = 'normal' * Check if a $content is available, * otherwise fallback to an 'inline' display type */ if ($this->content !== null) { $html = static::htmlSpan($this->content, $this->property); } else { $html = static::htmlProperty($this->property); } break; } } } elseif ($this->fallbackProperty) { // Process and return the HTML the way the user expects to if ($displayType) { switch ($displayType) { case 'span': $html = static::htmlSpan($html, $this->fallbackProperty, $this->fallbackType); break; case 'div': $html = static::htmlDiv($html, $this->fallbackProperty, $this->fallbackType); break; case 'meta': $html = $this->machineContent ?? $html; $html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType); break; default: // Default $displayType = 'inline' $html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty); break; } } else { /* * Process and return the HTML in an automatic way, * with the $Property expected Types and display everything in the right way, * check if the Property is 'nested' or must be rendered in a metadata tag */ switch (static::getExpectedDisplayType($this->fallbackType, $this->fallbackProperty)) { case 'meta': // Check if a $content is available, otherwise fallback to an 'inline' display Type if ($this->content !== null) { $html = $this->machineContent ?? $this->content; $html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType); } else { $html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty); } break; default: /* * Default expected display type = 'normal' * Check if a $content is available, * otherwise fallback to an 'inline' display Type */ if ($this->content !== null) { $html = static::htmlSpan($this->content, $this->fallbackProperty); $html = static::htmlSpan($html, '', $this->fallbackType); } else { $html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty); } break; } } } elseif (!$this->fallbackProperty && $this->fallbackType !== null) { $html = static::htmlScope($this->fallbackType); } // Reset params $this->resetParams(); return $html; } /** * Return the HTML of the current Scope * * @return string * * @since 3.2 */ public function displayScope() { // Control if the library output is enabled, otherwise return the $content or empty string if (!$this->enabled) { return ''; } return static::htmlScope($this->type); } /** * Return the sanitized $Type * * @param string $type The Type to sanitize * * @return string * * @since 3.2 */ public static function sanitizeType($type) { return ucfirst(trim($type)); } /** * Return the sanitized $Property * * @param string $property The Property to sanitize * * @return string * * @since 3.2 */ public static function sanitizeProperty($property) { return lcfirst(trim($property)); } /** * Return an array with all available Types and Properties from the http://schema.org vocabulary * * @return array * * @since 3.2 */ public static function getTypes() { static::loadTypes(); return static::$types; } /** * Return an array with all available Types from the http://schema.org vocabulary * * @return array * * @since 3.2 */ public static function getAvailableTypes() { static::loadTypes(); return array_keys(static::$types); } /** * Return the expected Types of the given Property * * @param string $type The Type to process * @param string $property The Property to process * * @return array * * @since 3.2 */ public static function getExpectedTypes($type, $property) { static::loadTypes(); $tmp = static::$types[$type]['properties']; // Check if the $Property is in the $Type if (isset($tmp[$property])) { return $tmp[$property]['expectedTypes']; } // Check if the $Property is inherit $extendedType = static::$types[$type]['extends']; // Recursive if (!empty($extendedType)) { return static::getExpectedTypes($extendedType, $property); } return []; } /** * Return the expected display type: [normal|nested|meta] * In which way to display the Property: * normal -> itemprop="name" * nested -> itemprop="director" itemscope itemtype="https://schema.org/Person" * meta -> `<meta itemprop="datePublished" content="1991-05-01">` * * @param string $type The Type where to find the Property * @param string $property The Property to process * * @return string * * @since 3.2 */ protected static function getExpectedDisplayType($type, $property) { $expectedTypes = static::getExpectedTypes($type, $property); // Retrieve the first expected type $type = $expectedTypes[0]; // Check if it's a 'meta' display if ($type === 'Date' || $type === 'DateTime' || $property === 'interactionCount') { return 'meta'; } // Check if it's a 'normal' display if ($type === 'Text' || $type === 'URL' || $type === 'Boolean' || $type === 'Number') { return 'normal'; } // Otherwise it's a 'nested' display return 'nested'; } /** * Recursive function, control if the given Type has the given Property * * @param string $type The Type where to check * @param string $property The Property to check * * @return boolean * * @since 3.2 */ public static function isPropertyInType($type, $property) { if (!static::isTypeAvailable($type)) { return false; } // Control if the $Property exists, and return 'true' if (\array_key_exists($property, static::$types[$type]['properties'])) { return true; } // Recursive: Check if the $Property is inherit $extendedType = static::$types[$type]['extends']; if (!empty($extendedType)) { return static::isPropertyInType($extendedType, $property); } return false; } /** * Control if the given Type class is available * * @param string $type The Type to check * * @return boolean * * @since 3.2 */ public static function isTypeAvailable($type) { static::loadTypes(); return \array_key_exists($type, static::$types); } /** * Return Microdata semantics in a `<meta>` tag with content for machines. * * @param string $content The machine content to display * @param string $property The Property * @param string $scope Optional, the Type scope to display * @param boolean $invert Optional, default = false, invert the $scope with the $property * * @return string * * @since 3.2 */ public static function htmlMeta($content, $property, $scope = '', $invert = false) { return static::htmlTag('meta', $content, $property, $scope, $invert); } /** * Return Microdata semantics in a `<span>` tag. * * @param string $content The human content * @param string $property Optional, the human content to display * @param string $scope Optional, the Type scope to display * @param boolean $invert Optional, default = false, invert the $scope with the $property * * @return string * * @since 3.2 */ public static function htmlSpan($content, $property = '', $scope = '', $invert = false) { return static::htmlTag('span', $content, $property, $scope, $invert); } /** * Return Microdata semantics in a `<div>` tag. * * @param string $content The human content * @param string $property Optional, the human content to display * @param string $scope Optional, the Type scope to display * @param boolean $invert Optional, default = false, invert the $scope with the $property * * @return string * * @since 3.2 */ public static function htmlDiv($content, $property = '', $scope = '', $invert = false) { return static::htmlTag('div', $content, $property, $scope, $invert); } /** * Return Microdata semantics in a specified tag. * * @param string $tag The HTML tag * @param string $content The human content * @param string $property Optional, the human content to display * @param string $scope Optional, the Type scope to display * @param boolean $invert Optional, default = false, invert the $scope with the $property * * @return string * * @since 3.3 */ public static function htmlTag($tag, $content, $property = '', $scope = '', $invert = false) { // Control if the $Property has already the 'itemprop' prefix if (!empty($property) && stripos($property, 'itemprop') !== 0) { $property = static::htmlProperty($property); } // Control if the $Scope have already the 'itemscope' prefix if (!empty($scope) && stripos($scope, 'itemscope') !== 0) { $scope = static::htmlScope($scope); } // Depending on the case, the $scope must precede the $property, or otherwise if ($invert) { $tmp = implode(' ', [$property, $scope]); } else { $tmp = implode(' ', [$scope, $property]); } $tmp = trim($tmp); $tmp = ($tmp) ? ' ' . $tmp : ''; // Control if it is an empty element without a closing tag if ($tag === 'meta') { return "<meta$tmp content='$content'>"; } return '<' . $tag . $tmp . '>' . $content . '</' . $tag . '>'; } /** * Return the HTML Scope * * @param string $scope The Scope to process * * @return string * * @since 3.2 */ public static function htmlScope($scope) { return "itemscope itemtype='https://schema.org/" . static::sanitizeType($scope) . "'"; } /** * Return the HTML Property * * @param string $property The Property to process * * @return string * * @since 3.2 */ public static function htmlProperty($property) { return "itemprop='$property'"; } } PK1A#]i�m�d�dsrc/Microdata/types.jsonnu�[���{"DataType":{"extends":"","properties":[]},"Boolean":{"extends":"DataType","properties":[]},"False":{"extends":"Boolean","properties":[]},"True":{"extends":"Boolean","properties":[]},"Date":{"extends":"DataType","properties":[]},"DateTime":{"extends":"DataType","properties":[]},"Number":{"extends":"DataType","properties":[]},"Float":{"extends":"Number","properties":[]},"Integer":{"extends":"Number","properties":[]},"Text":{"extends":"DataType","properties":[]},"URL":{"extends":"Text","properties":[]},"Time":{"extends":"DataType","properties":[]},"Thing":{"extends":"","properties":{"additionalType":{"expectedTypes":["URL"]},"alternateName":{"expectedTypes":["Text"]},"description":{"expectedTypes":["Text"]},"image":{"expectedTypes":["URL","ImageObject"]},"name":{"expectedTypes":["Text"]},"potentialAction":{"expectedTypes":["Action"]},"sameAs":{"expectedTypes":["URL"]},"url":{"expectedTypes":["URL"]}}},"Action":{"extends":"Thing","properties":{"actionStatus":{"expectedTypes":["ActionStatusType"]},"agent":{"expectedTypes":["Organization","Person"]},"endTime":{"expectedTypes":["DateTime"]},"error":{"expectedTypes":["Thing"]},"instrument":{"expectedTypes":["Thing"]},"location":{"expectedTypes":["PostalAddress","Place"]},"object":{"expectedTypes":["Thing"]},"participant":{"expectedTypes":["Organization","Person"]},"result":{"expectedTypes":["Thing"]},"startTime":{"expectedTypes":["DateTime"]},"target":{"expectedTypes":["EntryPoint"]}}},"AchieveAction":{"extends":"Action","properties":[]},"LoseAction":{"extends":"AchieveAction","properties":{"winner":{"expectedTypes":["Person"]}}},"TieAction":{"extends":"AchieveAction","properties":[]},"WinAction":{"extends":"AchieveAction","properties":{"loser":{"expectedTypes":["Person"]}}},"AssessAction":{"extends":"Action","properties":[]},"ChooseAction":{"extends":"AssessAction","properties":{"option":{"expectedTypes":["Text","Thing"]}}},"VoteAction":{"extends":"ChooseAction","properties":{"candidate":{"expectedTypes":["Person"]}}},"IgnoreAction":{"extends":"AssessAction","properties":[]},"ReactAction":{"extends":"AssessAction","properties":[]},"AgreeAction":{"extends":"ReactAction","properties":[]},"DisagreeAction":{"extends":"ReactAction","properties":[]},"DislikeAction":{"extends":"ReactAction","properties":[]},"EndorseAction":{"extends":"ReactAction","properties":{"endorsee":{"expectedTypes":["Organization","Person"]}}},"LikeAction":{"extends":"ReactAction","properties":[]},"WantAction":{"extends":"ReactAction","properties":[]},"ReviewAction":{"extends":"AssessAction","properties":{"resultReview":{"expectedTypes":["Review"]}}},"ConsumeAction":{"extends":"Action","properties":{"expectsAcceptanceOf":{"expectedTypes":["Offer"]}}},"DrinkAction":{"extends":"ConsumeAction","properties":[]},"EatAction":{"extends":"ConsumeAction","properties":[]},"InstallAction":{"extends":"ConsumeAction","properties":[]},"ListenAction":{"extends":"ConsumeAction","properties":[]},"ReadAction":{"extends":"ConsumeAction","properties":[]},"UseAction":{"extends":"ConsumeAction","properties":[]},"WearAction":{"extends":"UseAction","properties":[]},"ViewAction":{"extends":"ConsumeAction","properties":[]},"WatchAction":{"extends":"ConsumeAction","properties":[]},"ControlAction":{"extends":"Action","properties":[]},"ActivateAction":{"extends":"ControlAction","properties":[]},"DeactivateAction":{"extends":"ControlAction","properties":[]},"ResumeAction":{"extends":"ControlAction","properties":[]},"SuspendAction":{"extends":"ControlAction","properties":[]},"CreateAction":{"extends":"Action","properties":[]},"CookAction":{"extends":"CreateAction","properties":{"foodEstablishment":{"expectedTypes":["FoodEstablishment","Place"]},"foodEvent":{"expectedTypes":["FoodEvent"]},"recipe":{"expectedTypes":["Recipe"]}}},"DrawAction":{"extends":"CreateAction","properties":[]},"FilmAction":{"extends":"CreateAction","properties":[]},"PaintAction":{"extends":"CreateAction","properties":[]},"PhotographAction":{"extends":"CreateAction","properties":[]},"WriteAction":{"extends":"CreateAction","properties":{"language":{"expectedTypes":["Language"]}}},"FindAction":{"extends":"Action","properties":[]},"CheckAction":{"extends":"FindAction","properties":[]},"DiscoverAction":{"extends":"FindAction","properties":[]},"TrackAction":{"extends":"FindAction","properties":{"deliveryMethod":{"expectedTypes":["DeliveryMethod"]}}},"InteractAction":{"extends":"Action","properties":[]},"BefriendAction":{"extends":"InteractAction","properties":[]},"CommunicateAction":{"extends":"InteractAction","properties":{"about":{"expectedTypes":["Thing"]},"language":{"expectedTypes":["Language"]},"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"AskAction":{"extends":"CommunicateAction","properties":{"question":{"expectedTypes":["Text"]}}},"CheckInAction":{"extends":"CommunicateAction","properties":[]},"CheckOutAction":{"extends":"CommunicateAction","properties":[]},"CommentAction":{"extends":"CommunicateAction","properties":[]},"InformAction":{"extends":"CommunicateAction","properties":{"event":{"expectedTypes":["Event"]}}},"ConfirmAction":{"extends":"InformAction","properties":[]},"RsvpAction":{"extends":"InformAction","properties":{"additionalNumberOfGuests":{"expectedTypes":["Number"]},"rsvpResponse":{"expectedTypes":["RsvpResponseType"]}}},"InviteAction":{"extends":"CommunicateAction","properties":{"event":{"expectedTypes":["Event"]}}},"ReplyAction":{"extends":"CommunicateAction","properties":[]},"ShareAction":{"extends":"CommunicateAction","properties":[]},"FollowAction":{"extends":"InteractAction","properties":{"followee":{"expectedTypes":["Organization","Person"]}}},"JoinAction":{"extends":"InteractAction","properties":{"event":{"expectedTypes":["Event"]}}},"LeaveAction":{"extends":"InteractAction","properties":{"event":{"expectedTypes":["Event"]}}},"MarryAction":{"extends":"InteractAction","properties":[]},"RegisterAction":{"extends":"InteractAction","properties":[]},"SubscribeAction":{"extends":"InteractAction","properties":[]},"UnRegisterAction":{"extends":"InteractAction","properties":[]},"MoveAction":{"extends":"Action","properties":{"fromLocation":{"expectedTypes":["Place"]},"toLocation":{"expectedTypes":["Place"]}}},"ArriveAction":{"extends":"MoveAction","properties":[]},"DepartAction":{"extends":"MoveAction","properties":[]},"TravelAction":{"extends":"MoveAction","properties":{"distance":{"expectedTypes":["Distance"]}}},"OrganizeAction":{"extends":"Action","properties":[]},"AllocateAction":{"extends":"OrganizeAction","properties":{"purpose":{"expectedTypes":["MedicalDevicePurpose","Thing"]}}},"AcceptAction":{"extends":"AllocateAction","properties":[]},"AssignAction":{"extends":"AllocateAction","properties":[]},"AuthorizeAction":{"extends":"AllocateAction","properties":{"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"RejectAction":{"extends":"AllocateAction","properties":[]},"ApplyAction":{"extends":"OrganizeAction","properties":[]},"BookmarkAction":{"extends":"OrganizeAction","properties":[]},"PlanAction":{"extends":"OrganizeAction","properties":{"scheduledTime":{"expectedTypes":["DateTime"]}}},"CancelAction":{"extends":"PlanAction","properties":[]},"ReserveAction":{"extends":"PlanAction","properties":[]},"ScheduleAction":{"extends":"PlanAction","properties":[]},"PlayAction":{"extends":"Action","properties":{"audience":{"expectedTypes":["Audience"]},"event":{"expectedTypes":["Event"]}}},"ExerciseAction":{"extends":"PlayAction","properties":{"course":{"expectedTypes":["Place"]},"diet":{"expectedTypes":["Diet"]},"distance":{"expectedTypes":["Distance"]},"exercisePlan":{"expectedTypes":["ExercisePlan"]},"exerciseType":{"expectedTypes":["Text"]},"fromLocation":{"expectedTypes":["Place"]},"opponent":{"expectedTypes":["Person"]},"sportsActivityLocation":{"expectedTypes":["SportsActivityLocation"]},"sportsEvent":{"expectedTypes":["SportsEvent"]},"sportsTeam":{"expectedTypes":["SportsTeam"]},"toLocation":{"expectedTypes":["Place"]}}},"PerformAction":{"extends":"PlayAction","properties":{"entertainmentBusiness":{"expectedTypes":["EntertainmentBusiness"]}}},"SearchAction":{"extends":"Action","properties":{"query":{"expectedTypes":["Text","Class"]}}},"TradeAction":{"extends":"Action","properties":{"price":{"expectedTypes":["Text","Number"]},"priceSpecification":{"expectedTypes":["PriceSpecification"]}}},"BuyAction":{"extends":"TradeAction","properties":{"seller":{"expectedTypes":["Organization","Person"]},"warrantyPromise":{"expectedTypes":["WarrantyPromise"]}}},"DonateAction":{"extends":"TradeAction","properties":{"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"OrderAction":{"extends":"TradeAction","properties":{"deliveryMethod":{"expectedTypes":["DeliveryMethod"]}}},"PayAction":{"extends":"TradeAction","properties":{"purpose":{"expectedTypes":["MedicalDevicePurpose","Thing"]},"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"QuoteAction":{"extends":"TradeAction","properties":[]},"RentAction":{"extends":"TradeAction","properties":{"landlord":{"expectedTypes":["Organization","Person"]},"realEstateAgent":{"expectedTypes":["RealEstateAgent"]}}},"SellAction":{"extends":"TradeAction","properties":{"buyer":{"expectedTypes":["Person"]},"warrantyPromise":{"expectedTypes":["WarrantyPromise"]}}},"TipAction":{"extends":"TradeAction","properties":{"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"TransferAction":{"extends":"Action","properties":{"fromLocation":{"expectedTypes":["Place"]},"toLocation":{"expectedTypes":["Place"]}}},"BorrowAction":{"extends":"TransferAction","properties":{"lender":{"expectedTypes":["Person"]}}},"DownloadAction":{"extends":"TransferAction","properties":[]},"GiveAction":{"extends":"TransferAction","properties":{"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"LendAction":{"extends":"TransferAction","properties":{"borrower":{"expectedTypes":["Person"]}}},"ReceiveAction":{"extends":"TransferAction","properties":{"deliveryMethod":{"expectedTypes":["DeliveryMethod"]},"sender":{"expectedTypes":["Organization","Person","Audience"]}}},"ReturnAction":{"extends":"TransferAction","properties":{"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"SendAction":{"extends":"TransferAction","properties":{"deliveryMethod":{"expectedTypes":["DeliveryMethod"]},"recipient":{"expectedTypes":["Organization","Person","Audience"]}}},"TakeAction":{"extends":"TransferAction","properties":[]},"UpdateAction":{"extends":"Action","properties":{"collection":{"expectedTypes":["Thing"]}}},"AddAction":{"extends":"UpdateAction","properties":[]},"InsertAction":{"extends":"AddAction","properties":{"toLocation":{"expectedTypes":["Place"]}}},"AppendAction":{"extends":"InsertAction","properties":[]},"PrependAction":{"extends":"InsertAction","properties":[]},"DeleteAction":{"extends":"UpdateAction","properties":[]},"ReplaceAction":{"extends":"UpdateAction","properties":{"replacee":{"expectedTypes":["Thing"]},"replacer":{"expectedTypes":["Thing"]}}},"BroadcastService":{"extends":"Thing","properties":{"area":{"expectedTypes":["Place"]},"broadcaster":{"expectedTypes":["Organization"]},"parentService":{"expectedTypes":["BroadcastService"]}}},"CreativeWork":{"extends":"Thing","properties":{"about":{"expectedTypes":["Thing"]},"accessibilityAPI":{"expectedTypes":["Text"]},"accessibilityControl":{"expectedTypes":["Text"]},"accessibilityFeature":{"expectedTypes":["Text"]},"accessibilityHazard":{"expectedTypes":["Text"]},"accountablePerson":{"expectedTypes":["Person"]},"aggregateRating":{"expectedTypes":["AggregateRating"]},"alternativeHeadline":{"expectedTypes":["Text"]},"associatedMedia":{"expectedTypes":["MediaObject"]},"audience":{"expectedTypes":["Audience"]},"audio":{"expectedTypes":["AudioObject"]},"author":{"expectedTypes":["Organization","Person"]},"award":{"expectedTypes":["Text"]},"character":{"expectedTypes":["Person"]},"citation":{"expectedTypes":["CreativeWork","Text"]},"comment":{"expectedTypes":["UserComments","Comment"]},"commentCount":{"expectedTypes":["Integer"]},"contentLocation":{"expectedTypes":["Place"]},"contentRating":{"expectedTypes":["Text"]},"contributor":{"expectedTypes":["Organization","Person"]},"copyrightHolder":{"expectedTypes":["Organization","Person"]},"copyrightYear":{"expectedTypes":["Number"]},"creator":{"expectedTypes":["Organization","Person"]},"dateCreated":{"expectedTypes":["Date"]},"dateModified":{"expectedTypes":["Date"]},"datePublished":{"expectedTypes":["Date"]},"discussionUrl":{"expectedTypes":["URL"]},"editor":{"expectedTypes":["Person"]},"educationalAlignment":{"expectedTypes":["AlignmentObject"]},"educationalUse":{"expectedTypes":["Text"]},"encoding":{"expectedTypes":["MediaObject"]},"exampleOfWork":{"expectedTypes":["CreativeWork"]},"genre":{"expectedTypes":["Text"]},"hasPart":{"expectedTypes":["CreativeWork"]},"headline":{"expectedTypes":["Text"]},"inLanguage":{"expectedTypes":["Text"]},"interactionCount":{"expectedTypes":["Text"]},"interactivityType":{"expectedTypes":["Text"]},"isBasedOnUrl":{"expectedTypes":["URL"]},"isFamilyFriendly":{"expectedTypes":["Boolean"]},"isPartOf":{"expectedTypes":["CreativeWork"]},"keywords":{"expectedTypes":["Text"]},"learningResourceType":{"expectedTypes":["Text"]},"license":{"expectedTypes":["CreativeWork","URL"]},"mentions":{"expectedTypes":["Thing"]},"offers":{"expectedTypes":["Offer"]},"position":{"expectedTypes":["Integer","Text"]},"producer":{"expectedTypes":["Organization","Person"]},"provider":{"expectedTypes":["Organization","Person"]},"publisher":{"expectedTypes":["Organization"]},"publishingPrinciples":{"expectedTypes":["URL"]},"recordedAt":{"expectedTypes":["Event"]},"releasedEvent":{"expectedTypes":["PublicationEvent"]},"review":{"expectedTypes":["Review"]},"sourceOrganization":{"expectedTypes":["Organization"]},"text":{"expectedTypes":["Text"]},"thumbnailUrl":{"expectedTypes":["URL"]},"timeRequired":{"expectedTypes":["Duration"]},"translator":{"expectedTypes":["Organization","Person"]},"typicalAgeRange":{"expectedTypes":["Text"]},"version":{"expectedTypes":["Number"]},"video":{"expectedTypes":["VideoObject"]},"workExample":{"expectedTypes":["CreativeWork"]}}},"Answer":{"extends":"CreativeWork","properties":{"downvoteCount":{"expectedTypes":["Integer"]},"parentItem":{"expectedTypes":["Question"]},"upvoteCount":{"expectedTypes":["Integer"]}}},"Article":{"extends":"CreativeWork","properties":{"articleBody":{"expectedTypes":["Text"]},"articleSection":{"expectedTypes":["Text"]},"pageEnd":{"expectedTypes":["Integer","Text"]},"pageStart":{"expectedTypes":["Integer","Text"]},"pagination":{"expectedTypes":["Text"]},"wordCount":{"expectedTypes":["Integer"]}}},"BlogPosting":{"extends":"Article","properties":[]},"NewsArticle":{"extends":"Article","properties":{"dateline":{"expectedTypes":["Text"]},"printColumn":{"expectedTypes":["Text"]},"printEdition":{"expectedTypes":["Text"]},"printPage":{"expectedTypes":["Text"]},"printSection":{"expectedTypes":["Text"]}}},"ScholarlyArticle":{"extends":"Article","properties":[]},"MedicalScholarlyArticle":{"extends":"ScholarlyArticle","properties":{"publicationType":{"expectedTypes":["Text"]}}},"TechArticle":{"extends":"Article","properties":{"dependencies":{"expectedTypes":["Text"]},"proficiencyLevel":{"expectedTypes":["Text"]}}},"APIReference":{"extends":"TechArticle","properties":{"assembly":{"expectedTypes":["Text"]},"assemblyVersion":{"expectedTypes":["Text"]},"programmingModel":{"expectedTypes":["Text"]},"targetPlatform":{"expectedTypes":["Text"]}}},"Blog":{"extends":"CreativeWork","properties":{"blogPost":{"expectedTypes":["BlogPosting"]}}},"Book":{"extends":"CreativeWork","properties":{"bookEdition":{"expectedTypes":["Text"]},"bookFormat":{"expectedTypes":["BookFormatType"]},"illustrator":{"expectedTypes":["Person"]},"isbn":{"expectedTypes":["Text"]},"numberOfPages":{"expectedTypes":["Integer"]}}},"Clip":{"extends":"CreativeWork","properties":{"actor":{"expectedTypes":["Person"]},"clipNumber":{"expectedTypes":["Integer","Text"]},"director":{"expectedTypes":["Person"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"partOfEpisode":{"expectedTypes":["Episode"]},"partOfSeason":{"expectedTypes":["Season"]},"partOfSeries":{"expectedTypes":["Series"]},"publication":{"expectedTypes":["PublicationEvent"]}}},"RadioClip":{"extends":"Clip","properties":[]},"TVClip":{"extends":"Clip","properties":[]},"Code":{"extends":"CreativeWork","properties":{"codeRepository":{"expectedTypes":["URL"]},"programmingLanguage":{"expectedTypes":["Thing"]},"runtime":{"expectedTypes":["Text"]},"sampleType":{"expectedTypes":["Text"]},"targetProduct":{"expectedTypes":["SoftwareApplication"]}}},"Comment":{"extends":"CreativeWork","properties":{"downvoteCount":{"expectedTypes":["Integer"]},"parentItem":{"expectedTypes":["Question"]},"upvoteCount":{"expectedTypes":["Integer"]}}},"DataCatalog":{"extends":"CreativeWork","properties":{"dataset":{"expectedTypes":["Dataset"]}}},"Dataset":{"extends":"CreativeWork","properties":{"catalog":{"expectedTypes":["DataCatalog"]},"distribution":{"expectedTypes":["DataDownload"]},"spatial":{"expectedTypes":["Place"]},"temporal":{"expectedTypes":["DateTime"]}}},"Diet":{"extends":"CreativeWork","properties":{"dietFeatures":{"expectedTypes":["Text"]},"endorsers":{"expectedTypes":["Organization","Person"]},"expertConsiderations":{"expectedTypes":["Text"]},"overview":{"expectedTypes":["Text"]},"physiologicalBenefits":{"expectedTypes":["Text"]},"proprietaryName":{"expectedTypes":["Text"]},"risks":{"expectedTypes":["Text"]}}},"EmailMessage":{"extends":"CreativeWork","properties":[]},"Episode":{"extends":"CreativeWork","properties":{"actor":{"expectedTypes":["Person"]},"director":{"expectedTypes":["Person"]},"episodeNumber":{"expectedTypes":["Integer","Text"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"partOfSeason":{"expectedTypes":["Season"]},"partOfSeries":{"expectedTypes":["Series"]},"productionCompany":{"expectedTypes":["Organization"]},"publication":{"expectedTypes":["PublicationEvent"]},"trailer":{"expectedTypes":["VideoObject"]}}},"RadioEpisode":{"extends":"Episode","properties":[]},"TVEpisode":{"extends":"Episode","properties":[]},"ExercisePlan":{"extends":"CreativeWork","properties":{"activityDuration":{"expectedTypes":["Duration"]},"activityFrequency":{"expectedTypes":["Text"]},"additionalVariable":{"expectedTypes":["Text"]},"exerciseType":{"expectedTypes":["Text"]},"intensity":{"expectedTypes":["Text"]},"repetitions":{"expectedTypes":["Number"]},"restPeriods":{"expectedTypes":["Text"]},"workload":{"expectedTypes":["Energy"]}}},"Game":{"extends":"CreativeWork","properties":{"characterAttribute":{"expectedTypes":["Thing"]},"gameItem":{"expectedTypes":["Thing"]},"gameLocation":{"expectedTypes":["PostalAddress","URL","Place"]},"numberOfPlayers":{"expectedTypes":["QuantitativeValue"]},"quest":{"expectedTypes":["Thing"]}}},"VideoGame":{"extends":"Game","properties":{"actor":{"expectedTypes":["Person"]},"cheatCode":{"expectedTypes":["CreativeWork"]},"director":{"expectedTypes":["Person"]},"gamePlatform":{"expectedTypes":["Thing","Text","URL"]},"gameServer":{"expectedTypes":["GameServer"]},"gameTip":{"expectedTypes":["CreativeWork"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"playMode":{"expectedTypes":["GamePlayMode"]},"trailer":{"expectedTypes":["VideoObject"]}}},"Map":{"extends":"CreativeWork","properties":{"mapType":{"expectedTypes":["MapCategoryType"]}}},"MediaObject":{"extends":"CreativeWork","properties":{"associatedArticle":{"expectedTypes":["NewsArticle"]},"bitrate":{"expectedTypes":["Text"]},"contentSize":{"expectedTypes":["Text"]},"contentUrl":{"expectedTypes":["URL"]},"duration":{"expectedTypes":["Duration"]},"embedUrl":{"expectedTypes":["URL"]},"encodesCreativeWork":{"expectedTypes":["CreativeWork"]},"encodingFormat":{"expectedTypes":["Text"]},"expires":{"expectedTypes":["Date"]},"height":{"expectedTypes":["QuantitativeValue","Distance"]},"playerType":{"expectedTypes":["Text"]},"productionCompany":{"expectedTypes":["Organization"]},"publication":{"expectedTypes":["PublicationEvent"]},"regionsAllowed":{"expectedTypes":["Place"]},"requiresSubscription":{"expectedTypes":["Boolean"]},"uploadDate":{"expectedTypes":["Date"]},"width":{"expectedTypes":["QuantitativeValue","Distance"]}}},"AudioObject":{"extends":"MediaObject","properties":{"transcript":{"expectedTypes":["Text"]}}},"DataDownload":{"extends":"MediaObject","properties":[]},"ImageObject":{"extends":"MediaObject","properties":{"caption":{"expectedTypes":["Text"]},"exifData":{"expectedTypes":["Text"]},"representativeOfPage":{"expectedTypes":["Boolean"]},"thumbnail":{"expectedTypes":["ImageObject"]}}},"MusicVideoObject":{"extends":"MediaObject","properties":[]},"VideoObject":{"extends":"MediaObject","properties":{"actor":{"expectedTypes":["Person"]},"caption":{"expectedTypes":["Text"]},"director":{"expectedTypes":["Person"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"thumbnail":{"expectedTypes":["ImageObject"]},"transcript":{"expectedTypes":["Text"]},"videoFrameSize":{"expectedTypes":["Text"]},"videoQuality":{"expectedTypes":["Text"]}}},"Movie":{"extends":"CreativeWork","properties":{"actor":{"expectedTypes":["Person"]},"director":{"expectedTypes":["Person"]},"duration":{"expectedTypes":["Duration"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"productionCompany":{"expectedTypes":["Organization"]},"trailer":{"expectedTypes":["VideoObject"]}}},"MusicComposition":{"extends":"CreativeWork","properties":{"composer":{"expectedTypes":["Person","Organization"]},"firstPerformance":{"expectedTypes":["Event"]},"includedComposition":{"expectedTypes":["MusicComposition"]},"iswcCode":{"expectedTypes":["Text"]},"lyricist":{"expectedTypes":["Person"]},"musicArrangement":{"expectedTypes":["MusicComposition"]},"musicCompositionForm":{"expectedTypes":["Text"]},"musicalKey":{"expectedTypes":["Text"]},"recordedAs":{"expectedTypes":["MusicRecording"]}}},"MusicPlaylist":{"extends":"CreativeWork","properties":{"numTracks":{"expectedTypes":["Integer"]},"track":{"expectedTypes":["MusicRecording","ItemList"]}}},"MusicAlbum":{"extends":"MusicPlaylist","properties":{"albumProductionType":{"expectedTypes":["MusicAlbumProductionType"]},"albumRelease":{"expectedTypes":["MusicRelease"]},"albumReleaseType":{"expectedTypes":["MusicAlbumReleaseType"]},"byArtist":{"expectedTypes":["MusicGroup"]}}},"MusicRelease":{"extends":"MusicPlaylist","properties":{"catalogNumber":{"expectedTypes":["Text"]},"creditedTo":{"expectedTypes":["Organization","Person"]},"duration":{"expectedTypes":["Duration"]},"musicReleaseFormat":{"expectedTypes":["MusicReleaseFormatType"]},"recordLabel":{"expectedTypes":["Organization"]},"releaseOf":{"expectedTypes":["MusicAlbum"]}}},"MusicRecording":{"extends":"CreativeWork","properties":{"byArtist":{"expectedTypes":["MusicGroup"]},"duration":{"expectedTypes":["Duration"]},"inAlbum":{"expectedTypes":["MusicAlbum"]},"inPlaylist":{"expectedTypes":["MusicPlaylist"]},"isrcCode":{"expectedTypes":["Text"]},"recordingOf":{"expectedTypes":["MusicComposition"]}}},"Painting":{"extends":"CreativeWork","properties":[]},"Photograph":{"extends":"CreativeWork","properties":[]},"PublicationIssue":{"extends":"CreativeWork","properties":{"issueNumber":{"expectedTypes":["Integer","Text"]},"pageEnd":{"expectedTypes":["Integer","Text"]},"pageStart":{"expectedTypes":["Integer","Text"]},"pagination":{"expectedTypes":["Text"]}}},"PublicationVolume":{"extends":"CreativeWork","properties":{"pageEnd":{"expectedTypes":["Integer","Text"]},"pageStart":{"expectedTypes":["Integer","Text"]},"pagination":{"expectedTypes":["Text"]},"volumeNumber":{"expectedTypes":["Integer","Text"]}}},"Question":{"extends":"CreativeWork","properties":{"acceptedAnswer":{"expectedTypes":["Answer"]},"answerCount":{"expectedTypes":["Integer"]},"downvoteCount":{"expectedTypes":["Integer"]},"suggestedAnswer":{"expectedTypes":["Answer"]},"upvoteCount":{"expectedTypes":["Integer"]}}},"Recipe":{"extends":"CreativeWork","properties":{"cookTime":{"expectedTypes":["Duration"]},"cookingMethod":{"expectedTypes":["Text"]},"ingredients":{"expectedTypes":["Text"]},"nutrition":{"expectedTypes":["NutritionInformation"]},"prepTime":{"expectedTypes":["Duration"]},"recipeCategory":{"expectedTypes":["Text"]},"recipeCuisine":{"expectedTypes":["Text"]},"recipeInstructions":{"expectedTypes":["Text"]},"recipeYield":{"expectedTypes":["Text"]},"totalTime":{"expectedTypes":["Duration"]}}},"Review":{"extends":"CreativeWork","properties":{"itemReviewed":{"expectedTypes":["Thing"]},"reviewBody":{"expectedTypes":["Text"]},"reviewRating":{"expectedTypes":["Rating"]}}},"Sculpture":{"extends":"CreativeWork","properties":[]},"Season":{"extends":"CreativeWork","properties":{"endDate":{"expectedTypes":["Date"]},"episode":{"expectedTypes":["Episode"]},"numberOfEpisodes":{"expectedTypes":["Number"]},"partOfSeries":{"expectedTypes":["Series"]},"productionCompany":{"expectedTypes":["Organization"]},"seasonNumber":{"expectedTypes":["Integer","Text"]},"startDate":{"expectedTypes":["Date"]},"trailer":{"expectedTypes":["VideoObject"]}}},"RadioSeason":{"extends":"Season","properties":[]},"TVSeason":{"extends":"CreativeWork","properties":[]},"Series":{"extends":"CreativeWork","properties":{"endDate":{"expectedTypes":["Date"]},"startDate":{"expectedTypes":["Date"]}}},"BookSeries":{"extends":"Series","properties":[]},"MovieSeries":{"extends":"Series","properties":{"actor":{"expectedTypes":["Person"]},"director":{"expectedTypes":["Person"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"productionCompany":{"expectedTypes":["Organization"]},"trailer":{"expectedTypes":["VideoObject"]}}},"Periodical":{"extends":"Series","properties":{"issn":{"expectedTypes":["Text"]}}},"RadioSeries":{"extends":"Series","properties":{"actor":{"expectedTypes":["Person"]},"director":{"expectedTypes":["Person"]},"episode":{"expectedTypes":["Episode"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"numberOfEpisodes":{"expectedTypes":["Number"]},"numberOfSeasons":{"expectedTypes":["Number"]},"productionCompany":{"expectedTypes":["Organization"]},"season":{"expectedTypes":["Season"]},"trailer":{"expectedTypes":["VideoObject"]}}},"TVSeries":{"extends":"CreativeWork","properties":{"actor":{"expectedTypes":["Person"]},"director":{"expectedTypes":["Person"]},"episode":{"expectedTypes":["Episode"]},"musicBy":{"expectedTypes":["MusicGroup","Person"]},"numberOfEpisodes":{"expectedTypes":["Number"]},"numberOfSeasons":{"expectedTypes":["Number"]},"productionCompany":{"expectedTypes":["Organization"]},"season":{"expectedTypes":["Season"]},"trailer":{"expectedTypes":["VideoObject"]}}},"VideoGameSeries":{"extends":"Series","properties":{"actor":{"expectedTypes":["Person"]},"characterAttribute":{"expectedTypes":["Thing"]},"cheatCode":{"expectedTypes":["CreativeWork"]},"director":{"expectedTypes":["Person"]},"episode":{"expectedTypes":["Episode"]},"gameItem":{"expectedTypes":["Thing"]},"gamePlatform":{"expectedTypes":["Text","Thing","URL"]},"musicBy":{"expectedTypes":["Person","MusicGroup"]},"numberOfEpisodes":{"expectedTypes":["Number"]},"numberOfPlayers":{"expectedTypes":["QuantitativeValue"]},"numberOfSeasons":{"expectedTypes":["Number"]},"playMode":{"expectedTypes":["GamePlayMode"]},"productionCompany":{"expectedTypes":["Organization"]},"quest":{"expectedTypes":["Thing"]},"season":{"expectedTypes":["Season"]},"trailer":{"expectedTypes":["VideoObject"]}}},"SoftwareApplication":{"extends":"CreativeWork","properties":{"applicationCategory":{"expectedTypes":["Text","URL"]},"applicationSubCategory":{"expectedTypes":["Text","URL"]},"applicationSuite":{"expectedTypes":["Text"]},"countriesNotSupported":{"expectedTypes":["Text"]},"countriesSupported":{"expectedTypes":["Text"]},"device":{"expectedTypes":["Text"]},"downloadUrl":{"expectedTypes":["URL"]},"featureList":{"expectedTypes":["Text","URL"]},"fileFormat":{"expectedTypes":["Text"]},"fileSize":{"expectedTypes":["Integer"]},"installUrl":{"expectedTypes":["URL"]},"memoryRequirements":{"expectedTypes":["Text","URL"]},"operatingSystem":{"expectedTypes":["Text"]},"permissions":{"expectedTypes":["Text"]},"processorRequirements":{"expectedTypes":["Text"]},"releaseNotes":{"expectedTypes":["Text","URL"]},"requirements":{"expectedTypes":["Text","URL"]},"screenshot":{"expectedTypes":["ImageObject","URL"]},"softwareAddOn":{"expectedTypes":["SoftwareApplication"]},"softwareHelp":{"expectedTypes":["CreativeWork"]},"softwareVersion":{"expectedTypes":["Text"]},"storageRequirements":{"expectedTypes":["Text","URL"]}}},"MobileApplication":{"extends":"SoftwareApplication","properties":{"carrierRequirements":{"expectedTypes":["Text"]}}},"WebApplication":{"extends":"SoftwareApplication","properties":{"browserRequirements":{"expectedTypes":["Text"]}}},"VisualArtwork":{"extends":"CreativeWork","properties":{"artEdition":{"expectedTypes":["Integer","Text"]},"artform":{"expectedTypes":["Text","URL"]},"depth":{"expectedTypes":["Distance","QuantitativeValue"]},"height":{"expectedTypes":["Distance","QuantitativeValue"]},"material":{"expectedTypes":["Text","URL"]},"surface":{"expectedTypes":["Text","URL"]},"width":{"expectedTypes":["Distance","QuantitativeValue"]}}},"WebPage":{"extends":"CreativeWork","properties":{"breadcrumb":{"expectedTypes":["Text","BreadcrumbList"]},"lastReviewed":{"expectedTypes":["Date"]},"mainContentOfPage":{"expectedTypes":["WebPageElement"]},"primaryImageOfPage":{"expectedTypes":["ImageObject"]},"relatedLink":{"expectedTypes":["URL"]},"reviewedBy":{"expectedTypes":["Person","Organization"]},"significantLink":{"expectedTypes":["URL"]},"specialty":{"expectedTypes":["Specialty"]}}},"AboutPage":{"extends":"WebPage","properties":[]},"CheckoutPage":{"extends":"WebPage","properties":[]},"CollectionPage":{"extends":"WebPage","properties":[]},"ImageGallery":{"extends":"CollectionPage","properties":[]},"VideoGallery":{"extends":"CollectionPage","properties":[]},"ContactPage":{"extends":"WebPage","properties":[]},"ItemPage":{"extends":"WebPage","properties":[]},"MedicalWebPage":{"extends":"WebPage","properties":{"aspect":{"expectedTypes":["Text"]}}},"ProfilePage":{"extends":"WebPage","properties":[]},"QAPage":{"extends":"WebPage","properties":[]},"SearchResultsPage":{"extends":"WebPage","properties":[]},"WebPageElement":{"extends":"CreativeWork","properties":[]},"SiteNavigationElement":{"extends":"WebPageElement","properties":[]},"Table":{"extends":"WebPageElement","properties":[]},"WPAdBlock":{"extends":"WebPageElement","properties":[]},"WPFooter":{"extends":"WebPageElement","properties":[]},"WPHeader":{"extends":"WebPageElement","properties":[]},"WPSideBar":{"extends":"WebPageElement","properties":[]},"WebSite":{"extends":"CreativeWork","properties":[]},"Event":{"extends":"Thing","properties":{"attendee":{"expectedTypes":["Organization","Person"]},"doorTime":{"expectedTypes":["DateTime"]},"duration":{"expectedTypes":["Duration"]},"endDate":{"expectedTypes":["Date"]},"eventStatus":{"expectedTypes":["EventStatusType"]},"location":{"expectedTypes":["PostalAddress","Place"]},"offers":{"expectedTypes":["Offer"]},"organizer":{"expectedTypes":["Organization","Person"]},"performer":{"expectedTypes":["Organization","Person"]},"previousStartDate":{"expectedTypes":["Date"]},"recordedIn":{"expectedTypes":["CreativeWork"]},"startDate":{"expectedTypes":["Date"]},"subEvent":{"expectedTypes":["Event"]},"superEvent":{"expectedTypes":["Event"]},"typicalAgeRange":{"expectedTypes":["Text"]},"workPerformed":{"expectedTypes":["CreativeWork"]}}},"BusinessEvent":{"extends":"Event","properties":[]},"ChildrensEvent":{"extends":"Event","properties":[]},"ComedyEvent":{"extends":"Event","properties":[]},"DanceEvent":{"extends":"Event","properties":[]},"DeliveryEvent":{"extends":"Event","properties":{"accessCode":{"expectedTypes":["Text"]},"availableFrom":{"expectedTypes":["DateTime"]},"availableThrough":{"expectedTypes":["DateTime"]},"hasDeliveryMethod":{"expectedTypes":["DeliveryMethod"]}}},"EducationEvent":{"extends":"Event","properties":[]},"Festival":{"extends":"Event","properties":[]},"FoodEvent":{"extends":"Event","properties":[]},"LiteraryEvent":{"extends":"Event","properties":[]},"MusicEvent":{"extends":"Event","properties":[]},"PublicationEvent":{"extends":"Event","properties":{"free":{"expectedTypes":["Boolean"]},"publishedOn":{"expectedTypes":["BroadcastService"]}}},"BroadcastEvent":{"extends":"PublicationEvent","properties":[]},"OnDemandEvent":{"extends":"PublicationEvent","properties":[]},"SaleEvent":{"extends":"Event","properties":[]},"SocialEvent":{"extends":"Event","properties":[]},"SportsEvent":{"extends":"Event","properties":{"awayTeam":{"expectedTypes":["Person","SportsTeam"]},"competitor":{"expectedTypes":["Person","SportsTeam"]},"homeTeam":{"expectedTypes":["Person","SportsTeam"]}}},"TheaterEvent":{"extends":"Event","properties":[]},"UserInteraction":{"extends":"Event","properties":[]},"UserBlocks":{"extends":"UserInteraction","properties":[]},"UserCheckins":{"extends":"UserInteraction","properties":[]},"UserComments":{"extends":"UserInteraction","properties":{"commentText":{"expectedTypes":["Text"]},"commentTime":{"expectedTypes":["Date"]},"creator":{"expectedTypes":["Organization","Person"]},"discusses":{"expectedTypes":["CreativeWork"]},"replyToUrl":{"expectedTypes":["URL"]}}},"UserDownloads":{"extends":"UserInteraction","properties":[]},"UserLikes":{"extends":"UserInteraction","properties":[]},"UserPageVisits":{"extends":"UserInteraction","properties":[]},"UserPlays":{"extends":"UserInteraction","properties":[]},"UserPlusOnes":{"extends":"UserInteraction","properties":[]},"UserTweets":{"extends":"UserInteraction","properties":[]},"VisualArtsEvent":{"extends":"Event","properties":[]},"Intangible":{"extends":"Thing","properties":[]},"AlignmentObject":{"extends":"Intangible","properties":{"alignmentType":{"expectedTypes":["Text"]},"educationalFramework":{"expectedTypes":["Text"]},"targetDescription":{"expectedTypes":["Text"]},"targetName":{"expectedTypes":["Text"]},"targetUrl":{"expectedTypes":["URL"]}}},"Audience":{"extends":"Intangible","properties":{"audienceType":{"expectedTypes":["Text"]},"geographicArea":{"expectedTypes":["AdministrativeArea"]}}},"BusinessAudience":{"extends":"Audience","properties":{"numberOfEmployees":{"expectedTypes":["QuantitativeValue"]},"yearlyRevenue":{"expectedTypes":["QuantitativeValue"]},"yearsInOperation":{"expectedTypes":["QuantitativeValue"]}}},"EducationalAudience":{"extends":"Audience","properties":{"educationalRole":{"expectedTypes":["Text"]}}},"MedicalAudience":{"extends":"Audience","properties":[]},"PeopleAudience":{"extends":"Audience","properties":{"healthCondition":{"expectedTypes":["MedicalCondition"]},"requiredGender":{"expectedTypes":["Text"]},"requiredMaxAge":{"expectedTypes":["Integer"]},"requiredMinAge":{"expectedTypes":["Integer"]},"suggestedGender":{"expectedTypes":["Text"]},"suggestedMaxAge":{"expectedTypes":["Number"]},"suggestedMinAge":{"expectedTypes":["Number"]}}},"ParentAudience":{"extends":"PeopleAudience","properties":{"childMaxAge":{"expectedTypes":["Number"]},"childMinAge":{"expectedTypes":["Number"]}}},"Brand":{"extends":"Intangible","properties":{"logo":{"expectedTypes":["ImageObject","URL"]}}},"BusTrip":{"extends":"Intangible","properties":{"arrivalBusStop":{"expectedTypes":["BusStation","BusStop"]},"arrivalTime":{"expectedTypes":["DateTime"]},"busName":{"expectedTypes":["Text"]},"busNumber":{"expectedTypes":["Text"]},"departureBusStop":{"expectedTypes":["BusStation","BusStop"]},"departureTime":{"expectedTypes":["DateTime"]},"provider":{"expectedTypes":["Person","Organization"]}}},"Class":{"extends":"Intangible","properties":[]},"Demand":{"extends":"Intangible","properties":{"acceptedPaymentMethod":{"expectedTypes":["PaymentMethod"]},"advanceBookingRequirement":{"expectedTypes":["QuantitativeValue"]},"availability":{"expectedTypes":["ItemAvailability"]},"availabilityEnds":{"expectedTypes":["DateTime"]},"availabilityStarts":{"expectedTypes":["DateTime"]},"availableAtOrFrom":{"expectedTypes":["Place"]},"availableDeliveryMethod":{"expectedTypes":["DeliveryMethod"]},"businessFunction":{"expectedTypes":["BusinessFunction"]},"deliveryLeadTime":{"expectedTypes":["QuantitativeValue"]},"eligibleCustomerType":{"expectedTypes":["BusinessEntityType"]},"eligibleDuration":{"expectedTypes":["QuantitativeValue"]},"eligibleQuantity":{"expectedTypes":["QuantitativeValue"]},"eligibleRegion":{"expectedTypes":["GeoShape","Text"]},"eligibleTransactionVolume":{"expectedTypes":["PriceSpecification"]},"gtin13":{"expectedTypes":["Text"]},"gtin14":{"expectedTypes":["Text"]},"gtin8":{"expectedTypes":["Text"]},"includesObject":{"expectedTypes":["TypeAndQuantityNode"]},"inventoryLevel":{"expectedTypes":["QuantitativeValue"]},"itemCondition":{"expectedTypes":["OfferItemCondition"]},"itemOffered":{"expectedTypes":["Product"]},"mpn":{"expectedTypes":["Text"]},"priceSpecification":{"expectedTypes":["PriceSpecification"]},"seller":{"expectedTypes":["Organization","Person"]},"serialNumber":{"expectedTypes":["Text"]},"sku":{"expectedTypes":["Text"]},"validFrom":{"expectedTypes":["DateTime"]},"validThrough":{"expectedTypes":["DateTime"]},"warranty":{"expectedTypes":["WarrantyPromise"]}}},"EntryPoint":{"extends":"Intangible","properties":{"application":{"expectedTypes":["SoftwareApplication"]},"contentType":{"expectedTypes":["Text"]},"encodingType":{"expectedTypes":["Text"]},"httpMethod":{"expectedTypes":["Text"]},"urlTemplate":{"expectedTypes":["Text"]}}},"Enumeration":{"extends":"Intangible","properties":[]},"ActionStatusType":{"extends":"Enumeration","properties":[]},"BookFormatType":{"extends":"Enumeration","properties":[]},"BusinessEntityType":{"extends":"Enumeration","properties":[]},"BusinessFunction":{"extends":"Enumeration","properties":[]},"ContactPointOption":{"extends":"Enumeration","properties":[]},"DayOfWeek":{"extends":"Enumeration","properties":[]},"DeliveryMethod":{"extends":"Enumeration","properties":[]},"LockerDelivery":{"extends":"DeliveryMethod","properties":[]},"ParcelService":{"extends":"DeliveryMethod","properties":[]},"DrugCostCategory":{"extends":"Enumeration","properties":[]},"DrugPregnancyCategory":{"extends":"MedicalEnumeration","properties":[]},"DrugPrescriptionStatus":{"extends":"Enumeration","properties":[]},"EventStatusType":{"extends":"Enumeration","properties":[]},"GamePlayMode":{"extends":"Enumeration","properties":[]},"GameServerStatus":{"extends":"Enumeration","properties":[]},"InfectiousAgentClass":{"extends":"MedicalEnumeration","properties":[]},"ItemAvailability":{"extends":"Enumeration","properties":[]},"ItemListOrderType":{"extends":"Enumeration","properties":[]},"MapCategoryType":{"extends":"Enumeration","properties":[]},"MedicalDevicePurpose":{"extends":"Enumeration","properties":[]},"MedicalEnumeration":{"extends":"Enumeration","properties":[]},"MedicalEvidenceLevel":{"extends":"Enumeration","properties":[]},"MedicalImagingTechnique":{"extends":"Enumeration","properties":[]},"MedicalObservationalStudyDesign":{"extends":"Enumeration","properties":[]},"MedicalProcedureType":{"extends":"MedicalEnumeration","properties":[]},"MedicalSpecialty":{"extends":"Enumeration","properties":[]},"MedicalStudyStatus":{"extends":"Enumeration","properties":[]},"MedicalTrialDesign":{"extends":"Enumeration","properties":[]},"MedicineSystem":{"extends":"Enumeration","properties":[]},"PhysicalActivityCategory":{"extends":"MedicalEnumeration","properties":[]},"PhysicalExam":{"extends":"Enumeration","properties":[]},"MusicAlbumProductionType":{"extends":"Enumeration","properties":[]},"MusicAlbumReleaseType":{"extends":"Enumeration","properties":[]},"MusicReleaseFormatType":{"extends":"Enumeration","properties":[]},"OfferItemCondition":{"extends":"Enumeration","properties":[]},"OrderStatus":{"extends":"Enumeration","properties":[]},"PaymentMethod":{"extends":"Enumeration","properties":[]},"CreditCard":{"extends":"PaymentMethod","properties":[]},"QualitativeValue":{"extends":"Enumeration","properties":{"equal":{"expectedTypes":["QualitativeValue"]},"greater":{"expectedTypes":["QualitativeValue"]},"greaterOrEqual":{"expectedTypes":["QualitativeValue"]},"lesser":{"expectedTypes":["QualitativeValue"]},"lesserOrEqual":{"expectedTypes":["QualitativeValue"]},"nonEqual":{"expectedTypes":["QualitativeValue"]},"valueReference":{"expectedTypes":["Enumeration","StructuredValue"]}}},"ReservationStatusType":{"extends":"Enumeration","properties":[]},"RsvpResponseType":{"extends":"Enumeration","properties":[]},"Specialty":{"extends":"Enumeration","properties":[]},"WarrantyScope":{"extends":"Enumeration","properties":[]},"Flight":{"extends":"Intangible","properties":{"aircraft":{"expectedTypes":["Vehicle","Text"]},"arrivalAirport":{"expectedTypes":["Airport"]},"arrivalGate":{"expectedTypes":["Text"]},"arrivalTerminal":{"expectedTypes":["Text"]},"arrivalTime":{"expectedTypes":["DateTime"]},"departureAirport":{"expectedTypes":["Airport"]},"departureGate":{"expectedTypes":["Text"]},"departureTerminal":{"expectedTypes":["Text"]},"departureTime":{"expectedTypes":["DateTime"]},"estimatedFlightDuration":{"expectedTypes":["Duration","Text"]},"flightDistance":{"expectedTypes":["Text","Distance"]},"flightNumber":{"expectedTypes":["Text"]},"mealService":{"expectedTypes":["Text"]},"provider":{"expectedTypes":["Organization","Person"]},"seller":{"expectedTypes":["Organization","Person"]},"webCheckinTime":{"expectedTypes":["DateTime"]}}},"GameServer":{"extends":"Intangible","properties":{"game":{"expectedTypes":["VideoGame"]},"playersOnline":{"expectedTypes":["Number"]},"serverStatus":{"expectedTypes":["GameServerStatus"]}}},"Invoice":{"extends":"Intangible","properties":{"accountId":{"expectedTypes":["Text"]},"billingPeriod":{"expectedTypes":["Duration"]},"broker":{"expectedTypes":["Person","Organization"]},"category":{"expectedTypes":["Text","PhysicalActivityCategory","Thing"]},"confirmationNumber":{"expectedTypes":["Text"]},"customer":{"expectedTypes":["Person","Organization"]},"minimumPaymentDue":{"expectedTypes":["PriceSpecification"]},"paymentDue":{"expectedTypes":["DateTime"]},"paymentMethod":{"expectedTypes":["PaymentMethod"]},"paymentMethodId":{"expectedTypes":["Text"]},"paymentStatus":{"expectedTypes":["Text"]},"provider":{"expectedTypes":["Person","Organization"]},"referencesOrder":{"expectedTypes":["Order"]},"scheduledPaymentDate":{"expectedTypes":["Date"]},"totalPaymentDue":{"expectedTypes":["PriceSpecification"]}}},"ItemList":{"extends":"Intangible","properties":{"itemListElement":{"expectedTypes":["Text","ListItem","Thing"]},"itemListOrder":{"expectedTypes":["Text","ItemListOrderType"]},"numberOfItems":{"expectedTypes":["Number"]}}},"BreadcrumbList":{"extends":"ItemList","properties":[]},"JobPosting":{"extends":"Intangible","properties":{"baseSalary":{"expectedTypes":["Number","PriceSpecification"]},"benefits":{"expectedTypes":["Text"]},"datePosted":{"expectedTypes":["Date"]},"educationRequirements":{"expectedTypes":["Text"]},"employmentType":{"expectedTypes":["Text"]},"experienceRequirements":{"expectedTypes":["Text"]},"hiringOrganization":{"expectedTypes":["Organization"]},"incentives":{"expectedTypes":["Text"]},"industry":{"expectedTypes":["Text"]},"jobLocation":{"expectedTypes":["Place"]},"occupationalCategory":{"expectedTypes":["Text"]},"qualifications":{"expectedTypes":["Text"]},"responsibilities":{"expectedTypes":["Text"]},"salaryCurrency":{"expectedTypes":["Text"]},"skills":{"expectedTypes":["Text"]},"specialCommitments":{"expectedTypes":["Text"]},"title":{"expectedTypes":["Text"]},"workHours":{"expectedTypes":["Text"]}}},"Language":{"extends":"Intangible","properties":[]},"ListItem":{"extends":"Intangible","properties":{"item":{"expectedTypes":["Thing"]},"nextItem":{"expectedTypes":["ListItem"]},"position":{"expectedTypes":["Text","Integer"]},"previousItem":{"expectedTypes":["ListItem"]}}},"Offer":{"extends":"Intangible","properties":{"acceptedPaymentMethod":{"expectedTypes":["PaymentMethod"]},"addOn":{"expectedTypes":["Offer"]},"advanceBookingRequirement":{"expectedTypes":["QuantitativeValue"]},"aggregateRating":{"expectedTypes":["AggregateRating"]},"availability":{"expectedTypes":["ItemAvailability"]},"availabilityEnds":{"expectedTypes":["DateTime"]},"availabilityStarts":{"expectedTypes":["DateTime"]},"availableAtOrFrom":{"expectedTypes":["Place"]},"availableDeliveryMethod":{"expectedTypes":["DeliveryMethod"]},"businessFunction":{"expectedTypes":["BusinessFunction"]},"category":{"expectedTypes":["PhysicalActivityCategory","Thing","Text"]},"deliveryLeadTime":{"expectedTypes":["QuantitativeValue"]},"eligibleCustomerType":{"expectedTypes":["BusinessEntityType"]},"eligibleDuration":{"expectedTypes":["QuantitativeValue"]},"eligibleQuantity":{"expectedTypes":["QuantitativeValue"]},"eligibleRegion":{"expectedTypes":["GeoShape","Text"]},"eligibleTransactionVolume":{"expectedTypes":["PriceSpecification"]},"gtin13":{"expectedTypes":["Text"]},"gtin14":{"expectedTypes":["Text"]},"gtin8":{"expectedTypes":["Text"]},"includesObject":{"expectedTypes":["TypeAndQuantityNode"]},"ineligibleRegion":{"expectedTypes":["Place"]},"inventoryLevel":{"expectedTypes":["QuantitativeValue"]},"itemCondition":{"expectedTypes":["OfferItemCondition"]},"itemOffered":{"expectedTypes":["Product"]},"mpn":{"expectedTypes":["Text"]},"price":{"expectedTypes":["Number","Text"]},"priceCurrency":{"expectedTypes":["Text"]},"priceSpecification":{"expectedTypes":["PriceSpecification"]},"priceValidUntil":{"expectedTypes":["Date"]},"review":{"expectedTypes":["Review"]},"seller":{"expectedTypes":["Organization","Person"]},"serialNumber":{"expectedTypes":["Text"]},"sku":{"expectedTypes":["Text"]},"validFrom":{"expectedTypes":["DateTime"]},"validThrough":{"expectedTypes":["DateTime"]},"warranty":{"expectedTypes":["WarrantyPromise"]}}},"AggregateOffer":{"extends":"Offer","properties":{"highPrice":{"expectedTypes":["Text","Number"]},"lowPrice":{"expectedTypes":["Text","Number"]},"offerCount":{"expectedTypes":["Integer"]},"offers":{"expectedTypes":["Offer"]}}},"Order":{"extends":"Intangible","properties":{"acceptedOffer":{"expectedTypes":["Offer"]},"billingAddress":{"expectedTypes":["PostalAddress"]},"broker":{"expectedTypes":["Person","Organization"]},"confirmationNumber":{"expectedTypes":["Text"]},"customer":{"expectedTypes":["Person","Organization"]},"discount":{"expectedTypes":["Text","Number"]},"discountCode":{"expectedTypes":["Text"]},"discountCurrency":{"expectedTypes":["Text"]},"isGift":{"expectedTypes":["Boolean"]},"orderDate":{"expectedTypes":["DateTime"]},"orderNumber":{"expectedTypes":["Text"]},"orderStatus":{"expectedTypes":["OrderStatus"]},"orderedItem":{"expectedTypes":["Product"]},"partOfInvoice":{"expectedTypes":["Invoice"]},"paymentDue":{"expectedTypes":["DateTime"]},"paymentMethod":{"expectedTypes":["PaymentMethod"]},"paymentMethodId":{"expectedTypes":["Text"]},"paymentUrl":{"expectedTypes":["URL"]},"seller":{"expectedTypes":["Person","Organization"]}}},"ParcelDelivery":{"extends":"Intangible","properties":{"deliveryAddress":{"expectedTypes":["PostalAddress"]},"deliveryStatus":{"expectedTypes":["DeliveryEvent"]},"expectedArrivalFrom":{"expectedTypes":["DateTime"]},"expectedArrivalUntil":{"expectedTypes":["DateTime"]},"hasDeliveryMethod":{"expectedTypes":["DeliveryMethod"]},"itemShipped":{"expectedTypes":["Product"]},"originAddress":{"expectedTypes":["PostalAddress"]},"partOfOrder":{"expectedTypes":["Order"]},"provider":{"expectedTypes":["Person","Organization"]},"trackingNumber":{"expectedTypes":["Text"]},"trackingUrl":{"expectedTypes":["URL"]}}},"Permit":{"extends":"Intangible","properties":{"issuedBy":{"expectedTypes":["Organization"]},"issuedThrough":{"expectedTypes":["Service"]},"permitAudience":{"expectedTypes":["Audience"]},"validFor":{"expectedTypes":["Duration"]},"validFrom":{"expectedTypes":["DateTime"]},"validIn":{"expectedTypes":["AdministrativeArea"]},"validUntil":{"expectedTypes":["Date"]}}},"GovernmentPermit":{"extends":"Permit","properties":[]},"ProgramMembership":{"extends":"Intangible","properties":{"hostingOrganization":{"expectedTypes":["Organization"]},"member":{"expectedTypes":["Person","Organization"]},"membershipNumber":{"expectedTypes":["Text"]},"programName":{"expectedTypes":["Text"]}}},"Property":{"extends":"Intangible","properties":{"domainIncludes":{"expectedTypes":["Class"]},"inverseOf":{"expectedTypes":["Property"]},"rangeIncludes":{"expectedTypes":["Class"]},"supersededBy":{"expectedTypes":["Property"]}}},"PropertyValueSpecification":{"extends":"Intangible","properties":{"defaultValue":{"expectedTypes":["Text","Thing"]},"maxValue":{"expectedTypes":["Number"]},"minValue":{"expectedTypes":["Number"]},"multipleValues":{"expectedTypes":["Boolean"]},"readonlyValue":{"expectedTypes":["Boolean"]},"stepValue":{"expectedTypes":["Number"]},"valueMaxLength":{"expectedTypes":["Number"]},"valueMinLength":{"expectedTypes":["Number"]},"valueName":{"expectedTypes":["Text"]},"valuePattern":{"expectedTypes":["Text"]},"valueRequired":{"expectedTypes":["Boolean"]}}},"Quantity":{"extends":"Intangible","properties":[]},"Distance":{"extends":"Quantity","properties":[]},"Duration":{"extends":"Quantity","properties":[]},"Energy":{"extends":"Quantity","properties":[]},"Mass":{"extends":"Quantity","properties":[]},"Rating":{"extends":"Intangible","properties":{"bestRating":{"expectedTypes":["Number","Text"]},"ratingValue":{"expectedTypes":["Text"]},"worstRating":{"expectedTypes":["Number","Text"]}}},"AggregateRating":{"extends":"Rating","properties":{"itemReviewed":{"expectedTypes":["Thing"]},"ratingCount":{"expectedTypes":["Number"]},"reviewCount":{"expectedTypes":["Number"]}}},"Reservation":{"extends":"Intangible","properties":{"bookingTime":{"expectedTypes":["DateTime"]},"broker":{"expectedTypes":["Organization","Person"]},"modifiedTime":{"expectedTypes":["DateTime"]},"priceCurrency":{"expectedTypes":["Text"]},"programMembershipUsed":{"expectedTypes":["ProgramMembership"]},"provider":{"expectedTypes":["Organization","Person"]},"reservationFor":{"expectedTypes":["Thing"]},"reservationId":{"expectedTypes":["Text"]},"reservationStatus":{"expectedTypes":["ReservationStatusType"]},"reservedTicket":{"expectedTypes":["Ticket"]},"totalPrice":{"expectedTypes":["Number","PriceSpecification","Text"]},"underName":{"expectedTypes":["Organization","Person"]}}},"BusReservation":{"extends":"Reservation","properties":[]},"EventReservation":{"extends":"Reservation","properties":[]},"FlightReservation":{"extends":"Reservation","properties":{"boardingGroup":{"expectedTypes":["Text"]}}},"FoodEstablishmentReservation":{"extends":"Reservation","properties":{"endTime":{"expectedTypes":["DateTime"]},"partySize":{"expectedTypes":["Number","QuantitativeValue"]},"startTime":{"expectedTypes":["DateTime"]}}},"LodgingReservation":{"extends":"Reservation","properties":{"checkinTime":{"expectedTypes":["DateTime"]},"checkoutTime":{"expectedTypes":["DateTime"]},"lodgingUnitDescription":{"expectedTypes":["Text"]},"lodgingUnitType":{"expectedTypes":["Text","QualitativeValue"]},"numAdults":{"expectedTypes":["QuantitativeValue","Number"]},"numChildren":{"expectedTypes":["QuantitativeValue","Number"]}}},"RentalCarReservation":{"extends":"Reservation","properties":{"dropoffLocation":{"expectedTypes":["Place"]},"dropoffTime":{"expectedTypes":["DateTime"]},"pickupLocation":{"expectedTypes":["Place"]},"pickupTime":{"expectedTypes":["DateTime"]}}},"ReservationPackage":{"extends":"Reservation","properties":{"subReservation":{"expectedTypes":["Reservation"]}}},"TaxiReservation":{"extends":"Reservation","properties":{"partySize":{"expectedTypes":["Number","QuantitativeValue"]},"pickupLocation":{"expectedTypes":["Place"]},"pickupTime":{"expectedTypes":["DateTime"]}}},"TrainReservation":{"extends":"Reservation","properties":[]},"Role":{"extends":"Intangible","properties":{"endDate":{"expectedTypes":["Date"]},"roleName":{"expectedTypes":["Text","URL"]},"startDate":{"expectedTypes":["Date"]}}},"OrganizationRole":{"extends":"Role","properties":{"numberedPosition":{"expectedTypes":["Number"]}}},"EmployeeRole":{"extends":"OrganizationRole","properties":{"baseSalary":{"expectedTypes":["Number","PriceSpecification"]},"salaryCurrency":{"expectedTypes":["Text"]}}},"PerformanceRole":{"extends":"Role","properties":{"characterName":{"expectedTypes":["Text"]}}},"Seat":{"extends":"Intangible","properties":{"seatNumber":{"expectedTypes":["Text"]},"seatRow":{"expectedTypes":["Text"]},"seatSection":{"expectedTypes":["Text"]},"seatingType":{"expectedTypes":["Text","QualitativeValue"]}}},"Service":{"extends":"Intangible","properties":{"availableChannel":{"expectedTypes":["ServiceChannel"]},"produces":{"expectedTypes":["Thing"]},"provider":{"expectedTypes":["Person","Organization"]},"serviceArea":{"expectedTypes":["AdministrativeArea"]},"serviceAudience":{"expectedTypes":["Audience"]},"serviceType":{"expectedTypes":["Text"]}}},"GovernmentService":{"extends":"Service","properties":{"serviceOperator":{"expectedTypes":["Organization"]}}},"Taxi":{"extends":"Service","properties":[]},"ServiceChannel":{"extends":"Intangible","properties":{"availableLanguage":{"expectedTypes":["Language"]},"processingTime":{"expectedTypes":["Duration"]},"providesService":{"expectedTypes":["Service"]},"serviceLocation":{"expectedTypes":["Place"]},"servicePhone":{"expectedTypes":["ContactPoint"]},"servicePostalAddress":{"expectedTypes":["PostalAddress"]},"serviceSmsNumber":{"expectedTypes":["ContactPoint"]},"serviceUrl":{"expectedTypes":["URL"]}}},"StructuredValue":{"extends":"Intangible","properties":[]},"ContactPoint":{"extends":"StructuredValue","properties":{"areaServed":{"expectedTypes":["AdministrativeArea"]},"availableLanguage":{"expectedTypes":["Language"]},"contactOption":{"expectedTypes":["ContactPointOption"]},"contactType":{"expectedTypes":["Text"]},"email":{"expectedTypes":["Text"]},"faxNumber":{"expectedTypes":["Text"]},"hoursAvailable":{"expectedTypes":["OpeningHoursSpecification"]},"productSupported":{"expectedTypes":["Product","Text"]},"telephone":{"expectedTypes":["Text"]}}},"PostalAddress":{"extends":"ContactPoint","properties":{"addressCountry":{"expectedTypes":["Country"]},"addressLocality":{"expectedTypes":["Text"]},"addressRegion":{"expectedTypes":["Text"]},"postOfficeBoxNumber":{"expectedTypes":["Text"]},"postalCode":{"expectedTypes":["Text"]},"streetAddress":{"expectedTypes":["Text"]}}},"DatedMoneySpecification":{"extends":"StructuredValue","properties":{"amount":{"expectedTypes":["Number"]},"currency":{"expectedTypes":["Text"]},"endDate":{"expectedTypes":["Date"]},"startDate":{"expectedTypes":["Date"]}}},"GeoCoordinates":{"extends":"StructuredValue","properties":{"elevation":{"expectedTypes":["Text","Number"]},"latitude":{"expectedTypes":["Text","Number"]},"longitude":{"expectedTypes":["Text","Number"]}}},"GeoShape":{"extends":"StructuredValue","properties":{"box":{"expectedTypes":["Text"]},"circle":{"expectedTypes":["Text"]},"elevation":{"expectedTypes":["Number","Text"]},"line":{"expectedTypes":["Text"]},"polygon":{"expectedTypes":["Text"]}}},"NutritionInformation":{"extends":"StructuredValue","properties":{"calories":{"expectedTypes":["Energy"]},"carbohydrateContent":{"expectedTypes":["Mass"]},"cholesterolContent":{"expectedTypes":["Mass"]},"fatContent":{"expectedTypes":["Mass"]},"fiberContent":{"expectedTypes":["Mass"]},"proteinContent":{"expectedTypes":["Mass"]},"saturatedFatContent":{"expectedTypes":["Mass"]},"servingSize":{"expectedTypes":["Text"]},"sodiumContent":{"expectedTypes":["Mass"]},"sugarContent":{"expectedTypes":["Mass"]},"transFatContent":{"expectedTypes":["Mass"]},"unsaturatedFatContent":{"expectedTypes":["Mass"]}}},"OpeningHoursSpecification":{"extends":"StructuredValue","properties":{"closes":{"expectedTypes":["Time"]},"dayOfWeek":{"expectedTypes":["DayOfWeek"]},"opens":{"expectedTypes":["Time"]},"validFrom":{"expectedTypes":["DateTime"]},"validThrough":{"expectedTypes":["DateTime"]}}},"OwnershipInfo":{"extends":"StructuredValue","properties":{"acquiredFrom":{"expectedTypes":["Organization","Person"]},"ownedFrom":{"expectedTypes":["DateTime"]},"ownedThrough":{"expectedTypes":["DateTime"]},"typeOfGood":{"expectedTypes":["Product"]}}},"PriceSpecification":{"extends":"StructuredValue","properties":{"eligibleQuantity":{"expectedTypes":["QuantitativeValue"]},"eligibleTransactionVolume":{"expectedTypes":["PriceSpecification"]},"maxPrice":{"expectedTypes":["Number"]},"minPrice":{"expectedTypes":["Number"]},"price":{"expectedTypes":["Number","Text"]},"priceCurrency":{"expectedTypes":["Text"]},"validFrom":{"expectedTypes":["DateTime"]},"validThrough":{"expectedTypes":["DateTime"]},"valueAddedTaxIncluded":{"expectedTypes":["Boolean"]}}},"DeliveryChargeSpecification":{"extends":"PriceSpecification","properties":{"appliesToDeliveryMethod":{"expectedTypes":["DeliveryMethod"]},"eligibleRegion":{"expectedTypes":["Text","GeoShape"]}}},"PaymentChargeSpecification":{"extends":"PriceSpecification","properties":{"appliesToDeliveryMethod":{"expectedTypes":["DeliveryMethod"]},"appliesToPaymentMethod":{"expectedTypes":["PaymentMethod"]}}},"UnitPriceSpecification":{"extends":"PriceSpecification","properties":{"billingIncrement":{"expectedTypes":["Number"]},"priceType":{"expectedTypes":["Text"]},"unitCode":{"expectedTypes":["Text"]}}},"QuantitativeValue":{"extends":"StructuredValue","properties":{"maxValue":{"expectedTypes":["Number"]},"minValue":{"expectedTypes":["Number"]},"unitCode":{"expectedTypes":["Text"]},"value":{"expectedTypes":["Number"]},"valueReference":{"expectedTypes":["StructuredValue","Enumeration"]}}},"TypeAndQuantityNode":{"extends":"StructuredValue","properties":{"amountOfThisGood":{"expectedTypes":["Number"]},"businessFunction":{"expectedTypes":["BusinessFunction"]},"typeOfGood":{"expectedTypes":["Product"]},"unitCode":{"expectedTypes":["Text"]}}},"WarrantyPromise":{"extends":"StructuredValue","properties":{"durationOfWarranty":{"expectedTypes":["QuantitativeValue"]},"warrantyScope":{"expectedTypes":["WarrantyScope"]}}},"Ticket":{"extends":"Intangible","properties":{"dateIssued":{"expectedTypes":["DateTime"]},"issuedBy":{"expectedTypes":["Organization"]},"priceCurrency":{"expectedTypes":["Text"]},"ticketNumber":{"expectedTypes":["Text"]},"ticketToken":{"expectedTypes":["Text","URL"]},"ticketedSeat":{"expectedTypes":["Seat"]},"totalPrice":{"expectedTypes":["Text","Number","PriceSpecification"]},"underName":{"expectedTypes":["Person","Organization"]}}},"TrainTrip":{"extends":"Intangible","properties":{"arrivalPlatform":{"expectedTypes":["Text"]},"arrivalStation":{"expectedTypes":["TrainStation"]},"arrivalTime":{"expectedTypes":["DateTime"]},"departurePlatform":{"expectedTypes":["Text"]},"departureStation":{"expectedTypes":["TrainStation"]},"departureTime":{"expectedTypes":["DateTime"]},"provider":{"expectedTypes":["Person","Organization"]},"trainName":{"expectedTypes":["Text"]},"trainNumber":{"expectedTypes":["Text"]}}},"MedicalEntity":{"extends":"Thing","properties":{"code":{"expectedTypes":["MedicalCode"]},"guideline":{"expectedTypes":["MedicalGuideline"]},"medicineSystem":{"expectedTypes":["MedicineSystem"]},"recognizingAuthority":{"expectedTypes":["Organization"]},"relevantSpecialty":{"expectedTypes":["MedicalSpecialty"]},"study":{"expectedTypes":["MedicalStudy"]}}},"AnatomicalStructure":{"extends":"MedicalEntity","properties":{"associatedPathophysiology":{"expectedTypes":["Text"]},"bodyLocation":{"expectedTypes":["Text"]},"connectedTo":{"expectedTypes":["AnatomicalStructure"]},"diagram":{"expectedTypes":["ImageObject"]},"function":{"expectedTypes":["Text"]},"partOfSystem":{"expectedTypes":["AnatomicalSystem"]},"relatedCondition":{"expectedTypes":["MedicalCondition"]},"relatedTherapy":{"expectedTypes":["MedicalTherapy"]},"subStructure":{"expectedTypes":["AnatomicalStructure"]}}},"Bone":{"extends":"AnatomicalStructure","properties":[]},"BrainStructure":{"extends":"AnatomicalStructure","properties":[]},"Joint":{"extends":"AnatomicalStructure","properties":{"biomechnicalClass":{"expectedTypes":["Text"]},"functionalClass":{"expectedTypes":["Text"]},"structuralClass":{"expectedTypes":["Text"]}}},"Ligament":{"extends":"AnatomicalStructure","properties":[]},"Muscle":{"extends":"AnatomicalStructure","properties":{"antagonist":{"expectedTypes":["Muscle"]},"bloodSupply":{"expectedTypes":["Vessel"]},"insertion":{"expectedTypes":["AnatomicalStructure"]},"muscleAction":{"expectedTypes":["Text"]},"nerve":{"expectedTypes":["Nerve"]},"origin":{"expectedTypes":["AnatomicalStructure"]}}},"Nerve":{"extends":"AnatomicalStructure","properties":{"branch":{"expectedTypes":["AnatomicalStructure"]},"nerveMotor":{"expectedTypes":["Muscle"]},"sensoryUnit":{"expectedTypes":["SuperficialAnatomy","AnatomicalStructure"]},"sourcedFrom":{"expectedTypes":["BrainStructure"]}}},"Vessel":{"extends":"AnatomicalStructure","properties":[]},"Artery":{"extends":"Vessel","properties":{"arterialBranch":{"expectedTypes":["AnatomicalStructure"]},"source":{"expectedTypes":["AnatomicalStructure"]},"supplyTo":{"expectedTypes":["AnatomicalStructure"]}}},"LymphaticVessel":{"extends":"Vessel","properties":{"originatesFrom":{"expectedTypes":["Vessel"]},"regionDrained":{"expectedTypes":["AnatomicalSystem","AnatomicalStructure"]},"runsTo":{"expectedTypes":["Vessel"]}}},"Vein":{"extends":"Vessel","properties":{"drainsTo":{"expectedTypes":["Vessel"]},"regionDrained":{"expectedTypes":["AnatomicalSystem","AnatomicalStructure"]},"tributary":{"expectedTypes":["AnatomicalStructure"]}}},"AnatomicalSystem":{"extends":"MedicalEntity","properties":{"associatedPathophysiology":{"expectedTypes":["Text"]},"comprisedOf":{"expectedTypes":["AnatomicalSystem","AnatomicalStructure"]},"relatedCondition":{"expectedTypes":["MedicalCondition"]},"relatedStructure":{"expectedTypes":["AnatomicalStructure"]},"relatedTherapy":{"expectedTypes":["MedicalTherapy"]}}},"MedicalCause":{"extends":"MedicalEntity","properties":{"causeOf":{"expectedTypes":["MedicalEntity"]}}},"MedicalCondition":{"extends":"MedicalEntity","properties":{"associatedAnatomy":{"expectedTypes":["AnatomicalSystem","SuperficialAnatomy","AnatomicalStructure"]},"cause":{"expectedTypes":["MedicalCause"]},"differentialDiagnosis":{"expectedTypes":["DDxElement"]},"epidemiology":{"expectedTypes":["Text"]},"expectedPrognosis":{"expectedTypes":["Text"]},"naturalProgression":{"expectedTypes":["Text"]},"pathophysiology":{"expectedTypes":["Text"]},"possibleComplication":{"expectedTypes":["Text"]},"possibleTreatment":{"expectedTypes":["MedicalTherapy"]},"primaryPrevention":{"expectedTypes":["MedicalTherapy"]},"riskFactor":{"expectedTypes":["MedicalRiskFactor"]},"secondaryPrevention":{"expectedTypes":["MedicalTherapy"]},"signOrSymptom":{"expectedTypes":["MedicalSignOrSymptom"]},"stage":{"expectedTypes":["MedicalConditionStage"]},"subtype":{"expectedTypes":["Text"]},"typicalTest":{"expectedTypes":["MedicalTest"]}}},"InfectiousDisease":{"extends":"MedicalCondition","properties":{"infectiousAgent":{"expectedTypes":["Text"]},"infectiousAgentClass":{"expectedTypes":["InfectiousAgentClass"]},"transmissionMethod":{"expectedTypes":["Text"]}}},"MedicalContraindication":{"extends":"MedicalEntity","properties":[]},"MedicalDevice":{"extends":"MedicalEntity","properties":{"adverseOutcome":{"expectedTypes":["MedicalEntity"]},"contraindication":{"expectedTypes":["MedicalContraindication"]},"indication":{"expectedTypes":["MedicalIndication"]},"postOp":{"expectedTypes":["Text"]},"preOp":{"expectedTypes":["Text"]},"procedure":{"expectedTypes":["Text"]},"purpose":{"expectedTypes":["MedicalDevicePurpose","Thing"]},"seriousAdverseOutcome":{"expectedTypes":["MedicalEntity"]}}},"MedicalGuideline":{"extends":"MedicalEntity","properties":{"evidenceLevel":{"expectedTypes":["MedicalEvidenceLevel"]},"evidenceOrigin":{"expectedTypes":["Text"]},"guidelineDate":{"expectedTypes":["Date"]},"guidelineSubject":{"expectedTypes":["MedicalEntity"]}}},"MedicalGuidelineContraindication":{"extends":"MedicalGuideline","properties":[]},"MedicalGuidelineRecommendation":{"extends":"MedicalGuideline","properties":{"recommendationStrength":{"expectedTypes":["Text"]}}},"MedicalIndication":{"extends":"MedicalEntity","properties":[]},"ApprovedIndication":{"extends":"MedicalIndication","properties":[]},"PreventionIndication":{"extends":"MedicalIndication","properties":[]},"TreatmentIndication":{"extends":"MedicalIndication","properties":[]},"MedicalIntangible":{"extends":"MedicalEntity","properties":[]},"DDxElement":{"extends":"MedicalIntangible","properties":{"diagnosis":{"expectedTypes":["MedicalCondition"]},"distinguishingSign":{"expectedTypes":["MedicalSignOrSymptom"]}}},"DoseSchedule":{"extends":"MedicalIntangible","properties":{"doseUnit":{"expectedTypes":["Text"]},"doseValue":{"expectedTypes":["Number"]},"frequency":{"expectedTypes":["Text"]},"targetPopulation":{"expectedTypes":["Text"]}}},"MaximumDoseSchedule":{"extends":"DoseSchedule","properties":[]},"RecommendedDoseSchedule":{"extends":"DoseSchedule","properties":[]},"ReportedDoseSchedule":{"extends":"DoseSchedule","properties":[]},"DrugCost":{"extends":"MedicalIntangible","properties":{"applicableLocation":{"expectedTypes":["AdministrativeArea"]},"costCategory":{"expectedTypes":["DrugCostCategory"]},"costCurrency":{"expectedTypes":["Text"]},"costOrigin":{"expectedTypes":["Text"]},"costPerUnit":{"expectedTypes":["Text","Number"]},"drugUnit":{"expectedTypes":["Text"]}}},"DrugLegalStatus":{"extends":"MedicalIntangible","properties":{"applicableLocation":{"expectedTypes":["AdministrativeArea"]}}},"DrugStrength":{"extends":"MedicalIntangible","properties":{"activeIngredient":{"expectedTypes":["Text"]},"availableIn":{"expectedTypes":["AdministrativeArea"]},"strengthUnit":{"expectedTypes":["Text"]},"strengthValue":{"expectedTypes":["Number"]}}},"MedicalCode":{"extends":"MedicalIntangible","properties":{"codeValue":{"expectedTypes":["Text"]},"codingSystem":{"expectedTypes":["Text"]}}},"MedicalConditionStage":{"extends":"MedicalIntangible","properties":{"stageAsNumber":{"expectedTypes":["Number"]},"subStageSuffix":{"expectedTypes":["Text"]}}},"":{"extends":"","properties":[]},"MedicalProcedure":{"extends":"MedicalEntity","properties":{"followup":{"expectedTypes":["Text"]},"howPerformed":{"expectedTypes":["Text"]},"preparation":{"expectedTypes":["Text"]},"procedureType":{"expectedTypes":["MedicalProcedureType"]}}},"DiagnosticProcedure":{"extends":"MedicalProcedure","properties":[]},"PalliativeProcedure":{"extends":"MedicalTherapy","properties":[]},"TherapeuticProcedure":{"extends":"MedicalTherapy","properties":[]},"MedicalRiskEstimator":{"extends":"MedicalEntity","properties":{"estimatesRiskOf":{"expectedTypes":["MedicalEntity"]},"includedRiskFactor":{"expectedTypes":["MedicalRiskFactor"]}}},"MedicalRiskCalculator":{"extends":"MedicalRiskEstimator","properties":[]},"MedicalRiskScore":{"extends":"MedicalRiskEstimator","properties":{"algorithm":{"expectedTypes":["Text"]}}},"MedicalRiskFactor":{"extends":"MedicalEntity","properties":{"increasesRiskOf":{"expectedTypes":["MedicalEntity"]}}},"MedicalSignOrSymptom":{"extends":"MedicalEntity","properties":{"cause":{"expectedTypes":["MedicalCause"]},"possibleTreatment":{"expectedTypes":["MedicalTherapy"]}}},"MedicalSign":{"extends":"MedicalSignOrSymptom","properties":{"identifyingExam":{"expectedTypes":["PhysicalExam"]},"identifyingTest":{"expectedTypes":["MedicalTest"]}}},"MedicalSymptom":{"extends":"MedicalSignOrSymptom","properties":[]},"MedicalStudy":{"extends":"MedicalEntity","properties":{"outcome":{"expectedTypes":["Text"]},"population":{"expectedTypes":["Text"]},"sponsor":{"expectedTypes":["Organization"]},"status":{"expectedTypes":["MedicalStudyStatus"]},"studyLocation":{"expectedTypes":["AdministrativeArea"]},"studySubject":{"expectedTypes":["MedicalEntity"]}}},"MedicalObservationalStudy":{"extends":"MedicalStudy","properties":{"studyDesign":{"expectedTypes":["MedicalObservationalStudyDesign"]}}},"MedicalTrial":{"extends":"MedicalStudy","properties":{"phase":{"expectedTypes":["Text"]},"trialDesign":{"expectedTypes":["MedicalTrialDesign"]}}},"MedicalTest":{"extends":"MedicalEntity","properties":{"affectedBy":{"expectedTypes":["Drug"]},"normalRange":{"expectedTypes":["Text"]},"signDetected":{"expectedTypes":["MedicalSign"]},"usedToDiagnose":{"expectedTypes":["MedicalCondition"]},"usesDevice":{"expectedTypes":["MedicalDevice"]}}},"BloodTest":{"extends":"MedicalTest","properties":[]},"ImagingTest":{"extends":"MedicalTest","properties":{"imagingTechnique":{"expectedTypes":["MedicalImagingTechnique"]}}},"MedicalTestPanel":{"extends":"MedicalTest","properties":{"subTest":{"expectedTypes":["MedicalTest"]}}},"PathologyTest":{"extends":"MedicalTest","properties":{"tissueSample":{"expectedTypes":["Text"]}}},"MedicalTherapy":{"extends":"MedicalEntity","properties":{"adverseOutcome":{"expectedTypes":["MedicalEntity"]},"contraindication":{"expectedTypes":["MedicalContraindication"]},"duplicateTherapy":{"expectedTypes":["MedicalTherapy"]},"indication":{"expectedTypes":["MedicalIndication"]},"seriousAdverseOutcome":{"expectedTypes":["MedicalEntity"]}}},"DietarySupplement":{"extends":"MedicalTherapy","properties":{"activeIngredient":{"expectedTypes":["Text"]},"background":{"expectedTypes":["Text"]},"dosageForm":{"expectedTypes":["Text"]},"isProprietary":{"expectedTypes":["Boolean"]},"legalStatus":{"expectedTypes":["DrugLegalStatus"]},"manufacturer":{"expectedTypes":["Organization"]},"maximumIntake":{"expectedTypes":["MaximumDoseSchedule"]},"mechanismOfAction":{"expectedTypes":["Text"]},"nonProprietaryName":{"expectedTypes":["Text"]},"recommendedIntake":{"expectedTypes":["RecommendedDoseSchedule"]},"safetyConsideration":{"expectedTypes":["Text"]},"targetPopulation":{"expectedTypes":["Text"]}}},"Drug":{"extends":"MedicalTherapy","properties":{"activeIngredient":{"expectedTypes":["Text"]},"administrationRoute":{"expectedTypes":["Text"]},"alcoholWarning":{"expectedTypes":["Text"]},"availableStrength":{"expectedTypes":["DrugStrength"]},"breastfeedingWarning":{"expectedTypes":["Text"]},"clinicalPharmacology":{"expectedTypes":["Text"]},"cost":{"expectedTypes":["DrugCost"]},"dosageForm":{"expectedTypes":["Text"]},"doseSchedule":{"expectedTypes":["DoseSchedule"]},"drugClass":{"expectedTypes":["DrugClass"]},"foodWarning":{"expectedTypes":["Text"]},"interactingDrug":{"expectedTypes":["Drug"]},"isAvailableGenerically":{"expectedTypes":["Boolean"]},"isProprietary":{"expectedTypes":["Boolean"]},"labelDetails":{"expectedTypes":["URL"]},"legalStatus":{"expectedTypes":["DrugLegalStatus"]},"manufacturer":{"expectedTypes":["Organization"]},"mechanismOfAction":{"expectedTypes":["Text"]},"nonProprietaryName":{"expectedTypes":["Text"]},"overdosage":{"expectedTypes":["Text"]},"pregnancyCategory":{"expectedTypes":["DrugPregnancyCategory"]},"pregnancyWarning":{"expectedTypes":["Text"]},"prescribingInfo":{"expectedTypes":["URL"]},"prescriptionStatus":{"expectedTypes":["DrugPrescriptionStatus"]},"relatedDrug":{"expectedTypes":["Drug"]},"warning":{"expectedTypes":["Text","URL"]}}},"DrugClass":{"extends":"MedicalTherapy","properties":{"drug":{"expectedTypes":["Drug"]}}},"LifestyleModification":{"extends":"MedicalTherapy","properties":[]},"PhysicalActivity":{"extends":"LifestyleModification","properties":{"associatedAnatomy":{"expectedTypes":["AnatomicalSystem","SuperficialAnatomy","AnatomicalStructure"]},"category":{"expectedTypes":["PhysicalActivityCategory","Thing","Text"]},"epidemiology":{"expectedTypes":["Text"]},"pathophysiology":{"expectedTypes":["Text"]}}},"PhysicalTherapy":{"extends":"MedicalTherapy","properties":[]},"PsychologicalTreatment":{"extends":"MedicalTherapy","properties":[]},"RadiationTherapy":{"extends":"MedicalTherapy","properties":[]},"SuperficialAnatomy":{"extends":"MedicalEntity","properties":{"associatedPathophysiology":{"expectedTypes":["Text"]},"relatedAnatomy":{"expectedTypes":["AnatomicalSystem","AnatomicalStructure"]},"relatedCondition":{"expectedTypes":["MedicalCondition"]},"relatedTherapy":{"expectedTypes":["MedicalTherapy"]},"significance":{"expectedTypes":["Text"]}}},"Organization":{"extends":"Thing","properties":{"address":{"expectedTypes":["PostalAddress"]},"aggregateRating":{"expectedTypes":["AggregateRating"]},"brand":{"expectedTypes":["Brand","Organization"]},"contactPoint":{"expectedTypes":["ContactPoint"]},"department":{"expectedTypes":["Organization"]},"dissolutionDate":{"expectedTypes":["Date"]},"duns":{"expectedTypes":["Text"]},"email":{"expectedTypes":["Text"]},"employee":{"expectedTypes":["Person"]},"event":{"expectedTypes":["Event"]},"faxNumber":{"expectedTypes":["Text"]},"founder":{"expectedTypes":["Person"]},"foundingDate":{"expectedTypes":["Date"]},"foundingLocation":{"expectedTypes":["Place"]},"globalLocationNumber":{"expectedTypes":["Text"]},"hasPOS":{"expectedTypes":["Place"]},"interactionCount":{"expectedTypes":["Text"]},"isicV4":{"expectedTypes":["Text"]},"legalName":{"expectedTypes":["Text"]},"location":{"expectedTypes":["Place","PostalAddress"]},"logo":{"expectedTypes":["ImageObject","URL"]},"makesOffer":{"expectedTypes":["Offer"]},"member":{"expectedTypes":["Person","Organization"]},"memberOf":{"expectedTypes":["ProgramMembership","Organization"]},"naics":{"expectedTypes":["Text"]},"owns":{"expectedTypes":["Product","OwnershipInfo"]},"review":{"expectedTypes":["Review"]},"seeks":{"expectedTypes":["Demand"]},"subOrganization":{"expectedTypes":["Organization"]},"taxID":{"expectedTypes":["Text"]},"telephone":{"expectedTypes":["Text"]},"vatID":{"expectedTypes":["Text"]}}},"Airline":{"extends":"Organization","properties":{"iataCode":{"expectedTypes":["Text"]}}},"Corporation":{"extends":"Organization","properties":{"tickerSymbol":{"expectedTypes":["Text"]}}},"EducationalOrganization":{"extends":"Organization","properties":{"alumni":{"expectedTypes":["Person"]}}},"CollegeOrUniversity":{"extends":"EducationalOrganization","properties":[]},"ElementarySchool":{"extends":"EducationalOrganization","properties":[]},"HighSchool":{"extends":"EducationalOrganization","properties":[]},"MiddleSchool":{"extends":"EducationalOrganization","properties":[]},"Preschool":{"extends":"EducationalOrganization","properties":[]},"School":{"extends":"EducationalOrganization","properties":[]},"GovernmentOrganization":{"extends":"Organization","properties":[]},"LocalBusiness":{"extends":"Organization","properties":{"branchOf":{"expectedTypes":["Organization"]},"currenciesAccepted":{"expectedTypes":["Text"]},"openingHours":{"expectedTypes":["Duration"]},"paymentAccepted":{"expectedTypes":["Text"]},"priceRange":{"expectedTypes":["Text"]}}},"AnimalShelter":{"extends":"LocalBusiness","properties":[]},"AutomotiveBusiness":{"extends":"LocalBusiness","properties":[]},"AutoBodyShop":{"extends":"AutomotiveBusiness","properties":[]},"AutoDealer":{"extends":"AutomotiveBusiness","properties":[]},"AutoPartsStore":{"extends":"Store","properties":[]},"AutoRental":{"extends":"AutomotiveBusiness","properties":[]},"AutoRepair":{"extends":"AutomotiveBusiness","properties":[]},"AutoWash":{"extends":"AutomotiveBusiness","properties":[]},"GasStation":{"extends":"AutomotiveBusiness","properties":[]},"MotorcycleDealer":{"extends":"AutomotiveBusiness","properties":[]},"MotorcycleRepair":{"extends":"AutomotiveBusiness","properties":[]},"ChildCare":{"extends":"LocalBusiness","properties":[]},"DryCleaningOrLaundry":{"extends":"LocalBusiness","properties":[]},"EmergencyService":{"extends":"LocalBusiness","properties":[]},"FireStation":{"extends":"CivicStructure","properties":[]},"Hospital":{"extends":"MedicalOrganization","properties":{"availableService":{"expectedTypes":["MedicalTest","MedicalTherapy","MedicalProcedure"]},"medicalSpecialty":{"expectedTypes":["MedicalSpecialty"]}}},"PoliceStation":{"extends":"CivicStructure","properties":[]},"EmploymentAgency":{"extends":"LocalBusiness","properties":[]},"EntertainmentBusiness":{"extends":"LocalBusiness","properties":[]},"AdultEntertainment":{"extends":"EntertainmentBusiness","properties":[]},"AmusementPark":{"extends":"EntertainmentBusiness","properties":[]},"ArtGallery":{"extends":"EntertainmentBusiness","properties":[]},"Casino":{"extends":"EntertainmentBusiness","properties":[]},"ComedyClub":{"extends":"EntertainmentBusiness","properties":[]},"MovieTheater":{"extends":"EntertainmentBusiness","properties":[]},"NightClub":{"extends":"EntertainmentBusiness","properties":[]},"FinancialService":{"extends":"LocalBusiness","properties":[]},"AccountingService":{"extends":"FinancialService","properties":[]},"AutomatedTeller":{"extends":"FinancialService","properties":[]},"BankOrCreditUnion":{"extends":"FinancialService","properties":[]},"InsuranceAgency":{"extends":"FinancialService","properties":[]},"FoodEstablishment":{"extends":"LocalBusiness","properties":{"acceptsReservations":{"expectedTypes":["Text","Boolean","URL"]},"menu":{"expectedTypes":["Text","URL"]},"servesCuisine":{"expectedTypes":["Text"]}}},"Bakery":{"extends":"FoodEstablishment","properties":[]},"BarOrPub":{"extends":"FoodEstablishment","properties":[]},"Brewery":{"extends":"FoodEstablishment","properties":[]},"CafeOrCoffeeShop":{"extends":"FoodEstablishment","properties":[]},"FastFoodRestaurant":{"extends":"FoodEstablishment","properties":[]},"IceCreamShop":{"extends":"FoodEstablishment","properties":[]},"Restaurant":{"extends":"FoodEstablishment","properties":[]},"Winery":{"extends":"FoodEstablishment","properties":[]},"GovernmentOffice":{"extends":"LocalBusiness","properties":[]},"PostOffice":{"extends":"GovernmentOffice","properties":[]},"HealthAndBeautyBusiness":{"extends":"LocalBusiness","properties":[]},"BeautySalon":{"extends":"HealthAndBeautyBusiness","properties":[]},"DaySpa":{"extends":"HealthAndBeautyBusiness","properties":[]},"HairSalon":{"extends":"HealthAndBeautyBusiness","properties":[]},"HealthClub":{"extends":"HealthAndBeautyBusiness","properties":[]},"NailSalon":{"extends":"HealthAndBeautyBusiness","properties":[]},"TattooParlor":{"extends":"HealthAndBeautyBusiness","properties":[]},"HomeAndConstructionBusiness":{"extends":"LocalBusiness","properties":[]},"Electrician":{"extends":"HomeAndConstructionBusiness","properties":[]},"GeneralContractor":{"extends":"HomeAndConstructionBusiness","properties":[]},"HVACBusiness":{"extends":"HomeAndConstructionBusiness","properties":[]},"HousePainter":{"extends":"HomeAndConstructionBusiness","properties":[]},"Locksmith":{"extends":"HomeAndConstructionBusiness","properties":[]},"MovingCompany":{"extends":"HomeAndConstructionBusiness","properties":[]},"Plumber":{"extends":"HomeAndConstructionBusiness","properties":[]},"RoofingContractor":{"extends":"HomeAndConstructionBusiness","properties":[]},"InternetCafe":{"extends":"LocalBusiness","properties":[]},"Library":{"extends":"LocalBusiness","properties":[]},"LodgingBusiness":{"extends":"LocalBusiness","properties":[]},"BedAndBreakfast":{"extends":"LodgingBusiness","properties":[]},"Hostel":{"extends":"LodgingBusiness","properties":[]},"Hotel":{"extends":"LodgingBusiness","properties":[]},"Motel":{"extends":"LodgingBusiness","properties":[]},"MedicalOrganization":{"extends":"LocalBusiness","properties":[]},"Dentist":{"extends":"MedicalOrganization","properties":[]},"DiagnosticLab":{"extends":"MedicalOrganization","properties":{"availableTest":{"expectedTypes":["MedicalTest"]}}},"MedicalClinic":{"extends":"MedicalOrganization","properties":{"availableService":{"expectedTypes":["MedicalTherapy","MedicalProcedure","MedicalTest"]},"medicalSpecialty":{"expectedTypes":["MedicalSpecialty"]}}},"Optician":{"extends":"MedicalOrganization","properties":[]},"Pharmacy":{"extends":"MedicalOrganization","properties":[]},"Physician":{"extends":"MedicalOrganization","properties":{"availableService":{"expectedTypes":["MedicalTherapy","MedicalProcedure","MedicalTest"]},"hospitalAffiliation":{"expectedTypes":["Hospital"]},"medicalSpecialty":{"expectedTypes":["MedicalSpecialty"]}}},"VeterinaryCare":{"extends":"MedicalOrganization","properties":[]},"ProfessionalService":{"extends":"LocalBusiness","properties":[]},"Attorney":{"extends":"ProfessionalService","properties":[]},"Notary":{"extends":"ProfessionalService","properties":[]},"RadioStation":{"extends":"LocalBusiness","properties":[]},"RealEstateAgent":{"extends":"LocalBusiness","properties":[]},"RecyclingCenter":{"extends":"LocalBusiness","properties":[]},"SelfStorage":{"extends":"LocalBusiness","properties":[]},"ShoppingCenter":{"extends":"LocalBusiness","properties":[]},"SportsActivityLocation":{"extends":"LocalBusiness","properties":[]},"BowlingAlley":{"extends":"SportsActivityLocation","properties":[]},"ExerciseGym":{"extends":"SportsActivityLocation","properties":[]},"GolfCourse":{"extends":"SportsActivityLocation","properties":[]},"PublicSwimmingPool":{"extends":"SportsActivityLocation","properties":[]},"SkiResort":{"extends":"SportsActivityLocation","properties":[]},"SportsClub":{"extends":"SportsActivityLocation","properties":[]},"StadiumOrArena":{"extends":"CivicStructure","properties":[]},"TennisComplex":{"extends":"SportsActivityLocation","properties":[]},"Store":{"extends":"LocalBusiness","properties":[]},"BikeStore":{"extends":"Store","properties":[]},"BookStore":{"extends":"Store","properties":[]},"ClothingStore":{"extends":"Store","properties":[]},"ComputerStore":{"extends":"Store","properties":[]},"ConvenienceStore":{"extends":"Store","properties":[]},"DepartmentStore":{"extends":"Store","properties":[]},"ElectronicsStore":{"extends":"Store","properties":[]},"Florist":{"extends":"Store","properties":[]},"FurnitureStore":{"extends":"Store","properties":[]},"GardenStore":{"extends":"Store","properties":[]},"GroceryStore":{"extends":"Store","properties":[]},"HardwareStore":{"extends":"Store","properties":[]},"HobbyShop":{"extends":"Store","properties":[]},"HomeGoodsStore":{"extends":"Store","properties":[]},"JewelryStore":{"extends":"Store","properties":[]},"LiquorStore":{"extends":"Store","properties":[]},"MensClothingStore":{"extends":"Store","properties":[]},"MobilePhoneStore":{"extends":"Store","properties":[]},"MovieRentalStore":{"extends":"Store","properties":[]},"MusicStore":{"extends":"Store","properties":[]},"OfficeEquipmentStore":{"extends":"Store","properties":[]},"OutletStore":{"extends":"Store","properties":[]},"PawnShop":{"extends":"Store","properties":[]},"PetStore":{"extends":"Store","properties":[]},"ShoeStore":{"extends":"Store","properties":[]},"SportingGoodsStore":{"extends":"Store","properties":[]},"TireShop":{"extends":"Store","properties":[]},"ToyStore":{"extends":"Store","properties":[]},"WholesaleStore":{"extends":"Store","properties":[]},"TelevisionStation":{"extends":"LocalBusiness","properties":[]},"TouristInformationCenter":{"extends":"LocalBusiness","properties":[]},"TravelAgency":{"extends":"LocalBusiness","properties":[]},"NGO":{"extends":"Organization","properties":[]},"PerformingGroup":{"extends":"Organization","properties":[]},"DanceGroup":{"extends":"PerformingGroup","properties":[]},"MusicGroup":{"extends":"PerformingGroup","properties":{"album":{"expectedTypes":["MusicAlbum"]},"genre":{"expectedTypes":["Text"]},"track":{"expectedTypes":["ItemList","MusicRecording"]}}},"TheaterGroup":{"extends":"PerformingGroup","properties":[]},"SportsOrganization":{"extends":"Organization","properties":{"sport":{"expectedTypes":["Text","URL"]}}},"SportsTeam":{"extends":"SportsOrganization","properties":{"athlete":{"expectedTypes":["Person"]},"coach":{"expectedTypes":["Person"]}}},"Person":{"extends":"Thing","properties":{"additionalName":{"expectedTypes":["Text"]},"address":{"expectedTypes":["PostalAddress"]},"affiliation":{"expectedTypes":["Organization"]},"alumniOf":{"expectedTypes":["EducationalOrganization"]},"award":{"expectedTypes":["Text"]},"birthDate":{"expectedTypes":["Date"]},"birthPlace":{"expectedTypes":["Place"]},"brand":{"expectedTypes":["Brand","Organization"]},"children":{"expectedTypes":["Person"]},"colleague":{"expectedTypes":["Person"]},"contactPoint":{"expectedTypes":["ContactPoint"]},"deathDate":{"expectedTypes":["Date"]},"deathPlace":{"expectedTypes":["Place"]},"duns":{"expectedTypes":["Text"]},"email":{"expectedTypes":["Text"]},"familyName":{"expectedTypes":["Text"]},"faxNumber":{"expectedTypes":["Text"]},"follows":{"expectedTypes":["Person"]},"gender":{"expectedTypes":["Text"]},"givenName":{"expectedTypes":["Text"]},"globalLocationNumber":{"expectedTypes":["Text"]},"hasPOS":{"expectedTypes":["Place"]},"height":{"expectedTypes":["Distance","QuantitativeValue"]},"homeLocation":{"expectedTypes":["ContactPoint","Place"]},"honorificPrefix":{"expectedTypes":["Text"]},"honorificSuffix":{"expectedTypes":["Text"]},"interactionCount":{"expectedTypes":["Text"]},"isicV4":{"expectedTypes":["Text"]},"jobTitle":{"expectedTypes":["Text"]},"knows":{"expectedTypes":["Person"]},"makesOffer":{"expectedTypes":["Offer"]},"memberOf":{"expectedTypes":["ProgramMembership","Organization"]},"naics":{"expectedTypes":["Text"]},"nationality":{"expectedTypes":["Country"]},"netWorth":{"expectedTypes":["PriceSpecification"]},"owns":{"expectedTypes":["Product","OwnershipInfo"]},"parent":{"expectedTypes":["Person"]},"performerIn":{"expectedTypes":["Event"]},"relatedTo":{"expectedTypes":["Person"]},"seeks":{"expectedTypes":["Demand"]},"sibling":{"expectedTypes":["Person"]},"spouse":{"expectedTypes":["Person"]},"taxID":{"expectedTypes":["Text"]},"telephone":{"expectedTypes":["Text"]},"vatID":{"expectedTypes":["Text"]},"weight":{"expectedTypes":["QuantitativeValue"]},"workLocation":{"expectedTypes":["ContactPoint","Place"]},"worksFor":{"expectedTypes":["Organization"]}}},"Place":{"extends":"Thing","properties":{"address":{"expectedTypes":["PostalAddress"]},"aggregateRating":{"expectedTypes":["AggregateRating"]},"containedIn":{"expectedTypes":["Place"]},"event":{"expectedTypes":["Event"]},"faxNumber":{"expectedTypes":["Text"]},"geo":{"expectedTypes":["GeoCoordinates","GeoShape"]},"globalLocationNumber":{"expectedTypes":["Text"]},"hasMap":{"expectedTypes":["Map","URL"]},"interactionCount":{"expectedTypes":["Text"]},"isicV4":{"expectedTypes":["Text"]},"logo":{"expectedTypes":["ImageObject","URL"]},"openingHoursSpecification":{"expectedTypes":["OpeningHoursSpecification"]},"photo":{"expectedTypes":["ImageObject","Photograph"]},"review":{"expectedTypes":["Review"]},"telephone":{"expectedTypes":["Text"]}}},"AdministrativeArea":{"extends":"Place","properties":[]},"City":{"extends":"AdministrativeArea","properties":[]},"Country":{"extends":"AdministrativeArea","properties":[]},"State":{"extends":"AdministrativeArea","properties":[]},"CivicStructure":{"extends":"Place","properties":{"openingHours":{"expectedTypes":["Duration"]}}},"Airport":{"extends":"CivicStructure","properties":{"iataCode":{"expectedTypes":["Text"]},"icaoCode":{"expectedTypes":["Text"]}}},"Aquarium":{"extends":"CivicStructure","properties":[]},"Beach":{"extends":"CivicStructure","properties":[]},"BusStation":{"extends":"CivicStructure","properties":[]},"BusStop":{"extends":"CivicStructure","properties":[]},"Campground":{"extends":"CivicStructure","properties":[]},"Cemetery":{"extends":"CivicStructure","properties":[]},"Crematorium":{"extends":"CivicStructure","properties":[]},"EventVenue":{"extends":"CivicStructure","properties":[]},"GovernmentBuilding":{"extends":"CivicStructure","properties":[]},"CityHall":{"extends":"GovernmentBuilding","properties":[]},"Courthouse":{"extends":"GovernmentBuilding","properties":[]},"DefenceEstablishment":{"extends":"GovernmentBuilding","properties":[]},"Embassy":{"extends":"GovernmentBuilding","properties":[]},"LegislativeBuilding":{"extends":"GovernmentBuilding","properties":[]},"Museum":{"extends":"CivicStructure","properties":[]},"MusicVenue":{"extends":"CivicStructure","properties":[]},"Park":{"extends":"CivicStructure","properties":[]},"ParkingFacility":{"extends":"CivicStructure","properties":[]},"PerformingArtsTheater":{"extends":"CivicStructure","properties":[]},"PlaceOfWorship":{"extends":"CivicStructure","properties":[]},"BuddhistTemple":{"extends":"PlaceOfWorship","properties":[]},"CatholicChurch":{"extends":"PlaceOfWorship","properties":[]},"Church":{"extends":"PlaceOfWorship","properties":[]},"HinduTemple":{"extends":"PlaceOfWorship","properties":[]},"Mosque":{"extends":"PlaceOfWorship","properties":[]},"Synagogue":{"extends":"PlaceOfWorship","properties":[]},"Playground":{"extends":"CivicStructure","properties":[]},"RVPark":{"extends":"CivicStructure","properties":[]},"SubwayStation":{"extends":"CivicStructure","properties":[]},"TaxiStand":{"extends":"CivicStructure","properties":[]},"TrainStation":{"extends":"CivicStructure","properties":[]},"Zoo":{"extends":"CivicStructure","properties":[]},"Landform":{"extends":"Place","properties":[]},"BodyOfWater":{"extends":"Landform","properties":[]},"Canal":{"extends":"BodyOfWater","properties":[]},"LakeBodyOfWater":{"extends":"BodyOfWater","properties":[]},"OceanBodyOfWater":{"extends":"BodyOfWater","properties":[]},"Pond":{"extends":"BodyOfWater","properties":[]},"Reservoir":{"extends":"BodyOfWater","properties":[]},"RiverBodyOfWater":{"extends":"BodyOfWater","properties":[]},"SeaBodyOfWater":{"extends":"BodyOfWater","properties":[]},"Waterfall":{"extends":"BodyOfWater","properties":[]},"Continent":{"extends":"Landform","properties":[]},"Mountain":{"extends":"Landform","properties":[]},"Volcano":{"extends":"Landform","properties":[]},"LandmarksOrHistoricalBuildings":{"extends":"Place","properties":[]},"Residence":{"extends":"Place","properties":[]},"ApartmentComplex":{"extends":"Residence","properties":[]},"GatedResidenceCommunity":{"extends":"Residence","properties":[]},"SingleFamilyResidence":{"extends":"Residence","properties":[]},"TouristAttraction":{"extends":"Place","properties":[]},"Product":{"extends":"Thing","properties":{"aggregateRating":{"expectedTypes":["AggregateRating"]},"audience":{"expectedTypes":["Audience"]},"brand":{"expectedTypes":["Brand","Organization"]},"color":{"expectedTypes":["Text"]},"depth":{"expectedTypes":["Distance","QuantitativeValue"]},"gtin13":{"expectedTypes":["Text"]},"gtin14":{"expectedTypes":["Text"]},"gtin8":{"expectedTypes":["Text"]},"height":{"expectedTypes":["Distance","QuantitativeValue"]},"isAccessoryOrSparePartFor":{"expectedTypes":["Product"]},"isConsumableFor":{"expectedTypes":["Product"]},"isRelatedTo":{"expectedTypes":["Product"]},"isSimilarTo":{"expectedTypes":["Product"]},"itemCondition":{"expectedTypes":["OfferItemCondition"]},"logo":{"expectedTypes":["ImageObject","URL"]},"manufacturer":{"expectedTypes":["Organization"]},"model":{"expectedTypes":["Text","ProductModel"]},"mpn":{"expectedTypes":["Text"]},"offers":{"expectedTypes":["Offer"]},"productID":{"expectedTypes":["Text"]},"releaseDate":{"expectedTypes":["Date"]},"review":{"expectedTypes":["Review"]},"sku":{"expectedTypes":["Text"]},"weight":{"expectedTypes":["QuantitativeValue"]},"width":{"expectedTypes":["Distance","QuantitativeValue"]}}},"IndividualProduct":{"extends":"Product","properties":{"serialNumber":{"expectedTypes":["Text"]}}},"ProductModel":{"extends":"Product","properties":{"isVariantOf":{"expectedTypes":["ProductModel"]},"predecessorOf":{"expectedTypes":["ProductModel"]},"successorOf":{"expectedTypes":["ProductModel"]}}},"SomeProducts":{"extends":"Product","properties":{"inventoryLevel":{"expectedTypes":["QuantitativeValue"]}}},"Vehicle":{"extends":"Product","properties":[]},"Car":{"extends":"Vehicle","properties":[]}}PK1A#]YSo��src/Schema/ChangeItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Schema; use Joomla\CMS\Factory; use Joomla\Database\DatabaseDriver; use Joomla\Database\Exception\ExecutionFailureException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Each object represents one query, which is one line from a DDL SQL query. * This class is used to check the site's database to see if the DDL query has been run. * If not, it provides the ability to fix the database by re-running the DDL query. * The queries are parsed from the update files in the folder * `administrator/components/com_admin/sql/updates/<database>`. * These updates are run automatically if the site was updated using com_installer. * However, it is possible that the program files could be updated without updating * the database (for example, if a user just copies the new files over the top of an * existing installation). * * This is an abstract class. We need to extend it for each database and add a * buildCheckQuery() method that creates the query to check that a DDL query has been run. * * @since 2.5 */ abstract class ChangeItem { /** * Update file: full path file name where query was found * * @var string * @since 2.5 */ public $file = null; /** * Update query: query used to change the db schema (one line from the file) * * @var string * @since 2.5 */ public $updateQuery = null; /** * Check query: query used to check the db schema * * @var string * @since 2.5 */ public $checkQuery = null; /** * Check query result: expected result of check query if database is up to date * * @var string * @since 2.5 */ public $checkQueryExpected = 1; /** * DatabaseDriver object * * @var DatabaseDriver * @since 2.5 */ public $db = null; /** * Query type: To be used in building a language key for a * message to tell user what was checked / changed * Possible values: ADD_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX * * @var string * @since 2.5 */ public $queryType = null; /** * Array with values for use in a Text::sprintf statement indicating what was checked * * Tells you what the message should be, based on which elements are defined, as follows: * For ADD_TABLE: table * For ADD_COLUMN: table, column * For CHANGE_COLUMN_TYPE: table, column, type * For ADD_INDEX: table, index * * @var array * @since 2.5 */ public $msgElements = []; /** * Checked status * * @var integer 0=not checked, -1=skipped, -2=failed, 1=succeeded * @since 2.5 */ public $checkStatus = 0; /** * Rerun status * * @var integer 0=not rerun, -1=skipped, -2=failed, 1=succeeded * @since 2.5 */ public $rerunStatus = 0; /** * Constructor: builds check query and message from $updateQuery * * @param DatabaseDriver $db Database connector object * @param string $file Full path name of the sql file * @param string $query Text of the sql query (one line of the file) * * @since 2.5 */ public function __construct($db, $file, $query) { $this->updateQuery = $query; $this->file = $file; $this->db = $db; $this->buildCheckQuery(); } /** * Returns a reference to the ChangeItem object. * * @param DatabaseDriver $db Database connector object * @param string $file Full path name of the sql file * @param string $query Text of the sql query (one line of the file) * * @return ChangeItem instance based on the database driver * * @since 2.5 * @throws \RuntimeException if class for database driver not found */ public static function getInstance($db, $file, $query) { // Get the class name $serverType = $db->getServerType(); // For `mssql` server types, convert the type to `sqlsrv` if ($serverType === 'mssql') { $serverType = 'sqlsrv'; } $class = '\\Joomla\\CMS\\Schema\\ChangeItem\\' . ucfirst($serverType) . 'ChangeItem'; // If the class exists, return it. if (class_exists($class)) { return new $class($db, $file, $query); } throw new \RuntimeException(sprintf('ChangeItem child class not found for the %s database driver', $serverType), 500); } /** * Checks a DDL query to see if it is a known type * If yes, build a check query to see if the DDL has been run on the database. * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated. * The $msgElements contains the text to create the user message. * The $checkQuery contains the SQL query to check whether the schema change has * been run against the current database. The $queryType contains the type of * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX). * The $checkStatus field is set to zero if the query is created * * If not successful, $checkQuery is empty and , and $checkStatus is -1. * For example, this will happen if the current line is a non-DDL statement. * * @return void * * @since 2.5 */ abstract protected function buildCheckQuery(); /** * Runs the check query and checks that 1 row is returned * * @return integer 1 if success, -1 if skipped, -2 if check failed * * @since 2.5 */ public function check() { $this->checkStatus = -1; if ($this->checkQuery) { try { $this->db->setQuery($this->checkQuery); $rows = $this->db->loadRowList(0); } catch (\RuntimeException $e) { // Still render the error message from the Exception object Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); $this->checkStatus = -2; return $this->checkStatus; } if (\count($rows) === $this->checkQueryExpected) { $this->checkStatus = 1; return $this->checkStatus; } $this->checkStatus = -2; } return $this->checkStatus; } /** * Runs the update query to apply the change to the database * * @return void * * @since 2.5 */ public function fix() { if ($this->checkStatus === -2) { // At this point we have a failed query $query = $this->updateQuery; try { $this->db->setQuery($query); $this->db->execute(); if ($this->check()) { $this->checkStatus = 1; $this->rerunStatus = 1; } else { $this->rerunStatus = -2; } } catch (ExecutionFailureException | \RuntimeException $e) { $this->rerunStatus = -2; } } } } PK1A#]3h4��*src/Schema/ChangeItem/SqlsrvChangeItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Schema\ChangeItem; use Joomla\CMS\Schema\ChangeItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Checks the database schema against one SQL Server DDL query to see if it has been run. * * @since 2.5 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ class SqlsrvChangeItem extends ChangeItem { /** * Checks a DDL query to see if it is a known type * If yes, build a check query to see if the DDL has been run on the database. * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated. * The $msgElements contains the text to create the user message. * The $checkQuery contains the SQL query to check whether the schema change has * been run against the current database. The $queryType contains the type of * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX). * The $checkStatus field is set to zero if the query is created * * If not successful, $checkQuery is empty and , and $checkStatus is -1. * For example, this will happen if the current line is a non-DDL statement. * * @return void * * @since 2.5 */ protected function buildCheckQuery() { // Initialize fields in case we can't create a check query // Change status to skipped $this->checkStatus = -1; $result = null; // Remove any newlines $this->updateQuery = str_replace("\n", '', $this->updateQuery); // Fix up extra spaces around () and in general $find = ['#((\s*)\(\s*([^)\s]+)\s*)(\))#', '#(\s)(\s*)#']; $replace = ['($3)', '$1']; $updateQuery = preg_replace($find, $replace, $this->updateQuery); $wordArray = explode(' ', $updateQuery); // First, make sure we have an array of at least 6 elements // if not, we can't make a check query for this one if (\count($wordArray) < 6) { // Done with method return; } // We can only make check queries for alter table and create table queries $command = strtoupper($wordArray[0] . ' ' . $wordArray[1]); if ($command === 'ALTER TABLE') { $alterCommand = strtoupper($wordArray[3] . ' ' . $wordArray[4]); if ($alterCommand === 'ADD') { $result = 'SELECT * FROM INFORMATION_SCHEMA.Columns ' . $wordArray[2] . ' WHERE COLUMN_NAME = ' . $this->fixQuote($wordArray[5]); $this->queryType = 'ADD'; $this->msgElements = [$this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5])]; } elseif ($alterCommand === 'CREATE INDEX') { $index = $this->fixQuote(substr($wordArray[5], 0, strpos($wordArray[5], '('))); $result = 'SELECT * FROM SYS.INDEXES ' . $wordArray[2] . ' WHERE name = ' . $index; $this->queryType = 'CREATE INDEX'; $this->msgElements = [$this->fixQuote($wordArray[2]), $index]; } elseif (strtoupper($wordArray[3]) === 'MODIFY' || strtoupper($wordArray[3]) === 'CHANGE') { $result = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ' . $this->fixQuote($wordArray[2]); $this->queryType = 'ALTER COLUMN COLUMN_NAME =' . $this->fixQuote($wordArray[4]); $this->msgElements = [$this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[4])]; } } if ($command === 'CREATE TABLE') { $table = $wordArray[2]; $result = 'SELECT * FROM sys.TABLES WHERE NAME = ' . $this->fixQuote($table); $this->queryType = 'CREATE_TABLE'; $this->msgElements = [$this->fixQuote($table)]; } // Set fields based on results if ($this->checkQuery = $result) { // Unchecked status $this->checkStatus = 0; } else { // Skipped $this->checkStatus = -1; } } /** * Fix up integer. Fixes problem with MySQL integer descriptions. * If you change a column to "integer unsigned" it shows * as "int(10) unsigned" in the check query. * * @param string $type1 the column type * @param string $type2 the column attributes * * @return string The original or changed column type. * * @since 2.5 */ private function fixInteger($type1, $type2) { $result = $type1; if (strtolower($type1) === 'integer' && strtolower(substr($type2, 0, 8)) === 'unsigned') { $result = 'int'; } return $result; } /** * Fixes up a string for inclusion in a query. * Replaces name quote character with normal quote for literal. * Drops trailing semicolon. Injects the database prefix. * * @param string $string The input string to be cleaned up. * * @return string The modified string. * * @since 2.5 */ private function fixQuote($string) { $string = str_replace('[', '', $string); $string = str_replace(']', '', $string); $string = str_replace('"', '', $string); $string = str_replace(';', '', $string); $string = str_replace('#__', $this->db->getPrefix(), $string); return $this->db->quote($string); } } PK1A#]�&��5�5.src/Schema/ChangeItem/PostgresqlChangeItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Schema\ChangeItem; use Joomla\CMS\Schema\ChangeItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Checks the database schema against one PostgreSQL DDL query to see if it has been run. * * @since 3.0 */ class PostgresqlChangeItem extends ChangeItem { /** * Checks a DDL query to see if it is a known type * If yes, build a check query to see if the DDL has been run on the database. * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated. * The $msgElements contains the text to create the user message. * The $checkQuery contains the SQL query to check whether the schema change has * been run against the current database. The $queryType contains the type of * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX). * The $checkStatus field is set to zero if the query is created * * If not successful, $checkQuery is empty and , and $checkStatus is -1. * For example, this will happen if the current line is a non-DDL statement. * * @return void * * @since 3.0 */ protected function buildCheckQuery() { // Initialize fields in case we can't create a check query // Change status to skipped $this->checkStatus = -1; $result = null; $splitIntoWords = "~'[^']*'(*SKIP)(*F)|\s+~"; $splitIntoActions = "~'[^']*'(*SKIP)(*F)|\([^)]*\)(*SKIP)(*F)|,~"; // Remove any newlines $this->updateQuery = str_replace("\n", '', $this->updateQuery); // Remove trailing whitespace and semicolon $this->updateQuery = rtrim($this->updateQuery, "; \t\n\r\0\x0B"); // Fix up extra spaces around () and in general $find = ['#((\s*)\(\s*([^)\s]+)\s*)(\))#', '#(\s)(\s*)#']; $replace = ['($3)', '$1']; $updateQuery = preg_replace($find, $replace, $this->updateQuery); $wordArray = preg_split($splitIntoWords, $updateQuery, -1, PREG_SPLIT_NO_EMPTY); $totalWords = \count($wordArray); // First, make sure we have an array of at least 6 elements // if not, we can't make a check query for this one if ($totalWords < 6) { // Done with method return; } // We can only make check queries for alter table and create table queries $command = strtoupper($wordArray[0] . ' ' . $wordArray[1]); if ($command === 'ALTER TABLE') { // Check only the last action $actions = ltrim(substr($updateQuery, strpos($updateQuery, $wordArray[2]) + \strlen($wordArray[2]))); $actions = preg_split($splitIntoActions, $actions); // Get the last action $lastActionArray = preg_split($splitIntoWords, end($actions), -1, PREG_SPLIT_NO_EMPTY); // Replace all actions by the last one array_splice($wordArray, 3, $totalWords, $lastActionArray); $alterCommand = strtoupper($wordArray[3] . ' ' . $wordArray[4]); if ($alterCommand === 'RENAME TO') { $table = $this->fixQuote($wordArray[5]); $result = 'SELECT table_name FROM information_schema.tables WHERE table_name=' . $table; $this->queryType = 'RENAME_TABLE'; $this->checkQueryExpected = 1; $this->msgElements = [$table]; } elseif ($alterCommand === 'ADD COLUMN') { $result = 'SELECT column_name' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]); $this->queryType = 'ADD_COLUMN'; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), ]; } elseif ($alterCommand === 'DROP COLUMN') { $result = 'SELECT column_name' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]); $this->queryType = 'DROP_COLUMN'; $this->checkQueryExpected = 0; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), ]; } elseif ($alterCommand === 'ALTER COLUMN') { $alterAction = strtoupper($wordArray[6]); if ($alterAction === 'TYPE') { $type = implode(' ', \array_slice($wordArray, 7)); if ($pos = stripos($type, ' USING ')) { $type = substr($type, 0, $pos); } if ($pos = strpos($type, '(')) { $datatype = substr($type, 0, $pos); } else { $datatype = $type; } if ($datatype === 'varchar') { $datatype = 'character varying'; } $result = 'SELECT column_name, data_type ' . 'FROM information_schema.columns WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]) . ' AND data_type=' . $this->fixQuote($datatype); if ($datatype === 'character varying') { $result .= ' AND character_maximum_length = ' . (int) substr($type, $pos + 1); } $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), $type, ]; } elseif ($alterAction === 'SET') { $alterType = strtoupper($wordArray[7]); if ($alterType === 'NOT' && strtoupper($wordArray[8]) === 'NULL') { $result = 'SELECT column_name, data_type, is_nullable' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]) . ' AND is_nullable=' . $this->fixQuote('NO'); $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), 'NOT NULL', ]; } elseif ($alterType === 'DEFAULT') { $result = 'SELECT column_name, data_type, is_nullable' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]) . ' AND (CASE (position(' . $this->db->quote('::') . ' in column_default))' . ' WHEN 0 THEN ' . ' column_default = ' . $this->db->quote($wordArray[8]) . ' ELSE ' . ' substring(column_default, 1, (position(' . $this->db->quote('::') . ' in column_default) -1)) = ' . $this->db->quote($wordArray[8]) . ' END)'; $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), 'DEFAULT ' . $wordArray[8], ]; } } elseif ($alterAction === 'DROP') { $alterType = strtoupper($wordArray[7]); if ($alterType === 'DEFAULT') { $result = 'SELECT column_name, data_type, is_nullable , column_default' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]) . ' AND column_default IS NOT NULL'; $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->checkQueryExpected = 0; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), 'NOT DEFAULT', ]; } elseif ($alterType === 'NOT' && strtoupper($wordArray[8]) === 'NULL') { $result = 'SELECT column_name, data_type, is_nullable , column_default' . ' FROM information_schema.columns' . ' WHERE table_name=' . $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5]) . ' AND is_nullable = ' . $this->fixQuote('NO'); $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->checkQueryExpected = 0; $this->msgElements = [ $this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), 'NULL', ]; } } } } elseif ($command === 'DROP INDEX') { if (strtoupper($wordArray[2] . $wordArray[3]) === 'IFEXISTS') { $idx = $this->fixQuote($wordArray[4]); } else { $idx = $this->fixQuote($wordArray[2]); } $result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx; $this->queryType = 'DROP_INDEX'; $this->checkQueryExpected = 0; $this->msgElements = [$this->fixQuote($idx)]; } elseif ($command === 'CREATE INDEX' || (strtoupper($command . $wordArray[2]) === 'CREATE UNIQUE INDEX')) { if ($wordArray[1] === 'UNIQUE') { $idx = $this->fixQuote($wordArray[3]); $table = $this->fixQuote($wordArray[5]); } else { $idx = $this->fixQuote($wordArray[2]); $table = $this->fixQuote($wordArray[4]); } $result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx . ' AND tablename=' . $table; $this->queryType = 'ADD_INDEX'; $this->checkQueryExpected = 1; $this->msgElements = [$table, $idx]; } if ($command === 'CREATE TABLE') { if (strtoupper($wordArray[2] . $wordArray[3] . $wordArray[4]) === 'IFNOTEXISTS') { $table = $this->fixQuote($wordArray[5]); } else { $table = $this->fixQuote($wordArray[2]); } $result = 'SELECT table_name FROM information_schema.tables WHERE table_name=' . $table; $this->queryType = 'CREATE_TABLE'; $this->checkQueryExpected = 1; $this->msgElements = [$table]; } // Set fields based on results if ($this->checkQuery = $result) { // Unchecked status $this->checkStatus = 0; } else { // Skipped $this->checkStatus = -1; } } /** * Fix up integer. Fixes problem with PostgreSQL integer descriptions. * If you change a column to "integer unsigned" it shows * as "int(10) unsigned" in the check query. * * @param string $type1 the column type * @param string $type2 the column attributes * * @return string The original or changed column type. * * @since 3.0 */ private function fixInteger($type1, $type2) { $result = $type1; if (strtolower($type1) === 'integer' && strtolower(substr($type2, 0, 8)) === 'unsigned') { $result = 'unsigned int(10)'; } return $result; } /** * Fixes up a string for inclusion in a query. * Replaces name quote character with normal quote for literal. * Drops trailing semicolon. Injects the database prefix. * * @param string $string The input string to be cleaned up. * * @return string The modified string. * * @since 3.0 */ private function fixQuote($string) { $string = str_replace('"', '', $string); $string = str_replace(';', '', $string); $string = str_replace('#__', $this->db->getPrefix(), $string); return $this->db->quote($string); } } PK1A#]bOSy�;�;)src/Schema/ChangeItem/MysqlChangeItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Schema\ChangeItem; use Joomla\CMS\Schema\ChangeItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Checks the database schema against one MySQL DDL query to see if it has been run. * * @since 2.5 */ class MysqlChangeItem extends ChangeItem { /** * Checks a DDL query to see if it is a known type * If yes, build a check query to see if the DDL has been run on the database. * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated. * The $msgElements contains the text to create the user message. * The $checkQuery contains the SQL query to check whether the schema change has * been run against the current database. The $queryType contains the type of * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX). * The $checkStatus field is set to zero if the query is created * * If not successful, $checkQuery is empty and , and $checkStatus is -1. * For example, this will happen if the current line is a non-DDL statement. * * @return void * * @since 2.5 */ protected function buildCheckQuery() { // Initialize fields in case we can't create a check query // Change status to skipped $this->checkStatus = -1; $result = null; // Remove any newlines $this->updateQuery = str_replace("\n", '', $this->updateQuery); // Fix up extra spaces around () and in general $find = ['#((\s*)\(\s*([^)\s]+)\s*)(\))#', '#(\s)(\s*)#']; $replace = ['($3)', '$1']; $updateQuery = preg_replace($find, $replace, $this->updateQuery); $wordArray = preg_split("~'[^']*'(*SKIP)(*F)|\s+~u", trim($updateQuery, "; \t\n\r\0\x0B")); // First, make sure we have an array of at least 5 elements // if not, we can't make a check query for this one if (\count($wordArray) < 5) { // Done with method return; } // We can only make check queries for rename table, alter table and create table queries $command = strtoupper($wordArray[0] . ' ' . $wordArray[1]); if ($command === 'RENAME TABLE') { $table = $this->fixQuote($wordArray[4]); $this->checkQuery = 'SHOW TABLES LIKE ' . $table; $this->queryType = 'RENAME_TABLE'; $this->msgElements = [$table]; $this->checkStatus = 0; // Done with method return; } // For the remaining query types make sure we have an array of at least 6 elements if (\count($wordArray) < 6) { // Done with method return; } if ($command === 'ALTER TABLE') { $alterCommand = strtoupper($wordArray[3] . ' ' . $wordArray[4]); if ($alterCommand === 'ADD COLUMN') { $result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE field = ' . $this->fixQuote($wordArray[5]); $this->queryType = 'ADD_COLUMN'; $this->msgElements = [$this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5])]; } elseif ($alterCommand === 'ADD INDEX' || $alterCommand === 'ADD KEY') { if ($pos = strpos($wordArray[5], '(')) { $index = $this->fixQuote(substr($wordArray[5], 0, $pos)); } else { $index = $this->fixQuote($wordArray[5]); } $result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE Key_name = ' . $index; $this->queryType = 'ADD_INDEX'; $this->msgElements = [$this->fixQuote($wordArray[2]), $index]; } elseif ($alterCommand === 'ADD UNIQUE') { $idxIndexName = 5; if (isset($wordArray[6])) { $addCmdCheck = strtoupper($wordArray[5]); if ($addCmdCheck === 'INDEX' || $addCmdCheck === 'KEY') { $idxIndexName = 6; } } if ($pos = strpos($wordArray[$idxIndexName], '(')) { $index = $this->fixQuote(substr($wordArray[$idxIndexName], 0, $pos)); } else { $index = $this->fixQuote($wordArray[$idxIndexName]); } $result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE Key_name = ' . $index; $this->queryType = 'ADD_INDEX'; $this->msgElements = [$this->fixQuote($wordArray[2]), $index]; } elseif ($alterCommand === 'DROP INDEX' || $alterCommand === 'DROP KEY') { $index = $this->fixQuote($wordArray[5]); $result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE Key_name = ' . $index; $this->queryType = 'DROP_INDEX'; $this->checkQueryExpected = 0; $this->msgElements = [$this->fixQuote($wordArray[2]), $index]; } elseif ($alterCommand === 'DROP COLUMN') { $index = $this->fixQuote($wordArray[5]); $result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE Field = ' . $index; $this->queryType = 'DROP_COLUMN'; $this->checkQueryExpected = 0; $this->msgElements = [$this->fixQuote($wordArray[2]), $index]; } elseif (strtoupper($wordArray[3]) === 'MODIFY') { // Kludge to fix problem with "integer unsigned" $type = $wordArray[5]; if (isset($wordArray[6])) { $type = $this->fixInteger($wordArray[5], $wordArray[6]); } // Detect changes in NULL and in DEFAULT column attributes $changesArray = \array_slice($wordArray, 6); $defaultCheck = $this->checkDefault($changesArray, $type); $nullCheck = $this->checkNull($changesArray); /** * When we made the UTF8MB4 conversion then text becomes medium text - so loosen the checks to these two types * otherwise (for example) the profile fields profile_value check fails - see https://github.com/joomla/joomla-cms/issues/9258 */ $typeCheck = $this->fixUtf8mb4TypeChecks($type); $result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE field = ' . $this->fixQuote($wordArray[4]) . ' AND ' . $typeCheck . ($defaultCheck ? ' AND ' . $defaultCheck : '') . ($nullCheck ? ' AND ' . $nullCheck : ''); $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->msgElements = [$this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[4]), $type]; } elseif (strtoupper($wordArray[3]) === 'CHANGE') { // Kludge to fix problem with "integer unsigned" $type = $wordArray[6]; if (isset($wordArray[7])) { $type = $this->fixInteger($wordArray[6], $wordArray[7]); } // Detect changes in NULL and in DEFAULT column attributes $changesArray = \array_slice($wordArray, 6); $defaultCheck = $this->checkDefault($changesArray, $type); $nullCheck = $this->checkNull($changesArray); /** * When we made the UTF8MB4 conversion then text becomes medium text - so loosen the checks to these two types * otherwise (for example) the profile fields profile_value check fails - see https://github.com/joomla/joomla-cms/issues/9258 */ $typeCheck = $this->fixUtf8mb4TypeChecks($type); $result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE field = ' . $this->fixQuote($wordArray[5]) . ' AND ' . $typeCheck . ($defaultCheck ? ' AND ' . $defaultCheck : '') . ($nullCheck ? ' AND ' . $nullCheck : ''); $this->queryType = 'CHANGE_COLUMN_TYPE'; $this->msgElements = [$this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), $type]; } } if ($command === 'CREATE TABLE') { if (strtoupper($wordArray[2] . $wordArray[3] . $wordArray[4]) === 'IFNOTEXISTS') { $table = $wordArray[5]; } else { $table = $wordArray[2]; } $result = 'SHOW TABLES LIKE ' . $this->fixQuote($table); $this->queryType = 'CREATE_TABLE'; $this->msgElements = [$this->fixQuote($table)]; } // Set fields based on results if ($this->checkQuery = $result) { // Unchecked status $this->checkStatus = 0; } else { // Skipped $this->checkStatus = -1; } } /** * Fix up integer. Fixes problem with MySQL integer descriptions. * On MySQL 8 display length is not shown anymore. * This means we have to match e.g. both "int(10) unsigned" and * "int unsigned", or both "int(11)" and "int" and so on. * The same applies to the other integer data types "tinyint", * "smallint", "mediumint" and "bigint". * * @param string $type1 the column type * @param string $type2 the column attributes * * @return string The original or changed column type. * * @since 2.5 */ private function fixInteger($type1, $type2) { $result = $type1; if (preg_match('/^(?P<type>(big|medium|small|tiny)?int)(\([0-9]+\))?$/i', $type1, $matches)) { $result = strtolower($matches['type']); } if (strtolower(substr($type2, 0, 8)) === 'unsigned') { $result .= ' unsigned'; } return $result; } /** * Fixes up a string for inclusion in a query. * Replaces name quote character with normal quote for literal. * Drops trailing semicolon. Injects the database prefix. * * @param string $string The input string to be cleaned up. * * @return string The modified string. * * @since 2.5 */ private function fixQuote($string) { $string = str_replace('`', '', $string); $string = str_replace(';', '', $string); $string = str_replace('#__', $this->db->getPrefix(), $string); return $this->db->quote($string); } /** * Make check query for column changes/modifications tolerant * for automatic type changes of text columns, e.g. from TEXT * to MEDIUMTEXT, after conversion from utf8 to utf8mb4, and * fix integer columns without display length for MySQL 8 * (see also function "fixInteger" above). * * @param string $type The column type found in the update query * * @return string The condition for type check in the check query * * @since 3.5 */ private function fixUtf8mb4TypeChecks($type) { $uType = strtoupper(str_replace(';', '', $type)); switch ($uType) { case 'BIGINT UNSIGNED': case 'INT UNSIGNED': case 'MEDIUMINT UNSIGNED': case 'SMALLINT UNSIGNED': case 'TINYINT UNSIGNED': // Eg for "INT": "UPPER(type) REGEXP '^INT([(][0-9]+[)])? UNSIGNED$'" $typeCheck = 'UPPER(type) REGEXP ' . $this->db->quote('^' . str_replace(' ', '([(][0-9]+[)])? ', $uType) . '$'); break; case 'BIGINT': case 'INT': case 'MEDIUMINT': case 'SMALLINT': case 'TINYINT': // Eg for "INT": "UPPER(type) REGEXP '^INT([(][0-9]+[)])?$'" $typeCheck = 'UPPER(type) REGEXP ' . $this->db->quote('^' . $uType . '([(][0-9]+[)])?$'); break; case 'MEDIUMTEXT': $typeCheck = $this->db->hasUTF8mb4Support() ? 'UPPER(type) IN (' . $this->db->quote('MEDIUMTEXT') . ',' . $this->db->quote('LONGTEXT') . ')' : 'UPPER(type) = ' . $this->db->quote('MEDIUMTEXT'); break; case 'TEXT': $typeCheck = $this->db->hasUTF8mb4Support() ? 'UPPER(type) IN (' . $this->db->quote('TEXT') . ',' . $this->db->quote('MEDIUMTEXT') . ')' : 'UPPER(type) = ' . $this->db->quote('TEXT'); break; case 'TINYTEXT': $typeCheck = $this->db->hasUTF8mb4Support() ? 'UPPER(type) IN (' . $this->db->quote('TINYTEXT') . ',' . $this->db->quote('TEXT') . ')' : 'UPPER(type) = ' . $this->db->quote('TINYTEXT'); break; default: $typeCheck = 'UPPER(type) = ' . $this->db->quote($uType); } return $typeCheck; } /** * Create query clause for column changes/modifications for NULL attribute * * @param array $changesArray The array of words after COLUMN name * * @return string The query clause for NULL check in the check query * * @since 3.8.6 */ private function checkNull($changesArray) { // Find NULL keyword $index = array_search('null', array_map('strtolower', $changesArray)); // Create the check if ($index !== false) { if ($index == 0 || strtolower($changesArray[$index - 1]) !== 'not') { return ' `null` = ' . $this->db->quote('YES'); } else { return ' `null` = ' . $this->db->quote('NO'); } } return false; } /** * Create query clause for column changes/modifications for DEFAULT attribute * * @param array $changesArray The array of words after COLUMN name * @param string $type The type of the COLUMN * * @return string The query clause for DEFAULT check in the check query * * @since 3.8.6 */ private function checkDefault($changesArray, $type) { // Skip types that do not support default values $type = strtolower($type); if (substr($type, -4) === 'text' || substr($type, -4) === 'blob') { return false; } // Find DEFAULT keyword $index = array_search('default', array_map('strtolower', $changesArray)); // Create the check if ($index !== false) { if (strtolower($changesArray[$index + 1]) === 'null') { return ' `default` IS NULL'; } else { return ' `default` = ' . $changesArray[$index + 1]; } } return false; } } PK1A#]���#�#src/Schema/ChangeSet.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Schema; use Joomla\CMS\Filesystem\Folder; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Contains a set of JSchemaChange objects for a particular instance of Joomla. * Each of these objects contains a DDL query that should have been run against * the database when this database was created or updated. This enables the * Installation Manager to check that the current database schema is up to date. * * @since 2.5 */ class ChangeSet { /** * Array of ChangeItem objects * * @var ChangeItem[] * @since 2.5 */ protected $changeItems = []; /** * DatabaseDriver object * * @var DatabaseDriver * @since 2.5 */ protected $db = null; /** * Folder where SQL update files will be found * * @var string * @since 2.5 */ protected $folder = null; /** * The singleton instance of this object * * @var ChangeSet * @since 3.5.1 */ protected static $instance; /** * Constructor: builds array of $changeItems by processing the .sql files in a folder. * The folder for the Joomla core updates is `administrator/components/com_admin/sql/updates/<database>`. * * @param DatabaseDriver $db The current database object * @param string $folder The full path to the folder containing the update queries * * @since 2.5 */ public function __construct($db, $folder = null) { $this->db = $db; $this->folder = $folder; $updateFiles = $this->getUpdateFiles(); // If no files were found nothing more we can do - continue if ($updateFiles === false) { return; } $updateQueries = $this->getUpdateQueries($updateFiles); foreach ($updateQueries as $obj) { $this->changeItems[] = ChangeItem::getInstance($db, $obj->file, $obj->updateQuery); } // If on mysql, add a query at the end to check for utf8mb4 conversion status if ($this->db->getServerType() === 'mysql') { // Check if the #__utf8_conversion table exists $this->db->setQuery('SHOW TABLES LIKE ' . $this->db->quote($this->db->getPrefix() . 'utf8_conversion')); try { $rows = $this->db->loadRowList(0); $tableExists = \count($rows); } catch (\RuntimeException $e) { $tableExists = 0; } // If the table exists add a change item for utf8mb4 conversion to the end if ($tableExists > 0) { // Let the update query do nothing $tmpSchemaChangeItem = ChangeItem::getInstance( $db, 'database.php', 'UPDATE ' . $this->db->quoteName('#__utf8_conversion') . ' SET ' . $this->db->quoteName('converted') . ' = ' . $this->db->quoteName('converted') . ';' ); // Set to not skipped $tmpSchemaChangeItem->checkStatus = 0; // Set the check query $tmpSchemaChangeItem->queryType = 'UTF8_CONVERSION_UTF8MB4'; $tmpSchemaChangeItem->checkQuery = 'SELECT ' . $this->db->quoteName('converted') . ' FROM ' . $this->db->quoteName('#__utf8_conversion') . ' WHERE ' . $this->db->quoteName('converted') . ' = 5'; // Set expected records from check query $tmpSchemaChangeItem->checkQueryExpected = 1; $tmpSchemaChangeItem->msgElements = []; $this->changeItems[] = $tmpSchemaChangeItem; } } } /** * Returns a reference to the ChangeSet object, only creating it if it doesn't already exist. * * @param DatabaseDriver $db The current database object * @param string $folder The full path to the folder containing the update queries * * @return ChangeSet * * @since 2.5 */ public static function getInstance($db, $folder = null) { if (!\is_object(static::$instance)) { static::$instance = new static($db, $folder); } return static::$instance; } /** * Checks the database and returns an array of any errors found. * Note these are not database errors but rather situations where * the current schema is not up to date. * * @return array Array of errors if any. * * @since 2.5 */ public function check() { $errors = []; foreach ($this->changeItems as $item) { if ($item->check() === -2) { // Error found $errors[] = $item; } } return $errors; } /** * Runs the update query to apply the change to the database * * @return void * * @since 2.5 */ public function fix() { $this->check(); foreach ($this->changeItems as $item) { $item->fix(); } } /** * Returns an array of results for this set * * @return array associative array of changeitems grouped by unchecked, ok, error, and skipped * * @since 2.5 */ public function getStatus() { $result = ['unchecked' => [], 'ok' => [], 'error' => [], 'skipped' => []]; foreach ($this->changeItems as $item) { switch ($item->checkStatus) { case 0: $result['unchecked'][] = $item; break; case 1: $result['ok'][] = $item; break; case -2: $result['error'][] = $item; break; case -1: $result['skipped'][] = $item; break; } } return $result; } /** * Gets the current database schema, based on the highest version number. * Note that the .sql files are named based on the version and date, so * the file name of the last file should match the database schema version * in the #__schemas table. * * @return string the schema version for the database * * @since 2.5 */ public function getSchema() { $updateFiles = $this->getUpdateFiles(); // No schema files found - stop and return empty string if (empty($updateFiles)) { return ''; } $result = new \SplFileInfo(array_pop($updateFiles)); return $result->getBasename('.sql'); } /** * Get list of SQL update files for this database * * @return array|boolean list of sql update full-path names. False if directory doesn't exist * * @since 2.5 */ private function getUpdateFiles() { // Get the folder from the database name $sqlFolder = $this->db->getServerType(); // For `mssql` server types, convert the type to `sqlazure` if ($sqlFolder === 'mssql') { $sqlFolder = 'sqlazure'; } // Default folder to core com_admin if (!$this->folder) { $this->folder = JPATH_ADMINISTRATOR . '/components/com_admin/sql/updates/'; } // We don't want to enqueue an error if the directory doesn't exist - this can be handled elsewhere/ // So bail here. if (!is_dir($this->folder . '/' . $sqlFolder)) { return []; } return Folder::files( $this->folder . '/' . $sqlFolder, '\.sql$', 1, true, ['.svn', 'CVS', '.DS_Store', '__MACOSX'], ['^\..*', '.*~'], true ); } /** * Get array of SQL queries * * @param array $sqlfiles Array of .sql update filenames. * * @return array Array of \stdClass objects where: * file=filename, * update_query = text of SQL update query * * @since 2.5 */ private function getUpdateQueries(array $sqlfiles) { // Hold results as array of objects $result = []; foreach ($sqlfiles as $file) { $buffer = file_get_contents($file); // Create an array of queries from the sql file $queries = DatabaseDriver::splitSql($buffer); foreach ($queries as $query) { $fileQueries = new \stdClass(); $fileQueries->file = $file; $fileQueries->updateQuery = $query; $result[] = $fileQueries; } } return $result; } } PK1A#]%�[w�w�src/Filesystem/Stream.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Stream Interface * * The Joomla! stream interface is designed to handle files as streams * where as the legacy File static class treated files in a rather * atomic manner. * * @note This class adheres to the stream wrapper operations: * @link https://www.php.net/manual/en/function.stream-get-wrappers.php * @link https://www.php.net/manual/en/intro.stream.php PHP Stream Manual * @link https://www.php.net/manual/en/wrappers.php Stream Wrappers * @link https://www.php.net/manual/en/filters.php Stream Filters * @link https://www.php.net/manual/en/transports.php Socket Transports (used by some options, particularly HTTP proxy) * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream instead. */ class Stream extends CMSObject { /** * File Mode * * @var integer * @since 1.7.0 */ protected $filemode = 0644; /** * Directory Mode * * @var integer * @since 1.7.0 */ protected $dirmode = 0755; /** * Default Chunk Size * * @var integer * @since 1.7.0 */ protected $chunksize = 8192; /** * Filename * * @var string * @since 1.7.0 */ protected $filename; /** * Prefix of the connection for writing * * @var string * @since 1.7.0 */ protected $writeprefix; /** * Prefix of the connection for reading * * @var string * @since 1.7.0 */ protected $readprefix; /** * Read Processing method * @var string gz, bz, f * If a scheme is detected, fopen will be defaulted * To use compression with a network stream use a filter * @since 1.7.0 */ protected $processingmethod = 'f'; /** * Filters applied to the current stream * * @var array * @since 1.7.0 */ protected $filters = []; /** * File Handle * * @var resource * @since 3.0.0 */ protected $fh; /** * File size * * @var integer * @since 3.0.0 */ protected $filesize; /** * Context to use when opening the connection * * @var resource * @since 3.0.0 */ protected $context = null; /** * Context options; used to rebuild the context * * @var array * @since 3.0.0 */ protected $contextOptions; /** * The mode under which the file was opened * * @var string * @since 3.0.0 */ protected $openmode; /** * Constructor * * @param string $writeprefix Prefix of the stream (optional). Unlike the JPATH_*, this has a final path separator! * @param string $readprefix The read prefix (optional). * @param array $context The context options (optional). * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::__construct() instead. */ public function __construct($writeprefix = '', $readprefix = '', $context = []) { $this->writeprefix = $writeprefix; $this->readprefix = $readprefix; $this->contextOptions = $context; $this->_buildContext(); } /** * Destructor * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::__destruct() instead. */ public function __destruct() { // Attempt to close on destruction if there is a file handle if ($this->fh) { @$this->close(); } } /** * Generic File Operations * * Open a stream with some lazy loading smarts * * @param string $filename Filename * @param string $mode Mode string to use * @param boolean $useIncludePath Use the PHP include path * @param resource $context Context to use when opening * @param boolean $usePrefix Use a prefix to open the file * @param boolean $relative Filename is a relative path (if false, strips JPATH_ROOT to make it relative) * @param boolean $detectProcessingMode Detect the processing method for the file and use the appropriate function * to handle output automatically * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::open() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function open( $filename, $mode = 'r', $useIncludePath = false, $context = null, $usePrefix = false, $relative = false, $detectProcessingMode = false ) { $filename = $this->_getFilename($filename, $mode, $usePrefix, $relative); if (!$filename) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILENAME')); return false; } $this->filename = $filename; $this->openmode = $mode; $url = parse_url($filename); $retval = false; if (isset($url['scheme'])) { // If we're dealing with a Joomla! stream, load it if (FilesystemHelper::isJoomlaStream($url['scheme'])) { require_once __DIR__ . '/streams/' . $url['scheme'] . '.php'; } // We have a scheme! force the method to be f $this->processingmethod = 'f'; } elseif ($detectProcessingMode) { $ext = strtolower(File::getExt($this->filename)); switch ($ext) { case 'tgz': case 'gz': case 'gzip': $this->processingmethod = 'gz'; break; case 'tbz2': case 'bz2': case 'bzip2': $this->processingmethod = 'bz'; break; default: $this->processingmethod = 'f'; break; } } // Capture PHP errors $php_errormsg = 'Error Unknown whilst opening a file'; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); // Decide which context to use: switch ($this->processingmethod) { // Gzip doesn't support contexts or streams case 'gz': $this->fh = gzopen($filename, $mode, $useIncludePath); break; // Bzip2 is much like gzip except it doesn't use the include path case 'bz': $this->fh = bzopen($filename, $mode); break; // Fopen can handle streams case 'f': default: // One supplied at open; overrides everything if ($context) { $this->fh = fopen($filename, $mode, $useIncludePath, $context); } elseif ($this->context) { // One provided at initialisation $this->fh = fopen($filename, $mode, $useIncludePath, $this->context); } else { // No context; all defaults $this->fh = fopen($filename, $mode, $useIncludePath); } break; } if (!$this->fh) { $this->setError($php_errormsg); } else { $retval = true; } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Attempt to close a file handle * * Will return false if it failed and true on success * If the file is not open the system will return true, this function destroys the file handle as well * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::close() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function close() { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return true; } $retval = false; // Capture PHP errors $php_errormsg = 'Error Unknown'; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); switch ($this->processingmethod) { case 'gz': $res = gzclose($this->fh); break; case 'bz': $res = bzclose($this->fh); break; case 'f': default: $res = fclose($this->fh); break; } if (!$res) { $this->setError($php_errormsg); } else { // Reset this $this->fh = null; $retval = true; } // If we wrote, chmod the file after it's closed if ($this->openmode[0] == 'w') { $this->chmod(); } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Work out if we're at the end of the file for a stream * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::eof() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function eof() { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); switch ($this->processingmethod) { case 'gz': $res = gzeof($this->fh); break; case 'bz': case 'f': default: $res = feof($this->fh); break; } if ($php_errormsg) { $this->setError($php_errormsg); } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $res; } /** * Retrieve the file size of the path * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::filesize() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function filesize() { if (!$this->filename) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } $retval = false; // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $res = @filesize($this->filename); if (!$res) { $tmp_error = ''; if ($php_errormsg) { // Something went wrong. // Store the error in case we need it. $tmp_error = $php_errormsg; } $res = FilesystemHelper::remotefsize($this->filename); if (!$res) { if ($tmp_error) { // Use the php_errormsg from before $this->setError($tmp_error); } else { // Error but nothing from php? How strange! Create our own $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_SIZE')); } } else { $this->filesize = $res; $retval = $res; } } else { $this->filesize = $res; $retval = $res; } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Get a line from the stream source. * * @param integer $length The number of bytes (optional) to read. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::gets() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function gets($length = 0) { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } $retval = false; // Capture PHP errors $php_errormsg = 'Error Unknown'; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); switch ($this->processingmethod) { case 'gz': $res = $length ? gzgets($this->fh, $length) : gzgets($this->fh); break; case 'bz': case 'f': default: $res = $length ? fgets($this->fh, $length) : fgets($this->fh); break; } if (!$res) { $this->setError($php_errormsg); } else { $retval = $res; } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Read a file * * Handles user space streams appropriately otherwise any read will return 8192 * * @param integer $length Length of data to read * * @return mixed * * @link https://www.php.net/manual/en/function.fread.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::read() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function read($length = 0) { if (!$this->filesize && !$length) { // Get the filesize $this->filesize(); if (!$this->filesize) { // Set it to the biggest and then wait until eof $length = -1; } else { $length = $this->filesize; } } if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } $retval = false; // Capture PHP errors $php_errormsg = 'Error Unknown'; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $remaining = $length; do { // Do chunked reads where relevant switch ($this->processingmethod) { case 'bz': $res = ($remaining > 0) ? bzread($this->fh, $remaining) : bzread($this->fh, $this->chunksize); break; case 'gz': $res = ($remaining > 0) ? gzread($this->fh, $remaining) : gzread($this->fh, $this->chunksize); break; case 'f': default: $res = ($remaining > 0) ? fread($this->fh, $remaining) : fread($this->fh, $this->chunksize); break; } if (!$res) { $this->setError($php_errormsg); // Jump from the loop $remaining = 0; } else { if (!$retval) { $retval = ''; } $retval .= $res; if (!$this->eof()) { $len = \strlen($res); $remaining -= $len; } else { // If it's the end of the file then we've nothing left to read; reset remaining and len $remaining = 0; $length = \strlen($retval); } } } while ($remaining || !$length); // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Seek the file * * Note: the return value is different to that of fseek * * @param integer $offset Offset to use when seeking. * @param integer $whence Seek mode to use. * * @return boolean True on success, false on failure * * @link https://www.php.net/manual/en/function.fseek.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::seek() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function seek($offset, $whence = SEEK_SET) { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } $retval = false; // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); switch ($this->processingmethod) { case 'gz': $res = gzseek($this->fh, $offset, $whence); break; case 'bz': case 'f': default: $res = fseek($this->fh, $offset, $whence); break; } // Seek, interestingly, returns 0 on success or -1 on failure. if ($res == -1) { $this->setError($php_errormsg); } else { $retval = true; } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Returns the current position of the file read/write pointer. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::tell() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function tell() { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); switch ($this->processingmethod) { case 'gz': $res = gztell($this->fh); break; case 'bz': case 'f': default: $res = ftell($this->fh); break; } // May return 0 so check if it's really false if ($res === false) { $this->setError($php_errormsg); } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Return the result return $res; } /** * File write * * Whilst this function accepts a reference, the underlying fwrite * will do a copy! This will roughly double the memory allocation for * any write you do. Specifying chunked will get around this by only * writing in specific chunk sizes. This defaults to 8192 which is a * sane number to use most of the time (change the default with * JStream::set('chunksize', newsize);) * Note: This doesn't support gzip/bzip2 writing like reading does * * @param string $string Reference to the string to write. * @param integer $length Length of the string to write. * @param integer $chunk Size of chunks to write in. * * @return boolean * * @link https://www.php.net/manual/en/function.fwrite.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::write() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function write(&$string, $length = 0, $chunk = 0) { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } // If the length isn't set, set it to the length of the string. if (!$length) { $length = \strlen($string); } // If the chunk isn't set, set it to the default. if (!$chunk) { $chunk = $this->chunksize; } $retval = true; // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $remaining = $length; $start = 0; do { // If the amount remaining is greater than the chunk size, then use the chunk $amount = ($remaining > $chunk) ? $chunk : $remaining; $res = fwrite($this->fh, substr($string, $start), $amount); // Returns false on error or the number of bytes written if ($res === false) { // Returned error $this->setError($php_errormsg); $retval = false; $remaining = 0; } elseif ($res === 0) { // Wrote nothing? $remaining = 0; $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_NO_DATA_WRITTEN')); } else { // Wrote something $start += $amount; $remaining -= $res; } } while ($remaining); // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Chmod wrapper * * @param string $filename File name. * @param mixed $mode Mode to use. * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::chmod() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function chmod($filename = '', $mode = 0) { if (!$filename) { if (!isset($this->filename) || !$this->filename) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILENAME')); return false; } $filename = $this->filename; } // If no mode is set use the default if (!$mode) { $mode = $this->filemode; } $retval = false; // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $sch = parse_url($filename, PHP_URL_SCHEME); // Scheme specific options; ftp's chmod support is fun. switch ($sch) { case 'ftp': case 'ftps': $res = FilesystemHelper::ftpChmod($filename, $mode); break; default: $res = chmod($filename, $mode); break; } // Seek, interestingly, returns 0 on success or -1 on failure if (!$res) { $this->setError($php_errormsg); } else { $retval = true; } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); // Return the result return $retval; } /** * Get the stream metadata * * @return array|boolean header/metadata * * @link https://www.php.net/manual/en/function.stream-get-meta-data.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::get_meta_data() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function get_meta_data() { if (!$this->fh) { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN')); return false; } return stream_get_meta_data($this->fh); } /** * Stream contexts * Builds the context from the array * * @return void * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::_buildContext() instead. */ public function _buildContext() { // According to the manual this always works! if (\count($this->contextOptions)) { $this->context = @stream_context_create($this->contextOptions); } else { $this->context = null; } } /** * Updates the context to the array * * Format is the same as the options for stream_context_create * * @param array $context Options to create the context with * * @return void * * @link https://www.php.net/stream_context_create * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::setContextOptions() instead. */ public function setContextOptions($context) { $this->contextOptions = $context; $this->_buildContext(); } /** * Adds a particular options to the context * * @param string $wrapper The wrapper to use * @param string $name The option to set * @param string $value The value of the option * * @return void * * @link https://www.php.net/stream_context_create Stream Context Creation * @link https://www.php.net/manual/en/context.php Context Options for various streams * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::addContextEntry() instead. */ public function addContextEntry($wrapper, $name, $value) { $this->contextOptions[$wrapper][$name] = $value; $this->_buildContext(); } /** * Deletes a particular setting from a context * * @param string $wrapper The wrapper to use * @param string $name The option to unset * * @return void * * @link https://www.php.net/stream_context_create * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::deleteContextEntry() instead. */ public function deleteContextEntry($wrapper, $name) { // Check whether the wrapper is set if (isset($this->contextOptions[$wrapper])) { // Check that entry is set for that wrapper if (isset($this->contextOptions[$wrapper][$name])) { // Unset the item unset($this->contextOptions[$wrapper][$name]); // Check that there are still items there if (!\count($this->contextOptions[$wrapper])) { // Clean up an empty wrapper context option unset($this->contextOptions[$wrapper]); } } } // Rebuild the context and apply it to the stream $this->_buildContext(); } /** * Applies the current context to the stream * * Use this to change the values of the context after you've opened a stream * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::applyContextToStream() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function applyContextToStream() { $retval = false; if ($this->fh) { // Capture PHP errors $php_errormsg = 'Unknown error setting context option'; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $retval = @stream_context_set_option($this->fh, $this->contextOptions); if (!$retval) { $this->setError($php_errormsg); } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); } return $retval; } /** * Stream filters * Append a filter to the chain * * @param string $filterName The key name of the filter. * @param integer $readWrite Optional. Defaults to STREAM_FILTER_READ. * @param array $params An array of params for the stream_filter_append call. * * @return mixed * * @link https://www.php.net/manual/en/function.stream-filter-append.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::appendFilter() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function appendFilter($filterName, $readWrite = STREAM_FILTER_READ, $params = []) { $res = false; if ($this->fh) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $res = @stream_filter_append($this->fh, $filterName, $readWrite, $params); if (!$res && $php_errormsg) { $this->setError($php_errormsg); } else { $this->filters[] = &$res; } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); } return $res; } /** * Prepend a filter to the chain * * @param string $filterName The key name of the filter. * @param integer $readWrite Optional. Defaults to STREAM_FILTER_READ. * @param array $params An array of params for the stream_filter_prepend call. * * @return mixed * * @link https://www.php.net/manual/en/function.stream-filter-prepend.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::prependFilter() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function prependFilter($filterName, $readWrite = STREAM_FILTER_READ, $params = []) { $res = false; if ($this->fh) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $res = @stream_filter_prepend($this->fh, $filterName, $readWrite, $params); if (!$res && $php_errormsg) { // Set the error msg $this->setError($php_errormsg); } else { array_unshift($res, ''); $res[0] = &$this->filters; } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); } return $res; } /** * Remove a filter, either by resource (handed out from the append or prepend function) * or via getting the filter list) * * @param resource $resource The resource. * @param boolean $byindex The index of the filter. * * @return boolean Result of operation * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::removeFilter() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function removeFilter(&$resource, $byindex = false) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); if ($byindex) { $res = stream_filter_remove($this->filters[$resource]); } else { $res = stream_filter_remove($resource); } if ($res && $php_errormsg) { $this->setError($php_errormsg); } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); return $res; } /** * Copy a file from src to dest * * @param string $src The file path to copy from. * @param string $dest The file path to copy to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::copy() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function copy($src, $dest, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $chmodDest = $this->_getFilename($dest, 'w', $usePrefix, $relative); // Since we're going to open the file directly we need to get the filename. // We need to use the same prefix so force everything to write. $src = $this->_getFilename($src, 'w', $usePrefix, $relative); $dest = $this->_getFilename($dest, 'w', $usePrefix, $relative); if ($context) { // Use the provided context $res = @copy($src, $dest, $context); } elseif ($this->context) { // Use the objects context $res = @copy($src, $dest, $this->context); } else { // Don't use any context $res = @copy($src, $dest); } if (!$res && $php_errormsg) { $this->setError($php_errormsg); } else { $this->chmod($chmodDest); } // Restore error tracking to what it was before ini_set('track_errors', $track_errors); return $res; } /** * Moves a file * * @param string $src The file path to move from. * @param string $dest The file path to move to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::move() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function move($src, $dest, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $src = $this->_getFilename($src, 'w', $usePrefix, $relative); $dest = $this->_getFilename($dest, 'w', $usePrefix, $relative); if ($context) { // Use the provided context $res = @rename($src, $dest, $context); } elseif ($this->context) { // Use the object's context $res = @rename($src, $dest, $this->context); } else { // Don't use any context $res = @rename($src, $dest); } if (!$res && $php_errormsg) { $this->setError($php_errormsg()); } $this->chmod($dest); // Restore error tracking to what it was before ini_set('track_errors', $track_errors); return $res; } /** * Delete a file * * @param string $filename The file path to delete. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::delete() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function delete($filename, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $filename = $this->_getFilename($filename, 'w', $usePrefix, $relative); if ($context) { // Use the provided context $res = @unlink($filename, $context); } elseif ($this->context) { // Use the object's context $res = @unlink($filename, $this->context); } else { // Don't use any context $res = @unlink($filename); } if (!$res && $php_errormsg) { $this->setError($php_errormsg()); } // Restore error tracking to what it was before. ini_set('track_errors', $track_errors); return $res; } /** * Upload a file * * @param string $src The file path to copy from (usually a temp folder). * @param string $dest The file path to copy to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return mixed * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::upload() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public function upload($src, $dest, $context = null, $usePrefix = true, $relative = false) { if (is_uploaded_file($src)) { // Make sure it's an uploaded file return $this->copy($src, $dest, $context, $usePrefix, $relative); } else { $this->setError(Text::_('JLIB_FILESYSTEM_ERROR_STREAMS_NOT_UPLOADED_FILE')); return false; } } /** * Writes a chunk of data to a file. * * @param string $filename The file name. * @param string $buffer The data to write to the file. * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::writeFile() instead. */ public function writeFile($filename, &$buffer) { if ($this->open($filename, 'w')) { $result = $this->write($buffer); $this->chmod(); $this->close(); return $result; } return false; } /** * Determine the appropriate 'filename' of a file * * @param string $filename Original filename of the file * @param string $mode Mode string to retrieve the filename * @param boolean $usePrefix Controls the use of a prefix * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return string * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::_getFilename() instead. */ public function _getFilename($filename, $mode, $usePrefix, $relative) { if ($usePrefix) { // Get rid of binary or t, should be at the end of the string $tmode = trim($mode, 'btf123456789'); // Check if it's a write mode then add the appropriate prefix // Get rid of JPATH_ROOT (legacy compat) along the way if (\in_array($tmode, FilesystemHelper::getWriteModes())) { if (!$relative && $this->writeprefix) { $filename = str_replace(JPATH_ROOT, '', $filename); } $filename = $this->writeprefix . $filename; } else { if (!$relative && $this->readprefix) { $filename = str_replace(JPATH_ROOT, '', $filename); } $filename = $this->readprefix . $filename; } } return $filename; } /** * Return the internal file handle * * @return resource File handler * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream::getFileHandle() instead. */ public function getFileHandle() { return $this->fh; } } PK1A#]�{/{/src/Filesystem/Path.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; \defined('JPATH_PLATFORM') or die; use Joomla\CMS\Crypt\Crypt; if (!\defined('JPATH_ROOT')) { // Define a string constant for the root directory of the file system in native format \define('JPATH_ROOT', Path::clean(JPATH_SITE)); } /** * A Path handling class * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path instead. */ class Path { /** * Checks if a path's permissions can be changed. * * @param string $path Path to check. * * @return boolean True if path can have mode changed. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::canChmod() instead. */ public static function canChmod($path) { $perms = fileperms($path); if ($perms !== false) { if (@chmod($path, $perms ^ 0001)) { @chmod($path, $perms); return true; } } return false; } /** * Chmods files and directories recursively to given permissions. * * @param string $path Root path to begin changing mode [without trailing slash]. * @param string $filemode Octal representation of the value to change file mode to [null = no change]. * @param string $foldermode Octal representation of the value to change folder mode to [null = no change]. * * @return boolean True if successful [one fail means the whole operation failed]. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::setPermissions() instead. */ public static function setPermissions($path, $filemode = '0644', $foldermode = '0755') { // Initialise return value $ret = true; if (is_dir($path)) { $dh = opendir($path); while ($file = readdir($dh)) { if ($file != '.' && $file != '..') { $fullpath = $path . '/' . $file; if (is_dir($fullpath)) { if (!self::setPermissions($fullpath, $filemode, $foldermode)) { $ret = false; } } else { if (isset($filemode)) { if (!@ chmod($fullpath, octdec($filemode))) { $ret = false; } } } } } closedir($dh); if (isset($foldermode)) { if (!@ chmod($path, octdec($foldermode))) { $ret = false; } } } else { if (isset($filemode)) { $ret = @ chmod($path, octdec($filemode)); } } return $ret; } /** * Get the permissions of the file/folder at a given path. * * @param string $path The path of a file/folder. * * @return string Filesystem permissions. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::getPermissions() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function getPermissions($path) { $path = self::clean($path); $mode = @ decoct(@ fileperms($path) & 0777); if (\strlen($mode) < 3) { return '---------'; } $parsed_mode = ''; for ($i = 0; $i < 3; $i++) { // Read $parsed_mode .= ($mode[$i] & 04) ? 'r' : '-'; // Write $parsed_mode .= ($mode[$i] & 02) ? 'w' : '-'; // Execute $parsed_mode .= ($mode[$i] & 01) ? 'x' : '-'; } return $parsed_mode; } /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @throws \Exception * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::check() instead. */ public static function check($path) { if (strpos($path, '..') !== false) { // Don't translate throw new \Exception( sprintf( '%s() - Use of relative paths not permitted', __METHOD__ ) ); } $path = self::clean($path); if ((JPATH_ROOT != '') && strpos($path, self::clean(JPATH_ROOT)) !== 0) { throw new \Exception( sprintf( '%1$s() - Snooping out of bounds @ %2$s', __METHOD__, self::removeRoot($path) ) ); } return $path; } /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @throws \UnexpectedValueException * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::clean() instead. */ public static function clean($path, $ds = DIRECTORY_SEPARATOR) { if (!\is_string($path) && !empty($path)) { throw new \UnexpectedValueException( sprintf( '%s() - $path is not a string', __METHOD__ ) ); } if ($path === null) { @trigger_error( sprintf( 'Path can not be null, in 6.0 it will throw an exception', __METHOD__ ), E_USER_DEPRECATED ); $path = ''; } $path = trim($path); if (empty($path)) { $path = JPATH_ROOT; } elseif (($ds === '\\') && substr($path, 0, 2) === '\\\\') { // Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR // If dealing with a UNC path don't forget to prepend the path with a backslash. $path = "\\" . preg_replace('#[/\\\\]+#', $ds, $path); } else { $path = preg_replace('#[/\\\\]+#', $ds, $path); } return $path; } /** * Method to determine if script owns the path. * * @param string $path Path to check ownership. * * @return boolean True if the php script owns the path passed. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::isOwner() instead. */ public static function isOwner($path) { $tmp = md5(Crypt::genRandomBytes()); $ssp = ini_get('session.save_path'); $jtp = JPATH_SITE . '/tmp'; // Try to find a writable directory $dir = false; foreach ([$jtp, $ssp, '/tmp'] as $currentDir) { if (is_writable($currentDir)) { $dir = $currentDir; break; } } if ($dir) { $test = $dir . '/' . $tmp; // Create the test file $blank = ''; File::write($test, $blank, false); // Test ownership $return = (fileowner($test) == fileowner($path)); // Delete the test file File::delete($test); return $return; } return false; } /** * Searches the directory paths for a given file. * * @param mixed $paths A path string or array of path strings to search in * @param string $file The file name to look for. * * @return mixed The full path and file name for the target file, or boolean false if the file is not found in any of the paths. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::find() instead. */ public static function find($paths, $file) { // Force to array if (!\is_array($paths) && !($paths instanceof \Iterator)) { settype($paths, 'array'); } // Start looping through the path set foreach ($paths as $path) { // Get the path to the file $fullname = $path . '/' . $file; // Is the path based on a stream? if (strpos($path, '://') === false) { // Not a stream, so do a realpath() to avoid directory // traversal attempts on the local file system. // Needed for substr() later $path = realpath($path); $fullname = realpath($fullname); } /* * The substr() check added to make sure that the realpath() * results in a directory registered so that * non-registered directories are not accessible via directory * traversal attempts. */ if (file_exists($fullname) && substr($fullname, 0, \strlen($path)) === $path) { return $fullname; } } // Could not find the file in the set of paths return false; } /** * Resolves /./, /../ and multiple / in a string and returns the resulting absolute path, inspired by Flysystem * Removes trailing slashes * * @param string $path A path to resolve * * @return string The resolved path * * @since 3.9.25 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::resolve() instead. */ public static function resolve($path) { $path = static::clean($path); // Save start character for absolute path $startCharacter = ($path[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : ''; $parts = []; foreach (explode(DIRECTORY_SEPARATOR, $path) as $part) { switch ($part) { case '': case '.': break; case '..': if (empty($parts)) { throw new \Exception('Path is outside of the defined root'); } array_pop($parts); break; default: $parts[] = $part; break; } } return $startCharacter . implode(DIRECTORY_SEPARATOR, $parts); } /** * Remove all references to root directory path and the system tmp path from a message * * @param string $message The message to be cleaned * @param string $rootDirectory Optional root directory, defaults to JPATH_ROOT * * @return string * * @since 3.10.7 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Path::removeRoot() instead. */ public static function removeRoot($message, $rootDirectory = null) { if (empty($rootDirectory)) { $rootDirectory = JPATH_ROOT; } $makePattern = static function ($dir) { return '~' . str_replace('~', '\\~', preg_replace('~[/\\\\]+~', '[/\\\\\\\\]+', $dir)) . '~'; }; $replacements = [ $makePattern(static::clean($rootDirectory)) => '[ROOT]', $makePattern(sys_get_temp_dir()) => '[TMP]', ]; return preg_replace(array_keys($replacements), array_values($replacements), $message); } } PK1A#]�����'src/Filesystem/Streams/StreamString.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem\Streams; \defined('JPATH_PLATFORM') or die; use Joomla\CMS\Filesystem\Support\StringController; /** * String Stream Wrapper * * This class allows you to use a PHP string in the same way that * you would normally use a regular stream wrapper * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper instead. */ class StreamString { /** * The current string * * @var string * @since 3.0.0 */ protected $currentString; /** * The path * * @var string * @since 3.0.0 */ protected $path; /** * The mode * * @var string * @since 3.0.0 */ protected $mode; /** * Enter description here ... * * @var string * @since 3.0.0 */ protected $options; /** * Enter description here ... * * @var string * @since 3.0.0 */ protected $openedPath; /** * Current position * * @var integer * @since 3.0.0 */ protected $pos; /** * Length of the string * * @var string * @since 3.0.0 */ protected $len; /** * Statistics for a file * * @var array * @since 3.0.0 * * @link http://us.php.net/manual/en/function.stat.php */ protected $stat; /** * Method to open a file or URL. * * @param string $path The stream path. * @param string $mode Not used. * @param integer $options Not used. * @param string $openedPath Not used. * * @return boolean * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_open() instead. */ public function stream_open($path, $mode, $options, &$openedPath) { $this->currentString = &StringController::getRef(str_replace('string://', '', $path)); if ($this->currentString) { $this->len = \strlen($this->currentString); $this->pos = 0; $this->stat = $this->url_stat($path, 0); return true; } else { return false; } } /** * Method to retrieve information from a file resource * * @return array * * @link https://www.php.net/manual/en/streamwrapper.stream-stat.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_stat instead. */ public function stream_stat() { return $this->stat; } /** * Method to retrieve information about a file. * * @param string $path File path or URL to stat * @param integer $flags Additional flags set by the streams API * * @return array * * @link https://www.php.net/manual/en/streamwrapper.url-stat.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::url_stat() instead. */ public function url_stat($path, $flags = 0) { $now = time(); $string = &StringController::getRef(str_replace('string://', '', $path)); $stat = [ 'dev' => 0, 'ino' => 0, 'mode' => 0, 'nlink' => 1, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => \strlen($string), 'atime' => $now, 'mtime' => $now, 'ctime' => $now, 'blksize' => '512', 'blocks' => ceil(\strlen($string) / 512), ]; return $stat; } /** * Method to read a given number of bytes starting at the current position * and moving to the end of the string defined by the current position plus the * given number. * * @param integer $count Bytes of data from the current position should be returned. * * @return string * * @link https://www.php.net/manual/en/streamwrapper.stream-read.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_read() instead. */ public function stream_read($count) { $result = substr($this->currentString, $this->pos, $count); $this->pos += $count; return $result; } /** * Stream write, always returning false. * * @param string $data The data to write. * * @return boolean * * @since 1.7.0 * @note Updating the string is not supported. * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_write() instead. */ public function stream_write($data) { // We don't support updating the string. return false; } /** * Method to get the current position * * @return integer The position * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_tell() instead. */ public function stream_tell() { return $this->pos; } /** * End of field check * * @return boolean True if at end of field. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_eof() instead. */ public function stream_eof() { if ($this->pos > $this->len) { return true; } return false; } /** * Stream offset * * @param integer $offset The starting offset. * @param integer $whence SEEK_SET, SEEK_CUR, SEEK_END * * @return boolean True on success. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_seek() instead. */ public function stream_seek($offset, $whence) { // $whence: SEEK_SET, SEEK_CUR, SEEK_END if ($offset > $this->len) { // We can't seek beyond our len. return false; } switch ($whence) { case SEEK_SET: $this->pos = $offset; break; case SEEK_CUR: if (($this->pos + $offset) < $this->len) { $this->pos += $offset; } else { return false; } break; case SEEK_END: $this->pos = $this->len - $offset; break; } return true; } /** * Stream flush, always returns true. * * @return boolean * * @since 1.7.0 * @note Data storage is not supported * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Stream\StringWrapper::stream_flush() instead. */ public function stream_flush() { // We don't store data. return true; } } stream_wrapper_register('string', '\\Joomla\\CMS\\Filesystem\\Streams\\StreamString') or die('StreamString Wrapper Registration Failed'); PK1A#]��6HTHTsrc/Filesystem/File.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; use Joomla\CMS\Client\ClientHelper; use Joomla\CMS\Client\FtpClient; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A File handling class * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File instead. */ class File { /** * @var boolean true if OPCache enabled, and we have permission to invalidate files * @since 4.0.1 */ protected static $canFlushFileCache; /** * Gets the extension of a file name * * @param string $file The file name * * @return string The file extension * * @since 1.7.0 */ public static function getExt($file) { // String manipulation should be faster than pathinfo() on newer PHP versions. $dot = strrpos($file, '.'); if ($dot === false) { return ''; } $ext = substr($file, $dot + 1); // Extension cannot contain slashes. if (strpos($ext, '/') !== false || (DIRECTORY_SEPARATOR === '\\' && strpos($ext, '\\') !== false)) { return ''; } return $ext; } /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::stripExt() instead. */ public static function stripExt($file) { return preg_replace('#\.[^.]*$#', '', $file); } /** * Makes file name safe to use * * @param string $file The name of the file [not full path] * * @return string The sanitised string * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::makeSafe() instead. */ public static function makeSafe($file) { // Remove any trailing dots, as those aren't ever valid file names. $file = rtrim($file, '.'); // Try transliterating the file name using the native php function if (function_exists('transliterator_transliterate') && function_exists('iconv')) { // Using iconv to ignore characters that can't be transliterated $file = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", transliterator_transliterate('Any-Latin; Latin-ASCII', $file)); } $regex = ['#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#']; return trim(preg_replace($regex, '', $file)); } /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param boolean $useStreams True to use streams * * @return boolean True on success * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::copy() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function copy($src, $dest, $path = null, $useStreams = false) { // Prepend a base path if it exists if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Check src path if (!is_readable($src)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_FILE_FIND_COPY', __METHOD__, $src), Log::WARNING, 'jerror'); return false; } if ($useStreams) { $stream = Factory::getStream(); if (!$stream->copy($src, $dest)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_FILE_STREAMS', __METHOD__, $src, $dest, $stream->getError()), Log::WARNING, 'jerror'); return false; } self::invalidateFileCache($dest); return true; } else { $FTPOptions = ClientHelper::getCredentials('ftp'); if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // If the parent folder doesn't exist we must create it if (!file_exists(\dirname($dest))) { Folder::create(\dirname($dest)); } // Translate the destination path for the FTP account $dest = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/'); if (!$ftp->store($src, $dest)) { // FTP connector throws an error return false; } $ret = true; } else { if (!@ copy($src, $dest)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_COPY_FAILED_ERR01', $src, $dest), Log::WARNING, 'jerror'); return false; } $ret = true; } self::invalidateFileCache($dest); return $ret; } } /** * Invalidate opcache for a newly written/deleted file immediately, if opcache* functions exist and if this was a PHP file. * * @param string $filepath The path to the file just written to, to flush from opcache * @param boolean $force If set to true, the script will be invalidated regardless of whether invalidation is necessary * * @return boolean TRUE if the opcode cache for script was invalidated/nothing to invalidate, * or FALSE if the opcode cache is disabled or other conditions returning * FALSE from opcache_invalidate (like file not found). * * @since 4.0.1 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::invalidateFileCache() instead. */ public static function invalidateFileCache($filepath, $force = true) { if (self::canFlushFileCache() && '.php' === strtolower(substr($filepath, -4))) { return opcache_invalidate($filepath, $force); } return false; } /** * First we check if opcache is enabled * Then we check if the opcache_invalidate function is available * Lastly we check if the host has restricted which scripts can use opcache_invalidate using opcache.restrict_api. * * `$_SERVER['SCRIPT_FILENAME']` approximates the origin file's path, but `realpath()` * is necessary because `SCRIPT_FILENAME` can be a relative path when run from CLI. * If the host has this set, check whether the path in `opcache.restrict_api` matches * the beginning of the path of the origin file. * * @return boolean TRUE if we can proceed to use opcache_invalidate to flush a file from the OPCache * * @since 4.0.1 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::invalidateFileCache() instead. * This method will be removed without replacement. */ public static function canFlushFileCache() { if (isset(static::$canFlushFileCache)) { return static::$canFlushFileCache; } if ( ini_get('opcache.enable') && function_exists('opcache_invalidate') && (!ini_get('opcache.restrict_api') || stripos(realpath($_SERVER['SCRIPT_FILENAME']), ini_get('opcache.restrict_api')) === 0) ) { static::$canFlushFileCache = true; } else { static::$canFlushFileCache = false; } return static::$canFlushFileCache; } /** * Delete a file or array of files * * @param mixed $file The file name or an array of file names * * @return boolean True on success * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::delete() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function delete($file) { $FTPOptions = ClientHelper::getCredentials('ftp'); if (\is_array($file)) { $files = $file; } else { $files[] = $file; } // Do NOT use ftp if it is not enabled if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); } foreach ($files as $file) { $file = Path::clean($file); if (!is_file($file)) { continue; } /** * Try making the file writable first. If it's read-only, it can't be deleted * on Windows, even if the parent folder is writable */ @chmod($file, 0777); /** * Invalidate the OPCache for the file before actually deleting it * @see https://github.com/joomla/joomla-cms/pull/32915#issuecomment-812865635 * @see https://www.php.net/manual/en/function.opcache-invalidate.php#116372 */ self::invalidateFileCache($file); /** * In case of restricted permissions we delete it one way or the other * as long as the owner is either the webserver or the ftp */ if (@unlink($file)) { // Do nothing } elseif ($FTPOptions['enabled'] == 1) { $file = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $file), '/'); if (!$ftp->delete($file)) { // FTP connector throws an error return false; } } else { $filename = basename($file); Log::add(Text::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', $filename), Log::WARNING, 'jerror'); return false; } } return true; } /** * Moves a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param boolean $useStreams True to use streams * * @return boolean True on success * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::move() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function move($src, $dest, $path = '', $useStreams = false) { if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Check src path if (!is_readable($src)) { Log::add(Text::_('JLIB_FILESYSTEM_CANNOT_FIND_SOURCE_FILE'), Log::WARNING, 'jerror'); return false; } if ($useStreams) { $stream = Factory::getStream(); if (!$stream->move($src, $dest)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_GENERIC', __METHOD__, $stream->getError()), Log::WARNING, 'jerror'); return false; } self::invalidateFileCache($dest); return true; } else { $FTPOptions = ClientHelper::getCredentials('ftp'); // Invalidate the compiled OPCache of the old file so it's no longer used. self::invalidateFileCache($src); if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path for the FTP account $src = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $src), '/'); $dest = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/'); // Use FTP rename to simulate move if (!$ftp->rename($src, $dest)) { Log::add(Text::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'), Log::WARNING, 'jerror'); return false; } } else { if (!@ rename($src, $dest)) { Log::add(Text::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'), Log::WARNING, 'jerror'); return false; } } self::invalidateFileCache($dest); return true; } } /** * Write contents to a file * * @param string $file The full file path * @param string $buffer The buffer to write * @param boolean $useStreams Use streams * * @return boolean True on success * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::write() instead. */ public static function write($file, $buffer, $useStreams = false) { if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } // If the destination directory doesn't exist we need to create it if (!file_exists(\dirname($file))) { if (Folder::create(\dirname($file)) == false) { return false; } } if ($useStreams) { $stream = Factory::getStream(); // Beef up the chunk size to a meg $stream->set('chunksize', (1024 * 1024)); if (!$stream->writeFile($file, $buffer)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_WRITE_STREAMS', __METHOD__, $file, $stream->getError()), Log::WARNING, 'jerror'); return false; } self::invalidateFileCache($file); return true; } else { $FTPOptions = ClientHelper::getCredentials('ftp'); if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path for the FTP account and use FTP write buffer to file $file = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $file), '/'); $ret = $ftp->write($file, $buffer); } else { $file = Path::clean($file); $ret = \is_int(file_put_contents($file, $buffer)); } self::invalidateFileCache($file); return $ret; } } /** * Append contents to a file * * @param string $file The full file path * @param string $buffer The buffer to write * @param boolean $useStreams Use streams * * @return boolean True on success * * @since 3.6.0 * */ public static function append($file, $buffer, $useStreams = false) { if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } // If the file doesn't exist, just write instead of append if (!file_exists($file)) { return self::write($file, $buffer, $useStreams); } if ($useStreams) { $stream = Factory::getStream(); // Beef up the chunk size to a meg $stream->set('chunksize', (1024 * 1024)); if ($stream->open($file, 'ab') && $stream->write($buffer) && $stream->close()) { self::invalidateFileCache($file); return true; } Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_WRITE_STREAMS', __METHOD__, $file, $stream->getError()), Log::WARNING, 'jerror'); return false; } else { // Initialise variables. $FTPOptions = ClientHelper::getCredentials('ftp'); if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path for the FTP account and use FTP write buffer to file $file = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $file), '/'); $ret = $ftp->append($file, $buffer); } else { $file = Path::clean($file); $ret = \is_int(file_put_contents($file, $buffer, FILE_APPEND)); } self::invalidateFileCache($file); return $ret; } } /** * Moves an uploaded file to a destination folder * * @param string $src The name of the php (temporary) uploaded file * @param string $dest The path (including filename) to move the uploaded file to * @param boolean $useStreams True to use streams * @param boolean $allowUnsafe Allow the upload of unsafe files * @param array $safeFileOptions Options to InputFilter::isSafeFile * * @return boolean True on success * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\File::upload() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function upload($src, $dest, $useStreams = false, $allowUnsafe = false, $safeFileOptions = []) { if (!$allowUnsafe) { $descriptor = [ 'tmp_name' => $src, 'name' => basename($dest), 'type' => '', 'error' => '', 'size' => '', ]; $isSafe = InputFilter::isSafeFile($descriptor, $safeFileOptions); if (!$isSafe) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_WARNFS_ERR03', $dest), Log::WARNING, 'jerror'); return false; } } // Ensure that the path is valid and clean $dest = Path::clean($dest); // Create the destination directory if it does not exist $baseDir = \dirname($dest); if (!file_exists($baseDir)) { Folder::create($baseDir); } if ($useStreams) { $stream = Factory::getStream(); if (!$stream->upload($src, $dest)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_GENERIC', __METHOD__, $stream->getError()), Log::WARNING, 'jerror'); return false; } return true; } else { $FTPOptions = ClientHelper::getCredentials('ftp'); $ret = false; if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path for the FTP account $dest = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/'); // Copy the file to the destination directory if (is_uploaded_file($src) && $ftp->store($src, $dest)) { self::invalidateFileCache($src); unlink($src); $ret = true; } else { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_WARNFS_ERR04', $src, $dest), Log::WARNING, 'jerror'); } } else { self::invalidateFileCache($src); if (is_writable($baseDir) && move_uploaded_file($src, $dest)) { // Short circuit to prevent file permission errors if (Path::setPermissions($dest)) { $ret = true; } else { Log::add(Text::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR01'), Log::WARNING, 'jerror'); } } else { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_WARNFS_ERR04', $src, $dest), Log::WARNING, 'jerror'); } } self::invalidateFileCache($dest); return $ret; } } /** * Wrapper for the standard file_exists function * * @param string $file File path * * @return boolean True if path is a file * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use is_file() instead. */ public static function exists($file) { return is_file(Path::clean($file)); } } PK1A#]KWs�g&g&#src/Filesystem/FilesystemHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * File system helper * * Holds support functions for the filesystem, particularly the stream * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper instead. */ class FilesystemHelper { /** * Remote file size function for streams that don't support it * * @param string $url Link identifier * * @return mixed * * @link https://www.php.net/manual/en/function.filesize.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::remotefsize() instead. */ public static function remotefsize($url) { $sch = parse_url($url, PHP_URL_SCHEME); if (($sch !== 'http') && ($sch !== 'https') && ($sch !== 'ftp') && ($sch !== 'ftps')) { return false; } if (($sch === 'http') || ($sch === 'https')) { $headers = get_headers($url, 1); if ((!\array_key_exists('Content-Length', $headers))) { return false; } return $headers['Content-Length']; } if (($sch === 'ftp') || ($sch === 'ftps')) { $server = parse_url($url, PHP_URL_HOST); $port = parse_url($url, PHP_URL_PORT); $path = parse_url($url, PHP_URL_PATH); $user = parse_url($url, PHP_URL_USER); $pass = parse_url($url, PHP_URL_PASS); if ((!$server) || (!$path)) { return false; } if (!$port) { $port = 21; } if (!$user) { $user = 'anonymous'; } if (!$pass) { $pass = ''; } switch ($sch) { case 'ftp': $ftpid = ftp_connect($server, $port); break; case 'ftps': $ftpid = ftp_ssl_connect($server, $port); break; } if (!$ftpid) { return false; } $login = ftp_login($ftpid, $user, $pass); if (!$login) { return false; } $ftpsize = ftp_size($ftpid, $path); ftp_close($ftpid); if ($ftpsize == -1) { return false; } return $ftpsize; } } /** * Quick FTP chmod * * @param string $url Link identifier * @param integer $mode The new permissions, given as an octal value. * * @return mixed * * @link https://www.php.net/manual/en/function.ftp-chmod.php * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::ftpChmod() instead. */ public static function ftpChmod($url, $mode) { $sch = parse_url($url, PHP_URL_SCHEME); if (($sch !== 'ftp') && ($sch !== 'ftps')) { return false; } $server = parse_url($url, PHP_URL_HOST); $port = parse_url($url, PHP_URL_PORT); $path = parse_url($url, PHP_URL_PATH); $user = parse_url($url, PHP_URL_USER); $pass = parse_url($url, PHP_URL_PASS); if ((!$server) || (!$path)) { return false; } if (!$port) { $port = 21; } if (!$user) { $user = 'anonymous'; } if (!$pass) { $pass = ''; } switch ($sch) { case 'ftp': $ftpid = ftp_connect($server, $port); break; case 'ftps': $ftpid = ftp_ssl_connect($server, $port); break; } if (!$ftpid) { return false; } $login = ftp_login($ftpid, $user, $pass); if (!$login) { return false; } $res = ftp_chmod($ftpid, $mode, $path); ftp_close($ftpid); return $res; } /** * Modes that require a write operation * * @return array * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::getWriteModes() instead. */ public static function getWriteModes() { return ['w', 'w+', 'a', 'a+', 'r+', 'x', 'x+']; } /** * Stream and Filter Support Operations * * Returns the supported streams, in addition to direct file access * Also includes Joomla! streams as well as PHP streams * * @return array Streams * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::getSupported() instead. */ public static function getSupported() { // Really quite cool what php can do with arrays when you let it... static $streams; if (!$streams) { $streams = array_merge(stream_get_wrappers(), self::getJStreams()); } return $streams; } /** * Returns a list of transports * * @return array * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::getTransports() instead. */ public static function getTransports() { // Is this overkill? return stream_get_transports(); } /** * Returns a list of filters * * @return array * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::getFilters() instead. */ public static function getFilters() { // Note: This will look like the getSupported() function with J! filters. // @todo: add user space filter loading like user space stream loading return stream_get_filters(); } /** * Returns a list of J! streams * * @return array * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::getJStreams() instead. */ public static function getJStreams() { static $streams = []; if (!$streams) { $files = new \DirectoryIterator(__DIR__ . '/Streams'); /** @var $file \DirectoryIterator */ foreach ($files as $file) { // Only load for php files. if (!$file->isFile() || $file->getExtension() !== 'php') { continue; } $streams[] = str_replace('stream', '', strtolower($file->getBasename('.php'))); } } return $streams; } /** * Determine if a stream is a Joomla stream. * * @param string $streamname The name of a stream * * @return boolean True for a Joomla Stream * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Helper::isJoomlaStream() instead. */ public static function isJoomlaStream($streamname) { return \in_array($streamname, self::getJStreams()); } /** * Calculates the maximum upload file size and returns string with unit or the size in bytes * * Call it with JFilesystemHelper::fileUploadMaxSize(); * * @param bool $unitOutput This parameter determines whether the return value should be a string with a unit * * @return float|string The maximum upload size of files with the appropriate unit or in bytes * * @since 3.4 */ public static function fileUploadMaxSize($unitOutput = true) { static $max_size = false; static $output_type = true; if ($max_size === false || $output_type != $unitOutput) { $max_size = self::parseSize(ini_get('post_max_size')); $upload_max = self::parseSize(ini_get('upload_max_filesize')); if ($upload_max > 0 && ($upload_max < $max_size || $max_size == 0)) { $max_size = $upload_max; } if ($unitOutput == true) { $max_size = self::parseSizeUnit($max_size); } $output_type = $unitOutput; } return $max_size; } /** * Returns the size in bytes without the unit for the comparison * * @param string $size The size which is received from the PHP settings * * @return float The size in bytes without the unit * * @since 3.4 */ private static function parseSize($size) { $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); $size = preg_replace('/[^0-9\.]/', '', $size); $return = round($size); if ($unit) { $return = round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); } return $return; } /** * Creates the rounded size of the size with the appropriate unit * * @param float $maxSize The maximum size which is allowed for the uploads * * @return string String with the size and the appropriate unit * * @since 3.4 */ private static function parseSizeUnit($maxSize) { $base = log($maxSize) / log(1024); $suffixes = ['', 'k', 'M', 'G', 'T']; return round(pow(1024, $base - floor($base)), 0) . $suffixes[floor($base)]; } } PK1A#]� f��+src/Filesystem/Support/StringController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem\Support; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * String Controller * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Support\StringController instead. */ class StringController { /** * Defines a variable as an array * * @return array * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Support\StringController::getArray() instead. */ public function _getArray() { static $strings = []; return $strings; } /** * Create a reference * * @param string $reference The key * @param string $string The value * * @return void * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Support\StringController::createRef() instead. */ public function createRef($reference, &$string) { $ref = &self::_getArray(); $ref[$reference] = & $string; } /** * Get reference * * @param string $reference The key for the reference. * * @return mixed False if not set, reference if it exists * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Support\StringController::getRef() instead. */ public function getRef($reference) { $ref = &self::_getArray(); if (isset($ref[$reference])) { return $ref[$reference]; } else { return false; } } } PK1A#]���==src/Filesystem/Patcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A Unified Diff Format Patcher class * * @link http://sourceforge.net/projects/phppatcher/ This has been derived from the PhpPatcher version 0.1.1 written by Giuseppe Mazzotta * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher instead. */ class Patcher { /** * Regular expression for searching source files */ public const SRC_FILE = '/^---\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; /** * Regular expression for searching destination files */ public const DST_FILE = '/^\\+\\+\\+\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; /** * Regular expression for searching hunks of differences */ public const HUNK = '/@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@($)/A'; /** * Regular expression for splitting lines */ public const SPLIT = '/(\r\n)|(\r)|(\n)/'; /** * @var array sources files * @since 3.0.0 */ protected $sources = []; /** * @var array destination files * @since 3.0.0 */ protected $destinations = []; /** * @var array removal files * @since 3.0.0 */ protected $removals = []; /** * @var array patches * @since 3.0.0 */ protected $patches = []; /** * @var array instance of this class * @since 3.0.0 */ protected static $instance; /** * Constructor * * The constructor is protected to force the use of FilesystemPatcher::getInstance() * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::__construct() instead. */ protected function __construct() { } /** * Method to get a patcher * * @return Patcher an instance of the patcher * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::getInstance() instead. */ public static function getInstance() { if (!isset(static::$instance)) { static::$instance = new static(); } return static::$instance; } /** * Reset the patcher * * @return Patcher This object for chaining * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::reset() instead. */ public function reset() { $this->sources = []; $this->destinations = []; $this->removals = []; $this->patches = []; return $this; } /** * Apply the patches * * @return integer The number of files patched * * @since 3.0.0 * @throws \RuntimeException * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::apply() instead. */ public function apply() { foreach ($this->patches as $patch) { // Separate the input into lines $lines = self::splitLines($patch['udiff']); // Loop for each header while (self::findHeader($lines, $src, $dst)) { $done = false; $regex = '#^([^/]*/)*#'; if ($patch['strip'] !== null) { $regex = '#^([^/]*/){' . (int) $patch['strip'] . '}#'; } $src = $patch['root'] . preg_replace($regex, '', $src); $dst = $patch['root'] . preg_replace($regex, '', $dst); // Loop for each hunk of differences while (self::findHunk($lines, $src_line, $src_size, $dst_line, $dst_size)) { $done = true; // Apply the hunk of differences $this->applyHunk($lines, $src, $dst, $src_line, $src_size, $dst_line, $dst_size); } // If no modifications were found, throw an exception if (!$done) { throw new \RuntimeException('Invalid Diff'); } } } // Initialize the counter $done = 0; // Patch each destination file foreach ($this->destinations as $file => $content) { $buffer = implode("\n", $content); if (File::write($file, $buffer)) { if (isset($this->sources[$file])) { $this->sources[$file] = $content; } $done++; } } // Remove each removed file foreach ($this->removals as $file) { if (File::delete($file)) { if (isset($this->sources[$file])) { unset($this->sources[$file]); } $done++; } } // Clear the destinations cache $this->destinations = []; // Clear the removals $this->removals = []; // Clear the patches $this->patches = []; return $done; } /** * Add a unified diff file to the patcher * * @param string $filename Path to the unified diff file * @param string $root The files root path * @param integer $strip The number of '/' to strip * * @return Patcher $this for chaining * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::addFile() instead. */ public function addFile($filename, $root = JPATH_BASE, $strip = 0) { return $this->add(file_get_contents($filename), $root, $strip); } /** * Add a unified diff string to the patcher * * @param string $udiff Unified diff input string * @param string $root The files root path * @param integer $strip The number of '/' to strip * * @return Patcher $this for chaining * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::add() instead. */ public function add($udiff, $root = JPATH_BASE, $strip = 0) { $this->patches[] = [ 'udiff' => $udiff, 'root' => isset($root) ? rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : '', 'strip' => $strip, ]; return $this; } /** * Separate CR or CRLF lines * * @param string $data Input string * * @return array The lines of the inputdestination file * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::splitLines() instead. */ protected static function splitLines($data) { return preg_split(self::SPLIT, $data); } /** * Find the diff header * * The internal array pointer of $lines is on the next line after the finding * * @param array $lines The udiff array of lines * @param string $src The source file * @param string $dst The destination file * * @return boolean TRUE in case of success, FALSE in case of failure * * @since 3.0.0 * @throws \RuntimeException * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::findHeader() instead. */ protected static function findHeader(&$lines, &$src, &$dst) { // Get the current line $line = current($lines); // Search for the header while ($line !== false && !preg_match(self::SRC_FILE, $line, $m)) { $line = next($lines); } if ($line === false) { // No header found, return false return false; } // Set the source file $src = $m[1]; // Advance to the next line $line = next($lines); if ($line === false) { throw new \RuntimeException('Unexpected EOF'); } // Search the destination file if (!preg_match(self::DST_FILE, $line, $m)) { throw new \RuntimeException('Invalid Diff file'); } // Set the destination file $dst = $m[1]; // Advance to the next line if (next($lines) === false) { throw new \RuntimeException('Unexpected EOF'); } return true; } /** * Find the next hunk of difference * * The internal array pointer of $lines is on the next line after the finding * * @param array $lines The udiff array of lines * @param string $srcLine The beginning of the patch for the source file * @param string $srcSize The size of the patch for the source file * @param string $dstLine The beginning of the patch for the destination file * @param string $dstSize The size of the patch for the destination file * * @return boolean TRUE in case of success, false in case of failure * * @since 3.0.0 * @throws \RuntimeException * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::findHunk() instead. */ protected static function findHunk(&$lines, &$srcLine, &$srcSize, &$dstLine, &$dstSize) { $line = current($lines); if (preg_match(self::HUNK, $line, $m)) { $srcLine = (int) $m[1]; $srcSize = 1; if ($m[3] !== '') { $srcSize = (int) $m[3]; } $dstLine = (int) $m[4]; $dstSize = 1; if ($m[6] !== '') { $dstSize = (int) $m[6]; } if (next($lines) === false) { throw new \RuntimeException('Unexpected EOF'); } return true; } return false; } /** * Apply the patch * * @param array $lines The udiff array of lines * @param string $src The source file * @param string $dst The destination file * @param string $srcLine The beginning of the patch for the source file * @param string $srcSize The size of the patch for the source file * @param string $dstLine The beginning of the patch for the destination file * @param string $dstSize The size of the patch for the destination file * * @return void * * @since 3.0.0 * @throws \RuntimeException * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::applyHunk() instead. */ protected function applyHunk(&$lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize) { $srcLine--; $dstLine--; $line = current($lines); // Source lines (old file) $source = []; // New lines (new file) $destin = []; $src_left = $srcSize; $dst_left = $dstSize; do { if (!isset($line[0])) { $source[] = ''; $destin[] = ''; $src_left--; $dst_left--; } elseif ($line[0] == '-') { if ($src_left == 0) { throw new \RuntimeException(Text::sprintf('JLIB_FILESYSTEM_PATCHER_UNEXPECTED_REMOVE_LINE', key($lines))); } $source[] = substr($line, 1); $src_left--; } elseif ($line[0] == '+') { if ($dst_left == 0) { throw new \RuntimeException(Text::sprintf('JLIB_FILESYSTEM_PATCHER_UNEXPECTED_ADD_LINE', key($lines))); } $destin[] = substr($line, 1); $dst_left--; } elseif ($line != '\\ No newline at end of file') { $line = substr($line, 1); $source[] = $line; $destin[] = $line; $src_left--; $dst_left--; } if ($src_left == 0 && $dst_left == 0) { // Now apply the patch, finally! if ($srcSize > 0) { $src_lines = & $this->getSource($src); if (!isset($src_lines)) { throw new \RuntimeException( Text::sprintf( 'JLIB_FILESYSTEM_PATCHER_UNEXISTING_SOURCE', Path::removeRoot($src) ) ); } } if ($dstSize > 0) { if ($srcSize > 0) { $dst_lines = & $this->getDestination($dst, $src); $src_bottom = $srcLine + \count($source); for ($l = $srcLine; $l < $src_bottom; $l++) { if ($src_lines[$l] != $source[$l - $srcLine]) { throw new \RuntimeException( Text::sprintf( 'JLIB_FILESYSTEM_PATCHER_FAILED_VERIFY', Path::removeRoot($src), $l ) ); } } array_splice($dst_lines, $dstLine, \count($source), $destin); } else { $this->destinations[$dst] = $destin; } } else { $this->removals[] = $src; } next($lines); return; } $line = next($lines); } while ($line !== false); throw new \RuntimeException('Unexpected EOF'); } /** * Get the lines of a source file * * @param string $src The path of a file * * @return array The lines of the source file * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::getSource() instead. */ protected function &getSource($src) { if (!isset($this->sources[$src])) { $this->sources[$src] = null; if (is_readable($src)) { $this->sources[$src] = self::splitLines(file_get_contents($src)); } } return $this->sources[$src]; } /** * Get the lines of a destination file * * @param string $dst The path of a destination file * @param string $src The path of a source file * * @return array The lines of the destination file * * @since 3.0.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Patcher::getDestination() instead. */ protected function &getDestination($dst, $src) { if (!isset($this->destinations[$dst])) { $this->destinations[$dst] = $this->getSource($src); } return $this->destinations[$dst]; } } PK1A#]�f��o_o_src/Filesystem/Folder.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Filesystem; use Joomla\CMS\Client\ClientHelper; use Joomla\CMS\Client\FtpClient; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A Folder handling class * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder instead. */ abstract class Folder { /** * Copy a folder. * * @param string $src The path to the source folder. * @param string $dest The path to the destination folder. * @param string $path An optional base path to prefix to the file names. * @param boolean $force Force copy. * @param boolean $useStreams Optionally force folder/file overwrites. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::copy() instead. */ public static function copy($src, $dest, $path = '', $force = false, $useStreams = false) { if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } $FTPOptions = ClientHelper::getCredentials('ftp'); if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Eliminate trailing directory separators, if any $src = rtrim($src, DIRECTORY_SEPARATOR); $dest = rtrim($dest, DIRECTORY_SEPARATOR); if (!self::exists($src)) { throw new \RuntimeException('Source folder not found', -1); } if (self::exists($dest) && !$force) { throw new \RuntimeException('Destination folder already exists', -1); } // Make sure the destination exists if (!self::create($dest)) { throw new \RuntimeException('Cannot create destination folder', -1); } // If we're using ftp and don't have streams enabled if ($FTPOptions['enabled'] == 1 && !$useStreams) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); if (!($dh = @opendir($src))) { throw new \RuntimeException('Cannot open source folder', -1); } // Walk through the directory copying files and recursing into folders. while (($file = readdir($dh)) !== false) { $sfid = $src . '/' . $file; $dfid = $dest . '/' . $file; switch (filetype($sfid)) { case 'dir': if ($file != '.' && $file != '..') { $ret = self::copy($sfid, $dfid, null, $force); if ($ret !== true) { return $ret; } } break; case 'file': // Translate path for the FTP account $dfid = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dfid), '/'); if (!$ftp->store($sfid, $dfid)) { throw new \RuntimeException('Copy file failed', -1); } break; } } } else { if (!($dh = @opendir($src))) { throw new \RuntimeException('Cannot open source folder', -1); } // Walk through the directory copying files and recursing into folders. while (($file = readdir($dh)) !== false) { $sfid = $src . '/' . $file; $dfid = $dest . '/' . $file; switch (filetype($sfid)) { case 'dir': if ($file != '.' && $file != '..') { $ret = self::copy($sfid, $dfid, null, $force, $useStreams); if ($ret !== true) { return $ret; } } break; case 'file': if ($useStreams) { $stream = Factory::getStream(); if (!$stream->copy($sfid, $dfid)) { throw new \RuntimeException( sprintf( "Cannot copy file: %s", Path::removeRoot($stream->getError()) ), -1 ); } } else { if (!@copy($sfid, $dfid)) { throw new \RuntimeException('Copy file failed', -1); } } break; } } } return true; } /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return boolean True if successful. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::create() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function create($path = '', $mode = 0755) { $FTPOptions = ClientHelper::getCredentials('ftp'); static $nested = 0; // Check to make sure the path valid and clean $path = Path::clean($path); // Check if parent dir exists $parent = \dirname($path); if (!self::exists($parent)) { // Prevent infinite loops! $nested++; if (($nested > 20) || ($parent == $path)) { Log::add(__METHOD__ . ': ' . Text::_('JLIB_FILESYSTEM_ERROR_FOLDER_LOOP'), Log::WARNING, 'jerror'); $nested--; return false; } // Create the parent directory if (self::create($parent, $mode) !== true) { // Folder::create throws an error $nested--; return false; } // OK, parent directory has been created $nested--; } // Check if dir already exists if (self::exists($path)) { return true; } // Check for safe mode if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path to FTP path $path = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $path), '/'); $ret = $ftp->mkdir($path); $ftp->chmod($path, $mode); } else { // We need to get and explode the open_basedir paths $obd = ini_get('open_basedir'); // If open_basedir is set we need to get the open_basedir that the path is in if ($obd != null) { if (IS_WIN) { $obdSeparator = ';'; } else { $obdSeparator = ':'; } // Create the array of open_basedir paths $obdArray = explode($obdSeparator, $obd); $inBaseDir = false; // Iterate through open_basedir paths looking for a match foreach ($obdArray as $test) { $test = Path::clean($test); if (strpos($path, $test) === 0 || strpos($path, realpath($test)) === 0) { $inBaseDir = true; break; } } if ($inBaseDir == false) { // Return false for JFolder::create because the path to be created is not in open_basedir Log::add(__METHOD__ . ': ' . Text::_('JLIB_FILESYSTEM_ERROR_FOLDER_PATH'), Log::WARNING, 'jerror'); return false; } } // First set umask $origmask = @umask(0); // Create the path if (!$ret = @mkdir($path, $mode)) { @umask($origmask); Log::add( __METHOD__ . ': ' . Text::_('JLIB_FILESYSTEM_ERROR_COULD_NOT_CREATE_DIRECTORY') . 'Path: ' . $path, Log::WARNING, 'jerror' ); return false; } // Reset umask @umask($origmask); } return $ret; } /** * Delete a folder. * * @param string $path The path to the folder to delete. * * @return boolean True on success. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::delete() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function delete($path) { if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } // Sanity check if (!$path) { // Bad programmer! Bad Bad programmer! Log::add(__METHOD__ . ': ' . Text::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY'), Log::WARNING, 'jerror'); return false; } $FTPOptions = ClientHelper::getCredentials('ftp'); // Check to make sure the path valid and clean $path = Path::clean($path); // Is this really a folder? if (!is_dir($path)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Remove all the files in folder if they exist; disable all filtering $files = self::files($path, '.', false, true, [], []); if (!empty($files)) { if (File::delete($files) !== true) { // File::delete throws an error return false; } } // Remove sub-folders of folder; disable all filtering $folders = self::folders($path, '.', false, true, [], []); foreach ($folders as $folder) { if (is_link($folder)) { // Don't descend into linked directories, just delete the link. if (File::delete($folder) !== true) { // File::delete throws an error return false; } } elseif (self::delete($folder) !== true) { // Folder::delete throws an error return false; } } if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); } // In case of restricted permissions we zap it one way or the other // as long as the owner is either the webserver or the ftp. if (@rmdir($path)) { $ret = true; } elseif ($FTPOptions['enabled'] == 1) { // Translate path and delete $path = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $path), '/'); // FTP connector throws an error $ret = $ftp->delete($path); } else { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE', $path), Log::WARNING, 'jerror'); $ret = false; } return $ret; } /** * Moves a folder. * * @param string $src The path to the source folder. * @param string $dest The path to the destination folder. * @param string $path An optional base path to prefix to the file names. * @param boolean $useStreams Optionally use streams. * * @return mixed Error message on false or boolean true on success. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::move() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function move($src, $dest, $path = '', $useStreams = false) { $FTPOptions = ClientHelper::getCredentials('ftp'); if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } if (!self::exists($src)) { return Text::_('JLIB_FILESYSTEM_ERROR_FIND_SOURCE_FOLDER'); } if (self::exists($dest)) { return Text::_('JLIB_FILESYSTEM_ERROR_FOLDER_EXISTS'); } if ($useStreams) { $stream = Factory::getStream(); if (!$stream->move($src, $dest)) { return Text::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_RENAME', $stream->getError()); } $ret = true; } else { if ($FTPOptions['enabled'] == 1) { // Connect the FTP client $ftp = FtpClient::getInstance($FTPOptions['host'], $FTPOptions['port'], [], $FTPOptions['user'], $FTPOptions['pass']); // Translate path for the FTP account $src = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $src), '/'); $dest = Path::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/'); // Use FTP rename to simulate move if (!$ftp->rename($src, $dest)) { return Text::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'); } $ret = true; } else { if (!@rename($src, $dest)) { return Text::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'); } $ret = true; } } return $ret; } /** * Wrapper for the standard file_exists function * * @param string $path Folder name relative to installation dir * * @return boolean True if path is a folder * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use is_dir() instead. */ public static function exists($path) { return is_dir(Path::clean($path)); } /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludeFilter Array of filter to exclude * @param boolean $naturalSort False for asort, true for natsort * * @return array|boolean Files in the given folder. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::files() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function files( $path, $filter = '.', $recurse = false, $full = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], $excludeFilter = ['^\..*', '.*~'], $naturalSort = false ) { // Check to make sure the path valid and clean $path = Path::clean($path); // Is the path a folder? if (!is_dir($path)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Compute the excludefilter string if (\count($excludeFilter)) { $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; } else { $excludeFilterString = ''; } // Get the files $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, true); // Sort the files based on either natural or alpha method if ($naturalSort) { natsort($arr); } else { asort($arr); } return array_values($arr); } /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludeFilter Array with regular expressions matching folders which should not be shown in the result. * * @return array Folders in the given folder. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::folders() instead. * The framework class throws Exceptions in case of error which you have to catch. */ public static function folders( $path, $filter = '.', $recurse = false, $full = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], $excludeFilter = ['^\..*'] ) { // Check to make sure the path valid and clean $path = Path::clean($path); // Is the path a folder? if (!is_dir($path)) { Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Compute the excludefilter string if (\count($excludeFilter)) { $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; } else { $excludeFilterString = ''; } // Get the folders $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, false); // Sort the folders asort($arr); return array_values($arr); } /** * Function to read the files/folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param string $excludeFilterString Regexp of files to exclude * @param boolean $findFiles True to read the files, false to read the folders * * @return array Files. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::_items() instead. */ protected static function _items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, $findFiles) { if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } $arr = []; // Read the source directory if (!($handle = @opendir($path))) { return $arr; } while (($file = readdir($handle)) !== false) { if ( $file != '.' && $file != '..' && !\in_array($file, $exclude) && (empty($excludeFilterString) || !preg_match($excludeFilterString, $file)) ) { // Compute the fullpath $fullpath = $path . '/' . $file; // Compute the isDir flag $isDir = is_dir($fullpath); if (($isDir xor $findFiles) && preg_match("/$filter/", $file)) { // (fullpath is dir and folders are searched or fullpath is not dir and files are searched) and file matches the filter if ($full) { // Full path is requested $arr[] = $fullpath; } else { // Filename is requested $arr[] = $file; } } if ($isDir && $recurse) { // Search recursively if (\is_int($recurse)) { // Until depth 0 is reached $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse - 1, $full, $exclude, $excludeFilterString, $findFiles)); } else { $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse, $full, $exclude, $excludeFilterString, $findFiles)); } } } } closedir($handle); return $arr; } /** * Lists folder in format suitable for tree display. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param integer $maxLevel The maximum number of levels to recursively read, defaults to three. * @param integer $level The current level, optional. * @param integer $parent Unique identifier of the parent folder, if any. * * @return array Folders in the given folder. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::listFolderTree() instead. */ public static function listFolderTree($path, $filter, $maxLevel = 3, $level = 0, $parent = 0) { $dirs = []; if ($level == 0) { $GLOBALS['_JFolder_folder_tree_index'] = 0; } if ($level < $maxLevel) { $folders = self::folders($path, $filter); // First path, index foldernames foreach ($folders as $name) { $id = ++$GLOBALS['_JFolder_folder_tree_index']; $fullName = Path::clean($path . '/' . $name); $dirs[] = [ 'id' => $id, 'parent' => $parent, 'name' => $name, 'fullname' => $fullName, 'relname' => str_replace(JPATH_ROOT, '', $fullName), ]; $dirs2 = self::listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id); $dirs = array_merge($dirs, $dirs2); } } return $dirs; } /** * Makes path name safe to use. * * @param string $path The full path to sanitise. * * @return string The sanitised string. * * @since 1.7.0 * @deprecated 4.4 will be removed in 6.0 * Use Joomla\Filesystem\Folder::makeSafe() instead. */ public static function makeSafe($path) { $regex = ['#[^A-Za-z0-9_\\\/\(\)\[\]\{\}\#\$\^\+\.\'~`!@&=;,-]#']; return preg_replace($regex, '', $path); } } PK1A#]�G��Jsrc/Filesystem/Meta/language/en-GB/en-GB.lib_joomla_filesystem_patcher.ininu�[���; Joomla! Project ; (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> ; License GNU General Public License version 2 or later; see LICENSE.txt ; Note : All ini files need to be saved as UTF-8 JLIB_FILESYSTEM_PATCHER_FAILED_VERIFY="Failed source verification of file %s at line %d" JLIB_FILESYSTEM_PATCHER_INVALID_DIFF="Invalid unified diff block" JLIB_FILESYSTEM_PATCHER_INVALID_INPUT="Invalid input" JLIB_FILESYSTEM_PATCHER_UNEXISTING_SOURCE="Unexisting source file" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_ADD_LINE="Unexpected add line at line %d'" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_EOF="Unexpected end of file" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_REMOVE_LINE="Unexpected remove line at line %d" PK1A#]P�����src/Error/AbstractRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\FactoryInterface; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for error page renderers * * @since 4.0.0 */ abstract class AbstractRenderer implements RendererInterface { /** * The Document instance * * @var Document * @since 4.0.0 */ protected $document; /** * The format (type) of the error page * * @var string * @since 4.0.0 */ protected $type; /** * Retrieve the Document instance attached to this renderer * * @return Document * * @since 4.0.0 */ public function getDocument(): Document { // Load the document if not already if (!$this->document) { $this->document = $this->loadDocument(); } return $this->document; } /** * Get a renderer instance for the given type * * @param string $type The type of renderer to fetch * * @return static * * @since 4.0.0 * @throws \InvalidArgumentException */ public static function getRenderer(string $type) { // Build the class name $class = __NAMESPACE__ . '\\Renderer\\' . ucfirst(strtolower($type)) . 'Renderer'; // First check if an object may exist in the container and prefer that over everything else if (Factory::getContainer()->has($class)) { return Factory::getContainer()->get($class); } // Next check if a local class exists and use that if (class_exists($class)) { return new $class(); } // 404 Resource Not Found throw new \InvalidArgumentException(sprintf('There is not an error renderer for the "%s" format.', $type)); } /** * Create the Document object for this renderer * * @return Document * * @since 4.0.0 */ protected function loadDocument(): Document { $attributes = [ 'charset' => 'utf-8', 'lineend' => 'unix', 'tab' => "\t", 'language' => 'en-GB', 'direction' => 'ltr', ]; // If there is a Language instance in Factory then let's pull the language and direction from its metadata if (Factory::$language) { $attributes['language'] = Factory::getLanguage()->getTag(); $attributes['direction'] = Factory::getLanguage()->isRtl() ? 'rtl' : 'ltr'; } return Factory::getContainer()->get(FactoryInterface::class)->createDocument($this->type, $attributes); } } PK1A#]+;���src/Error/RendererInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error; use Joomla\CMS\Document\Document; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining the rendering engine for the error handling layer * * @since 4.0.0 */ interface RendererInterface { /** * Retrieve the Document instance attached to this renderer * * @return Document * * @since 4.0.0 */ public function getDocument(): Document; /** * Render the error page for the given object * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string; } PK1A#]`< �PP6src/Error/JsonApi/InvalidParameterExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for invalid param * * @since 4.0.0 */ class InvalidParameterExceptionHandler extends \Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler { /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 400; $error = ['title' => $e->getMessage()]; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]�W;>>:src/Error/JsonApi/AuthenticationFailedExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\Access\Exception\AuthenticationFailed; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for permission errors that should give a 401 * * @since 4.0.0 */ class AuthenticationFailedExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof AuthenticationFailed; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 401; $error = ['title' => 'Forbidden']; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]��@@2src/Error/JsonApi/InvalidRouteExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\Router\Exception\RouteNotFoundException; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for routing errors that should give a 404 * * @since 4.0.0 */ class InvalidRouteExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof RouteNotFoundException; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 404; $error = ['title' => 'Resource not found']; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]mr//*src/Error/JsonApi/SaveExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\MVC\Controller\Exception\Save; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for invalid checkin/checkout exceptions * * @since 4.0.0 */ class SaveExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof Save; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 400; if ($e->getCode()) { $status = $e->getCode(); } $error = [ 'title' => $e->getMessage(), 'code' => $status, ]; return new ResponseBag($status, [$error]); } } PK1A#]O`V��/src/Error/JsonApi/SendEmailExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\MVC\Controller\Exception\SendEmail; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for error when send email * * @since 4.0.0 */ class SendEmailExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof SendEmail; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 400; if ($e->getCode()) { $status = $e->getCode(); } $error = ['title' => $e->getMessage()]; return new ResponseBag($status, [$error]); } } PK1A#]|�Q$$0src/Error/JsonApi/NotAllowedExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\Access\Exception\NotAllowed; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for permission errors that should give a 403 * * @since 4.0.0 */ class NotAllowedExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof NotAllowed; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 403; $error = ['title' => 'Access Denied']; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]0{YB5src/Error/JsonApi/CheckinCheckoutExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\MVC\Controller\Exception\CheckinCheckout; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for invalid checkin/checkout exceptions * * @since 4.0.0 */ class CheckinCheckoutExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof CheckinCheckout; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 400; if ($e->getCode()) { $status = $e->getCode(); } $error = ['title' => $e->getMessage()]; return new ResponseBag($status, [$error]); } } PK1A#]���X003src/Error/JsonApi/NotAcceptableExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\Application\Exception\NotAcceptable; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for routing errors that should give a 406 * * @since 4.0.0 */ class NotAcceptableExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof NotAcceptable; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 406; $error = ['title' => 'Not Acceptable']; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]��CC6src/Error/JsonApi/ResourceNotFoundExceptionHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\JsonApi; use Exception; use Joomla\CMS\MVC\Controller\Exception\ResourceNotFound; use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Handler for invalid resource requests that should give a 404 * * @since 4.0.0 */ class ResourceNotFoundExceptionHandler implements ExceptionHandlerInterface { /** * If the exception handler is able to format a response for the provided exception, * then the implementation should return true. * * @param \Exception $e The exception to be handled * * @return boolean * * @since 4.0.0 */ public function manages(\Exception $e) { return $e instanceof ResourceNotFound; } /** * Handle the provided exception. * * @param \Exception $e The exception being handled * * @return \Tobscure\JsonApi\Exception\Handler\ResponseBag * * @since 4.0.0 */ public function handle(\Exception $e) { $status = 404; $error = ['title' => 'Resource not found']; $code = $e->getCode(); if ($code) { $error['code'] = $code; } return new ResponseBag($status, [$error]); } } PK1A#]� �yMM"src/Error/Renderer/CliRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; use Joomla\CMS\Error\AbstractRenderer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Cli error renderer * * @since 4.0.0 */ class CliRenderer extends AbstractRenderer { /** * The format (type) * * @var string * @since 4.0.0 */ protected $type = 'cli'; /** * Render the error for the given object. * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string { $buffer = PHP_EOL . 'Error occurred: ' . $error->getMessage() . PHP_EOL . $this->getTrace($error); if ($prev = $error->getPrevious()) { $buffer .= PHP_EOL . PHP_EOL . 'Previous Exception: ' . $prev->getMessage() . PHP_EOL . $this->getTrace($prev); } return $buffer; } /** * Returns a trace for the given error. * * @param \Throwable $error The error * * @return string * * @since 4.0.0 */ private function getTrace(\Throwable $error): string { // Include the stack trace only if in debug mode if (!JDEBUG) { return ''; } return PHP_EOL . $error->getTraceAsString() . PHP_EOL; } } PK1A#]7� ���#src/Error/Renderer/FeedRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RSS/Atom feed error page renderer * * @since 4.0.0 */ class FeedRenderer extends XmlRenderer { } PK1A#]�Ʋrgg&src/Error/Renderer/JsonapiRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; use Joomla\Application\WebApplicationInterface; use Joomla\CMS\Error\JsonApi\AuthenticationFailedExceptionHandler; use Joomla\CMS\Error\JsonApi\CheckinCheckoutExceptionHandler; use Joomla\CMS\Error\JsonApi\InvalidParameterExceptionHandler; use Joomla\CMS\Error\JsonApi\InvalidRouteExceptionHandler; use Joomla\CMS\Error\JsonApi\NotAcceptableExceptionHandler; use Joomla\CMS\Error\JsonApi\NotAllowedExceptionHandler; use Joomla\CMS\Error\JsonApi\ResourceNotFoundExceptionHandler; use Joomla\CMS\Error\JsonApi\SaveExceptionHandler; use Joomla\CMS\Error\JsonApi\SendEmailExceptionHandler; use Joomla\CMS\Factory; use Tobscure\JsonApi\ErrorHandler; use Tobscure\JsonApi\Exception\Handler\FallbackExceptionHandler; use Tobscure\JsonApi\Exception\Handler\ResponseBag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JSON error page renderer * * @since 4.0.0 */ class JsonapiRenderer extends JsonRenderer { /** * The format (type) of the error page * * @var string * @since 4.0.0 */ protected $type = 'jsonapi'; /** * Render the error page for the given object * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string { if ($error instanceof \Exception) { $errors = new ErrorHandler(); $errors->registerHandler(new InvalidRouteExceptionHandler()); $errors->registerHandler(new AuthenticationFailedExceptionHandler()); $errors->registerHandler(new NotAcceptableExceptionHandler()); $errors->registerHandler(new NotAllowedExceptionHandler()); $errors->registerHandler(new InvalidParameterExceptionHandler()); $errors->registerHandler(new ResourceNotFoundExceptionHandler()); $errors->registerHandler(new SaveExceptionHandler()); $errors->registerHandler(new CheckinCheckoutExceptionHandler()); $errors->registerHandler(new SendEmailExceptionHandler()); $errors->registerHandler(new FallbackExceptionHandler(JDEBUG)); $response = $errors->handle($error); } else { $code = 500; $errorInfo = ['code' => $code, 'title' => 'Internal server error']; if (JDEBUG) { $errorInfo['detail'] = (string) $error; } $response = new ResponseBag($code, $errorInfo); } $this->getDocument()->setErrors($response->getErrors()); $app = Factory::getApplication(); if ($app instanceof WebApplicationInterface) { $app->setHeader('status', $response->getStatus()); } if (ob_get_contents()) { ob_end_clean(); } return $this->getDocument()->render(); } } PK1A#]CtO��"src/Error/Renderer/XmlRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; use Joomla\CMS\Error\AbstractRenderer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * XML error page renderer * * @since 4.0.0 */ class XmlRenderer extends AbstractRenderer { /** * The format (type) of the error page * * @var string * @since 4.0.0 */ protected $type = 'xml'; /** * Render the error page for the given object * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string { // Create our data object to be rendered $xw = new \XMLWriter(); $xw->openMemory(); $xw->setIndent(true); $xw->setIndentString("\t"); $xw->startDocument('1.0', 'UTF-8'); $xw->startElement('error'); $xw->writeElement('code', $error->getCode()); $xw->writeElement('message', $error->getMessage()); // Include the stack trace if in debug mode if (JDEBUG) { $xw->writeElement('trace', $error->getTraceAsString()); } // End error element $xw->endElement(); // Push the data object into the document $this->getDocument()->setBuffer($xw->outputMemory(true)); if (ob_get_contents()) { ob_end_clean(); } return $this->getDocument()->render(); } } PK1A#]|A�==#src/Error/Renderer/JsonRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; use Joomla\Application\WebApplicationInterface; use Joomla\CMS\Error\AbstractRenderer; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JSON error page renderer * * @since 4.0.0 */ class JsonRenderer extends AbstractRenderer { /** * The format (type) of the error page * * @var string * @since 4.0.0 */ protected $type = 'json'; /** * Render the error page for the given object * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string { // Create our data object to be rendered $data = [ 'error' => true, 'code' => $error->getCode(), 'message' => $error->getMessage(), ]; // Include the stack trace if in debug mode if (JDEBUG) { $data['trace'] = $error->getTraceAsString(); } $app = Factory::getApplication(); if ($app instanceof WebApplicationInterface) { $errorCode = 500; if ($error->getCode() > 0) { $errorCode = $error->getCode(); } $app->setHeader('status', $errorCode); } // Push the data object into the document $this->getDocument()->setBuffer(json_encode($data)); if (ob_get_contents()) { ob_end_clean(); } return $this->getDocument()->render(); } } PK1A#]����#src/Error/Renderer/HtmlRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Error\Renderer; use Joomla\CMS\Error\AbstractRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML error page renderer * * @since 4.0.0 * @todo Change this renderer to use JDocumentHtml instead of JDocumentError, the latter is only used for B/C at this time */ class HtmlRenderer extends AbstractRenderer { /** * The format (type) of the error page * * @var string * @since 4.0.0 */ protected $type = 'error'; /** * Render the error page for the given object * * @param \Throwable $error The error object to be rendered * * @return string * * @since 4.0.0 */ public function render(\Throwable $error): string { $app = Factory::getApplication(); // Get the current template from the application $template = $app->getTemplate(true); // Push the error object into the document $this->getDocument()->setError($error); // Add registry file for the template asset $wa = $this->getDocument()->getWebAssetManager()->getRegistry(); $wa->addTemplateRegistryFile($template->template, $app->getClientId()); if (!empty($template->parent)) { $wa->addTemplateRegistryFile($template->parent, $app->getClientId()); } if (ob_get_contents()) { ob_end_clean(); } $this->getDocument()->setTitle(Text::_('Error') . ': ' . $error->getCode()); return $this->getDocument()->render( false, [ 'template' => $template->template, 'directory' => JPATH_THEMES, 'debug' => JDEBUG, 'csp_nonce' => $app->get('csp_nonce'), 'templateInherits' => $template->parent, 'params' => $template->params, ] ); } } PK1A#]�u�q��src/Service/Provider/Logger.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Log\Log; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Psr\Log\LoggerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's PSR-3 logger dependency * * @since 4.0.0 */ class Logger implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('logger', LoggerInterface::class) ->share( LoggerInterface::class, function (Container $container) { return Log::createDelegatedLogger(); }, true ); } } PK1A#]����� src/Service/Provider/Pathway.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Pathway\SitePathway; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's pathway dependency * * @since 4.0.0 */ class Pathway implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('SitePathway', SitePathway::class) ->alias('JPathwaySite', SitePathway::class) ->alias('pathway.site', SitePathway::class) ->share( SitePathway::class, function (Container $container) { return new SitePathway($container->get(SiteApplication::class)); }, true ); $container->alias('Pathway', \Joomla\CMS\Pathway\Pathway::class) ->alias('JPathway', \Joomla\CMS\Pathway\Pathway::class) ->alias('pathway', \Joomla\CMS\Pathway\Pathway::class) ->share( \Joomla\CMS\Pathway\Pathway::class, function (Container $container) { return new \Joomla\CMS\Pathway\Pathway(); }, true ); } } PK1A#]ɭ�2�� src/Service/Provider/Toolbar.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Toolbar\ContainerAwareToolbarFactory; use Joomla\CMS\Toolbar\ToolbarFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's toolbar dependency * * @since 4.0.0 */ class Toolbar implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('toolbar.factory', ToolbarFactoryInterface::class) ->alias(ContainerAwareToolbarFactory::class, ToolbarFactoryInterface::class) ->share( ToolbarFactoryInterface::class, function (Container $container) { $factory = new ContainerAwareToolbarFactory(); $factory->setContainer($container); return $factory; }, true ); } } PK1A#]v�D;�2�2 src/Service/Provider/Session.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Application\AdministratorApplication; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\CMS\Installation\Application\InstallationApplication; use Joomla\CMS\Session\EventListener\MetadataManagerListener; use Joomla\CMS\Session\MetadataManager; use Joomla\CMS\Session\SessionFactory; use Joomla\CMS\Session\SessionManager; use Joomla\CMS\Session\Storage\JoomlaStorage; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\Exception\DependencyResolutionException; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use Joomla\Event\LazyServiceEventListener; use Joomla\Event\Priority; use Joomla\Registry\Registry; use Joomla\Session\HandlerInterface; use Joomla\Session\SessionEvents; use Joomla\Session\SessionInterface; use Joomla\Session\Storage\RuntimeStorage; use Joomla\Session\StorageInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's session dependency * * @since 4.0.0 */ class Session implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->share( 'session.web.administrator', function (Container $container) { /** @var Registry $config */ $config = $container->get('config'); $app = Factory::getApplication(); // Generate a session name. $name = ApplicationHelper::getHash($config->get('session_name', AdministratorApplication::class)); // Calculate the session lifetime. $lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900; // Initialize the options for the Session object. $options = [ 'name' => $name, 'expire' => $lifetime, ]; if ($config->get('force_ssl') >= 1) { $options['force_ssl'] = true; } $handler = $container->get('session.factory')->createSessionHandler($options); if (!$container->has('session.handler')) { $this->registerSessionHandlerAsService($container, $handler); } return $this->buildSession( new JoomlaStorage($app->getInput(), $handler, $options), $app, $container->get(DispatcherInterface::class), $options ); }, true ); $container->share( 'session.web.installation', function (Container $container) { /** @var Registry $config */ $config = $container->get('config'); $app = Factory::getApplication(); /** * Session handler for the session is always filesystem so it doesn't flip to the database after * configuration.php has been written to */ $config->set('session_handler', 'filesystem'); /** * Generate a session name - unlike all the other apps we don't have either a secret or a session name * (that's not the app name) until we complete installation which then leads to us dropping things like * language preferences after installation as the app refreshes. */ $name = md5(serialize(JPATH_ROOT . InstallationApplication::class)); // Calculate the session lifetime. $lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900; // Initialize the options for the Session object. $options = [ 'name' => $name, 'expire' => $lifetime, ]; $handler = $container->get('session.factory')->createSessionHandler($options); if (!$container->has('session.handler')) { $this->registerSessionHandlerAsService($container, $handler); } return $this->buildSession( new JoomlaStorage($app->getInput(), $handler), $app, $container->get(DispatcherInterface::class), $options ); }, true ); $container->share( 'session.web.site', function (Container $container) { /** @var Registry $config */ $config = $container->get('config'); $app = Factory::getApplication(); // Generate a session name. $name = ApplicationHelper::getHash($config->get('session_name', SiteApplication::class)); // Calculate the session lifetime. $lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900; // Initialize the options for the Session object. $options = [ 'name' => $name, 'expire' => $lifetime, ]; if ($config->get('force_ssl') == 2) { $options['force_ssl'] = true; } $handler = $container->get('session.factory')->createSessionHandler($options); if (!$container->has('session.handler')) { $this->registerSessionHandlerAsService($container, $handler); } return $this->buildSession( new JoomlaStorage($app->getInput(), $handler, $options), $app, $container->get(DispatcherInterface::class), $options ); }, true ); $container->share( 'session.cli', function (Container $container) { /** @var Registry $config */ $config = $container->get('config'); $app = Factory::getApplication(); // Generate a session name. $name = ApplicationHelper::getHash($config->get('session_name', ConsoleApplication::class)); // Calculate the session lifetime. $lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900; // Initialize the options for the Session object. $options = [ 'name' => $name, 'expire' => $lifetime, ]; // Unlike the web apps, we will only toggle the force SSL setting based on it being enabled and not based on client if ($config->get('force_ssl') >= 1) { $options['force_ssl'] = true; } $handler = $container->get('session.factory')->createSessionHandler($options); if (!$container->has('session.handler')) { $this->registerSessionHandlerAsService($container, $handler); } return $this->buildSession( new RuntimeStorage(), $app, $container->get(DispatcherInterface::class), $options ); }, true ); $container->alias(SessionFactory::class, 'session.factory') ->share( 'session.factory', function (Container $container) { $factory = new SessionFactory(); $factory->setContainer($container); return $factory; }, true ); $container->alias(SessionManager::class, 'session.manager') ->share( 'session.manager', function (Container $container) { if (!$container->has('session.handler')) { throw new DependencyResolutionException( 'The "session.handler" service has not been created, make sure you have created the "session" service first.' ); } return new SessionManager($container->get('session.handler')); }, true ); $container->alias(MetadataManager::class, 'session.metadata_manager') ->share( 'session.metadata_manager', function (Container $container) { /* * Normally we should inject the application as a dependency via $container->get() however there is not * a 'app' or CMSApplicationInterface::class key for the primary application of the request so we need to * rely on the application having been injected to the global Factory otherwise we cannot build the service */ if (!Factory::$application) { throw new DependencyResolutionException( sprintf( 'Creating the "session.metadata_manager" service requires %s::$application be initialised.', Factory::class ) ); } return new MetadataManager(Factory::$application, $container->get(DatabaseInterface::class)); }, true ); $container->alias(MetadataManagerListener::class, 'session.event_listener.metadata_manager') ->share( 'session.event_listener.metadata_manager', function (Container $container) { return new MetadataManagerListener($container->get(MetadataManager::class), $container->get('config')); }, true ); $listener = new LazyServiceEventListener($container, 'session.event_listener.metadata_manager', 'onAfterSessionStart'); /** @var DispatcherInterface $dispatcher */ $dispatcher = $container->get(DispatcherInterface::class); $dispatcher->addListener(SessionEvents::START, $listener); } /** * Build the root session service * * @param StorageInterface $storage The session storage engine. * @param CMSApplicationInterface $app The application instance. * @param DispatcherInterface $dispatcher The event dispatcher. * @param array $options The configured session options. * * @return SessionInterface * * @since 4.0.0 */ private function buildSession( StorageInterface $storage, CMSApplicationInterface $app, DispatcherInterface $dispatcher, array $options ): SessionInterface { $input = $app->getInput(); if (method_exists($app, 'afterSessionStart')) { $dispatcher->addListener(SessionEvents::START, [$app, 'afterSessionStart'], Priority::HIGH); } $session = new \Joomla\CMS\Session\Session($storage, $dispatcher, $options); return $session; } /** * Registers the session handler as a service * * @param Container $container The container to register the service to. * @param \SessionHandlerInterface $sessionHandler The session handler. * * @return void * * @since 4.0.0 */ private function registerSessionHandlerAsService(Container $container, \SessionHandlerInterface $sessionHandler): void { // Alias the session handler to the core SessionHandlerInterface for improved autowiring and discoverability $container->alias(\SessionHandlerInterface::class, 'session.handler') ->share( 'session.handler', $sessionHandler, true ); // If the session handler implements the extended interface, register an alias for that as well if ($sessionHandler instanceof HandlerInterface) { $container->alias(HandlerInterface::class, 'session.handler'); } } } PK1A#]����%src/Service/Provider/HTMLRegistry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\HTML\Registry; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the HTML service registry * * @since 4.0.0 */ class HTMLRegistry implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->share( Registry::class, function (Container $container) { return new Registry(); }, true ); } } PK1A#]�f�1��src/Service/Provider/Config.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's config dependency * * @since 4.0.0 */ class Config implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('config', 'JConfig') ->share( 'JConfig', function (Container $container) { if (!is_file(JPATH_CONFIGURATION . '/configuration.php')) { return new Registry(); } \JLoader::register('JConfig', JPATH_CONFIGURATION . '/configuration.php'); if (!class_exists('JConfig')) { throw new \RuntimeException('Configuration class does not exist.'); } return new Registry(new \JConfig()); }, true ); } } PK1A#]:M�!!!src/Service/Provider/Language.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Language\CachingLanguageFactory; use Joomla\CMS\Language\LanguageFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the language dependency * * @since 4.0.0 */ class Language implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('language.factory', LanguageFactoryInterface::class) ->alias(CachingLanguageFactory::class, LanguageFactoryInterface::class) ->share( LanguageFactoryInterface::class, function (Container $container) { return new CachingLanguageFactory(); }, true ); } } PK1A#]l3,�,,src/Service/Provider/User.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\User\UserFactory; use Joomla\CMS\User\UserFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the user dependency * * @since 4.0.0 */ class User implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('user.factory', UserFactoryInterface::class) ->alias(UserFactory::class, UserFactoryInterface::class) ->share( UserFactoryInterface::class, function (Container $container) { return new UserFactory($container->get(DatabaseInterface::class)); }, true ); } } PK1A#]TO�g77#src/Service/Provider/Dispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\Dispatcher as EventDispatcher; use Joomla\Event\DispatcherInterface as EventDispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's event dispatcher dependency * * @since 4.0.0 */ class Dispatcher implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('dispatcher', EventDispatcherInterface::class) ->alias(EventDispatcher::class, EventDispatcherInterface::class) ->share( EventDispatcherInterface::class, function (Container $container) { return new EventDispatcher(); }, true ); } } PK1A#]��jl�%�%$src/Service/Provider/Application.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Application\AdministratorApplication; use Joomla\CMS\Application\ApiApplication; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Console\CheckJoomlaUpdatesCommand; use Joomla\CMS\Console\ExtensionDiscoverCommand; use Joomla\CMS\Console\ExtensionDiscoverInstallCommand; use Joomla\CMS\Console\ExtensionDiscoverListCommand; use Joomla\CMS\Console\ExtensionInstallCommand; use Joomla\CMS\Console\ExtensionRemoveCommand; use Joomla\CMS\Console\ExtensionsListCommand; use Joomla\CMS\Console\FinderIndexCommand; use Joomla\CMS\Console\GetConfigurationCommand; use Joomla\CMS\Console\Loader\WritableContainerLoader; use Joomla\CMS\Console\Loader\WritableLoaderInterface; use Joomla\CMS\Console\SessionGcCommand; use Joomla\CMS\Console\SessionMetadataGcCommand; use Joomla\CMS\Console\SetConfigurationCommand; use Joomla\CMS\Console\SiteDownCommand; use Joomla\CMS\Console\SiteUpCommand; use Joomla\CMS\Console\TasksListCommand; use Joomla\CMS\Console\TasksRunCommand; use Joomla\CMS\Console\TasksStateCommand; use Joomla\CMS\Console\UpdateCoreCommand; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageFactoryInterface; use Joomla\CMS\Menu\MenuFactoryInterface; use Joomla\CMS\User\UserFactoryInterface; use Joomla\Console\Application as BaseConsoleApplication; use Joomla\Console\Loader\LoaderInterface; use Joomla\Database\Command\ExportCommand; use Joomla\Database\Command\ImportCommand; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use Joomla\Session\SessionInterface; use Psr\Log\LoggerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Application service provider * * @since 4.0.0 */ class Application implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias(AdministratorApplication::class, 'JApplicationAdministrator') ->share( 'JApplicationAdministrator', function (Container $container) { $app = new AdministratorApplication(null, $container->get('config'), null, $container); // The session service provider needs Factory::$application, set it if still null if (Factory::$application === null) { Factory::$application = $app; } $app->setDispatcher($container->get(DispatcherInterface::class)); $app->setLogger($container->get(LoggerInterface::class)); $app->setSession($container->get(SessionInterface::class)); $app->setUserFactory($container->get(UserFactoryInterface::class)); $app->setMenuFactory($container->get(MenuFactoryInterface::class)); return $app; }, true ); $container->alias(SiteApplication::class, 'JApplicationSite') ->share( 'JApplicationSite', function (Container $container) { $app = new SiteApplication(null, $container->get('config'), null, $container); // The session service provider needs Factory::$application, set it if still null if (Factory::$application === null) { Factory::$application = $app; } $app->setDispatcher($container->get(DispatcherInterface::class)); $app->setLogger($container->get(LoggerInterface::class)); $app->setSession($container->get(SessionInterface::class)); $app->setUserFactory($container->get(UserFactoryInterface::class)); $app->setCacheControllerFactory($container->get(CacheControllerFactoryInterface::class)); $app->setMenuFactory($container->get(MenuFactoryInterface::class)); return $app; }, true ); $container->alias(ConsoleApplication::class, BaseConsoleApplication::class) ->share( BaseConsoleApplication::class, function (Container $container) { $dispatcher = $container->get(DispatcherInterface::class); // Console uses the default system language $config = $container->get('config'); $locale = $config->get('language'); $debug = $config->get('debug_lang'); $lang = $container->get(LanguageFactoryInterface::class)->createLanguage($locale, $debug); $app = new ConsoleApplication($config, $dispatcher, $container, $lang); // The session service provider needs Factory::$application, set it if still null if (Factory::$application === null) { Factory::$application = $app; } $app->setCommandLoader($container->get(LoaderInterface::class)); $app->setLogger($container->get(LoggerInterface::class)); $app->setSession($container->get(SessionInterface::class)); $app->setUserFactory($container->get(UserFactoryInterface::class)); $app->setDatabase($container->get(DatabaseInterface::class)); return $app; }, true ); $container->alias(WritableContainerLoader::class, LoaderInterface::class) ->alias(WritableLoaderInterface::class, LoaderInterface::class) ->share( LoaderInterface::class, function (Container $container) { $mapping = [ SessionGcCommand::getDefaultName() => SessionGcCommand::class, SessionMetadataGcCommand::getDefaultName() => SessionMetadataGcCommand::class, ExportCommand::getDefaultName() => ExportCommand::class, ImportCommand::getDefaultName() => ImportCommand::class, SiteDownCommand::getDefaultName() => SiteDownCommand::class, SiteUpCommand::getDefaultName() => SiteUpCommand::class, SetConfigurationCommand::getDefaultName() => SetConfigurationCommand::class, GetConfigurationCommand::getDefaultName() => GetConfigurationCommand::class, ExtensionsListCommand::getDefaultName() => ExtensionsListCommand::class, CheckJoomlaUpdatesCommand::getDefaultName() => CheckJoomlaUpdatesCommand::class, ExtensionRemoveCommand::getDefaultName() => ExtensionRemoveCommand::class, ExtensionInstallCommand::getDefaultName() => ExtensionInstallCommand::class, ExtensionDiscoverCommand::getDefaultName() => ExtensionDiscoverCommand::class, ExtensionDiscoverInstallCommand::getDefaultName() => ExtensionDiscoverInstallCommand::class, ExtensionDiscoverListCommand::getDefaultName() => ExtensionDiscoverListCommand::class, UpdateCoreCommand::getDefaultName() => UpdateCoreCommand::class, FinderIndexCommand::getDefaultName() => FinderIndexCommand::class, TasksListCommand::getDefaultName() => TasksListCommand::class, TasksRunCommand::getDefaultName() => TasksRunCommand::class, TasksStateCommand::getDefaultName() => TasksStateCommand::class, ]; return new WritableContainerLoader($container, $mapping); }, true ); $container->alias(ApiApplication::class, 'JApplicationApi') ->share( 'JApplicationApi', function (Container $container) { $app = new ApiApplication(null, $container->get('config'), null, $container); // The session service provider needs Factory::$application, set it if still null if (Factory::$application === null) { Factory::$application = $app; } $app->setDispatcher($container->get('Joomla\Event\DispatcherInterface')); $app->setLogger($container->get(LoggerInterface::class)); $app->setSession($container->get('Joomla\Session\SessionInterface')); $app->setMenuFactory($container->get(MenuFactoryInterface::class)); return $app; }, true ); } } PK1A#]@Z��src/Service/Provider/Router.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Application\ApiApplication; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Router\AdministratorRouter; use Joomla\CMS\Router\ApiRouter; use Joomla\CMS\Router\SiteRouter; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's API router dependency * * @since 4.0.0 */ class Router implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('SiteRouter', SiteRouter::class) ->alias('JRouterSite', SiteRouter::class) ->share( SiteRouter::class, function (Container $container) { return new SiteRouter($container->get(SiteApplication::class)); }, true ); $container->alias('AdministratorRouter', AdministratorRouter::class) ->alias('JRouterAdministrator', AdministratorRouter::class) ->share( AdministratorRouter::class, function (Container $container) { return new AdministratorRouter(); }, true ); $container->alias('ApiRouter', ApiRouter::class) ->share( ApiRouter::class, function (Container $container) { return new ApiRouter($container->get(ApiApplication::class)); }, true ); } } PK1A#]Iplccsrc/Service/Provider/Mailer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Mail\MailerFactory; use Joomla\CMS\Mail\MailerFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the mailer dependency * * @since 4.4.0 */ class Mailer implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.4.0 */ public function register(Container $container) { $container->share( MailerFactoryInterface::class, function (Container $container) { return new MailerFactory($container->get('config')); }, true ); } } PK1A#]�$;�NN(src/Service/Provider/CacheController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Cache\CacheControllerFactory; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the cache controller dependency * * @since 4.0.0 */ class CacheController implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('cache.controller.factory', CacheControllerFactoryInterface::class) ->alias(CacheControllerFactory::class, CacheControllerFactoryInterface::class) ->share( CacheControllerFactoryInterface::class, function (Container $container) { return new CacheControllerFactory(); }, true ); } } PK1A#]F� ��src/Service/Provider/Form.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Form\FormFactory; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the form dependency * * @since 4.0.0 */ class Form implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('form.factory', FormFactoryInterface::class) ->alias(FormFactory::class, FormFactoryInterface::class) ->share( FormFactoryInterface::class, function (Container $container) { $factory = new FormFactory(); $factory->setDatabase($container->get(DatabaseInterface::class)); return $factory; }, true ); } } PK1A#]ta� ^^ src/Service/Provider/Console.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Console\CheckJoomlaUpdatesCommand; use Joomla\CMS\Console\ExtensionDiscoverCommand; use Joomla\CMS\Console\ExtensionDiscoverInstallCommand; use Joomla\CMS\Console\ExtensionDiscoverListCommand; use Joomla\CMS\Console\ExtensionInstallCommand; use Joomla\CMS\Console\ExtensionRemoveCommand; use Joomla\CMS\Console\ExtensionsListCommand; use Joomla\CMS\Console\FinderIndexCommand; use Joomla\CMS\Console\GetConfigurationCommand; use Joomla\CMS\Console\SessionGcCommand; use Joomla\CMS\Console\SessionMetadataGcCommand; use Joomla\CMS\Console\SetConfigurationCommand; use Joomla\CMS\Console\SiteDownCommand; use Joomla\CMS\Console\SiteUpCommand; use Joomla\CMS\Console\TasksListCommand; use Joomla\CMS\Console\TasksRunCommand; use Joomla\CMS\Console\TasksStateCommand; use Joomla\CMS\Console\UpdateCoreCommand; use Joomla\CMS\Language\LanguageFactoryInterface; use Joomla\CMS\Session\MetadataManager; use Joomla\Database\Command\ExportCommand; use Joomla\Database\Command\ImportCommand; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's console services * * @since 4.0.0 */ class Console implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->share( SessionGcCommand::class, function (Container $container) { /* * The command will need the same session handler that web apps use to run correctly, * since this is based on an option we need to inject the container */ $command = new SessionGcCommand(); $command->setContainer($container); return $command; }, true ); $container->share( SessionMetadataGcCommand::class, function (Container $container) { return new SessionMetadataGcCommand($container->get('session'), $container->get(MetadataManager::class)); }, true ); $container->share( ExportCommand::class, function (Container $container) { return new ExportCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( ImportCommand::class, function (Container $container) { return new ImportCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( SiteDownCommand::class, function (Container $container) { return new SiteDownCommand(); }, true ); $container->share( SiteUpCommand::class, function (Container $container) { return new SiteUpCommand(); }, true ); $container->share( SetConfigurationCommand::class, function (Container $container) { return new SetConfigurationCommand(); }, true ); $container->share( GetConfigurationCommand::class, function (Container $container) { return new GetConfigurationCommand(); }, true ); $container->share( ExtensionsListCommand::class, function (Container $container) { return new ExtensionsListCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( CheckJoomlaUpdatesCommand::class, function (Container $container) { return new CheckJoomlaUpdatesCommand(); }, true ); $container->share( ExtensionRemoveCommand::class, function (Container $container) { return new ExtensionRemoveCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( ExtensionInstallCommand::class, function (Container $container) { return new ExtensionInstallCommand(); }, true ); $container->share( ExtensionDiscoverCommand::class, function (Container $container) { return new ExtensionDiscoverCommand(); }, true ); $container->share( ExtensionDiscoverInstallCommand::class, function (Container $container) { return new ExtensionDiscoverInstallCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( ExtensionDiscoverListCommand::class, function (Container $container) { return new ExtensionDiscoverListCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( UpdateCoreCommand::class, function (Container $container) { return new UpdateCoreCommand($container->get(DatabaseInterface::class)); }, true ); $container->share( FinderIndexCommand::class, function (Container $container) { $command = new FinderIndexCommand($container->get(DatabaseInterface::class)); $command->setLanguage($container->get(LanguageFactoryInterface::class)->createLanguage( $container->get('config')->get('language'), $container->get('config')->get('debug_lang') )); return $command; }, true ); $container->share( TasksListCommand::class, function (Container $container) { return new TasksListCommand(); }, true ); $container->share( TasksRunCommand::class, function (Container $container) { return new TasksRunCommand(); } ); $container->share( TasksStateCommand::class, function (Container $container) { return new TasksStateCommand(); } ); } } PK1A#]�&+��!src/Service/Provider/Database.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseInterface; use Joomla\Database\Mysql\MysqlDriver; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's database dependency * * @since 4.0.0 */ class Database implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('db', DatabaseInterface::class) ->alias('DatabaseDriver', DatabaseInterface::class) ->alias(DatabaseDriver::class, DatabaseInterface::class) ->share( DatabaseInterface::class, function (Container $container) { $conf = $container->get('config'); /** * @todo: This 'sensible' default is required in the installer for now. Eventually we need to * refactor the installer so it is not required */ $dbtype = $conf->get('dbtype', 'mysqli'); /* * In Joomla! 3.x and earlier the `mysql` type was used for the `ext/mysql` PHP extension, which is no longer supported. * The `pdomysql` type represented the PDO MySQL adapter. With the Framework's package in use, the PDO MySQL adapter * is now the `mysql` type. Therefore, we check two conditions: * * 1) Is the type `pdomysql`, if so switch to `mysql` * 2) Is the type `mysql`, if so make sure PDO MySQL is supported and if not switch to `mysqli` * * For these cases, if a connection cannot be made with MySQLi, the database API will handle throwing an Exception * so we don't need to make any additional checks for MySQLi. */ if (strtolower($dbtype) === 'pdomysql') { $dbtype = 'mysql'; } if (strtolower($dbtype) === 'mysql') { if (!MysqlDriver::isSupported()) { $dbtype = 'mysqli'; } } /* * Joomla! 4.0 removes support for the `ext/pgsql` PHP extension. To help with the migration, we will migrate the configuration * to the PDO PostgreSQL driver regardless of if the environment supports it. Instead of getting a "driver not found" type of * error, this will instead force the API to report that the driver is not supported. */ if (strtolower($dbtype) === 'postgresql') { $dbtype = 'pgsql'; } $options = [ 'driver' => $dbtype, 'host' => $conf->get('host'), 'user' => $conf->get('user'), 'password' => $conf->get('password'), 'database' => $conf->get('db'), 'prefix' => $conf->get('dbprefix'), ]; if ((int) $conf->get('dbencryption') !== 0) { $options['ssl'] = [ 'enable' => true, 'verify_server_cert' => (bool) $conf->get('dbsslverifyservercert'), ]; foreach (['cipher', 'ca', 'key', 'cert'] as $value) { $confVal = trim($conf->get('dbssl' . $value, '')); if ($confVal !== '') { $options['ssl'][$value] = $confVal; } } } // Enable utf8mb4 connections for mysql adapters if (strtolower($dbtype) === 'mysqli') { $options['utf8mb4'] = true; } if (strtolower($dbtype) === 'mysql') { $options['charset'] = 'utf8mb4'; } if (JDEBUG) { $options['monitor'] = new \Joomla\Database\Monitor\DebugMonitor(); } try { $db = DatabaseDriver::getInstance($options); } catch (\RuntimeException $e) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } jexit('Database Error: ' . $e->getMessage()); } $db->setDispatcher($container->get(DispatcherInterface::class)); return $db; }, true ); } } PK1A#]�����)src/Service/Provider/WebAssetRegistry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\WebAsset\WebAssetRegistry as Registry; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's WebAsset dependency * * @since 4.0.0 */ class WebAssetRegistry implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('webassetregistry', Registry::class) ->share( Registry::class, function (Container $container) { $registry = new Registry(); // Add Core registry files $registry ->addRegistryFile('media/vendor/joomla.asset.json') ->addRegistryFile('media/system/joomla.asset.json') ->addRegistryFile('media/legacy/joomla.asset.json'); return $registry; }, true ); } } PK1A#].f99src/Service/Provider/Menu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Menu\MenuFactory; use Joomla\CMS\Menu\MenuFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's menu dependency * * @since 4.0.0 */ class Menu implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('menu.factory', MenuFactoryInterface::class) ->alias(MenuFactory::class, MenuFactoryInterface::class) ->share( MenuFactoryInterface::class, function (Container $container) { $factory = new MenuFactory(); $factory->setCacheControllerFactory($container->get(CacheControllerFactoryInterface::class)); $factory->setDatabase($container->get(DatabaseInterface::class)); return $factory; }, true ); } } PK1A#]?δd��!src/Service/Provider/Document.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Document\Factory; use Joomla\CMS\Document\FactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the application's document dependency * * @since 4.0.0 */ class Document implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('document.factory', FactoryInterface::class) ->alias(Factory::class, FactoryInterface::class) ->share( FactoryInterface::class, function (Container $container) { $factory = new Factory(); $factory->setCacheControllerFactory($container->get(CacheControllerFactoryInterface::class)); return $factory; }, true ); } } PK1A#]�RmVII'src/Service/Provider/Authentication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Service\Provider; use Joomla\Authentication\Password\Argon2idHandler as BaseArgon2idHandler; use Joomla\Authentication\Password\Argon2iHandler as BaseArgon2iHandler; use Joomla\Authentication\Password\BCryptHandler as BaseBCryptHandler; use Joomla\CMS\Authentication\Password\Argon2idHandler; use Joomla\CMS\Authentication\Password\Argon2iHandler; use Joomla\CMS\Authentication\Password\BCryptHandler; use Joomla\CMS\Authentication\Password\ChainedHandler; use Joomla\CMS\Authentication\Password\MD5Handler; use Joomla\CMS\Authentication\Password\PHPassHandler; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the authentication dependencies * * @since 4.0.0 */ class Authentication implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->alias('password.handler.argon2i', Argon2iHandler::class) ->alias(BaseArgon2iHandler::class, Argon2iHandler::class) ->share( Argon2iHandler::class, function (Container $container) { return new Argon2iHandler(); }, true ); $container->alias('password.handler.argon2id', Argon2idHandler::class) ->alias(BaseArgon2idHandler::class, Argon2idHandler::class) ->share( Argon2idHandler::class, function (Container $container) { return new Argon2idHandler(); }, true ); $container->alias('password.handler.chained', ChainedHandler::class) ->share( ChainedHandler::class, function (Container $container) { $handler = new ChainedHandler(); // Load the chain with supported core handlers $handler->addHandler($container->get(BCryptHandler::class)); if (Argon2iHandler::isSupported()) { $handler->addHandler($container->get(Argon2iHandler::class)); } if (Argon2idHandler::isSupported()) { $handler->addHandler($container->get(Argon2idHandler::class)); } $handler->addHandler($container->get(PHPassHandler::class)); $handler->addHandler($container->get(MD5Handler::class)); return $handler; }, true ); // The Joomla default is BCrypt so alias this service $container->alias('password.handler.default', BCryptHandler::class) ->alias(BaseBCryptHandler::class, BCryptHandler::class) ->alias('password.handler.bcrypt', BCryptHandler::class) ->share( BCryptHandler::class, function (Container $container) { return new BCryptHandler(); }, true ); $container->alias('password.handler.md5', MD5Handler::class) ->share( MD5Handler::class, function (Container $container) { @trigger_error( sprintf( 'The "%1$s" class service is deprecated, use the "%2$s" service for the active password handler instead.', MD5Handler::class, 'password.handler.default' ), E_USER_DEPRECATED ); return new MD5Handler(); }, true ); $container->alias('password.handler.phpass', PHPassHandler::class) ->share( PHPassHandler::class, function (Container $container) { @trigger_error( sprintf( 'The "%1$s" class service is deprecated, use the "%2$s" service for the active password handler instead.', PHPassHandler::class, 'password.handler.default' ), E_USER_DEPRECATED ); return new PHPassHandler(); }, true ); } } PK1A#]�-���src/Input/Json.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Input; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Input JSON Class * * This class decodes a JSON string from the raw request data and makes it available via * the standard JInput interface. * * @since 3.0.1 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Json instead */ class Json extends Input { /** * @var string The raw JSON string from the request. * @since 3.0.1 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Json instead */ private $_raw; /** * Constructor. * * @param array $source Source data (Optional, default is the raw HTTP input decoded from JSON) * @param array $options Array of configuration parameters (Optional) * * @since 3.0.1 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Json instead */ public function __construct(array $source = null, array $options = []) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = InputFilter::getInstance(); } if (\is_null($source)) { $this->_raw = file_get_contents('php://input'); $this->data = json_decode($this->_raw, true); if (!is_array($this->data)) { $this->data = []; } } else { $this->data = &$source; } $this->options = $options; } /** * Gets the raw JSON string from the request. * * @return string The raw JSON string from the request. * * @since 3.0.1 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Json instead */ public function getRaw() { return $this->_raw; } } PK1A#]����src/Input/Input.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Input; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Input Base Class * * This is an abstracted input class used to manage retrieving data from the application environment. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead * * @property-read Input $get * @property-read Input $post * @property-read Input $request * @property-read Input $server * @property-read Input $env * @property-read Files $files * @property-read Cookie $cookie * @property-read Json $json */ class Input extends \Joomla\Input\Input { /** * Container with allowed superglobals * * @var array * @since 3.8.9 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ private static $allowedGlobals = ['REQUEST', 'GET', 'POST', 'FILES', 'SERVER', 'ENV']; /** * Input objects * * @var Input[] * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ protected $inputs = []; /** * Constructor. * * @param array $source Source data (Optional, default is $_REQUEST) * @param array $options Array of configuration parameters (Optional) * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ public function __construct($source = null, array $options = []) { if (!isset($options['filter'])) { $this->filter = InputFilter::getInstance(); } parent::__construct($source, $options); } /** * Magic method to get an input object * * @param mixed $name Name of the input object to retrieve. * * @return \Joomla\Input\Input The request input object * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ public function __get($name) { if (isset($this->inputs[$name])) { return $this->inputs[$name]; } $className = '\\Joomla\\CMS\\Input\\' . ucfirst($name); if (class_exists($className)) { $this->inputs[$name] = new $className(null, $this->options); return $this->inputs[$name]; } $superGlobal = '_' . strtoupper($name); if (\in_array(strtoupper($name), self::$allowedGlobals, true) && isset($GLOBALS[$superGlobal])) { $this->inputs[$name] = new Input($GLOBALS[$superGlobal], $this->options); return $this->inputs[$name]; } // Try using the parent class return parent::__get($name); } /** * Gets an array of values from the request. * * @param array $vars Associative array of keys and filter types to apply. * If empty and datasource is null, all the input data will be returned * but filtered using the filter given by the parameter defaultFilter in * InputFilter::clean. * @param mixed $datasource Array to retrieve data from, or null. * @param string $defaultFilter Default filter used in InputFilter::clean if vars is empty and * datasource is null. If 'unknown', the default case is used in * InputFilter::clean. * * @return mixed The filtered input data. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ public function getArray(array $vars = [], $datasource = null, $defaultFilter = 'unknown') { return $this->getArrayRecursive($vars, $datasource, $defaultFilter, false); } /** * Gets an array of values from the request. * * @param array $vars Associative array of keys and filter types to apply. * If empty and datasource is null, all the input data will be returned * but filtered using the filter given by the parameter defaultFilter in * InputFilter::clean. * @param mixed $datasource Array to retrieve data from, or null. * @param string $defaultFilter Default filter used in InputFilter::clean if vars is empty and * datasource is null. If 'unknown', the default case is used in * InputFilter::clean. * @param bool $recursion Flag to indicate a recursive function call. * * @return mixed The filtered input data. * * @since 3.4.2 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ protected function getArrayRecursive(array $vars = [], $datasource = null, $defaultFilter = 'unknown', $recursion = false) { if (empty($vars) && \is_null($datasource)) { $vars = $this->data; } else { if (!$recursion) { $defaultFilter = null; } } $results = []; foreach ($vars as $k => $v) { if (\is_array($v)) { if (\is_null($datasource)) { $results[$k] = $this->getArrayRecursive($v, $this->get($k, null, 'array'), $defaultFilter, true); } else { $results[$k] = $this->getArrayRecursive($v, $datasource[$k], $defaultFilter, true); } } else { $filter = $defaultFilter ?? $v; if (\is_null($datasource)) { $results[$k] = $this->get($k, null, $filter); } elseif (isset($datasource[$k])) { $results[$k] = $this->filter->clean($datasource[$k], $filter); } else { $results[$k] = $this->filter->clean(null, $filter); } } } return $results; } /** * Method to unserialize the input. * * @param string $input The serialized input. * * @return void * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Input instead */ public function unserialize($input) { // Unserialize the options, data, and inputs. list($this->options, $this->data, $this->inputs) = unserialize($input); // Load the filter. if (isset($this->options['filter'])) { $this->filter = $this->options['filter']; } else { $this->filter = InputFilter::getInstance(); } } } PK1A#]��¶vvsrc/Input/Files.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Input; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Input Files Class * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ class Files extends Input { /** * The pivoted data from a $_FILES or compatible array. * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ protected $decodedData = []; /** * The class constructor. * * @param array $source The source argument is ignored. $_FILES is always used. * @param array $options An optional array of configuration options: * filter : a custom InputFilter object. * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ public function __construct(array $source = null, array $options = []) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = InputFilter::getInstance(); } // Set the data source. $this->data = &$_FILES; // Set the options for the class. $this->options = $options; } /** * Gets a value from the input data. * * @param string $name The name of the input property (usually the name of the files INPUT tag) to get. * @param mixed $default The default value to return if the named property does not exist. * @param string $filter The filter to apply to the value. * * @return mixed The filtered input value. * * @see InputFilter::clean() * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ public function get($name, $default = null, $filter = 'cmd') { if (isset($this->data[$name])) { $results = $this->decodeData( [ $this->data[$name]['name'], $this->data[$name]['type'], $this->data[$name]['tmp_name'], $this->data[$name]['error'], $this->data[$name]['size'], ] ); // Prevent returning an unsafe file unless specifically requested if (strtoupper($filter) !== 'RAW') { $isSafe = InputFilter::isSafeFile($results); if (!$isSafe) { return $default; } } return $results; } return $default; } /** * Method to decode a data array. * * @param array $data The data array to decode. * * @return array * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ protected function decodeData(array $data) { $result = []; if (\is_array($data[0])) { foreach ($data[0] as $k => $v) { $result[$k] = $this->decodeData([$data[0][$k], $data[1][$k], $data[2][$k], $data[3][$k], $data[4][$k]]); } return $result; } return ['name' => $data[0], 'type' => $data[1], 'tmp_name' => $data[2], 'error' => $data[3], 'size' => $data[4]]; } /** * Sets a value. * * @param string $name The name of the input property to set. * @param mixed $value The value to assign to the input property. * * @return void * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Files instead */ public function set($name, $value) { } } PK1A#]d��~&&src/Input/Cli.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Input; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Input CLI Class * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ class Cli extends Input { /** * The executable that was called to run the CLI script. * * @var string * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ public $executable; /** * The additional arguments passed to the script that are not associated * with a specific argument name. * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ public $args = []; /** * Constructor. * * @param array $source Source data (Optional, default is $_REQUEST) * @param array $options Array of configuration parameters (Optional) * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ public function __construct(array $source = null, array $options = []) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = InputFilter::getInstance(); } // Get the command line options $this->parseArguments(); // Set the options for the class. $this->options = $options; } /** * Method to serialize the input. * * @return string The serialized input. * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ public function serialize() { // Load all of the inputs. $this->loadAllInputs(); // Remove $_ENV and $_SERVER from the inputs. $inputs = $this->inputs; unset($inputs['env']); unset($inputs['server']); // Serialize the executable, args, options, data, and inputs. return serialize([$this->executable, $this->args, $this->options, $this->data, $inputs]); } /** * Method to unserialize the input. * * @param string $input The serialized input. * * @return void * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ public function unserialize($input) { // Unserialize the executable, args, options, data, and inputs. list($this->executable, $this->args, $this->options, $this->data, $this->inputs) = unserialize($input); // Load the filter. if (isset($this->options['filter'])) { $this->filter = $this->options['filter']; } else { $this->filter = InputFilter::getInstance(); } } /** * Initialise the options and arguments * * Not supported: -abc c-value * * @return void * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the `joomla/console` package instead */ protected function parseArguments() { $argv = $_SERVER['argv']; $this->executable = array_shift($argv); $out = []; for ($i = 0, $j = \count($argv); $i < $j; $i++) { $arg = $argv[$i]; // --foo --bar=baz if (substr($arg, 0, 2) === '--') { $eqPos = strpos($arg, '='); // --foo if ($eqPos === false) { $key = substr($arg, 2); // --foo value if ($i + 1 < $j && $argv[$i + 1][0] !== '-') { $value = $argv[$i + 1]; $i++; } else { $value = $out[$key] ?? true; } $out[$key] = $value; } else { // --bar=baz $key = substr($arg, 2, $eqPos - 2); $value = substr($arg, $eqPos + 1); $out[$key] = $value; } } elseif (substr($arg, 0, 1) === '-') { // -k=value -abc // -k=value if (substr($arg, 2, 1) === '=') { $key = substr($arg, 1, 1); $value = substr($arg, 3); $out[$key] = $value; } else { // -abc $chars = str_split(substr($arg, 1)); foreach ($chars as $char) { $key = $char; $value = $out[$key] ?? true; $out[$key] = $value; } // -a a-value if ((\count($chars) === 1) && ($i + 1 < $j) && ($argv[$i + 1][0] !== '-')) { $out[$key] = $argv[$i + 1]; $i++; } } } else { // Plain-arg $this->args[] = $arg; } } $this->data = $out; } } PK1A#]/09Ғ�src/Input/Cookie.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Input; use Joomla\CMS\Filter\InputFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Input Cookie Class * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Cookie instead */ class Cookie extends Input { /** * Constructor. * * @param array $source Ignored. * @param array $options Array of configuration parameters (Optional) * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Cookie instead */ public function __construct(array $source = null, array $options = []) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = InputFilter::getInstance(); } // Set the data source. $this->data = &$_COOKIE; // Set the options for the class. $this->options = $options; } /** * Sets a value * * @param string $name Name of the value to set. * @param mixed $value Value to assign to the input. * @param array $options An associative array which may have any of the keys expires, path, domain, * secure, httponly and samesite. The values have the same meaning as described * for the parameters with the same name. The value of the samesite element * should be either Lax or Strict. If any of the allowed options are not given, * their default values are the same as the default values of the explicit * parameters. If the samesite element is omitted, no SameSite cookie attribute * is set. * * @return void * * @link http://www.ietf.org/rfc/rfc2109.txt * @see setcookie() * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0. * Use Joomla\Input\Cookie instead */ public function set($name, $value, $options = []) { // BC layer to convert old method parameters. if (is_array($options) === false) { trigger_deprecation( 'joomla/input', '1.4.0', 'The %s($name, $value, $expire, $path, $domain, $secure, $httpOnly) signature is deprecated and' . ' will not be supported once support' . ' for PHP 7.2 and earlier is dropped, use the %s($name, $value, $options) signature instead', __METHOD__, __METHOD__ ); $argList = func_get_args(); $options = [ 'expires' => $argList[2] ?? 0, 'path' => $argList[3] ?? '', 'domain' => $argList[4] ?? '', 'secure' => $argList[5] ?? false, 'httponly' => $argList[6] ?? false, ]; } // Set the cookie if (version_compare(PHP_VERSION, '7.3', '>=')) { if (\is_array($value)) { foreach ($value as $key => $val) { setcookie($name . "[$key]", $val, $options); } } else { setcookie($name, $value, $options); } } else { // Using the setcookie function before php 7.3, make sure we have default values. if (array_key_exists('expires', $options) === false) { $options['expires'] = 0; } if (array_key_exists('path', $options) === false) { $options['path'] = ''; } if (array_key_exists('domain', $options) === false) { $options['domain'] = ''; } if (array_key_exists('secure', $options) === false) { $options['secure'] = false; } if (array_key_exists('httponly', $options) === false) { $options['httponly'] = false; } if (\is_array($value)) { foreach ($value as $key => $val) { setcookie( $name . "[$key]", $val, $options['expires'], $options['path'], $options['domain'], $options['secure'], $options['httponly'] ); } } else { setcookie( $name, $value, $options['expires'], $options['path'], $options['domain'], $options['secure'], $options['httponly'] ); } } $this->data[$name] = $value; } } PK1A#]�+��y4y4src/Toolbar/Toolbar.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Log\Log; use Joomla\CMS\Toolbar\Button\BasicButton; use Joomla\CMS\Toolbar\Button\ConfirmButton; use Joomla\CMS\Toolbar\Button\CustomButton; use Joomla\CMS\Toolbar\Button\DropdownButton; use Joomla\CMS\Toolbar\Button\HelpButton; use Joomla\CMS\Toolbar\Button\InlinehelpButton; use Joomla\CMS\Toolbar\Button\LinkButton; use Joomla\CMS\Toolbar\Button\PopupButton; use Joomla\CMS\Toolbar\Button\SeparatorButton; use Joomla\CMS\Toolbar\Button\StandardButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * ToolBar handler * * @method StandardButton standardButton(string $name = '', string $text = '', string $task = '') * @method SeparatorButton separatorButton(string $name = '', string $text = '', string $task = '') * @method PopupButton popupButton(string $name = '', string $text = '', string $task = '') * @method LinkButton linkButton(string $name = '', string $text = '', string $task = '') * @method HelpButton helpButton(string $name = '', string $text = '', string $task = '') * @method InlinehelpButton inlinehelpButton(string $name = '', string $text = '', string $task = '') * @method CustomButton customButton(string $name = '', string $text = '', string $task = '') * @method ConfirmButton confirmButton(string $name = '', string $text = '', string $task = '') * @method BasicButton basicButton(string $name = '', string $text = '', string $task = '') * @method DropdownButton dropdownButton(string $name = '', string $text = '', string $task = '') * * @since 1.5 */ class Toolbar { use CoreButtonsTrait; /** * Toolbar name * * @var string * @since 1.5 */ protected $_name = ''; /** * Toolbar array * * @var array * @since 1.5 */ protected $_bar = []; /** * Directories, where button types can be stored. * * @var array * @since 1.5 */ protected $_buttonPath = []; /** * Stores the singleton instances of various toolbar. * * @var Toolbar[] * @since 2.5 */ protected static $instances = []; /** * Factory for creating Toolbar API objects * * @var ToolbarFactoryInterface * @since 4.0.0 */ protected $factory; /** * Constructor * * @param string $name The toolbar name. * @param ToolbarFactoryInterface $factory The toolbar factory. * * @since 1.5 */ public function __construct($name = 'toolbar', ToolbarFactoryInterface $factory = null) { $this->_name = $name; // At 5.0, require the factory to be injected if (!$factory) { @trigger_error( sprintf( 'As of Joomla! 5.0, a %1$s must be provided to a %2$s object when creating it.', ToolbarFactoryInterface::class, \get_class($this) ), E_USER_DEPRECATED ); $factory = new ContainerAwareToolbarFactory(); $factory->setContainer(Factory::getContainer()); } $this->setFactory($factory); // Set base path to find buttons. $this->_buttonPath[] = __DIR__ . '/Button'; } /** * Returns the global Toolbar object, only creating it if it doesn't already exist. * * @param string $name The name of the toolbar. * * @return Toolbar The Toolbar object. * * @since 1.5 * * @deprecated 4.0 will be removed in 6.0 * Use the ToolbarFactoryInterface instead * Example: * Factory::getContainer()->get(ToolbarFactoryInterface::class)->createToolbar($name) * * @todo Needs a proper replacement before removal as ToolbarFactoryInterface alone does not share the object everywhere * * @throws \Joomla\DI\Exception\KeyNotFoundException */ public static function getInstance($name = 'toolbar') { if (empty(self::$instances[$name])) { self::$instances[$name] = Factory::getContainer()->get(ToolbarFactoryInterface::class)->createToolbar($name); } return self::$instances[$name]; } /** * Set the factory instance * * @param ToolbarFactoryInterface $factory The factory instance * * @return $this * * @since 4.0.0 */ public function setFactory(ToolbarFactoryInterface $factory): self { $this->factory = $factory; return $this; } /** * Append a button to toolbar. * * @param ToolbarButton $button The button instance. * @param array $args The more arguments. * * @return ToolbarButton|boolean Return button instance to help chaining configure. If using legacy arguments * returns true * * @since 1.5 */ public function appendButton($button, ...$args) { if ($button instanceof ToolbarButton) { $button->setParent($this); $this->_bar[] = $button; return $button; } // B/C array_unshift($args, $button); $this->_bar[] = $args; @trigger_error( sprintf( '%s::appendButton() should only accept %s instance in Joomla 5.0.', static::class, ToolbarButton::class ), E_USER_DEPRECATED ); return true; } /** * Get the list of toolbar links. * * @return array * * @since 1.6 */ public function getItems() { return $this->_bar; } /** * Set the button list. * * @param ToolbarButton[] $items The button list array. * * @return static * * @since 4.0.0 */ public function setItems(array $items): self { $this->_bar = $items; return $this; } /** * Get the name of the toolbar. * * @return string * * @since 1.6 */ public function getName() { return $this->_name; } /** * Prepend a button to toolbar. * * @param ToolbarButton $button The button instance. * @param array $args The more arguments. * * @return ToolbarButton|boolean Return button instance to help chaining configure. If using legacy arguments * returns true * * @since 1.5 */ public function prependButton($button, ...$args) { if ($button instanceof ToolbarButton) { $button->setParent($this); array_unshift($this->_bar, $button); return $button; } // B/C array_unshift($args, $button); array_unshift($this->_bar, $args); @trigger_error( sprintf( '%s::prependButton() should only accept %s instance in Joomla 5.0.', static::class, ToolbarButton::class ), E_USER_DEPRECATED ); return true; } /** * Render a toolbar. * * @param array $options The options of toolbar. * * @return string HTML for the toolbar. * * @throws \Exception * @since 1.5 */ public function render(array $options = []) { $html = []; $isChild = !empty($options['is_child']); // Start toolbar div. if (!$isChild) { $layout = new FileLayout('joomla.toolbar.containeropen'); $html[] = $layout->render(['id' => $this->_name]); } $len = count($this->_bar); // Render each button in the toolbar. foreach ($this->_bar as $i => $button) { if ($button instanceof ToolbarButton) { // Child dropdown only support new syntax $button->setOption('is_child', $isChild); $button->setOption('is_first_child', $i === 0); $button->setOption('is_last_child', $i === $len - 1); $html[] = $button->render(); } else { // B/C $html[] = $this->renderButton($button); } } // End toolbar div. if (!$isChild) { $layout = new FileLayout('joomla.toolbar.containerclose'); $html[] = $layout->render([]); } return implode('', $html); } /** * Render a button. * * @param array &$node A toolbar node. * * @return string * * @since 1.5 * @throws \Exception */ public function renderButton(&$node) { // Get the button type. $type = $node[0]; $button = $this->loadButtonType($type); // Check for error. if ($button === false) { throw new \UnexpectedValueException(Text::sprintf('JLIB_HTML_BUTTON_NOT_DEFINED', $type)); } $button->setParent($this); return $button->render($node); } /** * Loads a button type. * * @param string $type Button Type * @param boolean $new False by default * * @return false|ToolbarButton * * @since 1.5 */ public function loadButtonType($type, $new = false) { // For B/C, catch the exceptions thrown by the factory try { return $this->factory->createButton($this, $type); } catch (\InvalidArgumentException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); return false; } } /** * Add a directory where Toolbar should search for button types in LIFO order. * * You may either pass a string or an array of directories. * * Toolbar will be searching for an element type in the same order you * added them. If the parameter type cannot be found in the custom folders, * it will look in libraries/joomla/html/toolbar/button. * * @param mixed $path Directory or directories to search. * * @return void * * @since 1.5 * * @deprecated 4.0 will be removed in 6.0 * ToolbarButton classes should be autoloaded via namespaces */ public function addButtonPath($path) { @trigger_error( sprintf( 'Registering lookup paths for toolbar buttons is deprecated and will be removed in Joomla 5.0.' . ' %1$s objects should be autoloaded or a custom %2$s implementation supporting path lookups provided.', ToolbarButton::class, ToolbarFactoryInterface::class ), E_USER_DEPRECATED ); // Loop through the path directories. foreach ((array) $path as $dir) { // No surrounding spaces allowed! $dir = trim($dir); // Add trailing separators as needed. if (substr($dir, -1) !== DIRECTORY_SEPARATOR) { // Directory $dir .= DIRECTORY_SEPARATOR; } // Add to the top of the search dirs. array_unshift($this->_buttonPath, $dir); } } /** * Get the lookup paths for button objects * * @return array * * @since 4.0.0 * @deprecated 4.0 will be removed in 6.0 * ToolbarButton buttons should be autoloaded via namespaces */ public function getButtonPath(): array { @trigger_error( sprintf( 'Lookup paths for %s objects is deprecated and will be removed in Joomla 5.0.', ToolbarButton::class ), E_USER_DEPRECATED ); return $this->_buttonPath; } /** * Create child toolbar. * * @param string $name The toolbar name. * * @return static * * @since 4.0.0 */ public function createChild($name): self { return new static($name, $this->factory); } /** * Magic method proxy. * * @param string $name The method name. * @param array $args The method arguments. * * @return ToolbarButton * * @throws \Exception * * @since 4.0.0 */ public function __call($name, $args) { if (strtolower(substr($name, -6)) === 'button') { $type = substr($name, 0, -6); $button = $this->factory->createButton($this, $type); $button->name($args[0] ?? '') ->text($args[1] ?? '') ->task($args[2] ?? ''); return $this->appendButton($button); } throw new \BadMethodCallException( sprintf( 'Method %s() not found in class: %s', $name, static::class ) ); } } PK1A#]�Nmg0\0\src/Toolbar/ToolbarHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Table\Table; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for the button bar. * * @since 1.5 */ abstract class ToolbarHelper { /** * Title cell. * For the title and toolbar to be rendered correctly, * this title function must be called before the starttable function and the toolbars icons * this is due to the nature of how the css has been used to position the title in respect to the toolbar. * * @param string $title The title. * @param string $icon The space-separated names of the image. * * @return void * * @since 1.5 */ public static function title($title, $icon = 'generic.png') { $layout = new FileLayout('joomla.toolbar.title'); $html = $layout->render(['title' => $title, 'icon' => $icon]); $app = Factory::getApplication(); $app->JComponentTitle = $html; $title = strip_tags($title) . ' - ' . $app->get('sitename'); if ($app->isClient('administrator')) { $title .= ' - ' . Text::_('JADMINISTRATION'); } Factory::getDocument()->setTitle($title); } /** * Writes a spacer cell. * * @param string $width The width for the cell * * @return void * * @since 1.5 */ public static function spacer($width = '') { $bar = Toolbar::getInstance('toolbar'); // Add a spacer. $bar->appendButton('Separator', 'spacer', $width); } /** * Writes a divider between menu buttons * * @return void * * @since 1.5 */ public static function divider() { $bar = Toolbar::getInstance('toolbar'); // Add a divider. $bar->appendButton('Separator', 'divider'); } /** * Writes a custom option and task button for the button bar. * * @param string $task The task to perform (picked up by the switch($task) blocks). * @param string $icon The image to display. * @param string $iconOver @deprecated 4.3 will be removed in 6.0 * @param string $alt The alt text for the icon image. * @param bool $listSelect True if required to check that a standard list item is checked. * @param string $formId The id of action form. * * @return void * * @since 1.5 */ public static function custom($task = '', $icon = '', $iconOver = '', $alt = '', $listSelect = true, $formId = null) { $bar = Toolbar::getInstance('toolbar'); // Strip extension. $icon = preg_replace('#\.[^.]*$#', '', $icon); // Add a standard button. $bar->appendButton('Standard', $icon, $alt, $task, $listSelect, $formId); } /** * Writes a preview button for a given option (opens a popup window). * * @param string $url The name of the popup file (excluding the file extension) * @param bool $updateEditors Unused * @param string $icon The image to display. * @param integer $bodyHeight The body height of the preview popup * @param integer $modalWidth The modal width of the preview popup * * @return void * * @since 1.5 */ public static function preview($url = '', $updateEditors = false, $icon = 'preview', $bodyHeight = null, $modalWidth = null) { $bar = Toolbar::getInstance('toolbar'); // Add a preview button. $bar->appendButton('Popup', $icon, 'Preview', $url . '&task=preview', 640, 480, $bodyHeight, $modalWidth); } /** * Writes a jooa11y accessibility checker button for a given option (opens a popup window). * * @param string $url The url to open * @param bool $updateEditors Unused * @param string $icon The image to display. * @param integer $bodyHeight The body height of the preview popup * @param integer $modalWidth The modal width of the preview popup * * @return void * * @since 4.1.0 */ public static function jooa11y($url = '', $updateEditors = false, $icon = 'icon-universal-access', $bodyHeight = null, $modalWidth = null) { $bar = Toolbar::getInstance('toolbar'); // Add a button. $bar->appendButton('Popup', $icon, 'Preview', $url . '&task=preview', 640, 480, $bodyHeight, $modalWidth); } /** * Writes a help button for a given option (opens a popup window). * * @param string $ref The name of the popup file (excluding the file extension for an xml file). * @param bool $com Use the help file in the component directory. * @param string $override Use this URL instead of any other * @param string $component Name of component to get Help (null for current component) * * @return void * * @since 1.5 */ public static function help($ref, $com = false, $override = null, $component = null) { // Don't show a help button if neither $ref nor $override is given if (!$ref && !$override) { return; } $bar = Toolbar::getInstance('toolbar'); // Add a help button. $bar->appendButton('Help', $ref, $com, $override, $component); } /** * Writes a help button for showing/hiding the inline help of a form * * @param string $class The class used by the inline help items. * * @return void * * @since 4.1.0 */ public static function inlinehelp(string $class = "hide-aware-inline-help") { $bar = Toolbar::getInstance('toolbar'); $bar->inlinehelp($class); } /** * Writes a cancel button that will go back to the previous page without doing * any other operation. * * @param string $alt Alternative text. * @param string $href URL of the href attribute. * * @return void * * @since 1.5 */ public static function back($alt = 'JTOOLBAR_BACK', $href = 'javascript:history.back();') { $bar = Toolbar::getInstance('toolbar'); // Add a back button. $arrow = Factory::getLanguage()->isRtl() ? 'arrow-right' : 'arrow-left'; $bar->appendButton('Link', $arrow, $alt, $href); } /** * Creates a button to redirect to a link * * @param string $url The link url * @param string $text Button text * @param string $name Name to be used as apart of the id * * @return void * * @since 3.5 */ public static function link($url, $text, $name = 'link') { $bar = Toolbar::getInstance('toolbar'); $bar->appendButton('Link', $name, $text, $url); } /** * Writes a media_manager button. * * @param string $directory The subdirectory to upload the media to. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function media_manager($directory = '', $alt = 'JTOOLBAR_UPLOAD') { $bar = Toolbar::getInstance('toolbar'); // Add an upload button. $bar->appendButton('Popup', 'upload', $alt, 'index.php?option=com_media&tmpl=component&task=popupUpload&folder=' . $directory, 800, 520); } /** * Writes a common 'default' button for a record. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function makeDefault($task = 'default', $alt = 'JTOOLBAR_DEFAULT') { $bar = Toolbar::getInstance('toolbar'); // Add a default button. $bar->appendButton('Standard', 'default', $alt, $task, true); } /** * Writes a common 'assign' button for a record. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function assign($task = 'assign', $alt = 'JTOOLBAR_ASSIGN') { $bar = Toolbar::getInstance('toolbar'); // Add an assign button. $bar->appendButton('Standard', 'assign', $alt, $task, true); } /** * Writes the common 'new' icon for the button bar. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * @param boolean $check True if required to check that a standard list item is checked. * * @return void * * @since 1.5 */ public static function addNew($task = 'add', $alt = 'JTOOLBAR_NEW', $check = false) { $bar = Toolbar::getInstance('toolbar'); // Add a new button. $bar->appendButton('Standard', 'new', $alt, $task, $check); } /** * Writes a common 'publish' button. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * @param boolean $check True if required to check that a standard list item is checked. * * @return void * * @since 1.5 */ public static function publish($task = 'publish', $alt = 'JTOOLBAR_PUBLISH', $check = false) { $bar = Toolbar::getInstance('toolbar'); // Add a publish button. $bar->appendButton('Standard', 'publish', $alt, $task, $check); } /** * Writes a common 'publish' button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function publishList($task = 'publish', $alt = 'JTOOLBAR_PUBLISH') { $bar = Toolbar::getInstance('toolbar'); // Add a publish button (list). $bar->appendButton('Standard', 'publish', $alt, $task, true); } /** * Writes a common 'unpublish' button. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * @param boolean $check True if required to check that a standard list item is checked. * * @return void * * @since 1.5 */ public static function unpublish($task = 'unpublish', $alt = 'JTOOLBAR_UNPUBLISH', $check = false) { $bar = Toolbar::getInstance('toolbar'); // Add an unpublish button $bar->appendButton('Standard', 'unpublish', $alt, $task, $check); } /** * Writes a common 'unpublish' button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function unpublishList($task = 'unpublish', $alt = 'JTOOLBAR_UNPUBLISH') { $bar = Toolbar::getInstance('toolbar'); // Add an unpublish button (list). $bar->appendButton('Standard', 'unpublish', $alt, $task, true); } /** * Writes a common 'archive' button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function archiveList($task = 'archive', $alt = 'JTOOLBAR_ARCHIVE') { $bar = Toolbar::getInstance('toolbar'); // Add an archive button. $bar->appendButton('Standard', 'archive', $alt, $task, true); } /** * Writes an unarchive button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function unarchiveList($task = 'unarchive', $alt = 'JTOOLBAR_UNARCHIVE') { $bar = Toolbar::getInstance('toolbar'); // Add an unarchive button (list). $bar->appendButton('Standard', 'unarchive', $alt, $task, true); } /** * Writes a common 'edit' button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function editList($task = 'edit', $alt = 'JTOOLBAR_EDIT') { $bar = Toolbar::getInstance('toolbar'); // Add an edit button. $bar->appendButton('Standard', 'edit', $alt, $task, true); } /** * Writes a common 'edit' button for a template html. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function editHtml($task = 'edit_source', $alt = 'JTOOLBAR_EDIT_HTML') { $bar = Toolbar::getInstance('toolbar'); // Add an edit html button. $bar->appendButton('Standard', 'edithtml', $alt, $task, true); } /** * Writes a common 'edit' button for a template css. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function editCss($task = 'edit_css', $alt = 'JTOOLBAR_EDIT_CSS') { $bar = Toolbar::getInstance('toolbar'); // Add an edit css button (hide). $bar->appendButton('Standard', 'editcss', $alt, $task, true); } /** * Writes a common 'delete' button for a list of records. * * @param string $msg Postscript for the 'are you sure' message. * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function deleteList($msg = '', $task = 'remove', $alt = 'JTOOLBAR_DELETE') { $bar = Toolbar::getInstance('toolbar'); // Add a delete button. if ($msg) { $bar->appendButton('Confirm', $msg, 'delete', $alt, $task, true); } else { $bar->appendButton('Standard', 'delete', $alt, $task, true); } } /** * Writes a common 'trash' button for a list of records. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * @param bool $check True to allow lists. * * @return void * * @since 1.5 */ public static function trash($task = 'remove', $alt = 'JTOOLBAR_TRASH', $check = true) { $bar = Toolbar::getInstance('toolbar'); // Add a trash button. $bar->appendButton('Standard', 'trash', $alt, $task, $check, false); } /** * Writes a save button for a given option. * Apply operation leads to a save action only (does not leave edit mode). * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function apply($task = 'apply', $alt = 'JTOOLBAR_APPLY') { $bar = Toolbar::getInstance('toolbar'); // Add an apply button $bar->apply($task, $alt); } /** * Writes a save button for a given option. * Save operation leads to a save and then close action. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function save($task = 'save', $alt = 'JTOOLBAR_SAVE') { $bar = Toolbar::getInstance('toolbar'); // Add a save button. $bar->save($task, $alt); } /** * Writes a save and create new button for a given option. * Save and create operation leads to a save and then add action. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.6 */ public static function save2new($task = 'save2new', $alt = 'JTOOLBAR_SAVE_AND_NEW') { $bar = Toolbar::getInstance('toolbar'); // Add a save and create new button. $bar->save2new($task, $alt); } /** * Writes a save as copy button for a given option. * Save as copy operation leads to a save after clearing the key, * then returns user to edit mode with new key. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.6 */ public static function save2copy($task = 'save2copy', $alt = 'JTOOLBAR_SAVE_AS_COPY') { $bar = Toolbar::getInstance('toolbar'); // Add a save and create new button. $bar->save2copy($task, $alt); } /** * Writes a checkin button for a given option. * * @param string $task An override for the task. * @param string $alt An override for the alt text. * @param boolean $check True if required to check that a standard list item is checked. * * @return void * * @since 1.7 */ public static function checkin($task = 'checkin', $alt = 'JTOOLBAR_CHECKIN', $check = true) { $bar = Toolbar::getInstance('toolbar'); // Add a save and create new button. $bar->appendButton('Standard', 'checkin', $alt, $task, $check); } /** * Writes a cancel button and invokes a cancel operation (eg a checkin). * * @param string $task An override for the task. * @param string $alt An override for the alt text. * * @return void * * @since 1.5 */ public static function cancel($task = 'cancel', $alt = 'JTOOLBAR_CANCEL') { $bar = Toolbar::getInstance('toolbar'); // Add a cancel button. $bar->appendButton('Standard', 'cancel', $alt, $task, false); } /** * Writes a configuration button and invokes a cancel operation (eg a checkin). * * @param string $component The name of the component, eg, com_content. * @param integer $height The height of the popup. [UNUSED] * @param integer $width The width of the popup. [UNUSED] * @param string $alt The name of the button. * @param string $path An alternative path for the configuration xml relative to JPATH_SITE. * * @return void * * @since 1.5 */ public static function preferences($component, $height = 550, $width = 875, $alt = 'JTOOLBAR_OPTIONS', $path = '') { $component = urlencode($component); $path = urlencode($path); $bar = Toolbar::getInstance('toolbar'); $uri = (string) Uri::getInstance(); $return = urlencode(base64_encode($uri)); // Add a button linking to config for component. $bar->appendButton( 'Link', 'options', $alt, 'index.php?option=com_config&view=component&component=' . $component . '&path=' . $path . '&return=' . $return ); } /** * Writes a version history * * @param string $typeAlias The component and type, for example 'com_content.article' * @param integer $itemId The id of the item, for example the article id. * @param integer $height The height of the popup. * @param integer $width The width of the popup. * @param string $alt The name of the button. * * @return void * * @since 3.2 */ public static function versions($typeAlias, $itemId, $height = 800, $width = 500, $alt = 'JTOOLBAR_VERSIONS') { $lang = Factory::getLanguage(); $lang->load('com_contenthistory', JPATH_ADMINISTRATOR, $lang->getTag(), true); /** @var \Joomla\CMS\Table\ContentType $contentTypeTable */ $contentTypeTable = Table::getInstance('Contenttype'); $typeId = $contentTypeTable->getTypeId($typeAlias); // Options array for Layout $options = []; $options['title'] = Text::_($alt); $options['height'] = $height; $options['width'] = $width; $options['itemId'] = $typeAlias . '.' . $itemId; $bar = Toolbar::getInstance('toolbar'); $layout = new FileLayout('joomla.toolbar.versions'); $bar->appendButton('Custom', $layout->render($options), 'versions'); } /** * Writes a save button for a given option, with an additional dropdown * * @param array $buttons An array of buttons * @param string $class The button class * * @return void * * @since 4.0.0 */ public static function saveGroup($buttons = [], $class = 'btn-success') { $validOptions = [ 'apply' => 'JTOOLBAR_APPLY', 'save' => 'JTOOLBAR_SAVE', 'save2new' => 'JTOOLBAR_SAVE_AND_NEW', 'save2copy' => 'JTOOLBAR_SAVE_AS_COPY', ]; $bar = Toolbar::getInstance('toolbar'); $saveGroup = $bar->dropdownButton('save-group'); $saveGroup->configure( function (Toolbar $childBar) use ($buttons, $validOptions) { foreach ($buttons as $button) { if (!\array_key_exists($button[0], $validOptions)) { continue; } $altText = $button[2] ?? $validOptions[$button[0]]; $childBar->{$button[0]}($button[1]) ->text($altText); } } ); } /** * Displays a modal button * * @param string $targetModalId ID of the target modal box * @param string $icon Icon class to show on modal button * @param string $alt Title for the modal button * @param string $class The button class * * @return void * * @since 3.2 */ public static function modal($targetModalId, $icon, $alt, $class = 'btn-primary') { $title = Text::_($alt); $dhtml = '<joomla-toolbar-button><button data-bs-toggle="modal" data-bs-target="#' . $targetModalId . '" class="btn ' . $class . '"> <span class="' . $icon . ' icon-fw" title="' . $title . '"></span> ' . $title . '</button></joomla-toolbar-button>'; $bar = Toolbar::getInstance('toolbar'); $bar->appendButton('Custom', $dhtml, $alt); } } PK1A#]��o�F F ,src/Toolbar/ContainerAwareToolbarFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating toolbar objects * * @since 4.0.0 */ class ContainerAwareToolbarFactory implements ToolbarFactoryInterface, ContainerAwareInterface { use ContainerAwareTrait; /** * Creates a new toolbar button. * * @param Toolbar $toolbar The Toolbar instance to attach to the button * @param string $type Button Type * * @return ToolbarButton * * @since 3.8.0 * @throws \InvalidArgumentException */ public function createButton(Toolbar $toolbar, string $type): ToolbarButton { $normalisedType = ucfirst($type); $buttonClass = $this->loadButtonClass($normalisedType); if (!$buttonClass) { $dirs = $toolbar->getButtonPath(); $file = InputFilter::getInstance()->clean(str_replace('_', DIRECTORY_SEPARATOR, strtolower($type)) . '.php', 'path'); if ($buttonFile = Path::find($dirs, $file)) { include_once $buttonFile; } else { Log::add(Text::sprintf('JLIB_HTML_BUTTON_NO_LOAD', $buttonClass, $buttonFile), Log::WARNING, 'jerror'); throw new \InvalidArgumentException(Text::sprintf('JLIB_HTML_BUTTON_NO_LOAD', $buttonClass, $buttonFile)); } } if (!class_exists($buttonClass)) { throw new \InvalidArgumentException(sprintf('Class `%1$s` does not exist, could not create a toolbar button.', $buttonClass)); } // Check for a possible service from the container otherwise manually instantiate the class if ($this->getContainer()->has($buttonClass)) { return $this->getContainer()->get($buttonClass); } /** @var ToolbarButton $button */ $button = new $buttonClass($normalisedType); return $button->setParent($toolbar); } /** * Creates a new Toolbar object. * * @param string $name The toolbar name. * * @return Toolbar * * @since 4.0.0 */ public function createToolbar(string $name = 'toolbar'): Toolbar { return new Toolbar($name, $this); } /** * Load the button class including the deprecated ones. * * @param string $type Button Type (normalized) * * @return string|null * * @since 4.0.0 */ private function loadButtonClass(string $type) { $buttonClasses = [ 'Joomla\\CMS\\Toolbar\\Button\\' . $type . 'Button', /** * @deprecated 4.3 will be removed in 6.0 */ 'JToolbarButton' . $type, ]; foreach ($buttonClasses as $buttonClass) { if (!class_exists($buttonClass)) { continue; } return $buttonClass; } return null; } } PK1A#]���� � 'src/Toolbar/Button/InlinehelpButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a button to show / hide the inline help text * * @method self targetclass(string $value) * @method string getTargetclass() * * @since 4.1.0 */ class InlinehelpButton extends BasicButton { /** * Property layout. * * @var string * * @since 4.1.3 */ protected $layout = 'joomla.toolbar.inlinehelp'; /** * Prepare options for this button. * * @param array $options The options for this button. * * @return void * * @since 4.1.0 */ protected function prepareOptions(array &$options) { $options['text'] = $options['text'] ?: 'JINLINEHELP'; $options['icon'] = $options['icon'] ?? 'fa-question-circle'; $options['button_class'] = $options['button_class'] ?? 'btn btn-info'; $options['attributes'] = array_merge( $options['attributes'] ?? [], [ 'data-class' => $options['targetclass'] ?? 'hide-aware-inline-help', ] ); parent::prepareOptions($options); } /** * Fetches the button HTML code. * * @param string $type Unused string. * @param string $targetClass The class of the DIVs holding the descriptions to toggle. * @param string $text Button label * @param string $icon Button icon * @param string $buttonClass Button class * * @return string * * @since 4.1.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton( $type = 'Inlinehelp', string $targetClass = 'hide-aware-inline-help', string $text = 'JINLINEHELP', string $icon = 'fa fa-question-circle', string $buttonClass = 'btn btn-info' ) { $this->name('inlinehelp') ->targetclass($targetClass) ->text($text) ->icon($icon) ->buttonClass($buttonClass); return $this->renderButton($this->options); } /** * Method to configure available option accessors. * * @return array * * @since 4.1.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'targetclass', ] ); } } PK1A#]jF\$src/Toolbar/Button/ConfirmButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a standard button with a confirm dialog * * @method self message(string $value) * @method bool getMessage() * * @since 3.0 */ class ConfirmButton extends StandardButton { /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { $options['message'] = Text::_($options['message'] ?? ''); parent::prepareOptions($options); } /** * Fetch the HTML for the button * * @param string $type Unused string. * @param string $msg Message to render * @param string $name Name to be used as apart of the id * @param string $text Button text * @param string $task The task associated with the button * @param boolean $list True to allow use of lists * @param boolean $hideMenu True to hide the menu on click * * @return string HTML string for the button * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton($type = 'Confirm', $msg = '', $name = '', $text = '', $task = '', $list = true, $hideMenu = false) { $this->name($name) ->text($text) ->listCheck($list) ->message($msg) ->task($task); return $this->renderButton($this->options); } /** * Get the JavaScript command for the button * * @return string JavaScript command string * * @since 3.0 */ protected function _getCommand() { Text::script($this->getListCheckMessage() ?: 'JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST'); Text::script('ERROR'); $msg = $this->getMessage(); $cmd = "if (confirm('" . $msg . "')) { Joomla.submitbutton('" . $this->getTask() . "'); }"; if ($this->getListCheck()) { $message = "{'error': [Joomla.Text._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST')]}"; $alert = 'Joomla.renderMessages(' . $message . ')'; $cmd = 'if (document.adminForm.boxchecked.value == 0) { ' . $alert . ' } else { ' . $cmd . ' }'; } return $cmd; } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'message', ] ); } } PK1A#]L���%src/Toolbar/Button/StandardButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a standard button * * @since 3.0 */ class StandardButton extends BasicButton { /** * Property layout. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.toolbar.standard'; /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { parent::prepareOptions($options); if (empty($options['is_child'])) { $class = $this->fetchButtonClass($this->getName()); $options['btnClass'] = $options['button_class'] = ($options['button_class'] ?? $class); } $options['onclick'] = $options['onclick'] ?? $this->_getCommand(); } /** * Fetch the HTML for the button * * @param string $type Unused string. * @param string $name The name of the button icon class. * @param string $text Button text. * @param string $task Task associated with the button. * @param boolean $list True to allow lists * @param string $formId The id of action form. * * @return string HTML string for the button * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton($type = 'Standard', $name = '', $text = '', $task = '', $list = true, $formId = null) { $this->name($name) ->text($text) ->task($task) ->listCheck($list); if ($formId !== null) { $this->form($formId); } return $this->renderButton($this->options); } /** * Fetch button class for standard buttons. * * @param string $name The button name. * * @return string * * @since 4.0.0 */ public function fetchButtonClass(string $name): string { switch ($name) { case 'apply': case 'new': case 'save': case 'save-new': case 'save-copy': case 'save-close': case 'publish': return 'btn btn-success'; case 'featured': return 'btn btn-warning'; case 'cancel': case 'trash': case 'delete': case 'unpublish': return 'btn btn-danger'; default: return 'btn btn-primary'; } } /** * Get the JavaScript command for the button * * @return string JavaScript command string * * @since 3.0 */ protected function _getCommand() { Text::script($this->getListCheckMessage() ?: 'JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST'); Text::script('ERROR'); $cmd = "Joomla.submitbutton('" . $this->getTask() . "');"; if ($this->getListCheck()) { $messages = "{error: [Joomla.Text._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST')]}"; $alert = 'Joomla.renderMessages(' . $messages . ')'; $cmd = 'if (document.adminForm.boxchecked.value == 0) { ' . $alert . ' } else { ' . $cmd . ' }'; } return $cmd; } } PK1A#]}k�qq"src/Toolbar/Button/PopupButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Toolbar\ToolbarButton; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a modal window button * * @method self url(string $value) * @method self icon(string $value) * @method self iframeWidth(int $value) * @method self iframeHeight(int $value) * @method self bodyHeight(int $value) * @method self modalWidth(int $value) * @method self onclose(string $value) * @method self title(string $value) * @method self footer(string $value) * @method self selector(string $value) * @method self listCheck(bool $value) * @method string getUrl() * @method int getIframeWidth() * @method int getIframeHeight() * @method int getBodyHeight() * @method int getModalWidth() * @method string getOnclose() * @method string getTitle() * @method string getFooter() * @method string getSelector() * @method bool getListCheck() * * @since 3.0 */ class PopupButton extends ToolbarButton { /** * Property layout. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.toolbar.popup'; /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { $options['icon'] = $options['icon'] ?? 'icon-square'; parent::prepareOptions($options); $options['doTask'] = $this->_getCommand($this->getUrl()); $options['selector'] = $options['selector'] ?? 'modal-' . $this->getName(); } /** * Fetch the HTML for the button * * @param string $type Unused string, formerly button type. * @param string $name Modal name, used to generate element ID * @param string $text The link text * @param string $url URL for popup * @param integer $iframeWidth Width of popup * @param integer $iframeHeight Height of popup * @param integer $bodyHeight Optional height of the modal body in viewport units (vh) * @param integer $modalWidth Optional width of the modal in viewport units (vh) * @param string $onClose JavaScript for the onClose event. * @param string $title The title text * @param string $footer The footer html * * @return string HTML string for the button * * @since 3.0 */ public function fetchButton( $type = 'Modal', $name = '', $text = '', $url = '', $iframeWidth = 640, $iframeHeight = 480, $bodyHeight = null, $modalWidth = null, $onClose = '', $title = '', $footer = null ) { $this->name($name) ->text($text) ->task($this->_getCommand($url)) ->url($url) ->icon('icon-' . $name) ->iframeWidth($iframeWidth) ->iframeHeight($iframeHeight) ->bodyHeight($bodyHeight) ->modalWidth($modalWidth) ->onclose($onClose) ->title($title) ->footer($footer); return $this->renderButton($this->options); } /** * Render button HTML. * * @param array $options The button options. * * @return string The button HTML. * * @since 4.0.0 */ protected function renderButton(array &$options): string { $html = []; $html[] = parent::renderButton($options); if ((string) $this->getUrl() !== '') { // Build the options array for the modal $params = []; $params['title'] = $options['title'] ?? $options['text']; $params['url'] = $this->getUrl(); $params['height'] = $options['iframeHeight'] ?? 480; $params['width'] = $options['iframeWidth'] ?? 640; $params['bodyHeight'] = $options['bodyHeight'] ?? null; $params['modalWidth'] = $options['modalWidth'] ?? null; // Place modal div and scripts in a new div $html[] = '<div class="btn-group" style="width: 0; margin: 0; padding: 0;">'; $selector = $options['selector']; $footer = $this->getFooter(); if ($footer !== null) { $params['footer'] = $footer; } $html[] = HTMLHelper::_('bootstrap.renderModal', $selector, $params); $html[] = '</div>'; // We have to move the modal, otherwise we get problems with the backdrop // @todo: There should be a better workaround than this Factory::getDocument()->addScriptDeclaration( <<<JS document.addEventListener('DOMContentLoaded', function() { var modal =document.getElementById('{$options['selector']}'); document.body.appendChild(modal); if (Joomla && Joomla.Bootstrap && Joomla.Bootstrap.Methods && Joomla.Bootstrap.Methods.Modal) { Joomla.Bootstrap.Methods.Initialise.Modal(modal); } }); JS ); } // If an $onClose event is passed, add it to the modal JS object if ((string) $this->getOnclose() !== '') { Factory::getDocument()->addScriptDeclaration( <<<JS document.addEventListener('DOMContentLoaded', function() { document.querySelector('#{$options['selector']}').addEventListener('hide.bs.modal', function() { {$options['onclose']} }); }); JS ); } return implode("\n", $html); } /** * Get the JavaScript command for the button * * @param string $url URL for popup * * @return string JavaScript command string * * @since 3.0 */ private function _getCommand($url) { $url = $url ?? ''; if (strpos($url, 'http') !== 0) { $url = Uri::base() . $url; } return $url; } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'url', 'iframeWidth', 'iframeHeight', 'bodyHeight', 'modalWidth', 'onclose', 'title', 'footer', 'selector', 'listCheck', ] ); } } PK1A#]&J $ !src/Toolbar/Button/HelpButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Help\Help; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a help popup window button * * @method self ref(string $value) * @method self component(string $value) * @method self useComponent(bool $value) * @method self url(string $value) * @method string getRef() * @method string getComponent() * @method bool getUseComponent() * @method string getUrl() * * @since 3.0 */ class HelpButton extends BasicButton { /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { $options['text'] = $options['text'] ?: 'JTOOLBAR_HELP'; $options['icon'] = $options['icon'] ?? 'icon-question'; $options['button_class'] = ($options['button_class'] ?? 'btn btn-info') . ' js-toolbar-help-btn'; $options['attributes']['data-url'] = $this->_getCommand(); $options['attributes']['data-title'] = Text::_('JHELP'); $options['attributes']['data-width'] = 700; $options['attributes']['data-height'] = 500; $options['attributes']['data-scroll'] = true; parent::prepareOptions($options); } /** * Fetches the button HTML code. * * @param string $type Unused string. * @param string $ref The name of the help screen (its key reference). * @param boolean $com Use the help file in the component directory. * @param string $override Use this URL instead of any other. * @param string $component Name of component to get Help (null for current component) * * @return string * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton($type = 'Help', $ref = '', $com = false, $override = null, $component = null) { $this->name('help') ->ref($ref) ->useComponent($com) ->component($component) ->url($override); return $this->renderButton($this->options); } /** * Get the JavaScript command for the button * * @return string JavaScript command string * * @since 3.0 */ protected function _getCommand() { // Get Help URL return Help::createUrl($this->getRef(), $this->getUseComponent(), $this->getUrl(), $this->getComponent()); } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'ref', 'useComponent', 'component', 'url', ] ); } } PK1A#]��!/� � %src/Toolbar/Button/DropdownButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\ToolbarButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Render dropdown buttons. * * @method self toggleSplit(bool $value) * @method self toggleButtonClass(string $value) * @method bool getToggleSplit() * @method string getToggleButtonClass() * * @since 4.0.0 */ class DropdownButton extends AbstractGroupButton { /** * Property layout. * * @var string * @since 4.0.0 */ protected $layout = 'joomla.toolbar.dropdown'; /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 * @throws \Exception */ protected function prepareOptions(array &$options) { parent::prepareOptions($options); $childToolbar = $this->getChildToolbar(); $options['hasButtons'] = \count($childToolbar->getItems()) > 0; $buttons = $childToolbar->getItems(); if ($options['hasButtons']) { if ($this->getOption('toggleSplit', true)) { /** @var ToolbarButton $button */ $button = array_shift($buttons); $childToolbar->setItems($buttons); $options['button'] = $button->render(); $options['caretClass'] = $options['toggleButtonClass'] ?? $button->getButtonClass(); $options['dropdownItems'] = $childToolbar->render(['is_child' => true]); } else { $options['dropdownItems'] = $childToolbar->render(['is_child' => true]); $button = new BasicButton($this->getName(), $this->getText(), $options); $options['button'] = $button ->setParent($this->parent) ->buttonClass($button->getButtonClass() . ' dropdown-toggle') ->attributes( [ 'data-bs-toggle' => 'dropdown', 'data-bs-target' => '#' . $this->fetchId(), 'aria-haspopup' => 'true', 'aria-expanded' => 'false', ] ) ->render(); } } } /** * Render button HTML. * * @param array $options The button options. * * @return string The button HTML. * * @since 4.0.0 */ protected function renderButton(array &$options): string { return parent::renderButton($options); } /** * Get the button CSS Id. * * @return string Button CSS Id * * @since 4.0.0 */ protected function fetchId() { return $this->parent->getName() . '-dropdown-' . $this->getName(); } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'toggleSplit', 'toggleButtonClass', ] ); } } PK1A#]����II"src/Toolbar/Button/BasicButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\ToolbarButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a basic button. * * @since 4.0.0 */ class BasicButton extends ToolbarButton { /** * Property layout. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.toolbar.basic'; /** * Fetch the HTML for the button * * @param string $type Unused string. * * @return void * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. * * @throws \LogicException */ public function fetchButton($type = 'Basic') { throw new \LogicException('This is a new button in 4.0, please use render() instead.'); } } PK1A#]mc-YY!src/Toolbar/Button/LinkButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\ToolbarButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a link button * * @method self url(string $value) * @method self target(string $value) * @method string getUrl() * @method string getTarget() * * @since 3.0 */ class LinkButton extends ToolbarButton { /** * Property layout. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.toolbar.link'; /** * Prepare options for this button. * * @param array $options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { parent::prepareOptions($options); unset($options['attributes']['type']); } /** * Fetch the HTML for the button * * @param string $type Unused string. * @param string $name Name to be used as apart of the id * @param string $text Button text * @param string $url The link url * * @return string HTML string for the button * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton($type = 'Link', $name = 'back', $text = '', $url = null) { $this->name($name) ->text($text) ->url($url); return $this->renderButton($this->options); } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'url', 'target', ] ); } } PK1A#]A�E66*src/Toolbar/Button/AbstractGroupButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\Toolbar; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The AbstractGroupButton class. * * @since 4.0.0 */ abstract class AbstractGroupButton extends BasicButton { /** * The child Toolbar instance. * * @var Toolbar * * @since 4.0.0 */ protected $child; /** * Add children buttons as dropdown. * * @param callable $handler The callback to configure dropdown items. * * @return static * * @since 4.0.0 */ public function configure(callable $handler): self { $child = $this->getChildToolbar(); $handler($child); return $this; } /** * Get child toolbar. * * @return Toolbar Return new child Toolbar instance. * * @since 4.0.0 */ public function getChildToolbar(): Toolbar { if (!$this->child) { $this->child = $this->parent->createChild($this->getName() . '-children'); } return $this->child; } /** * Get the button CSS Id. * * @return string Button CSS Id * * @since 4.0.0 */ protected function fetchId() { return $this->parent->getName() . '-group-' . $this->getName(); } } PK1A#]�8M��#src/Toolbar/Button/CustomButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\ToolbarButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a custom button * * @method self html(string $value) * @method string getHtml() * * @since 3.0 */ class CustomButton extends ToolbarButton { /** * Render button HTML. * * @param array $options The button options. * * @return string The button HTML. * * @since 4.0.0 */ protected function renderButton(array &$options): string { return (string) ($options['html'] ?? ''); } /** * Fetch the HTML for the button * * @param string $type Button type, unused string. * @param string $html HTML string for the button * @param string $id CSS id for the button * * @return string HTML string for the button * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton($type = 'Custom', $html = '', $id = 'custom') { return $html; } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return array_merge( parent::getAccessors(), [ 'html', ] ); } } PK1A#]���ě�&src/Toolbar/Button/SeparatorButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar\Button; use Joomla\CMS\Toolbar\ToolbarButton; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Renders a button separator * * @since 3.0 */ class SeparatorButton extends ToolbarButton { /** * Property layout. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.toolbar.separator'; /** * Empty implementation (not required for separator) * * @return void * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ public function fetchButton() { } } PK1A#]D��,�@�@ src/Toolbar/CoreButtonsTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Toolbar\Button\ConfirmButton; use Joomla\CMS\Toolbar\Button\CustomButton; use Joomla\CMS\Toolbar\Button\HelpButton; use Joomla\CMS\Toolbar\Button\InlinehelpButton; use Joomla\CMS\Toolbar\Button\LinkButton; use Joomla\CMS\Toolbar\Button\PopupButton; use Joomla\CMS\Toolbar\Button\SeparatorButton; use Joomla\CMS\Toolbar\Button\StandardButton; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Enhance Toolbar class to add more pre-defined methods. * * @since 4.0.0 */ trait CoreButtonsTrait { /** * Writes a divider between dropdown menu items. * * @param string $text The text of button. * * @return SeparatorButton * * @since 4.0.0 */ public function divider(string $text = ''): SeparatorButton { return $this->separatorButton('divider', $text); } /** * Writes a preview button for a given option (opens a popup window). * * @param string $url The name of the popup file (excluding the file extension) * @param string $text The text of button. * @param bool $newWindow Whether to open the preview in _blank or just a modal * * @return PopupButton|LinkButton * * @since 4.0.0 */ public function preview(string $url, string $text = 'JGLOBAL_PREVIEW', $newWindow = false) { if ($newWindow === true) { $button = $this->linkButton('link', $text) ->url($url) ->attributes(['target' => '_blank']) ->icon('icon-eye'); } else { $button = $this->popupButton('preview', $text) ->url($url) ->iframeWidth(640) ->iframeHeight(480) ->icon('icon-eye'); } return $button; } /** * Writes a jooa11y accessibility checker button for a given option (opens a popup window). * * @param string $url The url to open * @param string $text The text of button. * @param bool $newWindow Whether to open the preview in _blank or just a modal * * @return PopupButton|LinkButton * * @since 4.1.0 */ public function jooa11y(string $url, string $text = 'JGLOBAL_JOOA11Y', $newWindow = false) { if ($newWindow === true) { $button = $this->linkButton('jooa11y-link', $text) ->url($url) ->attributes(['target' => '_blank']) ->icon('icon-universal-access'); } else { $button = $this->popupButton('jooa11y-preview', $text) ->url($url) ->iframeWidth(640) ->iframeHeight(480) ->icon('icon-universal-access'); } return $button; } /** * Writes a help button for a given option (opens a popup window). * * @param string $ref The name of the popup file (excluding the file extension for an xml file). * @param bool $useComponent Use the help file in the component directory. * @param string $url Use this URL instead of any other. * @param string $component Name of component to get Help (null for current component) * * @return HelpButton * * @since 4.0.0 */ public function help($ref, $useComponent = false, $url = null, $component = null): HelpButton { return $this->helpButton('help', 'JTOOLBAR_HELP') ->ref($ref) ->useComponent($useComponent) ->url($url) ->component($component); } /** * Writes a help button for a given option (opens a popup window). * * @param string $class The class used by the inline help items. * * @return InlinehelpButton * * @since 4.3.0 */ public function inlinehelp(string $class = 'hide-aware-inline-help'): InlinehelpButton { return $this->inlinehelpButton('inlinehelp') ->targetclass($class) ->icon('fa fa-question-circle'); } /** * Writes a cancel button that will go back to the previous page without doing * any other operation. * * @param string $text The text of button. * * @return LinkButton * * @since 4.0.0 */ public function back(string $text = 'JTOOLBAR_BACK'): LinkButton { return $this->link('back', $text) ->url('javascript:history.back();'); } /** * Creates a button to redirect to a link. * * @param string $text Button text. * @param string $url The link url. * * @return LinkButton * * @since 4.0.0 */ public function link(string $text, string $url): LinkButton { return $this->linkButton('link', $text) ->url($url); } /** * Writes a media_manager button. * * @param string $directory The subdirectory to upload the media to. * @param string $text An override for the alt text. * * @return PopupButton * * @since 4.0.0 */ public function mediaManager(string $directory, string $text = 'JTOOLBAR_UPLOAD'): PopupButton { return $this->popupButton('upload', $text) ->iframeWidth(800) ->iframeHeight(520) ->url('index.php?option=com_media&tmpl=component&task=popupUpload&folder=' . $directory); } /** * Writes a common 'default' button for a record. * * @param string $task An override for the task. * @param string $text An override for the alt text. * * @return StandardButton * * @since 4.0.0 */ public function makeDefault(string $task, string $text = 'JTOOLBAR_DEFAULT'): StandardButton { return $this->standardButton('default', $text, $task); } /** * Writes a common 'assign' button for a record. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function assign(string $task, string $text = 'JTOOLBAR_ASSIGN'): StandardButton { return $this->standardButton('assign', $text, $task); } /** * Writes the common 'new' icon for the button bar. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function addNew(string $task, string $text = 'JTOOLBAR_NEW'): StandardButton { return $this->standardButton('new', $text, $task); } /** * Writes a common 'publish' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function publish(string $task, string $text = 'JTOOLBAR_PUBLISH'): StandardButton { return $this->standardButton('publish', $text, $task); } /** * Writes a common 'unpublish' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function unpublish(string $task, string $text = 'JTOOLBAR_UNPUBLISH'): StandardButton { return $this->standardButton('unpublish', $text, $task); } /** * Writes a common 'archive' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function archive(string $task, string $text = 'JTOOLBAR_ARCHIVE'): StandardButton { return $this->standardButton('archive', $text, $task); } /** * Writes a common 'unarchive' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function unarchive(string $task, string $text = 'JTOOLBAR_UNARCHIVE'): StandardButton { return $this->standardButton('unarchive', $text, $task); } /** * Writes a common 'edit' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function edit(string $task, string $text = 'JTOOLBAR_EDIT'): StandardButton { return $this->standardButton('edit', $text, $task); } /** * Writes a common 'editHtml' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function editHtml(string $task, string $text = 'JTOOLBAR_EDIT_HTML'): StandardButton { return $this->standardButton('edithtml', $text, $task); } /** * Writes a common 'editCss' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function editCss(string $task, string $text = 'JTOOLBAR_EDIT_CSS'): StandardButton { return $this->standardButton('editcss', $text, $task); } /** * Writes a common 'delete' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return ConfirmButton * * @since 4.0.0 */ public function delete(string $task, string $text = 'JTOOLBAR_DELETE'): ConfirmButton { return $this->confirmButton('delete', $text, $task); } /** * Writes a common 'trash' button. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function trash(string $task, string $text = 'JTOOLBAR_TRASH'): StandardButton { return $this->standardButton('trash', $text, $task); } /** * Writes a save button for a given option. * Apply operation leads to a save action only (does not leave edit mode). * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function apply(string $task, string $text = 'JTOOLBAR_APPLY'): StandardButton { return $this->standardButton('apply', $text, $task) ->formValidation(true); } /** * Writes a save button for a given option. * Save operation leads to a save and then close action. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function save(string $task, string $text = 'JTOOLBAR_SAVE'): StandardButton { return $this->standardButton('save', $text, $task) ->formValidation(true); } /** * Writes a save and create new button for a given option. * Save and create operation leads to a save and then add action. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function save2new(string $task, string $text = 'JTOOLBAR_SAVE_AND_NEW'): StandardButton { return $this->standardButton('save-new', $text, $task) ->formValidation(true); } /** * Writes a save as copy button for a given option. * Save as copy operation leads to a save after clearing the key, * then returns user to edit mode with new key. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function save2copy(string $task, string $text = 'JTOOLBAR_SAVE_AS_COPY'): StandardButton { return $this->standardButton('save-copy', $text, $task) ->formValidation(true); } /** * Writes a checkin button for a given option. * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function checkin(string $task, string $text = 'JTOOLBAR_CHECKIN'): StandardButton { return $this->standardButton('checkin', $text, $task) ->listCheck(true); } /** * Writes a cancel button and invokes a cancel operation (eg a checkin). * * @param string $task The task name of this button. * @param string $text The text of this button. * * @return StandardButton * * @since 4.0.0 */ public function cancel(string $task, string $text = 'JTOOLBAR_CLOSE'): StandardButton { return $this->standardButton('cancel', $text, $task); } /** * Writes a configuration button and invokes a cancel operation (eg a checkin). * * @param string $component The name of the component, eg, com_content. * @param string $text The text of this button. * @param string $path An alternative path for the configuration xml relative to JPATH_SITE. * * @return LinkButton * * @since 4.0.0 */ public function preferences(string $component, string $text = 'JTOOLBAR_OPTIONS', string $path = ''): LinkButton { $component = urlencode($component); $path = urlencode($path); $uri = (string) Uri::getInstance(); $return = urlencode(base64_encode($uri)); return $this->linkButton('options', $text) ->url('index.php?option=com_config&view=component&component=' . $component . '&path=' . $path . '&return=' . $return); } /** * Writes a version history * * @param string $typeAlias The component and type, for example 'com_content.article' * @param integer $itemId The id of the item, for example the article id. * @param integer $height The height of the popup. * @param integer $width The width of the popup. * @param string $text The name of the button. * * @return CustomButton * * @since 4.0.0 */ public function versions( string $typeAlias, int $itemId, int $height = 800, int $width = 500, string $text = 'JTOOLBAR_VERSIONS' ): CustomButton { $lang = Factory::getLanguage(); $lang->load('com_contenthistory', JPATH_ADMINISTRATOR, $lang->getTag(), true); // Options array for Layout $options = []; $options['title'] = Text::_($text); $options['height'] = $height; $options['width'] = $width; $options['itemId'] = $typeAlias . '.' . $itemId; $layout = new FileLayout('joomla.toolbar.versions'); return $this->customHtml($layout->render($options), 'version'); } /** * Writes a custom HTML to toolbar. * * @param string $html The HTML string to write. * @param string $name The button name. * * @return CustomButton * * @since 4.0.0 */ public function customHtml(string $html, string $name = 'custom'): CustomButton { return $this->customButton($name) ->html($html); } } PK1A#]��n//'src/Toolbar/ToolbarFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for creating toolbar objects * * @since 4.0.0 */ interface ToolbarFactoryInterface { /** * Creates a new toolbar button. * * @param Toolbar $toolbar The Toolbar instance to attach to the button * @param string $type Button Type * * @return ToolbarButton * * @since 4.0.0 * @throws \InvalidArgumentException */ public function createButton(Toolbar $toolbar, string $type): ToolbarButton; /** * Creates a new Toolbar object. * * @param string $name The toolbar name. * * @return Toolbar * * @since 4.0.0 */ public function createToolbar(string $name = 'toolbar'): Toolbar; } PK1A#]b��|�/�/src/Toolbar/ToolbarButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Toolbar; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The ToolbarButton class. * * @method self text(string $value) * @method self task(string $value) * @method self icon(string $value) * @method self buttonClass(string $value) * @method self attributes(array $value) * @method self onclick(string $value) * @method self listCheck(bool $value) * @method self listCheckMessage(string $value) * @method self form(string $value) * @method self formValidation(bool $value) * @method string getText() * @method string getTask() * @method string getIcon() * @method string getButtonClass() * @method array getAttributes() * @method string getOnclick() * @method bool getListCheck() * @method string getListCheckMessage() * @method string getForm() * @method bool getFormValidation() * * @since 4.0.0 */ abstract class ToolbarButton { /** * Name of this button. * * @var string * * @since 4.0.0 */ protected $name; /** * Reference to the object that instantiated the element * * @var Toolbar * * @since 4.0.0 */ protected $parent; /** * The layout path to render this button. * * @var string * * @since 4.0.0 */ protected $layout; /** * Button options. * * @var array * * @since 4.0.0 */ protected $options = []; /** * Used to track an ids, to avoid duplication * * @var array * * @since 4.0.0 */ protected static $idCounter = []; /** * Init this class. * * @param string $name Name of this button. * @param string $text The button text, will auto translate. * @param array $options Button options. * * @since 4.0.0 * * @throws \InvalidArgumentException */ public function __construct(string $name = '', string $text = '', array $options = []) { $this->name($name) ->text($text); $this->options = ArrayHelper::mergeRecursive($this->options, $options); } /** * Prepare options for this button. * * @param array &$options The options about this button. * * @return void * * @since 4.0.0 */ protected function prepareOptions(array &$options) { $options['name'] = $this->getName(); $options['text'] = Text::_($this->getText()); $options['class'] = $this->getIcon() ?: $this->fetchIconClass($this->getName()); $options['id'] = $this->ensureUniqueId($this->fetchId()); if (!empty($options['is_child'])) { $options['tagName'] = 'button'; $options['btnClass'] = ($options['button_class'] ?? '') . ' dropdown-item'; $options['attributes']['type'] = 'button'; if ($options['is_first_child']) { $options['btnClass'] .= ' first'; } if ($options['is_last_child']) { $options['btnClass'] .= ' last'; } } else { $options['tagName'] = 'button'; $options['btnClass'] = ($options['button_class'] ?? 'btn btn-primary'); $options['attributes']['type'] = 'button'; } } /** * Get the HTML to render the button * * @param array &$definition Parameters to be passed * * @return string * * @since 3.0 * * @throws \Exception */ public function render(&$definition = null) { if ($definition === null) { $action = $this->renderButton($this->options); } elseif (\is_array($definition)) { // For B/C $action = $this->fetchButton(...$definition); } else { throw new \InvalidArgumentException('Wrong argument: $definition, should be NULL or array.'); } // Build the HTML Button $layout = new FileLayout('joomla.toolbar.base'); return $layout->render( [ 'action' => $action, 'options' => $this->options, ] ); } /** * Render button HTML. * * @param array &$options The button options. * * @return string The button HTML. * * @since 4.0.0 */ protected function renderButton(array &$options): string { $this->prepareOptions($options); // Prepare custom attributes. unset( $options['attributes']['id'], $options['attributes']['class'] ); $options['htmlAttributes'] = ArrayHelper::toString($options['attributes']); // Isolate button class from icon class $buttonClass = str_replace('icon-', '', $this->getName()); $iconclass = $options['btnClass'] ?? ''; $options['btnClass'] = 'button-' . $buttonClass . ' ' . $iconclass; // Instantiate a new LayoutFile instance and render the layout $layout = new FileLayout($this->layout); return $layout->render($options); } /** * Get the button CSS Id. * * @return string Button CSS Id * * @since 3.0 */ protected function fetchId() { return $this->parent->getName() . '-' . str_ireplace(' ', '-', $this->getName()); } /** * Method to get the CSS class name for an icon identifier * * Can be redefined in the final class * * @param string $identifier Icon identification string * * @return string CSS class name * * @since 3.0 */ public function fetchIconClass($identifier) { // It's an ugly hack, but this allows templates to define the icon classes for the toolbar $layout = new FileLayout('joomla.toolbar.iconclass'); return $layout->render(['icon' => $identifier]); } /** * Get the button * * Defined in the final button class * * @return string * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Use render() instead. */ abstract public function fetchButton(); /** * Get parent toolbar instance. * * @return Toolbar * * @since 4.0.0 */ public function getParent(): Toolbar { return $this->parent; } /** * Set parent Toolbar instance. * * @param Toolbar $parent The parent Toolbar instance to set. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function setParent(Toolbar $parent): self { $this->parent = $parent; return $this; } /** * Get button options. * * @return array * * @since 4.0.0 */ public function getOptions(): array { return $this->options; } /** * Set all options. * * @param array $options The button options. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function setOptions(array $options): self { $this->options = $options; return $this; } /** * Get single option value. * * @param string $name The option name. * @param mixed $default The default value if this name not exists. * * @return mixed * * @since 4.0.0 */ public function getOption(string $name, $default = null) { return $this->options[$name] ?? $default; } /** * Set option value. * * @param string $name The option name to store value. * @param mixed $value The option value. * * @return static * * @since 4.0.0 */ public function setOption(string $name, $value): self { $this->options[$name] = $value; return $this; } /** * Get button name. * * @return string * * @since 4.0.0 */ public function getName(): string { return $this->name; } /** * Set button name. * * @param string $name The button name. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function name(string $name): self { $this->name = $name; return $this; } /** * Get layout path. * * @return string * * @since 4.0.0 */ public function getLayout(): string { return $this->layout; } /** * Set layout path. * * @param string $layout The layout path name to render. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function layout(string $layout): self { $this->layout = $layout; return $this; } /** * Make sure the id is unique * * @param string $id The id string. * * @return string * * @since 4.0.0 */ protected function ensureUniqueId(string $id): string { if (\array_key_exists($id, static::$idCounter)) { static::$idCounter[$id]++; $id .= static::$idCounter[$id]; } else { static::$idCounter[$id] = 0; } return $id; } /** * Magiix method to adapt option accessors. * * @param string $name The method name. * @param array $args The method arguments. * * @return mixed * * @throws \LogicException * * @since 4.0.0 */ public function __call(string $name, array $args) { // Getter if (stripos($name, 'get') === 0) { $fieldName = static::findOptionName(lcfirst(substr($name, 3))); if ($fieldName !== false) { return $this->getOption($fieldName); } } else { // Setter $fieldName = static::findOptionName($name); if ($fieldName !== false) { if (!\array_key_exists(0, $args)) { throw new \InvalidArgumentException( sprintf( '%s::%s() miss first argument.', \get_called_class(), $name ) ); } return $this->setOption($fieldName, $args[0]); } } throw new \BadMethodCallException( sprintf( 'Method %s() not found in class: %s', $name, \get_called_class() ) ); } /** * Find field option name from accessors. * * @param string $name The field name. * * @return boolean|string * * @since 4.0.0 */ private static function findOptionName(string $name) { $accessors = static::getAccessors(); if (\in_array($name, $accessors, true)) { return $accessors[array_search($name, $accessors, true)]; } // Getter with alias if (isset($accessors[$name])) { return $accessors[$name]; } return false; } /** * Method to configure available option accessors. * * @return array * * @since 4.0.0 */ protected static function getAccessors(): array { return [ 'text', 'task', 'icon', 'attributes', 'onclick', 'buttonClass' => 'button_class', 'listCheck', 'listCheckMessage', 'form', 'formValidation', ]; } } PK1A#]<�� � !src/Component/ComponentRecord.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Object representing a component extension record * * @since 3.7.0 */ class ComponentRecord { /** * Primary key * * @var integer * @since 3.7.0 */ public $id; /** * The component name * * @var integer * @since 3.7.0 */ public $option; /** * The component parameters * * @var string|Registry * @since 3.7.0 * @note This field is protected to require reading this field to proxy through the getter to convert the params to a Registry instance */ protected $params; /** * The extension namespace * * @var string * @since 4.0.0 */ public $namespace; /** * Indicates if this component is enabled * * @var integer * @since 3.7.0 */ public $enabled; /** * Class constructor * * @param array $data The component record data to load * * @since 3.7.0 */ public function __construct($data = []) { foreach ((array) $data as $key => $value) { $this->$key = $value; } } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.7.0 * * @deprecated 4.3 will be removed in 6.0 * Access the item parameters through the `getParams()` method * Example: * $componentRecord->getParams(); */ public function __get($name) { if ($name === 'params') { return $this->getParams(); } return $this->$name; } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.7.0 * * @deprecated 4.3 will be removed in 6.0 * Set the item parameters through the `setParams()` method * Example: * $componentRecord->setParams($value); */ public function __set($name, $value) { if ($name === 'params') { $this->setParams($value); return; } $this->$name = $value; } /** * Returns the menu item parameters * * @return Registry * * @since 3.7.0 */ public function getParams() { if (!($this->params instanceof Registry)) { $this->params = new Registry($this->params); } return $this->params; } /** * Sets the menu item parameters * * @param Registry|string $params The data to be stored as the parameters * * @return void * * @since 3.7.0 */ public function setParams($params) { $this->params = $params; } } PK1A#]ib��<�<!src/Component/ComponentHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component; use Joomla\CMS\Access\Access; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Component\Exception\MissingComponentException; use Joomla\CMS\Dispatcher\ApiDispatcher; use Joomla\CMS\Dispatcher\ComponentDispatcher; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Profiler\Profiler; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Component helper class * * @since 1.5 */ class ComponentHelper { /** * The component list cache * * @var ComponentRecord[] * @since 1.6 */ protected static $components = []; /** * Get the component information. * * @param string $option The component option. * @param boolean $strict If set and the component does not exist, the enabled attribute will be set to false. * * @return ComponentRecord An object with the information for the component. * * @since 1.5 */ public static function getComponent($option, $strict = false) { $components = static::getComponents(); if (isset($components[$option])) { return $components[$option]; } $result = new ComponentRecord(); $result->enabled = $strict ? false : true; $result->setParams(new Registry()); return $result; } /** * Checks if the component is enabled * * @param string $option The component option. * * @return boolean * * @since 1.5 */ public static function isEnabled($option) { $components = static::getComponents(); return isset($components[$option]) && $components[$option]->enabled; } /** * Checks if a component is installed * * @param string $option The component option. * * @return integer * * @since 3.4 */ public static function isInstalled($option) { $components = static::getComponents(); return isset($components[$option]) ? 1 : 0; } /** * Gets the parameter object for the component * * @param string $option The option for the component. * @param boolean $strict If set and the component does not exist, false will be returned * * @return Registry A Registry object. * * @see Registry * @since 1.5 */ public static function getParams($option, $strict = false) { return static::getComponent($option, $strict)->getParams(); } /** * Applies the global text filters to arbitrary text as per settings for current user groups * * @param string $text The string to filter * * @return string The filtered string * * @since 2.5 */ public static function filterText($text) { // Punyencoding utf8 email addresses $text = InputFilter::getInstance()->emailToPunycode($text); // Filter settings $config = static::getParams('com_config'); $user = Factory::getUser(); $userGroups = Access::getGroupsByUser($user->get('id')); $filters = $config->get('filters'); $forbiddenListTags = []; $forbiddenListAttributes = []; $customListTags = []; $customListAttributes = []; $allowedListTags = []; $allowedListAttributes = []; $allowedList = false; $forbiddenList = false; $customList = false; $unfiltered = false; // Cycle through each of the user groups the user is in. // Remember they are included in the Public group as well. foreach ($userGroups as $groupId) { // May have added a group by not saved the filters. if (!isset($filters->$groupId)) { continue; } // Each group the user is in could have different filtering properties. $filterData = $filters->$groupId; $filterType = strtoupper($filterData->filter_type); if ($filterType === 'NH') { // Maximum HTML filtering. } elseif ($filterType === 'NONE') { // No HTML filtering. $unfiltered = true; } else { // Forbidden list or allowed list. // Preprocess the tags and attributes. $tags = explode(',', $filterData->filter_tags); $attributes = explode(',', $filterData->filter_attributes); $tempTags = []; $tempAttributes = []; foreach ($tags as $tag) { $tag = trim($tag); if ($tag) { $tempTags[] = $tag; } } foreach ($attributes as $attribute) { $attribute = trim($attribute); if ($attribute) { $tempAttributes[] = $attribute; } } // Collect the forbidden list or allowed list tags and attributes. // Each list is cumulative. if ($filterType === 'BL') { $forbiddenList = true; $forbiddenListTags = array_merge($forbiddenListTags, $tempTags); $forbiddenListAttributes = array_merge($forbiddenListAttributes, $tempAttributes); } elseif ($filterType === 'CBL') { // Only set to true if Tags or Attributes were added if ($tempTags || $tempAttributes) { $customList = true; $customListTags = array_merge($customListTags, $tempTags); $customListAttributes = array_merge($customListAttributes, $tempAttributes); } } elseif ($filterType === 'WL') { $allowedList = true; $allowedListTags = array_merge($allowedListTags, $tempTags); $allowedListAttributes = array_merge($allowedListAttributes, $tempAttributes); } } } // Remove duplicates before processing (because the forbidden list uses both sets of arrays). $forbiddenListTags = array_unique($forbiddenListTags); $forbiddenListAttributes = array_unique($forbiddenListAttributes); $customListTags = array_unique($customListTags); $customListAttributes = array_unique($customListAttributes); $allowedListTags = array_unique($allowedListTags); $allowedListAttributes = array_unique($allowedListAttributes); if (!$unfiltered) { // Custom Forbidden list precedes Default forbidden list. if ($customList) { $filter = InputFilter::getInstance([], [], 1, 1); // Override filter's default forbidden tags and attributes if ($customListTags) { $filter->blockedTags = $customListTags; } if ($customListAttributes) { $filter->blockedAttributes = $customListAttributes; } } elseif ($forbiddenList) { // Forbidden list takes second precedence. // Remove the allowed tags and attributes from the forbidden list. $forbiddenListTags = array_diff($forbiddenListTags, $allowedListTags); $forbiddenListAttributes = array_diff($forbiddenListAttributes, $allowedListAttributes); $filter = InputFilter::getInstance( $forbiddenListTags, $forbiddenListAttributes, InputFilter::ONLY_BLOCK_DEFINED_TAGS, InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES ); // Remove the allowed tags from filter's default forbidden list. if ($allowedListTags) { $filter->blockedTags = array_diff($filter->blockedTags, $allowedListTags); } // Remove the allowed attributes from filter's default forbidden list. if ($allowedListAttributes) { $filter->blockedAttributes = array_diff($filter->blockedAttributes, $allowedListAttributes); } } elseif ($allowedList) { // Allowed lists take third precedence. // Turn off XSS auto clean $filter = InputFilter::getInstance($allowedListTags, $allowedListAttributes, 0, 0, 0); } else { // No HTML takes last place. $filter = InputFilter::getInstance(); } $text = $filter->clean($text, 'html'); } return $text; } /** * Render the component. * * @param string $option The component option. * @param array $params The component parameters * * @return string * * @since 1.5 * @throws MissingComponentException */ public static function renderComponent($option, $params = []) { $app = Factory::getApplication(); $lang = Factory::getLanguage(); if (!$app->isClient('api')) { // Load template language files. $template = $app->getTemplate(true)->template; $lang->load('tpl_' . $template, JPATH_BASE) || $lang->load('tpl_' . $template, JPATH_THEMES . "/$template"); } if (empty($option)) { throw new MissingComponentException(Text::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'), 404); } if (JDEBUG) { Profiler::getInstance('Application')->mark('beforeRenderComponent ' . $option); } // Record the scope $scope = $app->scope; // Set scope to component name $app->scope = $option; // Build the component path. $option = preg_replace('/[^A-Z0-9_\.-]/i', '', $option); // Define component path. if (!\defined('JPATH_COMPONENT')) { /** * Defines the path to the active component for the request * * Note this constant is application aware and is different for each application (site/admin). * * @var string * @since 1.5 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ \define('JPATH_COMPONENT', JPATH_BASE . '/components/' . $option); } if (!\defined('JPATH_COMPONENT_SITE')) { /** * Defines the path to the site element of the active component for the request * * @var string * @since 1.5 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ \define('JPATH_COMPONENT_SITE', JPATH_SITE . '/components/' . $option); } if (!\defined('JPATH_COMPONENT_ADMINISTRATOR')) { /** * Defines the path to the admin element of the active component for the request * * @var string * @since 1.5 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ \define('JPATH_COMPONENT_ADMINISTRATOR', JPATH_ADMINISTRATOR . '/components/' . $option); } // If component is disabled throw error if (!static::isEnabled($option)) { throw new MissingComponentException(Text::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'), 404); } ob_start(); $app->bootComponent($option)->getDispatcher($app)->dispatch(); $contents = ob_get_clean(); // Revert the scope $app->scope = $scope; if (JDEBUG) { Profiler::getInstance('Application')->mark('afterRenderComponent ' . $option); } return $contents; } /** * Load the installed components into the components property. * * @return boolean True on success * * @since 3.2 */ protected static function load() { $loader = function () { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName(['extension_id', 'element', 'params', 'enabled'], ['id', 'option', null, null])) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('type') . ' = ' . $db->quote('component'), $db->quoteName('state') . ' = 0', $db->quoteName('enabled') . ' = 1', ] ); $components = []; $db->setQuery($query); foreach ($db->getIterator() as $component) { $components[$component->option] = new ComponentRecord((array) $component); } return $components; }; /** @var CallbackController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('callback', ['defaultgroup' => '_system']); try { static::$components = $cache->get($loader, [], __METHOD__); } catch (CacheExceptionInterface $e) { static::$components = $loader(); } return true; } /** * Get installed components * * @return ComponentRecord[] The components property * * @since 3.6.3 */ public static function getComponents() { if (empty(static::$components)) { static::load(); } return static::$components; } /** * Returns the component name (eg. com_content) for the given object based on the class name. * If the object is not namespaced, then the alternative name is used. * * @param object $object The object controller or model * @param string $alternativeName Mostly the value of getName() from the object * * @return string The name * * @since 4.0.0 */ public static function getComponentName($object, string $alternativeName): string { $reflect = new \ReflectionClass($object); if (!$reflect->getNamespaceName() || \get_class($object) === ComponentDispatcher::class || \get_class($object) === ApiDispatcher::class) { return 'com_' . strtolower($alternativeName); } $from = strpos($reflect->getNamespaceName(), '\\Component'); $to = strpos(substr($reflect->getNamespaceName(), $from + 11), '\\'); return 'com_' . strtolower(substr($reflect->getNamespaceName(), $from + 11, $to)); } } PK1A#]�&�� 5src/Component/Exception/MissingComponentException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Exception; use Joomla\CMS\Router\Exception\RouteNotFoundException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an error for a missing component * * @since 3.7.0 */ class MissingComponentException extends RouteNotFoundException { /** * Constructor * * @param string $message The Exception message to throw. * @param integer $code The Exception code. * @param \Exception $previous The previous exception used for the exception chaining. * * @since 3.7.0 */ public function __construct($message = '', $code = 404, \Exception $previous = null) { parent::__construct($message, $code, $previous); } } PK1A#]֯��-src/Component/Router/Rules/RulesInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router\Rules; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RouterRules interface for Joomla * * @since 3.4 */ interface RulesInterface { /** * Prepares a query set to be handed over to the build() method. * This should complete a partial query set to work as a complete non-SEFed * URL and in general make sure that all information is present and properly * formatted. For example, the Itemid should be retrieved and set here. * * @param array &$query The query array to process * * @return void * * @since 3.4 */ public function preprocess(&$query); /** * Parses a URI to retrieve information for the right route through the component. * This method should retrieve all its input from its method arguments. * * @param array &$segments The URL segments to parse * @param array &$vars The vars that result from the segments * * @return void * * @since 3.4 */ public function parse(&$segments, &$vars); /** * Builds URI segments from a query to encode the necessary information for a route in a human-readable URL. * This method should retrieve all its input from its method arguments. * * @param array &$query The vars that should be converted * @param array &$segments The URL segments to create * * @return void * * @since 3.4 */ public function build(&$query, &$segments); } PK1A#]*�v�***src/Component/Router/Rules/NomenuRules.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router\Rules; use Joomla\CMS\Component\Router\RouterView; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Rule to process URLs without a menu item * * @since 3.4 */ class NomenuRules implements RulesInterface { /** * Router this rule belongs to * * @var RouterView * @since 3.4 */ protected $router; /** * Class constructor. * * @param RouterView $router Router this rule belongs to * * @since 3.4 */ public function __construct(RouterView $router) { $this->router = $router; } /** * Dummy method to fulfil the interface requirements * * @param array &$query The query array to process * * @return void * * @since 3.4 * @codeCoverageIgnore */ public function preprocess(&$query) { } /** * Parse a menu-less URL * * @param array &$segments The URL segments to parse * @param array &$vars The vars that result from the segments * * @return void * * @since 3.4 */ public function parse(&$segments, &$vars) { $active = $this->router->menu->getActive(); if (!\is_object($active)) { $views = $this->router->getViews(); if (isset($views[$segments[0]])) { $vars['view'] = array_shift($segments); $view = $views[$vars['view']]; if (isset($view->key) && isset($segments[0])) { if (\is_callable([$this->router, 'get' . ucfirst($view->name) . 'Id'])) { $input = $this->router->app->getInput(); if ($view->parent_key && $input->get($view->parent_key)) { $vars[$view->parent->key] = $input->get($view->parent_key); $vars[$view->parent_key] = $input->get($view->parent_key); } if ($view->nestable) { $vars[$view->key] = 0; while (count($segments)) { $segment = array_shift($segments); $result = \call_user_func_array([$this->router, 'get' . ucfirst($view->name) . 'Id'], [$segment, $vars]); if (!$result) { array_unshift($segments, $segment); break; } $vars[$view->key] = preg_replace('/-/', ':', $result, 1); } } else { $segment = array_shift($segments); $result = \call_user_func_array([$this->router, 'get' . ucfirst($view->name) . 'Id'], [$segment, $vars]); $vars[$view->key] = preg_replace('/-/', ':', $result, 1); } } else { $vars[$view->key] = preg_replace('/-/', ':', array_shift($segments), 1); } } } } } /** * Build a menu-less URL * * @param array &$query The vars that should be converted * @param array &$segments The URL segments to create * * @return void * * @since 3.4 */ public function build(&$query, &$segments) { $menu_found = false; if (isset($query['Itemid'])) { $item = $this->router->menu->getItem($query['Itemid']); if ( !isset($query['option']) || ($item && isset($item->query['option']) && $item->query['option'] === $query['option']) ) { $menu_found = true; } } if (!$menu_found && isset($query['view'])) { $views = $this->router->getViews(); if (isset($views[$query['view']])) { $view = $views[$query['view']]; $segments[] = $query['view']; if ($view->key && isset($query[$view->key])) { if (\is_callable([$this->router, 'get' . ucfirst($view->name) . 'Segment'])) { $result = \call_user_func_array([$this->router, 'get' . ucfirst($view->name) . 'Segment'], [$query[$view->key], $query]); if ($view->nestable) { array_pop($result); while (count($result)) { $segments[] = str_replace(':', '-', array_pop($result)); } } else { $segments[] = str_replace(':', '-', array_pop($result)); } } else { $segments[] = str_replace(':', '-', $query[$view->key]); } unset($query[$views[$query['view']]->key]); } unset($query['view']); } } } } PK1A#]���$$,src/Component/Router/Rules/StandardRules.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router\Rules; use Joomla\CMS\Component\Router\RouterView; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Rule for the standard handling of component routing * * @since 3.4 */ class StandardRules implements RulesInterface { /** * Router this rule belongs to * * @var RouterView * @since 3.4 */ protected $router; /** * Class constructor. * * @param RouterView $router Router this rule belongs to * * @since 3.4 */ public function __construct(RouterView $router) { $this->router = $router; } /** * Dummy method to fulfil the interface requirements * * @param array &$query The query array to process * * @return void * * @since 3.4 */ public function preprocess(&$query) { } /** * Parse the URL * * @param array &$segments The URL segments to parse * @param array &$vars The vars that result from the segments * * @return void * * @since 3.4 */ public function parse(&$segments, &$vars) { // Get the views and the currently active query vars $views = $this->router->getViews(); $active = $this->router->menu->getActive(); if ($active) { $vars = array_merge($active->query, $vars); } // We don't have a view or its not a view of this component! We stop here if (!isset($vars['view']) || !isset($views[$vars['view']])) { return; } // Copy the segments, so that we can iterate over all of them and at the same time modify the original segments $tempSegments = $segments; // Iterate over the segments as long as a segment fits foreach ($tempSegments as $segment) { // Our current view is nestable. We need to check first if the segment fits to that if ($views[$vars['view']]->nestable) { if (\is_callable([$this->router, 'get' . ucfirst($views[$vars['view']]->name) . 'Id'])) { $key = \call_user_func_array([$this->router, 'get' . ucfirst($views[$vars['view']]->name) . 'Id'], [$segment, $vars]); // Did we get a proper key? If not, we need to look in the child-views if ($key) { $vars[$views[$vars['view']]->key] = $key; array_shift($segments); continue; } } else { // The router is not complete. The get<View>Id() method is missing. return; } } // Lets find the right view that belongs to this segment $found = false; foreach ($views[$vars['view']]->children as $view) { if (!$view->key) { if ($view->name === $segment) { // The segment is a view name $parent = $views[$vars['view']]; $vars['view'] = $view->name; $found = true; if ($view->parent_key && isset($vars[$parent->key])) { $parent_key = $vars[$parent->key]; $vars[$view->parent_key] = $parent_key; unset($vars[$parent->key]); } break; } } elseif (\is_callable([$this->router, 'get' . ucfirst($view->name) . 'Id'])) { // Hand the data over to the router specific method and see if there is a content item that fits $key = \call_user_func_array([$this->router, 'get' . ucfirst($view->name) . 'Id'], [$segment, $vars]); if ($key) { // Found the right view and the right item $parent = $views[$vars['view']]; $vars['view'] = $view->name; $found = true; if ($view->parent_key && isset($vars[$parent->key])) { $parent_key = $vars[$parent->key]; $vars[$view->parent_key] = $parent_key; unset($vars[$parent->key]); } $vars[$view->key] = $key; break; } } } if (!$found) { return; } array_shift($segments); } } /** * Build a standard URL * * @param array &$query The vars that should be converted * @param array &$segments The URL segments to create * * @return void * * @since 3.4 */ public function build(&$query, &$segments) { if (!isset($query['Itemid'], $query['view'])) { return; } // Get the menu item belonging to the Itemid that has been found $item = $this->router->menu->getItem($query['Itemid']); if ( $item === null || $item->component !== 'com_' . $this->router->getName() || !isset($item->query['view']) ) { return; } // Get menu item layout $mLayout = isset($item->query['layout']) ? $item->query['layout'] : null; // Get menu item filter_tag $mFilterTag = isset($item->query['filter_tag']) ? $item->query['filter_tag'] : null; // Get all views for this component $views = $this->router->getViews(); // Return directly when the URL of the Itemid is identical with the URL to build if ($item->query['view'] === $query['view']) { $view = $views[$query['view']]; if (!$view->key) { unset($query['view']); if (isset($query['layout']) && $mLayout === $query['layout']) { unset($query['layout']); } if (isset($query['filter_tag']) && $mFilterTag === $query['filter_tag']) { unset($query['filter_tag']); } return; } if (isset($query[$view->key]) && $item->query[$view->key] == (int) $query[$view->key]) { unset($query[$view->key]); while ($view) { unset($query[$view->parent_key]); $view = $view->parent; } unset($query['view']); if (isset($query['layout']) && $mLayout === $query['layout']) { unset($query['layout']); } if (isset($query['filter_tag']) && $mFilterTag === $query['filter_tag']) { unset($query['filter_tag']); } return; } } // Get the path from the view of the current URL and parse it to the menu item $path = array_reverse($this->router->getPath($query), true); $found = false; foreach ($path as $element => $ids) { $view = $views[$element]; if ($found === false && $item->query['view'] === $element) { if ($view->nestable) { $found = true; } elseif ($view->children) { $found = true; continue; } } if ($found === false) { // Jump to the next view continue; } if ($ids) { if ($view->nestable) { $found2 = false; foreach (array_reverse($ids, true) as $id => $segment) { if ($found2) { $segments[] = str_replace(':', '-', $segment); } elseif ((int) $item->query[$view->key] === (int) $id) { $found2 = true; } } } elseif ($ids === true) { $segments[] = $element; } else { $segments[] = str_replace(':', '-', current($ids)); } } if ($view->parent_key) { // Remove parent key from query unset($query[$view->parent_key]); } } if ($found) { unset($query[$views[$query['view']]->key], $query['view']); if (isset($query['layout']) && $mLayout === $query['layout']) { unset($query['layout']); } if (isset($query['filter_tag']) && $mFilterTag === $query['filter_tag']) { unset($query['filter_tag']); } } } } PK1A#]�w�<"<"(src/Component/Router/Rules/MenuRules.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router\Rules; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Component\Router\RouterView; use Joomla\CMS\Language\Multilanguage; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Rule to identify the right Itemid for a view in a component * * @since 3.4 */ class MenuRules implements RulesInterface { /** * Router this rule belongs to * * @var RouterView * @since 3.4 */ protected $router; /** * Lookup array of the menu items * * @var array * @since 3.4 */ protected $lookup = []; /** * Class constructor. * * @param RouterView $router Router this rule belongs to * * @since 3.4 */ public function __construct(RouterView $router) { $this->router = $router; $this->buildLookup(); } /** * Finds the right Itemid for this query * * @param array &$query The query array to process * * @return void * * @since 3.4 */ public function preprocess(&$query) { $active = $this->router->menu->getActive(); /** * If the active item id is not the same as the supplied item id or we have a supplied item id and no active * menu item then we just use the supplied menu item and continue */ if (isset($query['Itemid']) && ($active === null || $query['Itemid'] != $active->id)) { return; } // Get query language $language = isset($query['lang']) ? $query['lang'] : '*'; // Set the language to the current one when multilang is enabled and item is tagged to ALL if (Multilanguage::isEnabled() && $language === '*') { $language = $this->router->app->get('language'); } if (!isset($this->lookup[$language])) { $this->buildLookup($language); } // Check if the active menu item matches the requested query if ($active !== null && isset($query['Itemid'])) { // Check if active->query and supplied query are the same $match = true; foreach ($active->query as $k => $v) { if (isset($query[$k]) && $v !== $query[$k]) { // Compare again without alias if (\is_string($v) && $v == current(explode(':', $query[$k], 2))) { continue; } $match = false; break; } } if ($match) { // Just use the supplied menu item return; } } $needles = $this->router->getPath($query); $layout = isset($query['layout']) && $query['layout'] !== 'default' ? ':' . $query['layout'] : ''; if ($needles) { foreach ($needles as $view => $ids) { $viewLayout = $view . $layout; if ($layout && isset($this->lookup[$language][$viewLayout])) { if (\is_bool($ids)) { $query['Itemid'] = $this->lookup[$language][$viewLayout]; return; } foreach ($ids as $id => $segment) { if (isset($this->lookup[$language][$viewLayout][(int) $id])) { $query['Itemid'] = $this->lookup[$language][$viewLayout][(int) $id]; return; } } } if (isset($this->lookup[$language][$view])) { if (\is_bool($ids)) { $query['Itemid'] = $this->lookup[$language][$view]; return; } foreach ($ids as $id => $segment) { if (isset($this->lookup[$language][$view][(int) $id])) { $query['Itemid'] = $this->lookup[$language][$view][(int) $id]; return; } } } } } // Check if the active menuitem matches the requested language if ( $active && $active->component === 'com_' . $this->router->getName() && ($language === '*' || \in_array($active->language, ['*', $language]) || !Multilanguage::isEnabled()) ) { $query['Itemid'] = $active->id; return; } // If not found, return language specific home link $default = $this->router->menu->getDefault($language); if (!empty($default->id)) { $query['Itemid'] = $default->id; } } /** * Method to build the lookup array * * @param string $language The language that the lookup should be built up for * * @return void * * @since 3.4 */ protected function buildLookup($language = '*') { // Prepare the reverse lookup array. if (!isset($this->lookup[$language])) { $this->lookup[$language] = []; $component = ComponentHelper::getComponent('com_' . $this->router->getName()); $views = $this->router->getViews(); $attributes = ['component_id']; $values = [(int) $component->id]; $attributes[] = 'language'; $values[] = [$language, '*']; $items = $this->router->menu->getItems($attributes, $values); foreach ($items as $item) { if (isset($item->query['view'], $views[$item->query['view']])) { $view = $item->query['view']; $layout = ''; if (isset($item->query['layout'])) { $layout = ':' . $item->query['layout']; } if ($views[$view]->key) { if (!isset($this->lookup[$language][$view . $layout])) { $this->lookup[$language][$view . $layout] = []; } if (!isset($this->lookup[$language][$view])) { $this->lookup[$language][$view] = []; } // If menuitem has no key set, we assume 0. if (!isset($item->query[$views[$view]->key])) { $item->query[$views[$view]->key] = 0; } /** * Here it will become a bit tricky * language != * can override existing entries * language == * cannot override existing entries */ if (!isset($this->lookup[$language][$view . $layout][$item->query[$views[$view]->key]]) || $item->language !== '*') { $this->lookup[$language][$view . $layout][$item->query[$views[$view]->key]] = $item->id; $this->lookup[$language][$view][$item->query[$views[$view]->key]] = $item->id; } } else { /** * Here it will become a bit tricky * language != * can override existing entries * language == * cannot override existing entries */ if (!isset($this->lookup[$language][$view . $layout]) || $item->language !== '*') { $this->lookup[$language][$view . $layout] = $item->id; } } } } } } /** * Dummy method to fulfil the interface requirements * * @param array &$segments The URL segments to parse * @param array &$vars The vars that result from the segments * * @return void * * @since 3.4 * @codeCoverageIgnore */ public function parse(&$segments, &$vars) { } /** * Dummy method to fulfil the interface requirements * * @param array &$query The vars that should be converted * @param array &$segments The URL segments to create * * @return void * * @since 3.4 * @codeCoverageIgnore */ public function build(&$query, &$segments) { } } PK1A#]h2%���#src/Component/Router/RouterBase.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base component routing class * * @since 3.3 */ abstract class RouterBase implements RouterInterface { /** * Application object to use in the router * * @var \Joomla\CMS\Application\CMSApplication * @since 3.4 */ public $app; /** * Menu object to use in the router * * @var \Joomla\CMS\Menu\AbstractMenu * @since 3.4 */ public $menu; /** * Class constructor. * * @param \Joomla\CMS\Application\CMSApplication $app Application-object that the router should use * @param \Joomla\CMS\Menu\AbstractMenu $menu Menu-object that the router should use * * @since 3.4 */ public function __construct($app = null, $menu = null) { if ($app) { $this->app = $app; } else { $this->app = Factory::getApplication(); } if ($menu) { $this->menu = $menu; } else { $this->menu = $this->app->getMenu(); } } /** * Generic method to preprocess a URL * * @param array $query An associative array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function preprocess($query) { return $query; } } PK1A#]� �f f &src/Component/Router/RouterFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Categories\CategoryFactoryInterface; use Joomla\CMS\Menu\AbstractMenu; use Joomla\Database\DatabaseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default router factory. * * @since 4.0.0 */ class RouterFactory implements RouterFactoryInterface { /** * The namespace to create the categories from. * * @var string * @since 4.0.0 */ private $namespace; /** * The category factory * * @var CategoryFactoryInterface * * @since 4.0.0 */ private $categoryFactory; /** * The db * * @var DatabaseInterface * * @since 4.0.0 */ private $db; /** * The namespace must be like: * Joomla\Component\Content * * @param string $namespace The namespace * @param CategoryFactoryInterface $categoryFactory The category object * @param DatabaseInterface $db The database object * * @since 4.0.0 */ public function __construct($namespace, CategoryFactoryInterface $categoryFactory = null, DatabaseInterface $db = null) { $this->namespace = $namespace; $this->categoryFactory = $categoryFactory; $this->db = $db; } /** * Creates a router. * * @param CMSApplicationInterface $application The application * @param AbstractMenu $menu The menu object to work with * * @return RouterInterface * * @since 4.0.0 */ public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface { $className = trim($this->namespace, '\\') . '\\' . ucfirst($application->getName()) . '\\Service\\Router'; if (!class_exists($className)) { throw new \RuntimeException('No router available for this application.'); } return new $className($application, $menu, $this->categoryFactory, $this->db); } } PK1A#]"��G��/src/Component/Router/RouterFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Menu\AbstractMenu; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Router factory interface * * @since 4.0.0 */ interface RouterFactoryInterface { /** * Creates a router. * * @param CMSApplicationInterface $application The application * @param AbstractMenu $menu The menu object to work with * * @return RouterInterface * * @since 4.0.0 */ public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface; } PK1A#]�ӟuWW0src/Component/Router/RouterViewConfiguration.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * View-configuration class for the view-based component router * * @since 3.5 */ class RouterViewConfiguration { /** * Name of the view * * @var string * @since 3.5 */ public $name; /** * Key of the view * * @var string * @since 3.5 */ public $key = false; /** * Parentview of this one * * @var RouterViewConfiguration * @since 3.5 */ public $parent = false; /** * Key of the parent view * * @var string * @since 3.5 */ public $parent_key = false; /** * Is this view nestable? * * @var bool * @since 3.5 */ public $nestable = false; /** * Layouts that are supported by this view * * @var array * @since 3.5 */ public $layouts = ['default']; /** * Child-views of this view * * @var RouterViewConfiguration[] * @since 3.5 */ public $children = []; /** * Keys used for this parent view by the child views * * @var array * @since 3.5 */ public $child_keys = []; /** * Path of views from this one to the root view * * @var array * @since 3.5 */ public $path = []; /** * Constructor for the View-configuration class * * @param string $name Name of the view * * @since 3.5 */ public function __construct($name) { $this->name = $name; $this->path[] = $name; } /** * Set the name of the view * * @param string $name Name of the view * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function setName($name) { $this->name = $name; array_pop($this->path); $this->path[] = $name; return $this; } /** * Set the key-identifier for the view * * @param string $key Key of the view * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function setKey($key) { $this->key = $key; return $this; } /** * Set the parent view of this view * * @param RouterViewConfiguration $parent Parent view object * @param string $parentKey Key of the parent view in this context * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function setParent(RouterViewConfiguration $parent, $parentKey = null) { if ($this->parent) { $key = array_search($this, $this->parent->children); if ($key !== false) { unset($this->parent->children[$key]); } if ($this->parent_key) { $child_key = array_search($this->parent_key, $this->parent->child_keys); unset($this->parent->child_keys[$child_key]); } } $this->parent = $parent; $parent->children[] = $this; $this->path = $parent->path; $this->path[] = $this->name; $this->parent_key = $parentKey ?? false; if ($parentKey) { $parent->child_keys[] = $parentKey; } return $this; } /** * Set if this view is nestable or not * * @param bool $isNestable If set to true, the view is nestable * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function setNestable($isNestable = true) { $this->nestable = (bool) $isNestable; return $this; } /** * Add a layout to this view * * @param string $layout Layouts that this view supports * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function addLayout($layout) { $this->layouts[] = $layout; $this->layouts = array_unique($this->layouts); return $this; } /** * Remove a layout from this view * * @param string $layout Layouts that this view supports * * @return RouterViewConfiguration This object for chaining * * @since 3.5 */ public function removeLayout($layout) { $key = array_search($layout, $this->layouts); if ($key !== false) { unset($this->layouts[$key]); } return $this; } } PK1A#]^� M) ) %src/Component/Router/RouterLegacy.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default routing class for missing or legacy component routers * * @since 3.3 */ class RouterLegacy implements RouterInterface { /** * Name of the component * * @var string * @since 3.3 */ protected $component; /** * Constructor * * @param string $component Component name without the com_ prefix this router should react upon * * @since 3.3 */ public function __construct($component) { $this->component = $component; } /** * Generic preprocess function for missing or legacy component router * * @param array $query An associative array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function preprocess($query) { return $query; } /** * Generic build function for missing or legacy component router * * @param array &$query An array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function build(&$query) { $function = $this->component . 'BuildRoute'; if (\function_exists($function)) { $segments = $function($query); $total = \count($segments); for ($i = 0; $i < $total; $i++) { $segments[$i] = str_replace(':', '-', $segments[$i]); } return $segments; } return []; } /** * Generic parse function for missing or legacy component router * * @param array &$segments The segments of the URL to parse. * * @return array The URL attributes to be used by the application. * * @since 3.3 */ public function parse(&$segments) { $function = $this->component . 'ParseRoute'; if (\function_exists($function)) { $total = \count($segments); for ($i = 0; $i < $total; $i++) { $segments[$i] = preg_replace('/-/', ':', $segments[$i], 1); } return $function($segments); } return []; } } PK1A#]v�"���+src/Component/Router/RouterServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Menu\AbstractMenu; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait to implement AssociationServiceInterface * * @since 4.0.0 */ trait RouterServiceTrait { /** * The router factory. * * @var RouterFactoryInterface * * @since 4.0.0 */ private $routerFactory = null; /** * Returns the router. * * @param CMSApplicationInterface $application The application object * @param AbstractMenu $menu The menu object to work with * * @return RouterInterface * * @since 4.0.0 */ public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface { return $this->routerFactory->createRouter($application, $menu); } /** * The router factory. * * @param RouterFactoryInterface $routerFactory The router factory * * @return void * * @since 4.0.0 */ public function setRouterFactory(RouterFactoryInterface $routerFactory) { $this->routerFactory = $routerFactory; } } PK1A#]�0N�(src/Component/Router/RouterInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Component routing interface * * @since 3.3 */ interface RouterInterface { /** * Prepare-method for URLs * This method is meant to validate and complete the URL parameters. * For example it can add the Itemid or set a language parameter. * This method is executed on each URL, regardless of SEF mode switched * on or not. * * @param array $query An associative array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function preprocess($query); /** * Build method for URLs * This method is meant to transform the query parameters into a more human * readable form. It is only executed when SEF mode is switched on. * * @param array &$query An array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function build(&$query); /** * Parse method for URLs * This method is meant to transform the human readable URL back into * query parameters. It is only executed when SEF mode is switched on. * * @param array &$segments The segments of the URL to parse. * * @return array The URL attributes to be used by the application. * * @since 3.3 */ public function parse(&$segments); } PK1A#]V�ñ#src/Component/Router/RouterView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Component\Router\Rules\RulesInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * View-based component routing class * * @since 3.5 */ abstract class RouterView extends RouterBase { /** * Name of the router of the component * * @var string * @since 3.5 */ protected $name; /** * Array of rules * * @var RulesInterface[] * @since 3.5 */ protected $rules = []; /** * Views of the component * * @var RouterViewConfiguration[] * @since 3.5 */ protected $views = []; /** * Register the views of a component * * @param RouterViewConfiguration $view View configuration object * * @return void * * @since 3.5 */ public function registerView(RouterViewConfiguration $view) { $this->views[$view->name] = $view; } /** * Return an array of registered view objects * * @return RouterViewConfiguration[] Array of registered view objects * * @since 3.5 */ public function getViews() { return $this->views; } /** * Get the path of views from target view to root view * including content items of a nestable view * * @param array $query Array of query elements * * @return array List of views including IDs of content items * * @since 3.5 */ public function getPath($query) { $views = $this->getViews(); $result = []; // Get the right view object if (isset($query['view']) && isset($views[$query['view']])) { $viewobj = $views[$query['view']]; } // Get the path from the current item to the root view with all IDs if (isset($viewobj)) { $path = array_reverse($viewobj->path); $start = true; $childkey = false; foreach ($path as $element) { $view = $views[$element]; if ($start) { $key = $view->key; $start = false; } else { $key = $childkey; } $childkey = $view->parent_key; if (($key || $view->key) && \is_callable([$this, 'get' . ucfirst($view->name) . 'Segment'])) { if (isset($query[$key])) { $result[$view->name] = \call_user_func_array([$this, 'get' . ucfirst($view->name) . 'Segment'], [$query[$key], $query]); } elseif (isset($query[$view->key])) { $result[$view->name] = \call_user_func_array([$this, 'get' . ucfirst($view->name) . 'Segment'], [$query[$view->key], $query]); } else { $result[$view->name] = []; } } else { $result[$view->name] = true; } } } return $result; } /** * Get all currently attached rules * * @return RulesInterface[] All currently attached rules in an array * * @since 3.5 */ public function getRules() { return $this->rules; } /** * Add a number of router rules to the object * * @param RulesInterface[] $rules Array of JComponentRouterRulesInterface objects * * @return void * * @since 3.5 */ public function attachRules($rules) { foreach ($rules as $rule) { $this->attachRule($rule); } } /** * Attach a build rule * * @param RulesInterface $rule The function to be called. * * @return void * * @since 3.5 */ public function attachRule(RulesInterface $rule) { $this->rules[] = $rule; } /** * Remove a build rule * * @param RulesInterface $rule The rule to be removed. * * @return boolean Was a rule removed? * * @since 3.5 */ public function detachRule(RulesInterface $rule) { foreach ($this->rules as $id => $r) { if ($r == $rule) { unset($this->rules[$id]); return true; } } return false; } /** * Generic method to preprocess a URL * * @param array $query An associative array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.5 */ public function preprocess($query) { // Process the parsed variables based on custom defined rules foreach ($this->rules as $rule) { $rule->preprocess($query); } return $query; } /** * Build method for URLs * * @param array &$query Array of query elements * * @return array Array of URL segments * * @since 3.5 */ public function build(&$query) { $segments = []; // Process the parsed variables based on custom defined rules foreach ($this->rules as $rule) { $rule->build($query, $segments); } return $segments; } /** * Parse method for URLs * * @param array &$segments Array of URL string-segments * * @return array Associative array of query values * * @since 3.5 */ public function parse(&$segments) { $vars = []; // Process the parsed variables based on custom defined rules foreach ($this->rules as $rule) { $rule->parse($segments, $vars); } return $vars; } /** * Method to return the name of the router * * @return string Name of the router * * @since 3.5 */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)Router/i', \get_class($this), $r)) { throw new \Exception('JLIB_APPLICATION_ERROR_ROUTER_GET_NAME', 500); } $this->name = str_replace('com_', '', ComponentHelper::getComponentName($this, strtolower($r[1]))); } return $this->name; } } PK1A#]�D�ݩ�/src/Component/Router/RouterServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Component\Router; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Menu\AbstractMenu; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The component router service. * * @since 4.0.0 */ interface RouterServiceInterface { /** * Returns the router. * * @param CMSApplicationInterface $application The application object * @param AbstractMenu $menu The menu object to work with * * @return RouterInterface * * @since 4.0.0 */ public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface; } PK1A#]ŌG�||&src/Form/Field/SessionhandlerField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Session\Session; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a select list of session handler options. * * @since 1.7.0 */ class SessionhandlerField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Sessionhandler'; /** * Method to get the session handler field options. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; // Get the options from the session object. foreach (Session::getHandlers() as $store) { $options[] = HTMLHelper::_('select.option', strtolower($store), Text::_('JLIB_FORM_VALUE_SESSION_' . $store), 'value', 'text'); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]�|1��6�6 src/Form/Field/CalendarField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use DateTime; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\Language\Text; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * * Provides a pop up date picker linked to a button. * Optionally may be filtered to use user's or server's time zone. * * @since 1.7.0 */ class CalendarField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Calendar'; /** * The allowable maxlength of calendar field. * * @var integer * @since 3.2 */ protected $maxlength; /** * The format of date and time. * * @var string * @since 3.2 */ protected $format; /** * The format will be used to filter submitted date and time. * * @var string * @since 4.0.1 */ protected $filterFormat; /** * The filter. * * @var string * @since 3.2 */ protected $filter; /** * The minimum year number to subtract/add from the current year * * @var integer * @since 3.7.0 */ protected $minyear; /** * The maximum year number to subtract/add from the current year * * @var integer * @since 3.7.0 */ protected $maxyear; /** * The today button flag * * @var string * @since 4.3.0 */ protected $todaybutton; /** * The week numbers flag * * @var string * @since 4.3.0 */ protected $weeknumbers; /** * The show time flag * * @var string * @since 4.3.0 */ protected $showtime; /** * The fill table flag * * @var string * @since 4.3.0 */ protected $filltable; /** * The time format * * @var integer * @since 4.3.0 */ protected $timeformat; /** * The single header flag * * @var string * @since 4.3.0 */ protected $singleheader; /** * Name of the layout being used to render the field * * @var string * @since 3.7.0 */ protected $layout = 'joomla.form.field.calendar'; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'maxlength': case 'format': case 'filterFormat': case 'filter': case 'timeformat': case 'todaybutton': case 'singleheader': case 'weeknumbers': case 'showtime': case 'filltable': case 'minyear': case 'maxyear': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'maxlength': case 'maxyear': case 'minyear': case 'timeformat': $this->$name = (int) $value; break; case 'todaybutton': case 'singleheader': case 'weeknumbers': case 'showtime': case 'filltable': case 'format': case 'filterFormat': case 'filter': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->maxlength = (int) $this->element['maxlength'] ? (int) $this->element['maxlength'] : 45; $this->format = (string) $this->element['format'] ? (string) $this->element['format'] : '%Y-%m-%d'; $this->filterFormat = (string) $this->element['filterformat'] ? (string) $this->element['filterformat'] : ''; $this->filter = (string) $this->element['filter'] ? (string) $this->element['filter'] : 'USER_UTC'; $this->todaybutton = (string) $this->element['todaybutton'] ? (string) $this->element['todaybutton'] : 'true'; $this->weeknumbers = (string) $this->element['weeknumbers'] ? (string) $this->element['weeknumbers'] : 'true'; $this->showtime = (string) $this->element['showtime'] ? (string) $this->element['showtime'] : 'false'; $this->filltable = (string) $this->element['filltable'] ? (string) $this->element['filltable'] : 'true'; $this->timeformat = (int) $this->element['timeformat'] ? (int) $this->element['timeformat'] : 24; $this->singleheader = (string) $this->element['singleheader'] ? (string) $this->element['singleheader'] : 'false'; $this->minyear = \strlen((string) $this->element['minyear']) ? (int) $this->element['minyear'] : null; $this->maxyear = \strlen((string) $this->element['maxyear']) ? (int) $this->element['maxyear'] : null; if ($this->maxyear < 0 || $this->minyear > 0) { $this->todaybutton = 'false'; } $translateFormat = (string) $this->element['translateformat']; if ($translateFormat && $translateFormat !== 'false') { $showTime = (string) $this->element['showtime']; $lang = Factory::getLanguage(); $debug = $lang->setDebug(false); if ($showTime && $showTime !== 'false') { $this->format = Text::_('DATE_FORMAT_CALENDAR_DATETIME'); $this->filterFormat = Text::_('DATE_FORMAT_FILTER_DATETIME'); } else { $this->format = Text::_('DATE_FORMAT_CALENDAR_DATE'); $this->filterFormat = Text::_('DATE_FORMAT_FILTER_DATE'); } $lang->setDebug($debug); } } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { $user = Factory::getApplication()->getIdentity(); // If a known filter is given use it. switch (strtoupper($this->filter)) { case 'SERVER_UTC': // Convert a date to UTC based on the server timezone. if ($this->value && $this->value != $this->getDatabase()->getNullDate()) { // Get a date object based on the correct timezone. $date = Factory::getDate($this->value, 'UTC'); $date->setTimezone(new \DateTimeZone(Factory::getApplication()->get('offset'))); // Transform the date string. $this->value = $date->format('Y-m-d H:i:s', true, false); } break; case 'USER_UTC': // Convert a date to UTC based on the user timezone. if ($this->value && $this->value != $this->getDatabase()->getNullDate()) { // Get a date object based on the correct timezone. $date = Factory::getDate($this->value, 'UTC'); $date->setTimezone($user->getTimezone()); // Transform the date string. $this->value = $date->format('Y-m-d H:i:s', true, false); } break; } // Format value when not nulldate ('0000-00-00 00:00:00'), otherwise blank it as it would result in 1970-01-01. if ($this->value && $this->value != $this->getDatabase()->getNullDate() && strtotime($this->value) !== false) { $tz = date_default_timezone_get(); date_default_timezone_set('UTC'); if ($this->filterFormat) { $date = \DateTimeImmutable::createFromFormat('U', strtotime($this->value)); $this->value = $date->format($this->filterFormat); } else { $this->value = strftime($this->format, strtotime($this->value)); } date_default_timezone_set($tz); } else { $this->value = ''; } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $lang = Factory::getApplication()->getLanguage(); $calendar = $lang->getCalendar(); $direction = strtolower(Factory::getDocument()->getDirection()); // Get the appropriate file for the current language date helper $helperPath = 'system/fields/calendar-locales/date/gregorian/date-helper.min.js'; if ($calendar && is_dir(JPATH_ROOT . '/media/system/js/fields/calendar-locales/date/' . strtolower($calendar))) { $helperPath = 'system/fields/calendar-locales/date/' . strtolower($calendar) . '/date-helper.min.js'; } $extraData = [ 'value' => $this->value, 'maxLength' => $this->maxlength, 'format' => $this->format, 'filter' => $this->filter, 'todaybutton' => ($this->todaybutton === 'true') ? 1 : 0, 'weeknumbers' => ($this->weeknumbers === 'true') ? 1 : 0, 'showtime' => ($this->showtime === 'true') ? 1 : 0, 'filltable' => ($this->filltable === 'true') ? 1 : 0, 'timeformat' => $this->timeformat, 'singleheader' => ($this->singleheader === 'true') ? 1 : 0, 'helperPath' => $helperPath, 'minYear' => $this->minyear, 'maxYear' => $this->maxyear, 'direction' => $direction, 'calendar' => $calendar, 'firstday' => $lang->getFirstDay(), 'weekend' => explode(',', $lang->getWeekEnd()), ]; return array_merge($data, $extraData); } /** * Method to filter a field value. * * @param mixed $value The optional value to use as the default for the field. * @param string $group The optional dot-separated form group path on which to find the field. * @param ?Registry $input An optional Registry object with the entire data set to filter * against the entire form. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter($value, $group = null, Registry $input = null) { // Make sure there is a valid SimpleXMLElement. if (!($this->element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); } if ((int) $value <= 0) { return ''; } if ($this->filterFormat) { $value = \DateTime::createFromFormat($this->filterFormat, $value)->format('Y-m-d H:i:s'); } $app = Factory::getApplication(); // Get the field filter type. $filter = (string) $this->element['filter']; $return = $value; switch (strtoupper($filter)) { // Convert a date to UTC based on the server timezone offset. case 'SERVER_UTC': // Return an SQL formatted datetime string in UTC. $return = Factory::getDate($value, $app->get('offset'))->toSql(); break; // Convert a date to UTC based on the user timezone offset. case 'USER_UTC': // Get the user timezone setting defaulting to the server timezone setting. $offset = $app->getIdentity()->getParam('timezone', $app->get('offset')); // Return an SQL formatted datetime string in UTC. $return = Factory::getDate($value, $offset)->toSql(); break; } return $return; } } PK1A#]t�{?ccsrc/Form/Field/StatusField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field to load a list of states * * @since 3.2 */ class StatusField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.2 */ public $type = 'Status'; /** * Available statuses * * @var string[] * @since 3.2 */ protected $predefinedOptions = [ -2 => 'JTRASHED', 0 => 'JUNPUBLISHED', 1 => 'JPUBLISHED', 2 => 'JARCHIVED', '*' => 'JALL', ]; } PK1A#]X�kksrc/Form/Field/CaptchaField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Captcha\Captcha; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Captcha field. * * @since 2.5 */ class CaptchaField extends FormField { /** * The field type. * * @var string * @since 2.5 */ protected $type = 'Captcha'; /** * The plugin of the captcha field. * * @var string * @since 4.3.0 */ protected $plugin; /** * The namespace of the captcha field. * * @var string * @since 4.3.0 */ protected $namespace; /** * The captcha base instance of our type. * * @var ?Captcha */ protected $_captcha; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'plugin': case 'namespace': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'plugin': case 'namespace': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 2.5 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); $app = Factory::getApplication(); $default = $app->get('captcha'); if ($app->isClient('site')) { $default = $app->getParams()->get('captcha', $default); } $plugin = $this->element['plugin'] ? (string) $this->element['plugin'] : $default; $this->plugin = $plugin; if ($plugin === 0 || $plugin === '0' || $plugin === '' || $plugin === null) { $this->hidden = true; return false; } else { // Force field to be required. There's no reason to have a captcha if it is not required. // Obs: Don't put required="required" in the xml file, you just need to have validate="captcha" $this->required = true; if (strpos($this->class, 'required') === false) { $this->class .= ' required'; } } $this->namespace = $this->element['namespace'] ? (string) $this->element['namespace'] : $this->form->getName(); try { // Get an instance of the captcha class that we are using $this->_captcha = Captcha::getInstance($this->plugin, ['namespace' => $this->namespace]); /** * Give the captcha instance a possibility to react on the setup-process, * e.g. by altering the XML structure of the field, for example hiding the label * when using invisible captchas. */ $this->_captcha->setupField($this, $element); } catch (\RuntimeException $e) { $this->_captcha = null; Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); return false; } return $result; } /** * Method to get the field input. * * @return string The field input. * * @since 2.5 */ protected function getInput() { if ($this->hidden || $this->_captcha == null) { return ''; } try { return $this->_captcha->display($this->name, $this->id, $this->class); } catch (\RuntimeException $e) { Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); } return ''; } } PK1A#]�VZj��src/Form/Field/ListField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\Form\FormHelper; use Joomla\CMS\Helper\ModuleHelper; use Joomla\CMS\Language\Associations; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a generic list of options. * * @since 1.7.0 */ class ListField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'List'; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.list'; /** * Method to get the field input markup for a generic list. * Use the multiple attribute to enable multiselect. * * @return string The field input markup. * * @since 3.7.0 */ protected function getInput() { $data = $this->getLayoutData(); $data['options'] = (array) $this->getOptions(); return $this->getRenderer($this->layout)->render($data); } /** * Method to get the field options. * * @return object[] The field option objects. * * @since 3.7.0 */ protected function getOptions() { $fieldname = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); $options = []; foreach ($this->element->xpath('option') as $option) { // Filter requirements $requires = explode(',', (string) $option['requires']); // Requires multilanguage if (\in_array('multilanguage', $requires) && !Multilanguage::isEnabled()) { continue; } // Requires associations if (\in_array('associations', $requires) && !Associations::isEnabled()) { continue; } // Requires adminlanguage if (\in_array('adminlanguage', $requires) && !ModuleHelper::isAdminMultilang()) { continue; } // Requires vote plugin if (\in_array('vote', $requires) && !PluginHelper::isEnabled('content', 'vote')) { continue; } // Requires record hits if (\in_array('hits', $requires) && !ComponentHelper::getParams('com_content')->get('record_hits', 1)) { continue; } $value = (string) $option['value']; $text = trim((string) $option) != '' ? trim((string) $option) : $value; $disabled = (string) $option['disabled']; $disabled = ($disabled === 'true' || $disabled === 'disabled' || $disabled === '1'); $disabled = $disabled || ($this->readonly && $value != $this->value); $checked = (string) $option['checked']; $checked = ($checked === 'true' || $checked === 'checked' || $checked === '1'); $selected = (string) $option['selected']; $selected = ($selected === 'true' || $selected === 'selected' || $selected === '1'); $tmp = [ 'value' => $value, 'text' => Text::alt($text, $fieldname), 'disable' => $disabled, 'class' => (string) $option['class'], 'selected' => ($checked || $selected), 'checked' => ($checked || $selected), ]; // Set some event handler attributes. But really, should be using unobtrusive js. $tmp['onclick'] = (string) $option['onclick']; $tmp['onchange'] = (string) $option['onchange']; if ((string) $option['showon']) { $encodedConditions = json_encode( FormHelper::parseShowOnConditions((string) $option['showon'], $this->formControl, $this->group) ); $tmp['optionattr'] = " data-showon='" . $encodedConditions . "'"; } // Add the option object to the result set. $options[] = (object) $tmp; } if ($this->element['useglobal']) { $tmp = new \stdClass(); $tmp->value = ''; $tmp->text = Text::_('JGLOBAL_USE_GLOBAL'); $component = Factory::getApplication()->getInput()->getCmd('option'); // Get correct component for menu items if ($component === 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new Uri($link); $component = $uri->getVar('option', 'com_menus'); } $params = ComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (\is_null($value)) { $value = Factory::getApplication()->get($this->fieldname); } // Try with menu configuration if (\is_null($value) && Factory::getApplication()->getInput()->getCmd('option') === 'com_menus') { $value = ComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!\is_null($value)) { $value = (string) $value; foreach ($options as $option) { if ($option->value === $value) { $value = $option->text; break; } } $tmp->text = Text::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } array_unshift($options, $tmp); } reset($options); return $options; } /** * Method to add an option to the list field. * * @param string $text Text/Language variable of the option. * @param string[] $attributes Array of attributes ('name' => 'value') format * * @return ListField For chaining. * * @since 3.7.0 */ public function addOption($text, $attributes = []) { if ($text && $this->element instanceof \SimpleXMLElement) { $child = $this->element->addChild('option', $text); foreach ($attributes as $name => $value) { $child->addAttribute($name, $value); } } return $this; } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.7.0 */ public function __get($name) { if ($name === 'options') { return $this->getOptions(); } return parent::__get($name); } } PK1A#]A'3�� src/Form/Field/CheckboxField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Single checkbox field. * This is a boolean field with null for false and the specified option for true * * @link https://html.spec.whatwg.org/multipage/input.html#checkbox-state-(type=checkbox) * @see CheckboxField * @since 1.7.0 */ class CheckboxField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Checkbox'; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.checkbox'; /** * The checked state of checkbox field. * * @var boolean * @since 3.2 */ protected $checked = false; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'checked') { return $this->checked; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { if ($name === 'checked') { $value = (string) $value; $this->checked = ($value === 'true' || $value == $name || $value === '1'); return; } parent::__set($name, $value); } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { // Handle the default attribute $default = (string) $element['default']; if ($default) { $test = $this->form->getValue((string) $element['name'], $group); $value = ($test == $default) ? $default : null; } $return = parent::setup($element, $value, $group); if ($return) { $checked = (string) $this->element['checked']; $this->checked = ($checked === 'true' || $checked === 'checked' || $checked === '1'); empty($this->value) || $this->checked ? null : $this->checked = true; } return $return; } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 4.0.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $data['value'] = $this->default ?: '1'; $data['checked'] = $this->checked || $this->value; return $data; } } PK1A#]��F�� src/Form/Field/TimezoneField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * * @since 1.7.0 */ class TimezoneField extends GroupedlistField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Timezone'; /** * The list of available timezone groups to use. * * @var string[] * @since 1.7.0 */ protected static $zones = ['Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific']; /** * The keyField of timezone field. * * @var integer * @since 3.2 */ protected $keyField; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'keyField') { return $this->keyField; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'keyField': $this->keyField = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->keyField = (string) $this->element['key_field']; } return $return; } /** * Method to get the time zone field option groups. * * @return array[] The field option objects as a nested array in groups. * * @since 1.7.0 */ protected function getGroups() { $groups = []; // Get the list of time zones from the server. $zones = \DateTimeZone::listIdentifiers(); // Build the group lists. foreach ($zones as $zone) { // Time zones not in a group we will ignore. if (strpos($zone, '/') === false) { continue; } // Get the group/locale from the timezone. list($group, $locale) = explode('/', $zone, 2); // Only use known groups. if (\in_array($group, self::$zones)) { // Initialize the group if necessary. if (!isset($groups[$group])) { $groups[$group] = []; } // Only add options where a locale exists. if (!empty($locale)) { $groups[$group][$zone] = HTMLHelper::_('select.option', $zone, str_replace('_', ' ', $locale), 'value', 'text', false); } } } // Sort the group lists. ksort($groups); foreach ($groups as &$location) { sort($location); } // Merge any additional groups in the XML definition. $groups = array_merge(parent::getGroups(), $groups); return $groups; } } PK1A#]�3 t� � &src/Form/Field/PredefinedlistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field to load a list of predefined values * * @since 3.2 */ abstract class PredefinedlistField extends ListField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Predefinedlist'; /** * Cached array of the category items. * * @var array[] * @since 3.2 */ protected static $options = []; /** * Available predefined options * * @var string[] * @since 3.2 */ protected $predefinedOptions = []; /** * Translate options labels ? * * @var boolean * @since 3.2 */ protected $translate = true; /** * Allows to use only specific values of the predefined list * * @var string[] * @since 4.0.0 */ protected $optionsFilter = []; /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see \Joomla\CMS\Form\FormField::setup() * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // Note: $this->element['optionsFilter'] is not cast to string here to allow empty string value. $this->optionsFilter = $this->element['optionsFilter'] ? explode(',', (string) $this->element['optionsFilter']) : []; } return $return; } /** * Method to get the options to populate list * * @return object[] The field option objects. * * @since 3.2 */ protected function getOptions() { // Hash for caching $hash = md5($this->element); $type = strtolower($this->type); if (!isset(static::$options[$type][$hash]) && !empty($this->predefinedOptions)) { static::$options[$type][$hash] = parent::getOptions(); $options = []; foreach ($this->predefinedOptions as $value => $text) { $val = (string) $value; if (empty($this->optionsFilter) || in_array($val, $this->optionsFilter, true)) { $text = $this->translate ? Text::_($text) : $text; $options[] = (object) [ 'value' => $value, 'text' => $text, ]; } } static::$options[$type][$hash] = array_merge(static::$options[$type][$hash], $options); } return static::$options[$type][$hash]; } } PK1A#]��=�#�#src/Form/Field/TextField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a one line text field. * * @link https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search) * @since 1.7.0 */ class TextField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Text'; /** * The allowable maxlength of the field. * * @var integer * @since 3.2 */ protected $maxLength; /** * Does this field support a character counter? * * @var boolean * @since 4.3.0 */ protected $charcounter = false; /** * The mode of input associated with the field. * * @var mixed * @since 3.2 */ protected $inputmode; /** * The name of the form field direction (ltr or rtl). * * @var string * @since 3.2 */ protected $dirname; /** * Input addon before * * @var string * @since 4.0.0 */ protected $addonBefore; /** * Input addon after * * @var string * @since 4.0.0 */ protected $addonAfter; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.text'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'maxLength': case 'dirname': case 'addonBefore': case 'addonAfter': case 'inputmode': case 'charcounter': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'maxLength': $this->maxLength = (int) $value; break; case 'dirname': $value = (string) $value; $this->dirname = ($value == $name || $value === 'true' || $value === '1'); break; case 'inputmode': $this->inputmode = (string) $value; break; case 'addonBefore': $this->addonBefore = (string) $value; break; case 'addonAfter': $this->addonAfter = (string) $value; break; case 'charcounter': $this->charcounter = strtolower($value) === 'true'; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result == true) { $inputmode = (string) $this->element['inputmode']; $dirname = (string) $this->element['dirname']; $this->inputmode = ''; $inputmode = preg_replace('/\s+/', ' ', trim($inputmode)); $inputmode = explode(' ', $inputmode); $defaultInputmode = \in_array('default', $inputmode) ? Text::_('JLIB_FORM_INPUTMODE') . ' ' : ''; foreach (array_keys($inputmode, 'default') as $key) { unset($inputmode[$key]); } $this->inputmode = $defaultInputmode . implode(' ', $inputmode); // Set the dirname. $dirname = ($dirname === 'dirname' || $dirname === 'true' || $dirname === '1'); $this->dirname = $dirname ? $this->getName($this->fieldname . '_dir') : false; $this->maxLength = (int) $this->element['maxlength']; $this->charcounter = isset($this->element['charcounter']) ? strtolower($this->element['charcounter']) === 'true' : false; $this->addonBefore = (string) $this->element['addonBefore']; $this->addonAfter = (string) $this->element['addonAfter']; } return $result; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if ($this->element['useglobal']) { $component = Factory::getApplication()->getInput()->getCmd('option'); // Get correct component for menu items if ($component === 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new Uri($link); $component = $uri->getVar('option', 'com_menus'); } $params = ComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (\is_null($value)) { $value = Factory::getApplication()->get($this->fieldname); } // Try with menu configuration if (\is_null($value) && Factory::getApplication()->getInput()->getCmd('option') === 'com_menus') { $value = ComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!\is_null($value)) { $value = (string) $value; $this->hint = Text::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the field options. * * @return object[] The field option objects. * * @since 3.4 */ protected function getOptions() { $options = []; foreach ($this->element->children() as $option) { // Only add <option /> elements. if ($option->getName() !== 'option') { continue; } // Create a new option object based on the <option /> element. $options[] = HTMLHelper::_( 'select.option', (string) $option['value'], Text::alt(trim((string) $option), preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname)), 'value', 'text' ); } return $options; } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; $inputmode = !empty($this->inputmode) ? ' inputmode="' . $this->inputmode . '"' : ''; $dirname = !empty($this->dirname) ? ' dirname="' . $this->dirname . '"' : ''; // Get the field options for the datalist. $options = (array) $this->getOptions(); $extraData = [ 'maxLength' => $maxLength, 'pattern' => $this->pattern, 'inputmode' => $inputmode, 'dirname' => $dirname, 'addonBefore' => $this->addonBefore, 'addonAfter' => $this->addonAfter, 'options' => $options, 'charcounter' => $this->charcounter, ]; return array_merge($data, $extraData); } } PK1A#]� ����#src/Form/Field/ModuleorderField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; use Joomla\CMS\Session\Session; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module Order field. * * @since 1.6 */ class ModuleorderField extends FormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ModuleOrder'; /** * Name of the layout being used to render the field * * @var string * @since 3.6.3 */ protected $layout = 'joomla.form.field.moduleorder'; /** * The linked property * * @var string * @since 4.2.7 */ protected $linked; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.6.3 */ public function __get($name) { if ($name === 'linked') { return $this->linked; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.6.3 */ public function __set($name, $value) { switch ($name) { case 'linked': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.6.3 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->linked = isset($this->element['linked']) ? (int) $this->element['linked'] : 'position'; } return $return; } /** * Method to get the field input markup for the moduleorder field. * * @return string The field input markup. * * @since 1.6 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.6.3 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'ordering' => $this->form->getValue('ordering'), 'clientId' => $this->form->getValue('client_id'), 'moduleId' => $this->form->getValue('id'), 'name' => $this->name, 'token' => Session::getFormToken() . '=1', 'element' => $this->form->getName() . '_' . $this->linked, ]; return array_merge($data, $extraData); } } PK1A#];�*��&src/Form/Field/ContenthistoryField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; use Joomla\CMS\Language\Text; use Joomla\CMS\Session\Session; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to select Content History from a modal list. * * @since 3.2 */ class ContenthistoryField extends FormField { /** * The form field type. * * @var string * @since 3.2 */ public $type = 'ContentHistory'; /** * Layout to render the label * * @var string */ protected $layout = 'joomla.form.field.contenthistory'; /** * Get the data that is going to be passed to the layout * * @return array */ public function getLayoutData() { // Get the basic field data $data = parent::getLayoutData(); $itemId = $this->element['data-typeAlias'] . '.' . $this->form->getValue('id'); $label = Text::_('JTOOLBAR_VERSIONS'); $link = 'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&field=' . $this->id . '&item_id=' . $itemId . '&' . Session::getFormToken() . '=1'; $extraData = [ 'item' => $itemId, 'label' => $label, 'link' => $link, ]; return array_merge($data, $extraData); } /** * Method to get the content history field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } } PK1A#] 5�Պ� src/Form/Field/MenuitemField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; use Joomla\Component\Menus\Administrator\Helper\MenusHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports an HTML grouped select list of menu item grouped by menu * * @since 1.6 */ class MenuitemField extends GroupedlistField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'MenuItem'; /** * The menu type. * * @var string * @since 3.2 */ protected $menuType; /** * The client id. * * @var int * @since 3.2 */ protected $clientId; /** * The language. * * @var string[] * @since 3.2 */ protected $language; /** * The published status. * * @var string[] * @since 3.2 */ protected $published; /** * The disabled status. * * @var string[] * @since 3.2 */ protected $disable; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'menuType': case 'clientId': case 'language': case 'published': case 'disable': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'menuType': $this->menuType = (string) $value; break; case 'clientId': $this->clientId = (int) $value; break; case 'language': case 'published': case 'disable': $value = (string) $value; $this->$name = $value ? explode(',', $value) : []; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { $this->menuType = (string) $this->element['menu_type']; $this->clientId = (int) $this->element['client_id']; $this->published = $this->element['published'] ? explode(',', (string) $this->element['published']) : []; $this->disable = $this->element['disable'] ? explode(',', (string) $this->element['disable']) : []; $this->language = $this->element['language'] ? explode(',', (string) $this->element['language']) : []; } return $result; } /** * Method to get the field option groups. * * @return array[] The field option objects as a nested array in groups. * * @since 1.6 */ protected function getGroups() { $groups = []; $menuType = $this->menuType; // Get the menu items. $items = MenusHelper::getMenuLinks($menuType, 0, 0, $this->published, $this->language, $this->clientId); // Build group for a specific menu type. if ($menuType) { // If the menutype is empty, group the items by menutype. $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__menu_types')) ->where($db->quoteName('menutype') . ' = :menuType') ->bind(':menuType', $menuType); $db->setQuery($query); try { $menuTitle = $db->loadResult(); } catch (\RuntimeException $e) { $menuTitle = $menuType; } // Initialize the group. $groups[$menuTitle] = []; // Build the options array. foreach ($items as $link) { $levelPrefix = str_repeat('- ', max(0, $link->level - 1)); // Displays language code if not set to All if ($link->language !== '*') { $lang = ' (' . $link->language . ')'; } else { $lang = ''; } $groups[$menuTitle][] = HTMLHelper::_( 'select.option', $link->value, $levelPrefix . $link->text . $lang, 'value', 'text', \in_array($link->type, $this->disable) ); } } else { // Build groups for all menu types. // Build the groups arrays. foreach ($items as $menu) { // Initialize the group. $groups[$menu->title] = []; // Build the options array. foreach ($menu->links as $link) { $levelPrefix = str_repeat('- ', max(0, $link->level - 1)); // Displays language code if not set to All if ($link->language !== '*') { $lang = ' (' . $link->language . ')'; } else { $lang = ''; } $groups[$menu->title][] = HTMLHelper::_( 'select.option', $link->value, $levelPrefix . $link->text . $lang, 'value', 'text', \in_array($link->type, $this->disable) ); } } } // Merge any additional groups in the XML definition. $groups = array_merge(parent::getGroups(), $groups); return $groups; } } PK1A#]�7@>��src/Form/Field/SpacerField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides spacer markup to be used in form layouts. * * @since 1.7.0 */ class SpacerField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Spacer'; /** * Method to get the field input markup for a spacer. * The spacer does not have accept input. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { return ' '; } /** * Method to get the field label markup for a spacer. * Use the label text or name from the XML element as the spacer or * Use a hr="true" to automatically generate plain hr markup * * @return string The field label markup. * * @since 1.7.0 */ protected function getLabel() { $html = []; $class = !empty($this->class) ? ' class="' . $this->class . '"' : ''; $html[] = '<span class="spacer">'; $html[] = '<span class="before"></span>'; $html[] = '<span' . $class . '>'; if ((string) $this->element['hr'] === 'true') { $html[] = '<hr' . $class . '>'; } else { $label = ''; // Get the label text from the XML element, defaulting to the element name. $text = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; $text = $this->translateLabel ? Text::_($text) : $text; // Build the class for the label. $class = !empty($this->description) ? 'hasPopover' : ''; $class = $this->required == true ? $class . ' required' : $class; // Add the opening label tag and main attributes attributes. $label .= '<label id="' . $this->id . '-lbl" class="' . $class . '"'; // If a description is specified, use it to build a tooltip. if (!empty($this->description)) { HTMLHelper::_('bootstrap.popover', '.hasPopover'); $label .= ' title="' . htmlspecialchars(trim($text, ':'), ENT_COMPAT, 'UTF-8') . '"'; $label .= ' data-bs-content="' . htmlspecialchars( $this->translateDescription ? Text::_($this->description) : $this->description, ENT_COMPAT, 'UTF-8' ) . '"'; if (Factory::getLanguage()->isRtl()) { $label .= ' data-bs-placement="left"'; } } // Add the label text and closing tag. $label .= '>' . $text . '</label>'; $html[] = $label; } $html[] = '</span>'; $html[] = '<span class="after"></span>'; $html[] = '</span>'; return implode('', $html); } /** * Method to get the field title. * * @return string The field title. * * @since 1.7.0 */ protected function getTitle() { return $this->getLabel(); } /** * Method to get a control group with label and input. * * @param array $options Options to be passed into the rendering of the field * * @return string A string containing the html for the control group * * @since 3.7.3 */ public function renderField($options = []) { $options['class'] = empty($options['class']) ? 'field-spacer' : $options['class'] . ' field-spacer'; return parent::renderField($options); } } PK1A#]Rj�YVV src/Form/Field/AliastagField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Framework. * * @since 2.5.0 */ class AliastagField extends ListField { /** * The field type. * * @var string * @since 3.6 */ protected $type = 'Aliastag'; /** * Method to get a list of options for a list input. * * @return object[] An array of JHtml options. * * @since 3.6 */ protected function getOptions() { // Get list of tag type alias $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ 'DISTINCT ' . $db->quoteName('type_alias', 'value'), $db->quoteName('type_alias', 'text'), ] ) ->from($db->quoteName('#__contentitem_tag_map')); $db->setQuery($query); $options = $db->loadObjectList(); $lang = Factory::getLanguage(); foreach ($options as $i => $item) { $parts = explode('.', $item->value); $extension = $parts[0]; $lang->load($extension . '.sys', JPATH_ADMINISTRATOR) || $lang->load($extension, Path::clean(JPATH_ADMINISTRATOR . '/components/' . $extension)); $options[$i]->text = Text::_(strtoupper($extension) . '_TAGS_' . strtoupper($parts[1])); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); // Sort by language value usort( $options, function ($a, $b) { return strcmp($a->text, $b->text); } ); return $options; } } PK1A#]9k�@ src/Form/Field/TextareaField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a multi line area for entry of plain text * * @link https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element * @since 1.7.0 */ class TextareaField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Textarea'; /** * The number of rows in textarea. * * @var mixed * @since 3.2 */ protected $rows; /** * The number of columns in textarea. * * @var mixed * @since 3.2 */ protected $columns; /** * The maximum number of characters in textarea. * * @var mixed * @since 3.4 */ protected $maxlength; /** * Does this field support a character counter? * * @var boolean * @since 4.0.0 */ protected $charcounter = false; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.textarea'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'rows': case 'columns': case 'maxlength': case 'charcounter': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'rows': case 'columns': case 'maxlength': $this->$name = (int) $value; break; case 'charcounter': $this->charcounter = strtolower($value) === 'true'; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->rows = isset($this->element['rows']) ? (int) $this->element['rows'] : false; $this->columns = isset($this->element['cols']) ? (int) $this->element['cols'] : false; $this->maxlength = isset($this->element['maxlength']) ? (int) $this->element['maxlength'] : false; $this->charcounter = isset($this->element['charcounter']) ? strtolower($this->element['charcounter']) === 'true' : false; } return $return; } /** * Method to get the textarea field input markup. * Use the rows and columns attributes to specify the dimensions of the area. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $columns = $this->columns ? ' cols="' . $this->columns . '"' : ''; $rows = $this->rows ? ' rows="' . $this->rows . '"' : ''; $maxlength = $this->maxlength ? ' maxlength="' . $this->maxlength . '"' : ''; $extraData = [ 'maxlength' => $maxlength, 'rows' => $rows, 'columns' => $columns, 'charcounter' => $this->charcounter, ]; return array_merge($data, $extraData); } } PK1A#]"�U^^!src/Form/Field/ImagelistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports an HTML select list of image * * @since 1.7.0 */ class ImagelistField extends FilelistField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Imagelist'; /** * Method to get the list of images field options. * Use the filter attribute to specify allowable file extensions. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { // Define the image file type filter. $this->fileFilter = '\.png$|\.gif$|\.jpg$|\.bmp$|\.ico$|\.jpeg$|\.psd$|\.eps$|\.avif$|\.webp$|\.heic$|\.wp2$'; // Get the field options. return parent::getOptions(); } } PK1A#]��,��%�%src/Form/Field/RulesField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Access\Access; use Joomla\CMS\Access\Rules; use Joomla\CMS\Form\FormField; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Field for assigning permissions to groups for a given asset * * @see JAccess * @since 1.7.0 */ class RulesField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Rules'; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.rules'; /** * The section. * * @var string * @since 3.2 */ protected $section; /** * The component. * * @var string * @since 3.2 */ protected $component; /** * The assetField. * * @var string * @since 3.2 */ protected $assetField; /** * The flag which indicates if it is the global config * * @var bool * @since 4.3.0 */ protected $isGlobalConfig; /** * The asset rules * * @var Rules * @since 4.3.0 */ protected $assetRules; /** * The actions * * @var object[] * @since 4.3.0 */ protected $actions; /** * The groups * * @var object[] * @since 4.3.0 */ protected $groups; /** * The asset Id * * @var int * @since 4.3.0 */ protected $assetId; /** * The parent asset Id * * @var int * @since 4.3.0 */ protected $parentAssetId; /** * The flag to indicate that it is a new item * * @var bool * @since 4.3.0 */ protected $newItem; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'section': case 'component': case 'assetField': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'section': case 'component': case 'assetField': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->section = $this->element['section'] ? (string) $this->element['section'] : ''; $this->component = $this->element['component'] ? (string) $this->element['component'] : ''; $this->assetField = $this->element['asset_field'] ? (string) $this->element['asset_field'] : 'asset_id'; } return $return; } /** * Method to get the field input markup for Access Control Lists. * Optionally can be associated with a specific component and section. * * @return string The field input markup. * * @since 1.7.0 * @todo: Add access check. */ protected function getInput() { // Initialise some field attributes. $section = $this->section; $assetField = $this->assetField; $component = empty($this->component) ? 'root.1' : $this->component; // Current view is global config? $this->isGlobalConfig = $component === 'root.1'; // Get the actions for the asset. $this->actions = Access::getActionsFromFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml', "/access/section[@name='" . $section . "']/" ); if ($this->actions === false) { $this->actions = []; } // Iterate over the children and add to the actions. foreach ($this->element->children() as $el) { if ($el->getName() === 'action') { $this->actions[] = (object) [ 'name' => (string) $el['name'], 'title' => (string) $el['title'], 'description' => (string) $el['description'], ]; } } // Get the asset id. // Note that for global configuration, com_config injects asset_id = 1 into the form. $this->assetId = (int) $this->form->getValue($assetField); $this->newItem = empty($this->assetId) && $this->isGlobalConfig === false && $section !== 'component'; // If the asset id is empty (component or new item). if (empty($this->assetId)) { // Get the component asset id as fallback. $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('name') . ' = :component') ->bind(':component', $component); $db->setQuery($query); $this->assetId = (int) $db->loadResult(); /** * @todo: incorrect info * When creating a new item (not saving) it uses the calculated permissions from the component (item <-> component <-> global config). * But if we have a section too (item <-> section(s) <-> component <-> global config) this is not correct. * Also, currently it uses the component permission, but should use the calculated permissions for achild of the component/section. */ } // If not in global config we need the parent_id asset to calculate permissions. if (!$this->isGlobalConfig) { // In this case we need to get the component rules too. $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('parent_id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = :assetId') ->bind(':assetId', $this->assetId, ParameterType::INTEGER); $db->setQuery($query); $this->parentAssetId = (int) $db->loadResult(); } // Get the rules for just this asset (non-recursive). $this->assetRules = Access::getAssetRules($this->assetId, false, false); // Get the available user groups. $this->groups = $this->getUserGroups(); // Trim the trailing line in the layout file return trim($this->getRenderer($this->layout)->render($this->getLayoutData())); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 4.0.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'groups' => $this->groups, 'section' => $this->section, 'actions' => $this->actions, 'assetId' => $this->assetId, 'newItem' => $this->newItem, 'assetRules' => $this->assetRules, 'isGlobalConfig' => $this->isGlobalConfig, 'parentAssetId' => $this->parentAssetId, 'component' => $this->component, ]; return array_merge($data, $extraData); } /** * Get a list of the user groups. * * @return object[] * * @since 1.7.0 */ protected function getUserGroups() { $options = UserGroupsHelper::getInstance()->getAll(); foreach ($options as &$option) { $option->value = $option->id; $option->text = $option->title; } return array_values($options); } } PK1A#]�7=�� � src/Form/Field/LimitboxField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to load a list of possible item count limits * * @since 3.2 */ class LimitboxField extends ListField { /** * The form field type. * * @var string * @since 3.2 */ public $type = 'Limitbox'; /** * Cached array of the category items. * * @var array[] * @since 3.2 */ protected static $options = []; /** * Default options * * @var int[] */ protected $defaultLimits = [5, 10, 15, 20, 25, 30, 50, 100, 200, 500]; /** * Method to get the options to populate to populate list * * @return object[] The field option objects. * * @since 3.2 */ protected function getOptions() { // Accepted modifiers $hash = md5($this->element->asXML()); if (!isset(static::$options[$hash])) { static::$options[$hash] = parent::getOptions(); $options = []; $limits = $this->defaultLimits; // Limits manually specified if (isset($this->element['limits'])) { $limits = explode(',', $this->element['limits']); } // User wants to add custom limits if (isset($this->element['append'])) { $limits = array_unique(array_merge($limits, explode(',', $this->element['append']))); } // User wants to remove some default limits if (isset($this->element['remove'])) { $limits = array_diff($limits, explode(',', $this->element['remove'])); } // Order the options asort($limits); // Add an option to show all? $showAll = isset($this->element['showall']) ? (string) $this->element['showall'] === 'true' : true; if ($showAll) { $limits[] = 0; } if (!empty($limits)) { foreach ($limits as $value) { $options[] = (object) [ 'value' => $value, 'text' => ($value != 0) ? Text::_('J' . $value) : Text::_('JALL'), ]; } static::$options[$hash] = array_merge(static::$options[$hash], $options); } } return static::$options[$hash]; } } PK1A#]D�X''src/Form/Field/TagField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Helper\TagsHelper; use Joomla\CMS\Language\Multilanguage; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * List of Tags field. * * @since 3.1 */ class TagField extends ListField { /** * A flexible tag list that respects access controls * * @var string * @since 3.1 */ public $type = 'Tag'; /** * Flag to work with nested tag field * * @var boolean * @since 3.1 */ public $isNested = null; /** * com_tags parameters * * @var \Joomla\Registry\Registry * @since 3.1 */ protected $comParams = null; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.tag'; /** * Constructor * * @since 3.1 */ public function __construct() { parent::__construct(); // Load com_tags config $this->comParams = ComponentHelper::getParams('com_tags'); } /** * Method to get the field input for a tag field. * * @return string The field input. * * @since 3.1 */ protected function getInput() { $data = $this->getLayoutData(); if (!\is_array($this->value) && !empty($this->value)) { if ($this->value instanceof TagsHelper) { if (empty($this->value->tags)) { $this->value = []; } else { $this->value = $this->value->tags; } } // String in format 2,5,4 if (\is_string($this->value)) { $this->value = explode(',', $this->value); } // Integer is given if (\is_int($this->value)) { $this->value = [$this->value]; } $data['value'] = $this->value; } $data['remoteSearch'] = $this->isRemoteSearch(); $data['options'] = $this->getOptions(); $data['isNested'] = $this->isNested(); $data['allowCustom'] = $this->allowCustom(); $data['minTermLength'] = (int) $this->comParams->get('min_term_length', 3); return $this->getRenderer($this->layout)->render($data); } /** * Method to get a list of tags * * @return object[] The field option objects. * * @since 3.1 */ protected function getOptions() { $published = (string) $this->element['published'] ?: [0, 1]; $app = Factory::getApplication(); $language = null; $options = []; // This limit is only used with isRemoteSearch $prefillLimit = 30; $isRemoteSearch = $this->isRemoteSearch(); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.path'), $db->quoteName('a.title', 'text'), $db->quoteName('a.level'), $db->quoteName('a.published'), $db->quoteName('a.lft'), ] ) ->from($db->quoteName('#__tags', 'a')); // Limit Options in multilanguage if ($app->isClient('site') && Multilanguage::isEnabled()) { if (ComponentHelper::getParams('com_tags')->get('tag_list_language_filter') === 'current_language') { $language = [$app->getLanguage()->getTag(), '*']; } } elseif (!empty($this->element['language'])) { // Filter language if (strpos($this->element['language'], ',') !== false) { $language = explode(',', $this->element['language']); } else { $language = [$this->element['language']]; } } if ($language) { $query->whereIn($db->quoteName('a.language'), $language, ParameterType::STRING); } $query->where($db->quoteName('a.lft') . ' > 0'); // Filter on the published state if (is_numeric($published)) { $published = (int) $published; $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $published, ParameterType::INTEGER); } elseif (\is_array($published)) { $published = ArrayHelper::toInteger($published); $query->whereIn($db->quoteName('a.published'), $published); } $query->order($db->quoteName('a.lft') . ' ASC'); // Preload only active values and 30 most used tags or fill up if ($isRemoteSearch) { // Load the most $prefillLimit used tags $topQuery = $db->getQuery(true) ->select($db->quoteName('tag_id')) ->from($db->quoteName('#__contentitem_tag_map')) ->group($db->quoteName('tag_id')) ->order('count(*)') ->setLimit($prefillLimit); $db->setQuery($topQuery); $topIds = $db->loadColumn(); // Merge the used values into the most used tags if (!empty($this->value) && is_array($this->value)) { $topIds = array_unique(array_merge($topIds, $this->value)); } // Set the default limit for the main query $query->setLimit($prefillLimit); if (!empty($topIds)) { // Filter the ids to the most used tags and the selected tags $preQuery = clone $query; $preQuery->clear('limit') ->whereIn($db->quoteName('a.id'), $topIds); $db->setQuery($preQuery); try { $options = $db->loadObjectList(); } catch (\RuntimeException $e) { return []; } // Limit the main query to the missing amount of tags $count = count($options); $prefillLimit = $prefillLimit - $count; $query->setLimit($prefillLimit); // Exclude the already loaded tags from the main query if ($count > 0) { $query->whereNotIn($db->quoteName('a.id'), ArrayHelper::getColumn($options, 'value')); } } } // Only execute the query if we need more tags not already loaded by the $preQuery query if (!$isRemoteSearch || $prefillLimit > 0) { // Get the options. $db->setQuery($query); try { $options = array_merge($options, $db->loadObjectList()); } catch (\RuntimeException $e) { return []; } } // Block the possibility to set a tag as it own parent if ($this->form->getName() === 'com_tags.tag') { $id = (int) $this->form->getValue('id', null, 0); foreach ($options as $option) { if ($option->value == $id) { $option->disable = true; } } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); // Prepare nested data if ($this->isNested()) { $this->prepareOptionsNested($options); } else { $options = TagsHelper::convertPathsToNames($options); } return $options; } /** * Add "-" before nested tags, depending on level * * @param object[] &$options Array of tags * * @return object[] The field option objects. * * @since 3.1 */ protected function prepareOptionsNested(&$options) { if ($options) { foreach ($options as &$option) { $repeat = (isset($option->level) && $option->level - 1 >= 0) ? $option->level - 1 : 0; $option->text = str_repeat('- ', $repeat) . $option->text; } } return $options; } /** * Determine if the field has to be tagnested * * @return boolean * * @since 3.1 */ public function isNested() { if ($this->isNested === null) { // If mode="nested" || ( mode not set & config = nested ) if ( isset($this->element['mode']) && (string) $this->element['mode'] === 'nested' || !isset($this->element['mode']) && $this->comParams->get('tag_field_ajax_mode', 1) == 0 ) { $this->isNested = true; } } return $this->isNested; } /** * Determines if the field allows or denies custom values * * @return boolean */ public function allowCustom() { if ($this->element['custom'] && \in_array((string) $this->element['custom'], ['0', 'false', 'deny'])) { return false; } return Factory::getUser()->authorise('core.create', 'com_tags'); } /** * Check whether need to enable AJAX search * * @return boolean * * @since 4.0.0 */ public function isRemoteSearch() { if ($this->element['remote-search']) { return !\in_array((string) $this->element['remote-search'], ['0', 'false', '']); } return $this->comParams->get('tag_field_ajax_mode', 1) == 1; } } PK1A#]��Jmm$src/Form/Field/ModulelayoutField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; use Joomla\Filesystem\Folder; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field to display a list of the layouts for module display from the module or template overrides. * * @since 1.6 */ class ModulelayoutField extends FormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ModuleLayout'; /** * Method to get the field input for module layouts. * * @return string The field input. * * @since 1.6 */ protected function getInput() { // Get the client id. $clientId = $this->element['client_id']; if ($clientId === null && $this->form instanceof Form) { $clientId = $this->form->getValue('client_id'); } $clientId = (int) $clientId; $client = ApplicationHelper::getClientInfo($clientId); // Get the module. $module = (string) $this->element['module']; if (empty($module) && ($this->form instanceof Form)) { $module = $this->form->getValue('module'); } $module = preg_replace('#\W#', '', $module); // Get the template. $template = (string) $this->element['template']; $template = preg_replace('#\W#', '', $template); // Get the style. $template_style_id = 0; if ($this->form instanceof Form) { $template_style_id = $this->form->getValue('template_style_id', null, 0); $template_style_id = (int) preg_replace('#\W#', '', $template_style_id); } // If an extension and view are present build the options. if ($module && $client) { // Load language file $lang = Factory::getLanguage(); $lang->load($module . '.sys', $client->path) || $lang->load($module . '.sys', $client->path . '/modules/' . $module); // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('element'), $db->quoteName('name'), ] ) ->from($db->quoteName('#__extensions', 'e')) ->where( [ $db->quoteName('e.client_id') . ' = :clientId', $db->quoteName('e.type') . ' = ' . $db->quote('template'), $db->quoteName('e.enabled') . ' = 1', ] ) ->bind(':clientId', $clientId, ParameterType::INTEGER); if ($template) { $query->where($db->quoteName('e.element') . ' = :template') ->bind(':template', $template); } if ($template_style_id) { $query->join('LEFT', $db->quoteName('#__template_styles', 's'), $db->quoteName('s.template') . ' = ' . $db->quoteName('e.element')) ->where($db->quoteName('s.id') . ' = :style') ->bind(':style', $template_style_id, ParameterType::INTEGER); } // Set the query and load the templates. $db->setQuery($query); $templates = $db->loadObjectList('element'); // Build the search paths for module layouts. $module_path = Path::clean($client->path . '/modules/' . $module . '/tmpl'); // Prepare array of component layouts $module_layouts = []; // Prepare the grouped list $groups = []; // Add the layout options from the module path. if (is_dir($module_path) && ($module_layouts = Folder::files($module_path, '^[^_]*\.php$'))) { // Create the group for the module $groups['_'] = []; $groups['_']['id'] = $this->id . '__'; $groups['_']['text'] = Text::sprintf('JOPTION_FROM_MODULE'); $groups['_']['items'] = []; foreach ($module_layouts as $file) { // Add an option to the module group $value = basename($file, '.php'); $text = $lang->hasKey($key = strtoupper($module . '_LAYOUT_' . $value)) ? Text::_($key) : $value; $groups['_']['items'][] = HTMLHelper::_('select.option', '_:' . $value, $text); } } // Loop on all templates if ($templates) { foreach ($templates as $template) { // Load language file $lang->load('tpl_' . $template->element . '.sys', $client->path) || $lang->load('tpl_' . $template->element . '.sys', $client->path . '/templates/' . $template->element); $template_path = Path::clean($client->path . '/templates/' . $template->element . '/html/' . $module); // Add the layout options from the template path. if (is_dir($template_path) && ($files = Folder::files($template_path, '^[^_]*\.php$'))) { foreach ($files as $i => $file) { // Remove layout that already exist in component ones if (\in_array($file, $module_layouts)) { unset($files[$i]); } } if (\count($files)) { // Create the group for the template $groups[$template->element] = []; $groups[$template->element]['id'] = $this->id . '_' . $template->element; $groups[$template->element]['text'] = Text::sprintf('JOPTION_FROM_TEMPLATE', $template->name); $groups[$template->element]['items'] = []; foreach ($files as $file) { // Add an option to the template group $value = basename($file, '.php'); $text = $lang->hasKey($key = strtoupper('TPL_' . $template->element . '_' . $module . '_LAYOUT_' . $value)) ? Text::_($key) : $value; $groups[$template->element]['items'][] = HTMLHelper::_('select.option', $template->element . ':' . $value, $text); } } } } } // Compute attributes for the grouped list $attr = $this->element['size'] ? ' size="' . (int) $this->element['size'] . '"' : ''; $attr .= $this->element['class'] ? ' class="' . (string) $this->element['class'] . '"' : ''; // Prepare HTML code $html = []; // Compute the current selected values $selected = [$this->value]; // Add a grouped list $html[] = HTMLHelper::_( 'select.groupedlist', $groups, $this->name, ['id' => $this->id, 'group.id' => 'id', 'list.attr' => $attr, 'list.select' => $selected] ); return implode($html); } else { return ''; } } } PK1A#]!�h�CC#src/Form/Field/ContenttypeField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Content Type field. * * @since 3.1 */ class ContenttypeField extends ListField { /** * A flexible tag list that respects access controls * * @var string * @since 3.1 */ public $type = 'Contenttype'; /** * Method to get the field input for a list of content types. * * @return string The field input. * * @since 3.1 */ protected function getInput() { if (!\is_array($this->value)) { if (\is_object($this->value)) { $this->value = $this->value->tags; } if (\is_string($this->value)) { $this->value = explode(',', $this->value); } } return parent::getInput(); } /** * Method to get a list of content types * * @return object[] The field option objects. * * @since 3.1 */ protected function getOptions() { $lang = Factory::getLanguage(); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.type_id', 'value'), $db->quoteName('a.type_title', 'text'), $db->quoteName('a.type_alias', 'alias'), ] ) ->from($db->quoteName('#__content_types', 'a')) ->order($db->quoteName('a.type_title') . ' ASC'); // Get the options. $db->setQuery($query); try { $options = $db->loadObjectList(); } catch (\RuntimeException $e) { return []; } foreach ($options as $option) { // Make up the string from the component sys.ini file $parts = explode('.', $option->alias); $comp = array_shift($parts); // Make sure the component sys.ini is loaded $lang->load($comp . '.sys', JPATH_ADMINISTRATOR) || $lang->load($comp . '.sys', JPATH_ADMINISTRATOR . '/components/' . $comp); $option->string = implode('_', $parts); $option->string = $comp . '_CONTENT_TYPE_' . $option->string; if ($lang->hasKey($option->string)) { $option->text = Text::_($option->string); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]ER#V�'�''src/Form/Field/ComponentlayoutField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; use Joomla\Filesystem\Folder; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field to display a list of the layouts for a component view from * the extension or template overrides. * * @since 1.6 */ class ComponentlayoutField extends FormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ComponentLayout'; /** * Method to get the field input for a component layout field. * * @return string The field input. * * @since 1.6 */ protected function getInput() { // Get the client id. $clientId = $this->element['client_id']; if ($clientId === null && $this->form instanceof Form) { $clientId = $this->form->getValue('client_id'); } $clientId = (int) $clientId; $client = ApplicationHelper::getClientInfo($clientId); // Get the extension. $extension = (string) $this->element['extension']; if (empty($extension) && ($this->form instanceof Form)) { $extension = $this->form->getValue('extension'); } $extension = preg_replace('#\W#', '', $extension); $template = (string) $this->element['template']; $template = preg_replace('#\W#', '', $template); $template_style_id = 0; if ($this->form instanceof Form) { $template_style_id = $this->form->getValue('template_style_id', null, 0); $template_style_id = (int) preg_replace('#\W#', '', $template_style_id); } $view = (string) $this->element['view']; $view = preg_replace('#\W#', '', $view); // If a template, extension and view are present build the options. if ($extension && $view && $client) { // Load language file $lang = Factory::getLanguage(); $lang->load($extension . '.sys', JPATH_ADMINISTRATOR) || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR . '/components/' . $extension); // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('e.element'), $db->quoteName('e.name'), ] ) ->from($db->quoteName('#__extensions', 'e')) ->where( [ $db->quoteName('e.client_id') . ' = :clientId', $db->quoteName('e.type') . ' = ' . $db->quote('template'), $db->quoteName('e.enabled') . ' = 1', ] ) ->bind(':clientId', $clientId, ParameterType::INTEGER); if ($template) { $query->where($db->quoteName('e.element') . ' = :template') ->bind(':template', $template); } if ($template_style_id) { $query->join('LEFT', $db->quoteName('#__template_styles', 's'), $db->quoteName('s.template') . ' = ' . $db->quoteName('e.element')) ->where($db->quoteName('s.id') . ' = :style') ->bind(':style', $template_style_id, ParameterType::INTEGER); } // Set the query and load the templates. $db->setQuery($query); $templates = $db->loadObjectList('element'); // Build the search paths for component layouts. $component_path = Path::clean($client->path . '/components/' . $extension . '/tmpl/' . $view); // Check if the new layouts folder exists, else use the old one if (!is_dir($component_path)) { $component_path = Path::clean($client->path . '/components/' . $extension . '/views/' . $view . '/tmpl'); } // Prepare array of component layouts $component_layouts = []; // Prepare the grouped list $groups = []; // Add a Use Global option if useglobal="true" in XML file if ((string) $this->element['useglobal'] === 'true') { $groups[Text::_('JOPTION_FROM_STANDARD')]['items'][] = HTMLHelper::_('select.option', '', Text::_('JGLOBAL_USE_GLOBAL')); } // Add the layout options from the component path. if (is_dir($component_path) && ($component_layouts = Folder::files($component_path, '^[^_]*\.xml$', false, true))) { // Create the group for the component $groups['_'] = []; $groups['_']['id'] = $this->id . '__'; $groups['_']['text'] = Text::sprintf('JOPTION_FROM_COMPONENT'); $groups['_']['items'] = []; foreach ($component_layouts as $i => $file) { // Attempt to load the XML file. if (!$xml = simplexml_load_file($file)) { unset($component_layouts[$i]); continue; } // Get the help data from the XML file if present. if (!$menu = $xml->xpath('layout[1]')) { unset($component_layouts[$i]); continue; } $menu = $menu[0]; // Add an option to the component group $value = basename($file, '.xml'); $component_layouts[$i] = $value; $text = isset($menu['option']) ? Text::_($menu['option']) : (isset($menu['title']) ? Text::_($menu['title']) : $value); $groups['_']['items'][] = HTMLHelper::_('select.option', '_:' . $value, $text); } } // Loop on all templates if ($templates) { foreach ($templates as $template) { // Load language file $lang->load('tpl_' . $template->element . '.sys', $client->path) || $lang->load('tpl_' . $template->element . '.sys', $client->path . '/templates/' . $template->element); $template_path = Path::clean( $client->path . '/templates/' . $template->element . '/html/' . $extension . '/' . $view ); // Add the layout options from the template path. if (is_dir($template_path) && ($files = Folder::files($template_path, '^[^_]*\.php$', false, true))) { foreach ($files as $i => $file) { // Remove layout files that exist in the component folder if (\in_array(basename($file, '.php'), $component_layouts)) { unset($files[$i]); } } if (\count($files)) { // Create the group for the template $groups[$template->name] = []; $groups[$template->name]['id'] = $this->id . '_' . $template->element; $groups[$template->name]['text'] = Text::sprintf('JOPTION_FROM_TEMPLATE', $template->name); $groups[$template->name]['items'] = []; foreach ($files as $file) { // Add an option to the template group $value = basename($file, '.php'); $text = $lang ->hasKey( $key = strtoupper( 'TPL_' . $template->name . '_' . $extension . '_' . $view . '_LAYOUT_' . $value ) ) ? Text::_($key) : $value; $groups[$template->name]['items'][] = HTMLHelper::_('select.option', $template->element . ':' . $value, $text); } } } } } // Compute attributes for the grouped list $attr = $this->element['size'] ? ' size="' . (int) $this->element['size'] . '"' : ''; $attr .= $this->element['class'] ? ' class="' . (string) $this->element['class'] . '"' : ''; // Prepare HTML code $html = []; // Compute the current selected values $selected = [$this->value]; // Add a grouped list $html[] = HTMLHelper::_( 'select.groupedlist', $groups, $this->name, ['id' => $this->id, 'group.id' => 'id', 'list.attr' => $attr, 'list.select' => $selected] ); return implode($html); } else { return ''; } } } PK1A#]*���src/Form/Field/MenuField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports an HTML select list of menus * * @since 1.6 */ class MenuField extends GroupedlistField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'Menu'; /** * Method to get the field option groups. * * @return array[] The field option objects as a nested array in groups. * * @since 1.7.0 * @throws \UnexpectedValueException */ protected function getGroups() { $clientId = (string) $this->element['clientid']; $accessType = (string) $this->element['accesstype']; $showAll = (string) $this->element['showAll'] === 'true'; $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('id'), $db->quoteName('menutype', 'value'), $db->quoteName('title', 'text'), $db->quoteName('client_id'), ] ) ->from($db->quoteName('#__menu_types')) ->order( [ $db->quoteName('client_id'), $db->quoteName('title'), ] ); if (\strlen($clientId)) { $client = (int) $clientId; $query->where($db->quoteName('client_id') . ' = :client') ->bind(':client', $client, ParameterType::INTEGER); } $menus = $db->setQuery($query)->loadObjectList(); if ($accessType) { $user = Factory::getUser(); foreach ($menus as $key => $menu) { switch ($accessType) { case 'create': case 'manage': if (!$user->authorise('core.' . $accessType, 'com_menus.menu.' . (int) $menu->id)) { unset($menus[$key]); } break; // Editing a menu item is a bit tricky, we have to check the current menutype for core.edit and all others for core.create case 'edit': $check = $this->value == $menu->value ? 'edit' : 'create'; if (!$user->authorise('core.' . $check, 'com_menus.menu.' . (int) $menu->id)) { unset($menus[$key]); } break; } } } $opts = []; // Protected menutypes can be shown if requested if ($clientId == 1 && $showAll) { $opts[] = (object) [ 'value' => 'main', 'text' => Text::_('COM_MENUS_MENU_TYPE_PROTECTED_MAIN_LABEL'), 'client_id' => 1, ]; } $options = array_merge($opts, $menus); $groups = []; if (\strlen($clientId)) { $groups[0] = $options; } else { foreach ($options as $option) { // If client id is not specified we group the items. $label = ($option->client_id == 1 ? Text::_('JADMINISTRATOR') : Text::_('JSITE')); $groups[$label][] = $option; } } // Merge any additional options in the XML definition. return array_merge(parent::getGroups(), $groups); } } PK1A#]��]�%�%src/Form/Field/SqlField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\QueryInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports a custom SQL select list * * @since 1.7.0 */ class SqlField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ public $type = 'SQL'; /** * The keyField. * * @var string * @since 3.2 */ protected $keyField; /** * The valueField. * * @var string * @since 3.2 */ protected $valueField; /** * The translate. * * @var boolean * @since 3.2 */ protected $translate = false; /** * The header. * * @var mixed * @since 4.3.0 */ protected $header; /** * The query. * * @var string * @since 3.2 */ protected $query; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'keyField': case 'valueField': case 'translate': case 'header': case 'query': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'translate': $this->$name = (bool) $value; break; case 'keyField': case 'valueField': case 'header': case 'query': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // Check if its using the old way $this->query = (string) $this->element['query']; if (empty($this->query)) { // Get the query from the form $query = []; $defaults = []; $sql_select = (string) $this->element['sql_select']; $sql_from = (string) $this->element['sql_from']; if ($sql_select && $sql_from) { $query['select'] = $sql_select; $query['from'] = $sql_from; $query['join'] = (string) $this->element['sql_join']; $query['where'] = (string) $this->element['sql_where']; $query['group'] = (string) $this->element['sql_group']; $query['order'] = (string) $this->element['sql_order']; // Get the filters $filters = isset($this->element['sql_filter']) ? explode(',', $this->element['sql_filter']) : []; // Get the default value for query if empty foreach ($filters as $filter) { $name = "sql_default_{$filter}"; $attrib = (string) $this->element[$name]; if (!empty($attrib)) { $defaults[$filter] = $attrib; } } // Process the query $this->query = $this->processQuery($query, $filters, $defaults); } } $this->keyField = (string) $this->element['key_field'] ?: 'value'; $this->valueField = (string) $this->element['value_field'] ?: (string) $this->element['name']; $this->translate = (string) $this->element['translate'] ?: false; $this->header = (string) $this->element['header'] ?: false; } return $return; } /** * Method to process the query from form. * * @param string[] $conditions The conditions from the form. * @param string[] $filters The columns to filter. * @param string[] $defaults The defaults value to set if condition is empty. * * @return QueryInterface The query object. * * @since 3.5 */ protected function processQuery($conditions, $filters, $defaults) { // Get the database object. $db = $this->getDatabase(); // Get the query object $query = $db->getQuery(true); // Select fields $query->select($conditions['select']); // From selected table $query->from($conditions['from']); // Join over the groups if (!empty($conditions['join'])) { $query->join('LEFT', $conditions['join']); } // Where condition if (!empty($conditions['where'])) { $query->where($conditions['where']); } // Group by if (!empty($conditions['group'])) { $query->group($conditions['group']); } // Process the filters if (\is_array($filters)) { // @TODO: Loading the filtering value from the request need to be deprecated. $html_filters = $this->context ? Factory::getApplication()->getUserStateFromRequest($this->context . '.filter', 'filter', [], 'array') : false; $form = $this->form; foreach ($filters as $k => $value) { // Get the filter value from the linked filter field $filterFieldValue = $form->getValue($value, $this->group); if ($html_filters && !empty($html_filters[$value])) { $escape = $db->quote($db->escape($html_filters[$value]), false); $query->where("{$value} = {$escape}"); } elseif ($filterFieldValue !== null) { $escape = $db->quote($db->escape($filterFieldValue), false); $query->where("{$value} = {$escape}"); } elseif (!empty($defaults[$value])) { $escape = $db->quote($db->escape($defaults[$value]), false); $query->where("{$value} = {$escape}"); } } } // Add order to query if (!empty($conditions['order'])) { $query->order($conditions['order']); } return $query; } /** * Method to get the custom field options. * Use the query attribute to supply a query to generate the list. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; // Initialize some field attributes. $key = $this->keyField; $value = $this->valueField; $header = $this->header; if ($this->query) { // Get the database object. $db = $this->getDatabase(); // Set the query and get the result list. $db->setQuery($this->query); try { $items = $db->loadObjectList(); } catch (ExecutionFailureException $e) { Factory::getApplication()->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); } } // Add header. if (!empty($header)) { $header_title = Text::_($header); $options[] = HTMLHelper::_('select.option', '', $header_title); } // Build the field options. if (!empty($items)) { foreach ($items as $item) { if ($this->translate == true) { $options[] = HTMLHelper::_('select.option', $item->$key, Text::_($item->$value)); } else { $options[] = HTMLHelper::_('select.option', $item->$key, $item->$value); } } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]w �"src/Form/Field/UseractiveField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to show a list of available user active statuses * * @since 3.2 */ class UseractiveField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'UserActive'; /** * Available statuses * * @var string[] * @since 3.2 */ protected $predefinedOptions = [ '0' => 'COM_USERS_ACTIVATED', '1' => 'COM_USERS_UNACTIVATED', ]; /** * Method to instantiate the form field object. * * @param Form $form The form to attach to the form field object. * * @since 1.7.0 */ public function __construct($form = null) { parent::__construct($form); // Load the required language $lang = Factory::getLanguage(); $lang->load('com_users', JPATH_ADMINISTRATOR); } } PK1A#]��=���%src/Form/Field/WorkflowstageField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Workflow Stages field. * * @since 4.0.0 */ class WorkflowstageField extends GroupedlistField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Workflowstage'; /** * The component and section separated by ".". * * @var string * @since 4.0.0 */ protected $extension = ''; /** * Show only the stages which has an item attached * * @var boolean * @since 4.0.0 */ protected $activeonly = false; /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result) { if (\strlen($element['extension'])) { $this->extension = (string) $element['extension']; } else { $this->extension = Factory::getApplication()->getInput()->getCmd('extension'); } if ((string) $element['activeonly'] === '1' || (string) $element['activeonly'] === 'true') { $this->activeonly = true; } } return $result; } /** * Method to get the field option groups. * * @return array[] The field option objects as a nested array in groups. * * @since 4.0.0 * @throws \UnexpectedValueException */ protected function getGroups() { $db = $this->getDatabase(); $query = $db->getQuery(true); // Select distinct stages for existing articles $query ->select( [ 'DISTINCT ' . $db->quoteName('ws.id', 'workflow_stage_id'), $db->quoteName('ws.title', 'workflow_stage_title'), $db->quoteName('w.title', 'workflow_title'), $db->quoteName('w.id', 'workflow_id'), $db->quoteName('w.ordering', 'ordering'), $db->quoteName('ws.ordering', 'workflow_stage_ordering'), ] ) ->from($db->quoteName('#__workflow_stages', 'ws')) ->from($db->quoteName('#__workflows', 'w')) ->where( [ $db->quoteName('ws.workflow_id') . ' = ' . $db->quoteName('w.id'), $db->quoteName('w.extension') . ' = :extension', ] ) ->bind(':extension', $this->extension) ->order( [ $db->quoteName('w.ordering'), $db->quoteName('ws.ordering'), ] ); if ($this->activeonly) { $query ->from($db->quoteName('#__workflow_associations', 'wa')) ->where( [ $db->quoteName('wa.stage_id') . ' = ' . $db->quoteName('ws.id'), $db->quoteName('wa.extension') . ' = :associationExtension', ] ) ->bind(':associationExtension', $this->extension); } $stages = $db->setQuery($query)->loadObjectList(); $workflowStages = []; // Grouping the stages by workflow foreach ($stages as $stage) { // Using workflow ID to differentiate workflows having same title $workflowStageKey = Text::_($stage->workflow_title) . ' (' . $stage->workflow_id . ')'; if (!\array_key_exists($workflowStageKey, $workflowStages)) { $workflowStages[$workflowStageKey] = []; } $workflowStages[$workflowStageKey][] = HTMLHelper::_('select.option', $stage->workflow_stage_id, Text::_($stage->workflow_stage_title)); } // Merge any additional options in the XML definition. return array_merge(parent::getGroups(), $workflowStages); } } PK1A#]?"꾐�#src/Form/Field/ChromestyleField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Form\Form; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Chrome Styles field. * * @since 3.0 */ class ChromestyleField extends GroupedlistField { /** * The form field type. * * @var string * @since 3.0 */ public $type = 'ChromeStyle'; /** * The client ID. * * @var integer * @since 3.2 */ protected $clientId; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'clientId') { return $this->clientId; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to get the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'clientId': $this->clientId = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { // Get the client id. $clientId = $this->element['client_id']; if (!isset($clientId)) { $clientName = $this->element['client']; if (isset($clientName)) { $client = ApplicationHelper::getClientInfo($clientName, true); $clientId = $client->id; } } if (!isset($clientId) && $this->form instanceof Form) { $clientId = $this->form->getValue('client_id'); } $this->clientId = (int) $clientId; } return $result; } /** * Method to get the list of template chrome style options * grouped by template. * * @return array[] The field option objects as a nested array in groups. * * @since 3.0 */ protected function getGroups() { $groups = []; // Add Module Style Field $tmp = '---' . Text::_('JLIB_FORM_VALUE_FROM_TEMPLATE') . '---'; $groups[$tmp][] = HTMLHelper::_('select.option', '0', Text::_('JLIB_FORM_VALUE_INHERITED')); $templateStyles = $this->getTemplateModuleStyles(); // Create one new option object for each available style, grouped by templates foreach ($templateStyles as $template => $styles) { $template = ucfirst($template); $groups[$template] = []; foreach ($styles as $style) { $tmp = HTMLHelper::_('select.option', $template . '-' . $style, $style); $groups[$template][] = $tmp; } } reset($groups); return $groups; } /** * Method to get the templates module styles. * * @return string[] The array of styles, grouped by templates. * * @since 3.0 */ protected function getTemplateModuleStyles() { $moduleStyles = []; // Global Layouts $layouts = Folder::files(JPATH_SITE . '/layouts/chromes', '.*\.php'); foreach ($layouts as &$layout) { $layout = basename($layout, '.php'); } $moduleStyles['system'] = $layouts; $templates = $this->getTemplates(); $path = JPATH_ADMINISTRATOR; if ($this->clientId === 0) { $path = JPATH_SITE; } foreach ($templates as $template) { $chromeLayoutPath = $path . '/templates/' . $template->element . '/html/layouts/chromes'; if (!Folder::exists($chromeLayoutPath)) { continue; } $layouts = Folder::files($chromeLayoutPath, '.*\.php'); if ($layouts) { foreach ($layouts as &$layout) { $layout = basename($layout, '.php'); } $moduleStyles[$template->element] = $layouts; } } return $moduleStyles; } /** * Return a list of templates * * @return object[] List of templates * * @since 3.2.1 */ protected function getTemplates() { $db = $this->getDatabase(); // Get the database object and a new query object. $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('element'), $db->quoteName('name'), ] ) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('client_id') . ' = :clientId', $db->quoteName('type') . ' = ' . $db->quote('template'), $db->quoteName('enabled') . ' = 1', ] ) ->bind(':clientId', $this->clientId, ParameterType::INTEGER); // Set the query and load the templates. $db->setQuery($query); return $db->loadObjectList('element'); } } PK1A#]�g� ��src/Form/Field/EmailField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides and input field for email addresses * * @link https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email) * @see \Joomla\CMS\Form\Rule\EmailRule * @since 1.7.0 */ class EmailField extends TextField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Email'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.email'; /** * Method to get the field input markup for email addresses. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'maxLength' => $this->maxLength, 'multiple' => $this->multiple, ]; return array_merge($data, $extraData); } } PK1A#]H��T55src/Form/Field/SubformField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormField; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The Field to load the form inside current form * * @Example with all attributes: * <field name="field-name" type="subform" * formsource="path/to/form.xml" min="1" max="3" multiple="true" buttons="add,remove,move" * layout="joomla.form.field.subform.repeatable-table" groupByFieldset="false" component="com_example" client="site" * label="Field Label" description="Field Description" /> * * @since 3.6 */ class SubformField extends FormField { /** * The form field type. * @var string */ protected $type = 'Subform'; /** * Form source * @var string */ protected $formsource; /** * Minimum items in repeat mode * @var integer */ protected $min = 0; /** * Maximum items in repeat mode * @var integer */ protected $max = 1000; /** * Layout to render the form * @var string */ protected $layout = 'joomla.form.field.subform.default'; /** * Whether group subform fields by it`s fieldset * @var boolean */ protected $groupByFieldset = false; /** * Which buttons to show in multiple mode * @var boolean[] $buttons */ protected $buttons = ['add' => true, 'remove' => true, 'move' => true]; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.6 */ public function __get($name) { switch ($name) { case 'formsource': case 'min': case 'max': case 'layout': case 'groupByFieldset': case 'buttons': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.6 */ public function __set($name, $value) { switch ($name) { case 'formsource': $this->formsource = (string) $value; // Add root path if we have a path to XML file if (strrpos($this->formsource, '.xml') === \strlen($this->formsource) - 4) { $this->formsource = Path::clean(JPATH_ROOT . '/' . $this->formsource); } break; case 'min': $this->min = (int) $value; break; case 'max': if ($value) { $this->max = max(1, (int) $value); } break; case 'groupByFieldset': if ($value !== null) { $value = (string) $value; $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0'); } break; case 'layout': $this->layout = (string) $value; // Make sure the layout is not empty. if (!$this->layout) { // Set default value depend from "multiple" mode $this->layout = !$this->multiple ? 'joomla.form.field.subform.default' : 'joomla.form.field.subform.repeatable'; } break; case 'buttons': if (!$this->multiple) { $this->buttons = []; break; } if ($value && !\is_array($value)) { $value = explode(',', (string) $value); $value = array_fill_keys(array_filter($value), true); } if ($value) { $value = array_merge(['add' => false, 'remove' => false, 'move' => false], $value); $this->buttons = $value; } break; case 'value': // We allow a json encoded string or an array if (is_string($value)) { $value = json_decode($value, true); } $this->value = $value !== null ? (array) $value : null; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the <field /> tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. * * @return boolean True on success. * * @since 3.6 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { if (!parent::setup($element, $value, $group)) { return false; } foreach (['formsource', 'min', 'max', 'layout', 'groupByFieldset', 'buttons'] as $attributeName) { $this->__set($attributeName, $element[$attributeName]); } if ((string) $element['fieldname']) { $this->__set('fieldname', $element['fieldname']); } if ($this->value && \is_string($this->value)) { // Guess here is the JSON string from 'default' attribute $this->value = json_decode($this->value, true); } if (!$this->formsource && $element->form) { // Set the formsource parameter from the content of the node $this->formsource = $element->form->saveXML(); } return true; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.6 */ protected function getInput() { // Prepare data for renderer $data = $this->getLayoutData(); $tmpl = null; $control = $this->name; try { $tmpl = $this->loadSubForm(); $forms = $this->loadSubFormData($tmpl); } catch (\Exception $e) { return $e->getMessage(); } $data['tmpl'] = $tmpl; $data['forms'] = $forms; $data['min'] = $this->min; $data['max'] = $this->max; $data['control'] = $control; $data['buttons'] = $this->buttons; $data['fieldname'] = $this->fieldname; $data['fieldId'] = $this->id; $data['groupByFieldset'] = $this->groupByFieldset; /** * For each rendering process of a subform element, we want to have a * separate unique subform id present to could distinguish the eventhandlers * regarding adding/moving/removing rows from nested subforms from their parents. */ static $unique_subform_id = 0; $data['unique_subform_id'] = ('sr-' . ($unique_subform_id++)); // Prepare renderer $renderer = $this->getRenderer($this->layout); // Allow to define some Layout options as attribute of the element if ($this->element['component']) { $renderer->setComponent((string) $this->element['component']); } if ($this->element['client']) { $renderer->setClient((string) $this->element['client']); } // Render $html = $renderer->render($data); // Add hidden input on front of the subform inputs, in multiple mode // for allow to submit an empty value if ($this->multiple) { $html = '<input name="' . $this->name . '" type="hidden" value="">' . $html; } return $html; } /** * Method to get the name used for the field input tag. * * @param string $fieldName The field element name. * * @return string The name to be used for the field input tag. * * @since 3.6 */ protected function getName($fieldName) { $name = ''; // If there is a form control set for the attached form add it first. if ($this->formControl) { $name .= $this->formControl; } // If the field is in a group add the group control to the field name. if ($this->group) { // If we already have a name segment add the group control as another level. $groups = explode('.', $this->group); if ($name) { foreach ($groups as $group) { $name .= '[' . $group . ']'; } } else { $name .= array_shift($groups); foreach ($groups as $group) { $name .= '[' . $group . ']'; } } } // If we already have a name segment add the field name as another level. if ($name) { $name .= '[' . $fieldName . ']'; } else { $name .= $fieldName; } return $name; } /** * Loads the form instance for the subform. * * @return Form The form instance. * * @throws \InvalidArgumentException if no form provided. * @throws \RuntimeException if the form could not be loaded. * * @since 3.9.7 */ public function loadSubForm() { $control = $this->name; if ($this->multiple) { $control .= '[' . $this->fieldname . 'X]'; } // Prepare the form template $formname = 'subform.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $this->name); $tmpl = Form::getInstance($formname, $this->formsource, ['control' => $control]); return $tmpl; } /** * Binds given data to the subform and its elements. * * @param Form $subForm Form instance of the subform. * * @return Form[] Array of Form instances for the rows. * * @since 3.9.7 */ protected function loadSubFormData(Form $subForm) { $value = $this->value ? (array) $this->value : []; // Simple form, just bind the data and return one row. if (!$this->multiple) { $subForm->bind($value); return [$subForm]; } // Multiple rows possible: Construct array and bind values to their respective forms. $forms = []; $value = array_values($value); // Show as many rows as we have values, but at least min and at most max. $c = max($this->min, min(\count($value), $this->max)); for ($i = 0; $i < $c; $i++) { $control = $this->name . '[' . $this->fieldname . $i . ']'; $itemForm = Form::getInstance($subForm->getName() . $i, $this->formsource, ['control' => $control]); if (!empty($value[$i])) { $itemForm->bind($value[$i]); } $forms[] = $itemForm; } return $forms; } /** * Method to filter a field value. * * @param mixed $value The optional value to use as the default for the field. * @param string $group The optional dot-separated form group path on which to find the field. * @param ?Registry $input An optional Registry object with the entire data set to filter * against the entire form. * * @return mixed The filtered value. * * @since 4.0.0 * @throws \UnexpectedValueException */ public function filter($value, $group = null, Registry $input = null) { // Make sure there is a valid SimpleXMLElement. if (!($this->element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); } // Get the field filter type. $filter = (string) $this->element['filter']; if ($filter !== '') { return parent::filter($value, $group, $input); } // Dirty way of ensuring required fields in subforms are submitted and filtered the way other fields are $subForm = $this->loadSubForm(); // Subform field may have a default value, that is a JSON string if ($value && is_string($value)) { $value = json_decode($value, true); // The string is invalid json if (!$value) { return null; } } if ($this->multiple) { $return = []; if ($value) { foreach ($value as $key => $val) { $return[$key] = $subForm->filter($val); } } } else { $return = $subForm->filter($value); } return $return; } } PK1A#]^�WW"src/Form/Field/TransitionField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Workflow Transitions field. * * @since 4.0.0 */ class TransitionField extends GroupedlistField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Transition'; /** * The component and section separated by ".". * * @var string * @since 4.0.0 */ protected $extension; /** * The workflow stage to use. * * @var integer */ protected $workflowStage; /** * Method to setup the extension * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result) { $input = Factory::getApplication()->getInput(); if (\strlen($element['extension'])) { $this->extension = (string) $element['extension']; } else { $this->extension = $input->getCmd('extension'); } if (\strlen($element['workflow_stage'])) { $this->workflowStage = (int) $element['workflow_stage']; } else { $this->workflowStage = $input->getInt('id'); } } return $result; } /** * Method to get a list of options for a list input. * * @return array[] An array of HTMLHelper options. * * @since 4.0.0 */ protected function getGroups() { // Initialise variable. $db = $this->getDatabase(); $extension = $this->extension; $workflowStage = (int) $this->workflowStage; $query = $db->getQuery(true) ->select( [ $db->quoteName('t.id', 'value'), $db->quoteName('t.title', 'text'), ] ) ->from( [ $db->quoteName('#__workflow_transitions', 't'), $db->quoteName('#__workflow_stages', 's'), $db->quoteName('#__workflow_stages', 's2'), ] ) ->whereIn($db->quoteName('t.from_stage_id'), [-1, $workflowStage]) ->where( [ $db->quoteName('t.to_stage_id') . ' = ' . $db->quoteName('s.id'), $db->quoteName('s.workflow_id') . ' = ' . $db->quoteName('s2.workflow_id'), $db->quoteName('s.workflow_id') . ' = ' . $db->quoteName('t.workflow_id'), $db->quoteName('s2.id') . ' = :stage1', $db->quoteName('t.published') . ' = 1', $db->quoteName('s.published') . ' = 1', ] ) ->bind(':stage1', $workflowStage, ParameterType::INTEGER) ->order($db->quoteName('t.ordering')); $items = $db->setQuery($query)->loadObjectList(); Factory::getLanguage()->load('com_workflow', JPATH_ADMINISTRATOR); $parts = explode('.', $extension); $component = reset($parts); if (\count($items)) { $user = Factory::getUser(); $items = array_filter( $items, function ($item) use ($user, $component) { return $user->authorise('core.execute.transition', $component . '.transition.' . $item->value); } ); foreach ($items as $item) { $item->text = Text::_($item->text); } } // Get workflow stage title $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__workflow_stages')) ->where($db->quoteName('id') . ' = :stage') ->bind(':stage', $workflowStage, ParameterType::INTEGER); $workflowName = $db->setQuery($query)->loadResult(); $default = [[HTMLHelper::_('select.option', '', Text::_($workflowName))]]; $groups = parent::getGroups(); if (\count($items)) { $groups[Text::_('COM_CONTENT_RUN_TRANSITION')] = $items; } if (\count($groups)) { $default[][] = HTMLHelper::_('select.option', '-1', '--------', ['disable' => true]); } // Merge with defaults return array_merge($default, $groups); } } PK1A#]Sm���"src/Form/Field/ComponentsField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Framework. * * @since 3.7.0 */ class ComponentsField extends ListField { /** * The form field type. * * @var string * @since 3.7.0 */ protected $type = 'Components'; /** * Method to get a list of options for a list input. * * @return object[] An array of JHtml options. * * @since 2.5.0 */ protected function getOptions() { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('name', 'text'), $db->quoteName('element', 'value'), ] ) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('enabled') . ' >= 1', $db->quoteName('type') . ' = ' . $db->quote('component'), ] ); $items = $db->setQuery($query)->loadObjectList(); if ($items) { $lang = Factory::getLanguage(); foreach ($items as &$item) { // Load language $extension = $item->value; $lang->load("$extension.sys", JPATH_ADMINISTRATOR) || $lang->load("$extension.sys", JPATH_ADMINISTRATOR . '/components/' . $extension); // Translate component name $item->text = Text::_($item->text); } // Sort by component name $items = ArrayHelper::sortObjects($items, 'text', 1, true, true); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $items); return $options; } } PK1A#]p:t9��!src/Form/Field/HeadertagField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla! CMS. * * @since 3.0 */ class HeadertagField extends ListField { /** * The form field type. * * @var string * @since 3.0 */ protected $type = 'HeaderTag'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 3.0 */ protected function getOptions() { $options = []; $tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div']; // Create one new option object for each tag foreach ($tags as $tag) { $tmp = HTMLHelper::_('select.option', $tag, $tag); $options[] = $tmp; } reset($options); return $options; } } PK1A#]�k!W�4�4src/Form/Field/MediaField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\FormField; use Joomla\CMS\Helper\MediaHelper; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Provides a modal media selector including upload mechanism * * @since 1.6 */ class MediaField extends FormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'Media'; /** * The authorField. * * @var string * @since 3.2 */ protected $authorField; /** * The asset. * * @var string * @since 3.2 */ protected $asset; /** * The link. * * @var string * @since 3.2 */ protected $link; /** * Modal width. * * @var integer * @since 3.4.5 */ protected $width; /** * Modal height. * * @var integer * @since 3.4.5 */ protected $height; /** * The preview. * * @var string * @since 3.2 */ protected $preview; /** * The directory. * * @var string * @since 3.2 */ protected $directory; /** * The previewWidth. * * @var integer * @since 3.2 */ protected $previewWidth; /** * The previewHeight. * * @var integer * @since 3.2 */ protected $previewHeight; /** * The folder. * * @var string * @since 4.3.0 */ protected $folder; /** * Comma separated types of files for Media Manager * Possible values: images,audios,videos,documents * * @var string * @since 4.0.0 */ protected $types; /** * Layout to render * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.media'; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'authorField': case 'asset': case 'link': case 'width': case 'height': case 'preview': case 'directory': case 'previewWidth': case 'previewHeight': case 'folder': case 'types': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'authorField': case 'asset': case 'link': case 'preview': case 'directory': case 'folder': case 'types': $this->$name = (string) $value; break; case 'height': case 'previewWidth': case 'previewHeight': case 'width': $this->$name = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { $assetField = $this->element['asset_field'] ? (string) $this->element['asset_field'] : 'asset_id'; $this->authorField = $this->element['created_by_field'] ? (string) $this->element['created_by_field'] : 'created_by'; $this->asset = $this->form->getValue($assetField) ?: (string) $this->element['asset_id']; $this->link = (string) $this->element['link']; $this->width = isset($this->element['width']) ? (int) $this->element['width'] : 800; $this->height = isset($this->element['height']) ? (int) $this->element['height'] : 500; $this->preview = (string) $this->element['preview']; $this->directory = (string) $this->element['directory']; $this->previewWidth = isset($this->element['preview_width']) ? (int) $this->element['preview_width'] : 200; $this->previewHeight = isset($this->element['preview_height']) ? (int) $this->element['preview_height'] : 200; $this->types = isset($this->element['types']) ? (string) $this->element['types'] : 'images'; } return $result; } /** * Method to get the field input markup for a media selector. * Use attributes to identify specific created_by and asset_id fields * * @return string The field input markup. * * @since 1.6 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Get the data that is going to be passed to the layout * * @return array */ public function getLayoutData() { // Get the basic field data $data = parent::getLayoutData(); $asset = $this->asset; if ($asset === '') { $asset = Factory::getApplication()->getInput()->get('option'); } // Value in new format such as images/headers/blue-flower.jpg#joomlaImage://local-images/headers/blue-flower.jpg?width=700&height=180 if ($this->value && strpos($this->value, '#') !== false) { $uri = new Uri(explode('#', $this->value)[1]); $adapter = $uri->getHost(); $path = $uri->getPath(); // Remove filename from stored path to get the path to the folder which file is stored $pos = strrpos($path, '/'); if ($pos !== false) { $path = substr($path, 0, $pos); } if ($path === '') { $path = '/'; } $this->folder = $adapter . ':' . $path; } elseif ($this->value && is_file(JPATH_ROOT . '/' . $this->value)) { /** * Local image, for example images/sampledata/cassiopeia/nasa2-640.jpg. We need to validate and make sure * the top level folder is one of the directories configured in the filesystem local plugin to avoid an error * message being displayed when users click on Select button to select a new image. */ $paths = explode('/', Path::clean($this->value, '/')); // Remove filename from $paths array array_pop($paths); if (MediaHelper::isValidLocalDirectory($paths[0])) { $adapterName = array_shift($paths); $this->folder = 'local-' . $adapterName . ':/' . implode('/', $paths); } } elseif ($this->directory && is_dir(JPATH_ROOT . '/' . ComponentHelper::getParams('com_media')->get('image_path', 'images') . '/' . $this->directory)) { /** * This is the case where a folder is configured in directory attribute of the form field. The directory needs * to be a relative folder of the folder configured in Path to Images Folder config option of Media component. * Same with an already stored local image above, we need to validate and make sure the top level folder is one of the * directories configured in the filesystem local plugin. */ $path = ComponentHelper::getParams('com_media')->get('image_path', 'images') . '/' . $this->directory; $paths = explode('/', Path::clean($path, '/')); if (MediaHelper::isValidLocalDirectory($paths[0])) { $adapterName = array_shift($paths); $this->folder = 'local-' . $adapterName . ':/' . implode('/', $paths); } } elseif ($this->directory && strpos($this->directory, ':')) { /** * Directory contains adapter information and path, for example via programming or directly defined in xml * via directory attribute */ $this->folder = $this->directory; } else { $this->folder = ''; } $mediaTypes = array_map('trim', explode(',', $this->types)); $types = []; $imagesExt = array_map( 'trim', explode( ',', ComponentHelper::getParams('com_media')->get( 'image_extensions', 'bmp,gif,jpg,jpeg,png,webp' ) ) ); $audiosExt = array_map( 'trim', explode( ',', ComponentHelper::getParams('com_media')->get( 'audio_extensions', 'mp3,m4a,mp4a,ogg' ) ) ); $videosExt = array_map( 'trim', explode( ',', ComponentHelper::getParams('com_media')->get( 'video_extensions', 'mp4,mp4v,mpeg,mov,webm' ) ) ); $documentsExt = array_map( 'trim', explode( ',', ComponentHelper::getParams('com_media')->get( 'doc_extensions', 'doc,odg,odp,ods,odt,pdf,ppt,txt,xcf,xls,csv' ) ) ); $imagesAllowedExt = []; $audiosAllowedExt = []; $videosAllowedExt = []; $documentsAllowedExt = []; // Cleanup the media types array_map( function ($mediaType) use (&$types, &$imagesAllowedExt, &$audiosAllowedExt, &$videosAllowedExt, &$documentsAllowedExt, $imagesExt, $audiosExt, $videosExt, $documentsExt) { switch ($mediaType) { case 'images': $types[] = '0'; $imagesAllowedExt = $imagesExt; break; case 'audios': $types[] = '1'; $audiosAllowedExt = $audiosExt; break; case 'videos': $types[] = '2'; $videosAllowedExt = $videosExt; break; case 'documents': $types[] = '3'; $documentsAllowedExt = $documentsExt; break; default: break; } }, $mediaTypes ); sort($types); $extraData = [ 'asset' => $asset, 'authorField' => $this->authorField, 'authorId' => $this->form->getValue($this->authorField), 'folder' => $this->folder, 'link' => $this->link, 'preview' => $this->preview, 'previewHeight' => $this->previewHeight, 'previewWidth' => $this->previewWidth, 'mediaTypes' => implode(',', $types), 'imagesExt' => $imagesExt, 'audiosExt' => $audiosExt, 'videosExt' => $videosExt, 'documentsExt' => $documentsExt, 'imagesAllowedExt' => $imagesAllowedExt, 'audiosAllowedExt' => $audiosAllowedExt, 'videosAllowedExt' => $videosAllowedExt, 'documentsAllowedExt' => $documentsAllowedExt, ]; return array_merge($data, $extraData); } } PK1A#]���]] src/Form/Field/FilelistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; use Joomla\Filesystem\Path; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports an HTML select list of files * * @since 1.7.0 */ class FilelistField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Filelist'; /** * The filename filter. * * @var string * @since 4.0.0 */ protected $fileFilter; /** * The exclude. * * @var string * @since 3.2 */ protected $exclude; /** * The hideNone. * * @var boolean * @since 3.2 */ protected $hideNone = false; /** * The hideDefault. * * @var boolean * @since 3.2 */ protected $hideDefault = false; /** * The stripExt. * * @var boolean * @since 3.2 */ protected $stripExt = false; /** * The directory. * * @var string * @since 3.2 */ protected $directory; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'fileFilter': case 'exclude': case 'hideNone': case 'hideDefault': case 'stripExt': case 'directory': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'fileFilter': case 'directory': case 'exclude': $this->$name = (string) $value; break; case 'hideNone': case 'hideDefault': case 'stripExt': $value = (string) $value; $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->fileFilter = (string) $this->element['fileFilter']; $this->exclude = (string) $this->element['exclude']; $hideNone = (string) $this->element['hide_none']; $this->hideNone = ($hideNone === 'true' || $hideNone === 'hideNone' || $hideNone === '1'); $hideDefault = (string) $this->element['hide_default']; $this->hideDefault = ($hideDefault === 'true' || $hideDefault === 'hideDefault' || $hideDefault === '1'); $stripExt = (string) $this->element['stripext']; $this->stripExt = ($stripExt === 'true' || $stripExt === 'stripExt' || $stripExt === '1'); // Get the path in which to search for file options. $this->directory = (string) $this->element['directory']; } return $return; } /** * Method to get the list of files for the field options. * Specify the target directory with a directory attribute * Attributes allow an exclude mask and stripping of extensions from file name. * Default attribute may optionally be set to null (no file) or -1 (use a default). * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; $path = $this->directory; if (!is_dir($path)) { $path = JPATH_ROOT . '/' . $path; } $path = Path::clean($path); // Prepend some default options based on field attributes. if (!$this->hideNone) { $options[] = HTMLHelper::_('select.option', '-1', Text::alt('JOPTION_DO_NOT_USE', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } if (!$this->hideDefault) { $options[] = HTMLHelper::_('select.option', '', Text::alt('JOPTION_USE_DEFAULT', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } // Get a list of files in the search path with the given filter. $files = Folder::files($path, $this->fileFilter); // Build the options list from the list of files. if (\is_array($files)) { foreach ($files as $file) { // Check to see if the file is in the exclude mask. if ($this->exclude) { if (preg_match(\chr(1) . $this->exclude . \chr(1), $file)) { continue; } } // If the extension is to be stripped, do it. if ($this->stripExt) { $file = File::stripExt($file); } $options[] = HTMLHelper::_('select.option', $file, $file); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]\? ;)src/Form/Field/WorkflowconditionField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Workflow\WorkflowServiceInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Workflow States field. * * @since 4.0.0 */ class WorkflowconditionField extends ListField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Workflowcondition'; /** * The component and section separated by ".". * * @var string * @since 4.0.0 */ protected $extension = ''; /** * Determinate if the "All" value should be added * * @var boolean * @since 4.0.0 */ protected $hideAll = false; /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $success = parent::setup($element, $value, $group); if ($success) { if (\strlen($element['extension'])) { $this->extension = (string) $element['extension']; } else { $this->extension = Factory::getApplication()->getInput()->getCmd('extension'); } if (\strlen($element['hide_all'])) { $this->hideAll = (string) $element['hide_all'] === 'true' || (string) $element['hide_all'] === 'yes'; } } return $success; } /** * Method to get the field options. * * @return object[] The field option objects. * * @since 4.0.0 */ protected function getOptions() { $fieldname = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); $options = []; $conditions = []; $parts = explode('.', $this->extension); $component = Factory::getApplication()->bootComponent($parts[0]); if ($component instanceof WorkflowServiceInterface) { $conditions = $component->getConditions($this->extension); } foreach ($conditions as $value => $option) { $text = trim((string) $option) != '' ? trim((string) $option) : $value; $selected = ((int) $this->value === $value); $tmp = [ 'value' => $value, 'text' => Text::alt($text, $fieldname), 'selected' => $selected, 'checked' => $selected, ]; // Add the option object to the result set. $options[] = (object) $tmp; } if (!$this->hideAll) { $options[] = (object) [ 'value' => '*', 'text' => Text::_('JALL'), 'selected' => $this->value === '*', 'checked' => $this->value === '*', ]; } // Merge any additional options in the XML definition. return array_merge(parent::getOptions(), $options); } } PK1A#]�n��((!src/Form/Field/UserstateField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to load a list of available users statuses * * @since 3.2 */ class UserstateField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'UserState'; /** * Available statuses * * @var string[] * @since 3.2 */ protected $predefinedOptions = [ '0' => 'JENABLED', '1' => 'JDISABLED', ]; } PK1A#]�#src/Form/Field/AccesslevelField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a list of access levels. Access levels control what users in specific * groups can see. * * @see JAccess * @since 1.7.0 */ class AccesslevelField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Accesslevel'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 4.0.0 */ protected function getOptions() { return array_merge(parent::getOptions(), HTMLHelper::_('access.assetgroups')); } } PK1A#]ۓyV�� src/Form/Field/PasswordField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Text field for passwords * * @link https://html.spec.whatwg.org/multipage/input.html#password-state-(type=password) * @note Two password fields may be validated as matching using \Joomla\CMS\Form\Rule\EqualsRule * @since 1.7.0 */ class PasswordField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Password'; /** * The threshold of password field. * * @var integer * @since 3.2 */ protected $threshold = 66; /** * The allowable minimum length of password. * * @var integer * @since 4.3.0 */ protected $minLength; /** * The allowable maxlength of password. * * @var integer * @since 3.2 */ protected $maxLength; /** * The allowable minimum length of integers. * * @var integer * @since 4.3.0 */ protected $minIntegers; /** * The allowable minimum length of symbols. * * @var integer * @since 4.3.0 */ protected $minSymbols; /** * The allowable minimum length of upper case characters. * * @var integer * @since 4.3.0 */ protected $minUppercase; /** * The allowable minimum length of lower case characters. * * @var integer * @since 4.3.0 */ protected $minLowercase; /** * Whether to attach a password strength meter or not. * * @var boolean * @since 3.2 */ protected $meter = false; /** * Whether to attach a password strength meter or not. * * @var boolean * @since 4.0.0 */ protected $force = false; /** * The rules flag. * * @var bool * @since 4.3.0 */ protected $rules = false; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.password'; /** * Attach an unlock button and disable the input field, * also remove the value from the output. * * @var boolean * @since 3.9.24 */ protected $lock = false; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'lock': case 'threshold': case 'maxLength': case 'meter': case 'force': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'maxLength': case 'threshold': $this->$name = (int) $value; break; case 'lock': case 'meter': case 'force': $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $lock = (string) $this->element['lock']; $this->lock = ($lock === 'true' || $lock === 'on' || $lock === '1'); $this->maxLength = $this->element['maxlength'] ? (int) $this->element['maxlength'] : 99; $this->threshold = $this->element['threshold'] ? (int) $this->element['threshold'] : 66; $meter = (string) $this->element['strengthmeter']; $this->meter = ($meter === 'true' || $meter === 'on' || $meter === '1'); $force = (string) $this->element['forcePassword']; $this->force = (($force === 'true' || $force === 'on' || $force === '1') && $this->meter === true); $rules = (string) $this->element['rules']; $this->rules = (($rules === 'true' || $rules === 'on' || $rules === '1') && $this->meter === true); // Set some initial values $this->minLength = 12; $this->minIntegers = 0; $this->minSymbols = 0; $this->minUppercase = 0; $this->minLowercase = 0; if (Factory::getApplication()->get('db') != '' && !Factory::getApplication()->isClient('cli_installation')) { $this->minLength = (int) ComponentHelper::getParams('com_users')->get('minimum_length', 12); $this->minIntegers = (int) ComponentHelper::getParams('com_users')->get('minimum_integers', 0); $this->minSymbols = (int) ComponentHelper::getParams('com_users')->get('minimum_symbols', 0); $this->minUppercase = (int) ComponentHelper::getParams('com_users')->get('minimum_uppercase', 0); $this->minLowercase = (int) ComponentHelper::getParams('com_users')->get('minimum_lowercase', 0); } } return $return; } /** * Method to get the field input markup for password. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = [ 'lock' => $this->lock, 'maxLength' => $this->maxLength, 'meter' => $this->meter, 'threshold' => $this->threshold, 'minLength' => $this->minLength, 'minIntegers' => $this->minIntegers, 'minSymbols' => $this->minSymbols, 'minUppercase' => $this->minUppercase, 'minLowercase' => $this->minLowercase, 'forcePassword' => $this->force, 'rules' => $this->rules, ]; return array_merge($data, $extraData); } } PK1A#]5�N]00src/Form/Field/MeterField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a meter to show value in a range. * * @link https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search) * @since 3.2 */ class MeterField extends FormField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Meter'; /** * Whether the field is active or not. * * @var boolean * @since 3.2 */ protected $active = false; /** * Whether the field is animated or not. * * @var boolean * @since 3.2 */ protected $animated = true; /** * The min value of the progress bar * * @var int * @since 4.4.0 */ protected $min = 0; /** * The max value of the progress bar * * @var int * @since 4.0.0 */ protected $max = 100; /** * The width of the progress bar * * @var string * @since 4.4.0 */ protected $width; /** * The color of the progress bar * * @var string * @since 4.4.0 */ protected $color; /** * The striped class for the progress bar * * @var boolean * @since 4.0.0 */ protected $striped; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.meter'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'active': case 'width': case 'animated': case 'color': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'max': case 'min': $this->$name = (int) $value; break; case 'width': case 'color': $this->$name = (string) $value; break; case 'active': $value = (string) $value; $this->active = ($value === 'true' || $value === $name || $value === '1'); break; case 'animated': $value = (string) $value; $this->animated = !($value === 'false' || $value === 'off' || $value === '0'); break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->width = isset($this->element['width']) ? (string) $this->element['width'] : ''; $this->color = isset($this->element['color']) ? (string) $this->element['color'] : ''; $active = (string) $this->element['active']; $this->active = ($active === 'true' || $active === 'on' || $active === '1'); $animated = (string) $this->element['animated']; $this->animated = !($animated === 'false' || $animated === 'off' || $animated === '0'); } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = [ 'width' => $this->width, 'color' => $this->color, 'animated' => $this->animated, 'active' => $this->active, 'max' => $this->max, 'min' => $this->min, ]; return array_merge($data, $extraData); } } PK1A#]6���jjsrc/Form/Field/UserField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\Language\Text; use Joomla\CMS\User\User; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to select a user ID from a modal list. * * @since 1.6 */ class UserField extends FormField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'User'; /** * Filtering groups * * @var array * @since 3.5 * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $groups = null; /** * Users to exclude from the list of users * * @var array * @since 3.5 * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $excluded = null; /** * Layout to render * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.user'; /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 3.7.0 * * @see FormField::setup() */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); // If user can't access com_users the field should be readonly. if ($return && !$this->readonly) { $this->readonly = !Factory::getUser()->authorise('core.manage', 'com_users'); } return $return; } /** * Method to get the user field input markup. * * @return string The field input markup. * * @since 1.6 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Get the data that is going to be passed to the layout * * @return array * * @since 3.5 */ public function getLayoutData() { // Get the basic field data $data = parent::getLayoutData(); // Initialize value $name = Text::_('JLIB_FORM_SELECT_USER'); if (is_numeric($this->value)) { $name = User::getInstance($this->value)->name; } elseif (strtoupper($this->value) === 'CURRENT') { // Handle the special case for "current". // 'CURRENT' is not a reasonable value to be placed in the html $current = Factory::getUser(); $this->value = $current->id; $data['value'] = $this->value; $name = $current->name; } // User lookup went wrong, we assign the value instead. if ($name === null && $this->value) { $name = $this->value; } $extraData = [ 'userName' => $name, 'groups' => $this->getGroups(), 'excluded' => $this->getExcluded(), ]; return array_merge($data, $extraData); } /** * Method to get the filtering groups (null means no filtering) * * @return string[] Array of filtering groups or null. * * @since 1.6 */ protected function getGroups() { if (isset($this->element['groups'])) { return explode(',', $this->element['groups']); } return []; } /** * Method to get the users to exclude from the list of users * * @return string[] Array of users to exclude or null to not exclude them * * @since 1.6 */ protected function getExcluded() { if (isset($this->element['exclude'])) { return explode(',', $this->element['exclude']); } return []; } } PK1A#]� 8���#src/Form/Field/GroupedlistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a grouped list select field. * * @since 1.7.0 */ class GroupedlistField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Groupedlist'; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.groupedlist'; /** * Method to get the field option groups. * * @return array[] The field option objects as a nested array in groups. * * @since 1.7.0 * @throws \UnexpectedValueException */ protected function getGroups() { $groups = []; $label = 0; foreach ($this->element->children() as $element) { switch ($element->getName()) { // The element is an <option /> case 'option': // Initialize the group if necessary. if (!isset($groups[$label])) { $groups[$label] = []; } $disabled = (string) $element['disabled']; $disabled = ($disabled === 'true' || $disabled === 'disabled' || $disabled === '1'); // Create a new option object based on the <option /> element. $tmp = HTMLHelper::_( 'select.option', ($element['value']) ? (string) $element['value'] : trim((string) $element), Text::alt(trim((string) $element), preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname)), 'value', 'text', $disabled ); // Set some option attributes. $tmp->class = (string) $element['class']; // Set some JavaScript option attributes. $tmp->onclick = (string) $element['onclick']; // Add the option. $groups[$label][] = $tmp; break; // The element is a <group /> case 'group': // Get the group label. if ($groupLabel = (string) $element['label']) { $label = Text::_($groupLabel); } // Initialize the group if necessary. if (!isset($groups[$label])) { $groups[$label] = []; } // Iterate through the children and build an array of options. foreach ($element->children() as $option) { // Only add <option /> elements. if ($option->getName() !== 'option') { continue; } $disabled = (string) $option['disabled']; $disabled = ($disabled === 'true' || $disabled === 'disabled' || $disabled === '1'); // Create a new option object based on the <option /> element. $tmp = HTMLHelper::_( 'select.option', ($option['value']) ? (string) $option['value'] : Text::_(trim((string) $option)), Text::_(trim((string) $option)), 'value', 'text', $disabled ); // Set some option attributes. $tmp->class = (string) $option['class']; // Set some JavaScript option attributes. $tmp->onclick = (string) $option['onclick']; // Add the option. $groups[$label][] = $tmp; } if ($groupLabel) { $label = \count($groups); } break; // Unknown element type. default: throw new \UnexpectedValueException(sprintf('Unsupported element %s in GroupedlistField', $element->getName()), 500); } } reset($groups); return $groups; } /** * Method to get the field input markup for a grouped list. * Multiselect is enabled by using the multiple attribute. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { $data = $this->getLayoutData(); // Get the field groups. $data['groups'] = (array) $this->getGroups(); return $this->getRenderer($this->layout)->render($data); } } PK1A#]��=Ք�src/Form/Field/FileField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides an input field for files * * @link https://html.spec.whatwg.org/multipage/input.html#file-upload-state-(type=file) * @since 1.7.0 */ class FileField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'File'; /** * The accepted file type list. * * @var mixed * @since 3.2 */ protected $accept; /** * Name of the layout being used to render the field * * @var string * @since 3.6 */ protected $layout = 'joomla.form.field.file'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'accept') { return $this->accept; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'accept': $this->accept = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->accept = (string) $this->element['accept']; } return $return; } /** * Method to get the field input markup for the file field. * Field attributes allow specification of a maximum file size and a string * of accepted file extensions. * * @return string The field input markup. * * @note The field does not include an upload mechanism. * @see \Joomla\CMS\Form\Field\MediaField * @since 1.7.0 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.6 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'accept' => $this->accept, 'multiple' => $this->multiple, ]; return array_merge($data, $extraData); } } PK1A#]p`�88src/Form/Field/TimeField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a select list of integers with specified first, last and step values. * * @since 4.0.0 */ class TimeField extends FormField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Time'; /** * The allowable minimal value of the field. * * @var integer * @since 4.0.0 */ protected $min; /** * The allowable maximal value of the field. * * @var integer * @since 4.0.0 */ protected $max; /** * Steps between different values * * @var integer * @since 4.0.0 */ protected $step; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.time'; /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 4.0.0 */ public function __set($name, $value) { switch ($name) { case 'max': case 'min': case 'step': $this->$name = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @see FormField::setup() * @return boolean True on success. * * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // It is better not to force any default limits if none is specified $this->max = isset($this->element['max']) ? (int) $this->element['max'] : null; $this->min = isset($this->element['min']) ? (int) $this->element['min'] : null; $this->step = isset($this->element['step']) ? (int) $this->element['step'] : null; } return $return; } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 4.0.0 */ public function __get($name) { switch ($name) { case 'min': case 'max': case 'step': return $this->$name; } return parent::__get($name); } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 4.0.0 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 4.0.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'min' => $this->min, 'max' => $this->max, 'step' => $this->step, ]; return array_merge($data, $extraData); } } PK1A#]�%src/Form/Field/TemplatestyleField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports a select grouped list of template styles * * @since 1.6 */ class TemplatestyleField extends GroupedlistField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'TemplateStyle'; /** * The client name. * * @var mixed * @since 3.2 */ protected $clientName; /** * The template. * * @var mixed * @since 3.2 */ protected $template; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'clientName': case 'template': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'clientName': case 'template': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { // Get the clientName template. $this->clientName = $this->element['client'] ? (string) $this->element['client'] : 'site'; $this->template = (string) $this->element['template']; } return $result; } /** * Method to get the list of template style options grouped by template. * Use the client attribute to specify a specific client. * Use the template attribute to specify a specific template * * @return array[] The field option objects as a nested array in groups. * * @since 1.6 */ protected function getGroups() { $groups = []; $lang = Factory::getLanguage(); // Get the client and client_id. $client = ApplicationHelper::getClientInfo($this->clientName, true); // Get the template. $template = $this->template; // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Build the query. $query->select( [ $db->quoteName('s.id'), $db->quoteName('s.title'), $db->quoteName('e.name'), $db->quoteName('s.template'), ] ) ->from($db->quoteName('#__template_styles', 's')) ->where($db->quoteName('s.client_id') . ' = :clientId') ->bind(':clientId', $client->id, ParameterType::INTEGER) ->order( [ $db->quoteName('template'), $db->quoteName('title'), ] ); if ($template) { $query->where('s.template = ' . $db->quote($template)); } $query->join('LEFT', '#__extensions as e on e.element=s.template') ->where('e.enabled = 1') ->where($db->quoteName('e.type') . ' = ' . $db->quote('template')); // Set the query and load the styles. $db->setQuery($query); $styles = $db->loadObjectList(); // Build the grouped list array. if ($styles) { foreach ($styles as $style) { $template = $style->template; $lang->load('tpl_' . $template . '.sys', $client->path) || $lang->load('tpl_' . $template . '.sys', $client->path . '/templates/' . $template); $name = Text::_($style->name); // Initialize the group if necessary. if (!isset($groups[$name])) { $groups[$name] = []; } $groups[$name][] = HTMLHelper::_('select.option', $style->id, $style->title); } } // Merge any additional groups in the XML definition. $groups = array_merge(parent::getGroups(), $groups); return $groups; } } PK1A#]�Ԗ���(src/Form/Field/FrontendlanguageField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Provides a list of published content languages with home pages * * @see \Joomla\CMS\Form\Field\LanguageField for a select list of application languages. * @since 3.5 */ class FrontendlanguageField extends ListField { /** * The form field type. * * @var string * @since 3.5 */ public $type = 'Frontend_Language'; /** * Method to get the field options for frontend published content languages with homes. * * @return object[] The options the field is going to show. * * @since 3.5 */ protected function getOptions() { // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); $query->select('a.lang_code AS value, a.title AS text') ->from($db->quoteName('#__languages') . ' AS a') ->where('a.published = 1') ->order('a.title'); // Select the language home pages. $query->select('l.home, l.language') ->innerJoin($db->quoteName('#__menu') . ' AS l ON l.language=a.lang_code AND l.home=1 AND l.published=1 AND l.language <> ' . $db->quote('*')) ->innerJoin($db->quoteName('#__extensions') . ' AS e ON e.element = a.lang_code') ->where('e.client_id = 0') ->where('e.enabled = 1') ->where('e.state = 0'); $db->setQuery($query); try { $languages = $db->loadObjectList(); } catch (\RuntimeException $e) { $languages = []; if (Factory::getUser()->authorise('core.admin')) { Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); } } // Merge any additional options in the XML definition. return array_merge(parent::getOptions(), $languages); } } PK1A#]5�!!src/Form/Field/UrlField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a URL text field * * @link https://html.spec.whatwg.org/multipage/input.html#url-state-(type=url) * @see \Joomla\CMS\Form\Rule\UrlRule for validation of full urls * @since 1.7.0 */ class UrlField extends TextField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Url'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.url'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.1.2 (CMS) */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; // Note that the input type "url" is suitable only for external URLs, so if internal URLs are allowed // we have to use the input type "text" instead. $inputType = $this->element['relative'] ? 'type="text"' : 'type="url"'; $extraData = [ 'maxLength' => $maxLength, 'inputType' => $inputType, ]; return array_merge($data, $extraData); } } PK1A#]KБyy*src/Form/Field/LastvisitdaterangeField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to show a list of available date ranges to filter on last visit date. * * @since 3.6 */ class LastvisitdaterangeField extends PredefinedlistField { /** * Method to instantiate the form field object. * * @param Form $form The form to attach to the form field object. * * @since 1.7.0 */ public function __construct($form = null) { parent::__construct($form); // Set the type $this->type = 'LastvisitDateRange'; // Load the required language $lang = Factory::getLanguage(); $lang->load('com_users', JPATH_ADMINISTRATOR); // Set the pre-defined options $this->predefinedOptions = [ 'today' => 'COM_USERS_OPTION_RANGE_TODAY', 'past_week' => 'COM_USERS_OPTION_RANGE_PAST_WEEK', 'past_1month' => 'COM_USERS_OPTION_RANGE_PAST_1MONTH', 'past_3month' => 'COM_USERS_OPTION_RANGE_PAST_3MONTH', 'past_6month' => 'COM_USERS_OPTION_RANGE_PAST_6MONTH', 'past_year' => 'COM_USERS_OPTION_RANGE_PAST_YEAR', 'post_year' => 'COM_USERS_OPTION_RANGE_POST_YEAR', 'never' => 'COM_USERS_OPTION_RANGE_NEVER', ]; } } PK1A#]�fWW!src/Form/Field/TelephoneField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a text field telephone numbers. * * @link https://html.spec.whatwg.org/multipage/input.html#telephone-state-(type=tel) * @see \Joomla\CMS\Form\Rule\TelRule for telephone number validation * @see JHtmlTel for rendering of telephone numbers * @since 1.7.0 */ class TelephoneField extends TextField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Telephone'; /** * Name of the layout being used to render the field * * @var string * @since 3.7.0 */ protected $layout = 'joomla.form.field.tel'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; $extraData = [ 'maxLength' => $maxLength, ]; return array_merge($data, $extraData); } } PK1A#]��ׁ��1src/Form/Field/WorkflowComponentSectionsField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Workflow\WorkflowServiceInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Framework. * * @since 4.0.0 */ class WorkflowComponentSectionsField extends ComponentsField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'WorkflowComponentSections'; /** * Method to get a list of options for a list input. * * @return object[] An array of JHtml options. * * @since 4.0.0 */ protected function getOptions() { $app = Factory::getApplication(); $items = parent::getOptions(); $options = []; $options[] = HTMLHelper::_('select.option', ' ', Text::_('JNONE')); foreach ($items as $item) { if (substr($item->value, 0, 4) !== 'com_') { continue; } $component = $app->bootComponent($item->value); if (!($component instanceof WorkflowServiceInterface)) { continue; } foreach ($component->getWorkflowContexts() as $extension => $text) { $options[] = HTMLHelper::_('select.option', $extension, Text::sprintf('JWORKFLOW_FIELD_COMPONENT_SECTIONS_TEXT', $item->text, $text)); } } return $options; } } PK1A#]�n����!src/Form/Field/ModuletagField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module Tag field. * * @since 3.0 */ class ModuletagField extends ListField { /** * The form field type. * * @var string * @since 3.0 */ protected $type = 'ModuleTag'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 3.0 */ protected function getOptions() { $options = []; $tags = ['address', 'article', 'aside', 'details', 'div', 'footer', 'header', 'main', 'nav', 'section', 'summary']; // Create one new option object for each tag foreach ($tags as $tag) { $tmp = HTMLHelper::_('select.option', $tag, $tag); $options[] = $tmp; } reset($options); return $options; } } PK1A#]_���"src/Form/Field/RadiobasicField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides radio button inputs using default styling * * @since 4.0.0 */ class RadiobasicField extends ListField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Radiobasic'; /** * Name of the layout being used to render the field * * @var string * @since 4.0.0 */ protected $layout = 'joomla.form.field.radiobasic'; /** * Method to get the radio button field input markup. * * @return string The field input markup. * * @since 4.0.0 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 4.0.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'options' => $this->getOptions(), 'value' => (string) $this->value, ]; return array_merge($data, $extraData); } } PK1A#]�)$src/Form/Field/PluginstatusField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Plugin Status field. * * @since 3.5 */ class PluginstatusField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.5 */ public $type = 'Plugin_Status'; /** * Available statuses * * @var string[] * @since 3.5 */ protected $predefinedOptions = [ '0' => 'JDISABLED', '1' => 'JENABLED', ]; } PK1A#]�O$��%src/Form/Field/UsergrouplistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Access\Access; use Joomla\CMS\Helper\UserGroupsHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Field to load a dropdown list of available user groups * * @since 3.2 */ class UsergrouplistField extends ListField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'UserGroupList'; /** * Cached array of the category items. * * @var array[] * @since 3.2 */ protected static $options = []; /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 1.7.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { if (\is_string($value) && strpos($value, ',') !== false) { $value = explode(',', $value); } return parent::setup($element, $value, $group); } /** * Method to get the options to populate list * * @return object[] The field option objects. * * @since 3.2 */ protected function getOptions() { $options = parent::getOptions(); $checkSuperUser = (int) $this->getAttribute('checksuperusergroup', 0); // Cache user groups base on checksuperusergroup attribute value if (!isset(static::$options[$checkSuperUser])) { $groups = UserGroupsHelper::getInstance()->getAll(); $cacheOptions = []; foreach ($groups as $group) { // Don't list super user groups. if ($checkSuperUser && Access::checkGroup($group->id, 'core.admin')) { continue; } $cacheOptions[] = (object) [ 'text' => str_repeat('- ', $group->level) . $group->title, 'value' => $group->id, 'level' => $group->level, ]; } static::$options[$checkSuperUser] = $cacheOptions; } return array_merge($options, static::$options[$checkSuperUser]); } } PK1A#]�=�'src/Form/Field/ContentlanguageField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Provides a list of content languages * * @see \Joomla\CMS\Form\Field\LanguageField for a select list of application languages. * @since 1.6 */ class ContentlanguageField extends ListField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'ContentLanguage'; /** * Method to get the field options for content languages. * * @return object[] The options the field is going to show. * * @since 1.6 */ protected function getOptions() { return array_merge(parent::getOptions(), HTMLHelper::_('contentlanguage.existing')); } } PK1A#]\p��&�&src/Form/Field/ColorField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Color Form Field class for the Joomla Platform. * This implementation is designed to be compatible with HTML5's `<input type="color">` * * @link https://html.spec.whatwg.org/multipage/input.html#color-state-(type=color) * @since 1.7.3 */ class ColorField extends FormField { /** * The form field type. * * @var string * @since 1.7.3 */ protected $type = 'Color'; /** * The control. * * @var mixed * @since 3.2 */ protected $control = 'hue'; /** * Default color when there is no value. * * @var string * @since 4.0.0 */ protected $default; /** * The type of value the slider should display: 'hue', 'saturation' or 'light'. * * @var string * @since 4.0.0 */ protected $display = 'hue'; /** * The format. * * @var string * @since 3.6.0 */ protected $format = 'hex'; /** * The keywords (transparent,initial,inherit). * * @var string * @since 3.6.0 */ protected $keywords = ''; /** * The position. * * @var mixed * @since 3.2 */ protected $position = 'default'; /** * The colors. * * @var mixed * @since 3.2 */ protected $colors; /** * Shows preview of the selected color * * @var boolean * @since 4.0.0 */ protected $preview = false; /** * Color format to use when value gets saved * * @var string * @since 4.0.0 */ protected $saveFormat = 'hex'; /** * The split. * * @var integer * @since 3.2 */ protected $split = 3; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.color'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'colors': case 'control': case 'default': case 'display': case 'format': case 'keywords': case 'preview': case 'saveFormat': case 'split': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'colors': case 'control': case 'default': case 'display': case 'format': case 'keywords': case 'saveFormat': $this->$name = (string) $value; break; case 'split': $this->$name = (int) $value; break; case 'preview': $this->$name = (bool) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->colors = (string) $this->element['colors']; $this->control = isset($this->element['control']) ? (string) $this->element['control'] : 'hue'; $this->default = (string) $this->element['default']; $this->display = isset($this->element['display']) ? (string) $this->element['display'] : 'hue'; $this->format = isset($this->element['format']) ? (string) $this->element['format'] : 'hex'; $this->keywords = (string) $this->element['keywords']; $this->position = isset($this->element['position']) ? (string) $this->element['position'] : 'default'; $this->preview = isset($this->element['preview']) ? (string) $this->element['preview'] : false; $this->saveFormat = isset($this->element['saveFormat']) ? (string) $this->element['saveFormat'] : 'hex'; $this->split = isset($this->element['split']) ? (int) $this->element['split'] : 3; } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.3 */ protected function getInput() { // Switch the layouts if ($this->control === 'simple' || $this->control === 'slider') { $this->layout .= '.' . $this->control; } else { $this->layout .= '.advanced'; } // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $lang = Factory::getApplication()->getLanguage(); $data = parent::getLayoutData(); $color = strtolower($this->value); $color = !$color && $color !== '0' ? '' : $color; // Position of the panel can be: right (default), left, top or bottom (default RTL is left) $position = ' data-position="' . (($lang->isRtl() && $this->position === 'default') ? 'left' : $this->position) . '"'; if ($color === '' || \in_array($color, ['none', 'transparent'])) { $color = 'none'; } elseif ($color[0] !== '#' && $this->format === 'hex') { $color = '#' . $color; } switch ($this->control) { case 'simple': $controlModeData = $this->getSimpleModeLayoutData(); break; case 'slider': $controlModeData = $this->getSliderModeLayoutData(); break; case 'advanced': default: $controlModeData = $this->getAdvancedModeLayoutData($lang); break; } $extraData = [ 'color' => $color, 'format' => $this->format, 'keywords' => $this->keywords, 'position' => $position, 'validate' => $this->validate, ]; return array_merge($data, $extraData, $controlModeData); } /** * Method to get the data for the simple mode to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getSimpleModeLayoutData() { $colors = strtolower($this->colors); if (empty($colors)) { $colors = [ 'none', '#049cdb', '#46a546', '#9d261d', '#ffc40d', '#f89406', '#c3325f', '#7a43b6', '#ffffff', '#999999', '#555555', '#000000', ]; } else { $colors = explode(',', $colors); } if (!$this->split) { $count = \count($colors); if ($count % 5 == 0) { $split = 5; } else { if ($count % 4 == 0) { $split = 4; } } } $split = $this->split ?: 3; return [ 'colors' => $colors, 'split' => $split, ]; } /** * Method to get the data for the advanced mode to be passed to the layout for rendering. * * @param object $lang The language object * * @return array * * @since 3.5 */ protected function getAdvancedModeLayoutData($lang) { return [ 'colors' => $this->colors, 'control' => $this->control, 'lang' => $lang, ]; } /** * Method to get the data for the slider * * @return array * * @since 4.0.0 */ protected function getSliderModeLayoutData() { return [ 'default' => $this->default, 'display' => $this->display, 'preview' => $this->preview, 'saveFormat' => $this->saveFormat, ]; } } PK1A#]��"&EE src/Form/Field/OrderingField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\UCM\UCMType; use Joomla\Database\ParameterType; use Joomla\Database\QueryInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Ordering field. * * @since 3.2 */ class OrderingField extends FormField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Ordering'; /** * The form field content type. * * @var string * @since 3.2 */ protected $contentType; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'contentType') { return $this->contentType; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'contentType': $this->contentType = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { $this->contentType = (string) $this->element['content_type']; } return $result; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { $html = []; $attr = ''; // Initialize some field attributes. $attr .= !empty($this->class) ? ' class="form-select' . $this->class . '"' : ' class="form-select"'; $attr .= $this->disabled ? ' disabled' : ''; $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : ''; // Initialize JavaScript field attributes. $attr .= !empty($this->onchange) ? ' onchange="' . $this->onchange . '"' : ''; $itemId = (int) $this->getItemId(); $query = $this->getQuery(); // Create a read-only list (no name) with a hidden input to store the value. if ($this->readonly) { $html[] = HTMLHelper::_('list.ordering', '', $query, trim($attr), $this->value, $itemId ? 0 : 1, $this->id); $html[] = '<input type="hidden" name="' . $this->name . '" value="' . $this->value . '">'; } else { // Create a regular list. $html[] = HTMLHelper::_('list.ordering', $this->name, $query, trim($attr), $this->value, $itemId ? 0 : 1, $this->id); } return implode($html); } /** * Builds the query for the ordering list. * * @return QueryInterface The query for the ordering form field * * @since 3.2 */ protected function getQuery() { $categoryId = (int) $this->form->getValue('catid'); $ucmType = new UCMType(); $ucmRow = $ucmType->getType($ucmType->getTypeId($this->contentType)); $ucmMapCommon = json_decode($ucmRow->field_mappings)->common; if (\is_object($ucmMapCommon)) { $ordering = $ucmMapCommon->core_ordering; $title = $ucmMapCommon->core_title; } elseif (\is_array($ucmMapCommon)) { $ordering = $ucmMapCommon[0]->core_ordering; $title = $ucmMapCommon[0]->core_title; } $db = $this->getDatabase(); $query = $db->getQuery(true); $query->select([$db->quoteName($ordering, 'value'), $db->quoteName($title, 'text')]) ->from($db->quoteName(json_decode($ucmRow->table)->special->dbtable)) ->where($db->quoteName('catid') . ' = :categoryId') ->order($db->quoteName('ordering')) ->bind(':categoryId', $categoryId, ParameterType::INTEGER); return $query; } /** * Retrieves the current Item's Id. * * @return integer The current item ID * * @since 3.2 */ protected function getItemId() { return (int) $this->form->getValue('id'); } } PK1A#]���wwsrc/Form/Field/AuthorField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field to load a list of content authors * * @since 3.2 */ class AuthorField extends ListField { /** * The form field type. * * @var string * @since 3.2 */ public $type = 'Author'; /** * Cached array of the category items. * * @var array[] * @since 3.2 */ protected static $options = []; /** * Method to get the options to populate list * * @return object[] The field option objects. * * @since 3.2 */ protected function getOptions() { // Accepted modifiers $hash = md5($this->element); if (!isset(static::$options[$hash])) { static::$options[$hash] = parent::getOptions(); $db = $this->getDatabase(); // Construct the query $query = $db->getQuery(true) ->select( [ $db->quoteName('u.id', 'value'), $db->quoteName('u.name', 'text'), ] ) ->from($db->quoteName('#__users', 'u')) ->join('INNER', $db->quoteName('#__content', 'c'), $db->quoteName('c.created_by') . ' = ' . $db->quoteName('u.id')) ->group( [ $db->quoteName('u.id'), $db->quoteName('u.name'), ] ) ->order($db->quoteName('u.name')); // Setup the query $db->setQuery($query); // Return the result if ($options = $db->loadObjectList()) { static::$options[$hash] = array_merge(static::$options[$hash], $options); } } return static::$options[$hash]; } } PK1A#]n�mm-src/Form/Field/RegistrationdaterangeField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Registration Date Range field. * * @since 3.2 */ class RegistrationdaterangeField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'RegistrationDateRange'; /** * Available options * * @var string[] * @since 3.2 */ protected $predefinedOptions = [ 'today' => 'COM_USERS_OPTION_RANGE_TODAY', 'past_week' => 'COM_USERS_OPTION_RANGE_PAST_WEEK', 'past_1month' => 'COM_USERS_OPTION_RANGE_PAST_1MONTH', 'past_3month' => 'COM_USERS_OPTION_RANGE_PAST_3MONTH', 'past_6month' => 'COM_USERS_OPTION_RANGE_PAST_6MONTH', 'past_year' => 'COM_USERS_OPTION_RANGE_PAST_YEAR', 'post_year' => 'COM_USERS_OPTION_RANGE_POST_YEAR', ]; /** * Method to instantiate the form field object. * * @param Form $form The form to attach to the form field object. * * @since 1.7.0 */ public function __construct($form = null) { parent::__construct($form); // Load the required language $lang = Factory::getLanguage(); $lang->load('com_users', JPATH_ADMINISTRATOR); } } PK1A#]��zDDsrc/Form/Field/IntegerField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a select list of integers with specified first, last and step values. * * @since 1.7.0 */ class IntegerField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Integer'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; // Initialize some field attributes. $first = (int) $this->element['first']; $last = (int) $this->element['last']; $step = (int) $this->element['step']; // Sanity checks. if ($step == 0) { // Step of 0 will create an endless loop. return $options; } elseif ($first < $last && $step < 0) { // A negative step will never reach the last number. return $options; } elseif ($first > $last && $step > 0) { // A position step will never reach the last number. return $options; } elseif ($step < 0) { // Build the options array backwards. for ($i = $first; $i >= $last; $i += $step) { $options[] = HTMLHelper::_('select.option', $i); } } else { // Build the options array. for ($i = $first; $i <= $last; $i += $step) { $options[] = HTMLHelper::_('select.option', $i); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]��|���src/Form/Field/ComboField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Implements a combo box field. * * @since 1.7.0 */ class ComboField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Combo'; /** * Name of the layout being used to render the field * * @var string * @since 3.8.0 */ protected $layout = 'joomla.form.field.combo'; /** * Method to get the field input markup for a combo box field. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.8.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Get the field options. $options = $this->getOptions(); $extraData = [ 'options' => $options, ]; return array_merge($data, $extraData); } } PK1A#]+?x.,$,$src/Form/Field/EditorField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Editor\Editor; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A textarea field for content creation * * @see JEditor * @since 1.6 */ class EditorField extends TextareaField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'Editor'; /** * The Editor object. * * @var Editor * @since 1.6 */ protected $editor; /** * The height of the editor. * * @var string * @since 3.2 */ protected $height; /** * The width of the editor. * * @var string * @since 3.2 */ protected $width; /** * The assetField of the editor. * * @var string * @since 3.2 */ protected $assetField; /** * The authorField of the editor. * * @var string * @since 3.2 */ protected $authorField; /** * The asset of the editor. * * @var string * @since 3.2 */ protected $asset; /** * The buttons of the editor. * * @var mixed * @since 3.2 */ protected $buttons; /** * The hide of the editor. * * @var string[] * @since 3.2 */ protected $hide; /** * The editorType of the editor. * * @var string[] * @since 3.2 */ protected $editorType; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'height': case 'width': case 'assetField': case 'authorField': case 'asset': case 'buttons': case 'hide': case 'editorType': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'height': case 'width': case 'assetField': case 'authorField': case 'asset': $this->$name = (string) $value; break; case 'buttons': $value = (string) $value; if ($value === 'true' || $value === 'yes' || $value === '1') { $this->buttons = true; } elseif ($value === 'false' || $value === 'no' || $value === '0') { $this->buttons = false; } else { $this->buttons = explode(',', $value); } break; case 'hide': $value = (string) $value; $this->hide = $value ? explode(',', $value) : []; break; case 'editorType': // Can be in the form of: editor="desired|alternative". $this->editorType = explode('|', trim((string) $value)); break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { $this->height = $this->element['height'] ? (string) $this->element['height'] : '500'; $this->width = $this->element['width'] ? (string) $this->element['width'] : '100%'; $this->assetField = $this->element['asset_field'] ? (string) $this->element['asset_field'] : 'asset_id'; $this->authorField = $this->element['created_by_field'] ? (string) $this->element['created_by_field'] : 'created_by'; $this->asset = $this->form->getValue($this->assetField) ?: (string) $this->element['asset_id']; $buttons = (string) $this->element['buttons']; $hide = (string) $this->element['hide']; $editorType = (string) $this->element['editor']; if ($buttons === 'true' || $buttons === 'yes' || $buttons === '1') { $this->buttons = true; } elseif ($buttons === 'false' || $buttons === 'no' || $buttons === '0') { $this->buttons = false; } else { $this->buttons = !empty($hide) ? explode(',', $buttons) : []; } $this->hide = !empty($hide) ? explode(',', (string) $this->element['hide']) : []; $this->editorType = !empty($editorType) ? explode('|', trim($editorType)) : []; } return $result; } /** * Method to get the field input markup for the editor area * * @return string The field input markup. * * @since 1.6 */ protected function getInput() { // Get an editor object. $editor = $this->getEditor(); $params = [ 'autofocus' => $this->autofocus, 'readonly' => $this->readonly || $this->disabled, 'syntax' => (string) $this->element['syntax'], ]; return $editor->display( $this->name, htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8'), $this->width, $this->height, $this->columns, $this->rows, $this->buttons ? (\is_array($this->buttons) ? array_merge($this->buttons, $this->hide) : $this->hide) : false, $this->id, $this->asset, $this->form->getValue($this->authorField), $params ); } /** * Method to get an Editor object based on the form field. * * @return Editor The Editor object. * * @since 1.6 */ protected function getEditor() { // Only create the editor if it is not already created. if (empty($this->editor)) { $editor = null; if ($this->editorType) { // Get the list of editor types. $types = $this->editorType; // Get the database object. $db = $this->getDatabase(); // Build the query. $query = $db->getQuery(true) ->select($db->quoteName('element')) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('element') . ' = :editor', $db->quoteName('folder') . ' = ' . $db->quote('editors'), $db->quoteName('enabled') . ' = 1', ] ); // Declare variable before binding. $element = ''; $query->bind(':editor', $element); $query->setLimit(1); // Iterate over the types looking for an existing editor. foreach ($types as $element) { // Check if the editor exists. $db->setQuery($query); $editor = $db->loadResult(); // If an editor was found stop looking. if ($editor) { break; } } } // Create the JEditor instance based on the given editor. if ($editor === null) { $editor = Factory::getApplication()->get('editor'); } $this->editor = Editor::getInstance($editor); } return $this->editor; } } PK1A#]�-�##src/Form/Field/RadioField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides radio button inputs * * @link https://html.spec.whatwg.org/multipage/input.html#radio-button-state-(type=radio) * @since 1.7.0 */ class RadioField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Radio'; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.radio.buttons'; /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = [ 'options' => $this->getOptions(), 'value' => (string) $this->value, ]; return array_merge($data, $extraData); } } PK1A#]��Y� � *src/Form/Field/DatabaseconnectionField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a list of available database connections, optionally limiting to * a given list. * * @see DatabaseDriver * @since 1.7.3 */ class DatabaseconnectionField extends ListField { /** * The form field type. * * @var string * @since 1.7.3 */ protected $type = 'Databaseconnection'; /** * Method to get the list of database options. * * This method produces a drop down list of available databases supported * by DatabaseDriver classes that are also supported by the application. * * @return array The field option objects. * * @since 1.7.3 * @see DatabaseDriver::getConnectors() */ protected function getOptions() { $options = []; // This gets the connectors available in the platform and supported by the server. $available = array_map('strtolower', DatabaseDriver::getConnectors()); /** * This gets the list of database types supported by the application. * This should be entered in the form definition as a comma separated list. * If no supported databases are listed, it is assumed all available databases * are supported. */ $supported = $this->element['supported']; if (!empty($supported)) { $supported = explode(',', $supported); foreach ($supported as $support) { if (\in_array($support, $available)) { $options[$support] = Text::_(ucfirst($support)); } } } else { foreach ($available as $support) { $options[$support] = Text::_(ucfirst($support)); } } // This will come into play if an application is installed that requires // a database that is not available on the server. if (empty($options)) { $options[''] = Text::_('JNONE'); } return $options; } } PK1A#]�g�yysrc/Form/Field/RangeField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a horizontal scroll bar to specify a value in a range. * * @link https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range) * @since 3.2 */ class RangeField extends NumberField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Range'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.range'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = [ 'max' => $this->max, 'min' => $this->min, 'step' => $this->step, ]; return array_merge($data, $extraData); } } PK1A#]���"src/Form/Field/CheckboxesField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Displays options as a list of checkboxes. * Multiselect may be forced to be true. * * @see CheckboxesField * @since 1.7.0 */ class CheckboxesField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Checkboxes'; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.checkboxes'; /** * Flag to tell the field to always be in multiple values mode. * * @var boolean * @since 1.7.0 */ protected $forceMultiple = true; /** * The comma separated list of checked checkboxes value. * * @var mixed * @since 3.2 */ public $checkedOptions; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'forceMultiple': case 'checkedOptions': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'checkedOptions': $this->checkedOptions = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to get the radio button field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->checkedOptions = (string) $this->element['checked']; } return $return; } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); // True if the field has 'value' set. In other words, it has been stored, don't use the default values. $hasValue = (isset($this->value) && !empty($this->value)); // If a value has been stored, use it. Otherwise, use the defaults. $checkedOptions = $hasValue ? $this->value : $this->checkedOptions; $extraData = [ 'checkedOptions' => \is_array($checkedOptions) ? $checkedOptions : explode(',', (string) $checkedOptions), 'hasValue' => $hasValue, 'options' => $this->getOptions(), ]; return array_merge($data, $extraData); } } PK1A#]@���� � src/Form/Field/NoteField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a one line text field. * * @link https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search) * @since 1.7.0 */ class NoteField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Note'; /** * Hide the label when rendering the form field. * * @var boolean * @since 4.0.0 */ protected $hiddenLabel = true; /** * Hide the description when rendering the form field. * * @var boolean * @since 4.0.0 */ protected $hiddenDescription = true; /** * Method to get the field label markup. * * @return string The field label markup. * * @since 1.7.0 */ protected function getLabel() { if (empty($this->element['label']) && empty($this->element['description'])) { return ''; } $html = []; $class = []; if (!empty($this->class)) { $class[] = $this->class; } if ($close = (string) $this->element['close']) { HTMLHelper::_('bootstrap.alert'); $close = $close === 'true' ? 'alert' : $close; $html[] = '<button type="button" class="btn-close" data-bs-dismiss="' . $close . '"></button>'; $class[] = 'alert-dismissible show'; } $class = $class ? ' class="' . implode(' ', $class) . '"' : ''; $title = $this->element['label'] ? (string) $this->element['label'] : ($this->element['title'] ? (string) $this->element['title'] : ''); $heading = $this->element['heading'] ? (string) $this->element['heading'] : 'h4'; $description = (string) $this->element['description']; $html[] = !empty($title) ? '<' . $heading . '>' . Text::_($title) . '</' . $heading . '>' : ''; $html[] = !empty($description) ? Text::_($description) : ''; return '</div><div ' . $class . '>' . implode('', $html); } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { return ''; } } PK1A#]U�J���&src/Form/Field/ModulepositionField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module Position field. * * @since 1.6 */ class ModulepositionField extends TextField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ModulePosition'; /** * The client ID. * * @var integer * @since 3.2 */ protected $clientId; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'clientId') { return $this->clientId; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'clientId': $this->clientId = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result === true) { // Get the client id. $clientId = $this->element['client_id']; if (!isset($clientId)) { $clientName = $this->element['client']; if (isset($clientName)) { $client = ApplicationHelper::getClientInfo($clientName, true); $clientId = $client->id; } } if (!isset($clientId) && $this->form instanceof Form) { $clientId = $this->form->getValue('client_id'); } $this->clientId = (int) $clientId; } return $result; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.6 */ protected function getInput() { // Build the script. $script = []; $script[] = ' function jSelectPosition_' . $this->id . '(name) {'; $script[] = ' document.getElementById("' . $this->id . '").value = name;'; $script[] = ' jModalClose();'; $script[] = ' }'; // Add the script to the document head. Factory::getDocument()->addScriptDeclaration(implode("\n", $script)); // Setup variables for display. $html = []; $link = 'index.php?option=com_modules&view=positions&layout=modal&tmpl=component&function=jSelectPosition_' . $this->id . '&client_id=' . $this->clientId; // The current user display field. $html[] = '<div class="input-append">'; $html[] = parent::getInput() . '<a class="btn" title="' . Text::_('COM_MODULES_CHANGE_POSITION_TITLE') . '" href="' . $link . '" data-bs-toggle="modal" data-bs-target="#modulePositionModal">' . Text::_('COM_MODULES_CHANGE_POSITION_BUTTON') . '</a>'; $html[] = HTMLHelper::_( 'bootstrap.renderModal', 'modulePositionModal', [ 'url' => $link, 'title' => Text::_('COM_MODULES_CHANGE_POSITION_BUTTON'), 'height' => '100%', 'width' => '100%', 'modalWidth' => '800', 'bodyHeight' => '450', 'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-hidden="true">' . Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>', ] ); $html[] = '</div>'; return implode("\n", $html); } } PK1A#]0�g� � src/Form/Field/CategoryField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports an HTML select list of categories * * @since 1.6 */ class CategoryField extends ListField { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'Category'; /** * Method to get the field options for category * Use the extension attribute in a form to specify the.specific extension for * which categories should be displayed. * Use the show_root attribute to specify whether to show the global category root in the list. * * @return object[] The field option objects. * * @since 1.6 */ protected function getOptions() { $options = []; $extension = $this->element['extension'] ? (string) $this->element['extension'] : (string) $this->element['scope']; $published = (string) $this->element['published']; $language = (string) $this->element['language']; // Load the category options for a given extension. if (!empty($extension)) { // Filter over published state or not depending upon if it is present. $filters = []; if ($published) { $filters['filter.published'] = explode(',', $published); } // Filter over language depending upon if it is present. if ($language) { $filters['filter.language'] = explode(',', $language); } if ($filters === []) { $options = HTMLHelper::_('category.options', $extension); } else { $options = HTMLHelper::_('category.options', $extension, $filters); } // Verify permissions. If the action attribute is set, then we scan the options. if ((string) $this->element['action']) { // Get the current user object. $user = Factory::getUser(); foreach ($options as $i => $option) { /* * To take save or create in a category you need to have create rights for that category * unless the item is already in that category. * Unset the option if the user isn't authorised for it. In this field assets are always categories. */ if ($user->authorise('core.create', $extension . '.category.' . $option->value) === false) { unset($options[$i]); } } } if (isset($this->element['show_root'])) { array_unshift($options, HTMLHelper::_('select.option', '0', Text::_('JGLOBAL_ROOT'))); } } else { Log::add(Text::_('JLIB_FORM_ERROR_FIELDS_CATEGORY_ERROR_EXTENSION_EMPTY'), Log::WARNING, 'jerror'); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]ۊ~N$src/Form/Field/CachehandlerField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Cache\Cache; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a list of available cache handlers * * @see JCache * @since 1.7.0 */ class CachehandlerField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Cachehandler'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; // Convert to name => name array. foreach (Cache::getStores() as $store) { $options[] = HTMLHelper::_('select.option', $store, Text::_('JLIB_FORM_VALUE_CACHE_' . $store), 'value', 'text'); } $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]nH?G��src/Form/Field/HiddenField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Form\FormField; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a hidden field * * @link https://html.spec.whatwg.org/multipage/input.html#hidden-state-(type=hidden) * @since 1.7.0 */ class HiddenField extends FormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Hidden'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.hidden'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { return parent::getLayoutData(); } } PK1A#]���"��"src/Form/Field/FolderlistField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Filesystem\Folder; use Joomla\Filesystem\Path; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Supports an HTML select list of folder * * @since 1.7.0 */ class FolderlistField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Folderlist'; /** * The folder name filter. * * @var string * @since 4.0.0 */ protected $folderFilter; /** * The exclude. * * @var string * @since 3.2 */ protected $exclude; /** * The recursive. * * @var bool * @since 3.6 */ protected $recursive; /** * The hideNone. * * @var boolean * @since 3.2 */ protected $hideNone = false; /** * The hideDefault. * * @var boolean * @since 3.2 */ protected $hideDefault = false; /** * The directory. * * @var string * @since 3.2 */ protected $directory; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'folderFilter': case 'exclude': case 'recursive': case 'hideNone': case 'hideDefault': case 'directory': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'folderFilter': case 'directory': case 'exclude': case 'recursive': $this->$name = (string) $value; break; case 'hideNone': case 'hideDefault': $value = (string) $value; $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->folderFilter = (string) $this->element['folderFilter']; $this->exclude = (string) $this->element['exclude']; $recursive = (string) $this->element['recursive']; $this->recursive = ($recursive === 'true' || $recursive === 'recursive' || $recursive === '1'); $hideNone = (string) $this->element['hide_none']; $this->hideNone = ($hideNone === 'true' || $hideNone === 'hideNone' || $hideNone === '1'); $hideDefault = (string) $this->element['hide_default']; $this->hideDefault = ($hideDefault === 'true' || $hideDefault === 'hideDefault' || $hideDefault === '1'); // Get the path in which to search for file options. $this->directory = (string) $this->element['directory']; } return $return; } /** * Method to get the field options. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = []; $path = $this->directory; if (!is_dir($path)) { if (is_dir(JPATH_ROOT . '/' . $path)) { $path = JPATH_ROOT . '/' . $path; } else { return []; } } $path = Path::clean($path); // Prepend some default options based on field attributes. if (!$this->hideNone) { $options[] = HTMLHelper::_('select.option', '-1', Text::alt('JOPTION_DO_NOT_USE', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } if (!$this->hideDefault) { $options[] = HTMLHelper::_('select.option', '', Text::alt('JOPTION_USE_DEFAULT', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } // Get a list of folders in the search path with the given filter. $folders = Folder::folders($path, $this->folderFilter, $this->recursive, true); // Build the options list from the list of folders. if (\is_array($folders)) { foreach ($folders as $folder) { // Remove the root part and the leading / $folder = trim(str_replace($path, '', $folder), DIRECTORY_SEPARATOR); // Check to see if the file is in the exclude mask. if ($this->exclude) { if (preg_match(\chr(1) . $this->exclude . \chr(1), $folder)) { continue; } } $options[] = HTMLHelper::_('select.option', $folder, $folder); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK1A#]�p�X X src/Form/Field/LanguageField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Supports a list of installed application languages * * @see \Joomla\CMS\Form\Field\ContentlanguageField for a select list of content languages. * @since 1.7.0 */ class LanguageField extends ListField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Language'; /** * Method to get the field options. * * @return object[] The field option objects. * * @since 1.7.0 */ protected function getOptions() { // Initialize some field attributes. $client = (string) $this->element['client']; if ($client !== 'site' && $client !== 'administrator') { $client = 'site'; } // Make sure the languages are sorted base on locale instead of random sorting $languages = LanguageHelper::createLanguageList($this->value, \constant('JPATH_' . strtoupper($client)), true, true); if (\count($languages) > 1) { usort( $languages, function ($a, $b) { return strcmp($a['value'], $b['value']); } ); } // Merge any additional options in the XML definition. $options = array_merge( parent::getOptions(), $languages ); // Set the default value active language $langParams = ComponentHelper::getParams('com_languages'); switch ((string) $this->value) { case 'site': case 'frontend': case '0': $this->value = $langParams->get('site', 'en-GB'); break; case 'admin': case 'administrator': case 'backend': case '1': $this->value = $langParams->get('administrator', 'en-GB'); break; case 'active': case 'auto': $lang = Factory::getLanguage(); $this->value = $lang->getTag(); break; default: break; } return $options; } } PK1A#]�s��src/Form/Field/PluginsField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Framework. * * @since 2.5.0 */ class PluginsField extends ListField { /** * The field type. * * @var string * @since 2.5.0 */ protected $type = 'Plugins'; /** * The path to folder for plugins. * * @var string * @since 3.2 */ protected $folder; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { if ($name === 'folder') { return $this->folder; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'folder': $this->folder = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->folder = (string) $this->element['folder']; } return $return; } /** * Method to get a list of options for a list input. * * @return object[] An array of JHtml options. * * @since 2.5.0 */ protected function getOptions() { $folder = $this->folder; $parentOptions = parent::getOptions(); if (empty($folder)) { Log::add(Text::_('JFRAMEWORK_FORM_FIELDS_PLUGINS_ERROR_FOLDER_EMPTY'), Log::WARNING, 'jerror'); return $parentOptions; } // Get list of plugins $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('element', 'value'), $db->quoteName('name', 'text'), ] ) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('folder') . ' = :folder', $db->quoteName('enabled') . ' = 1', ] ) ->bind(':folder', $folder) ->order( [ $db->quoteName('ordering'), $db->quoteName('name'), ] ); if ((string) $this->element['useaccess'] === 'true') { $query->whereIn($db->quoteName('access'), Factory::getUser()->getAuthorisedViewLevels()); } $options = $db->setQuery($query)->loadObjectList(); $lang = Factory::getLanguage(); $useGlobal = $this->element['useglobal']; if ($useGlobal) { $globalValue = Factory::getApplication()->get($this->fieldname); } foreach ($options as $i => $item) { $source = JPATH_PLUGINS . '/' . $folder . '/' . $item->value; $extension = 'plg_' . $folder . '_' . $item->value; $lang->load($extension . '.sys', JPATH_ADMINISTRATOR) || $lang->load($extension . '.sys', $source); $item->text = Text::_($item->text); // If we are using useglobal update the use global value text with the plugin text. if ($useGlobal && isset($parentOptions[0]) && $item->value === $globalValue) { $text = Text::_($extension); $parentOptions[0]->text = Text::sprintf('JGLOBAL_USE_GLOBAL_VALUE', ($text === '' || $text === $extension ? $item->value : $text)); } } return array_merge($parentOptions, $options); } } PK1A#]u4(��src/Form/Field/NumberField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormField; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Field class for the Joomla Platform. * Provides a one line text box with up-down handles to set a number in the field. * * @link https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number) * @since 3.2 */ class NumberField extends FormField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Number'; /** * The allowable maximum value of the field. * * @var float * @since 3.2 */ protected $max = null; /** * The allowable minimum value of the field. * * @var float * @since 3.2 */ protected $min = null; /** * The step by which value of the field increased or decreased. * * @var float * @since 3.2 */ protected $step = 0; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.number'; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'max': case 'min': case 'step': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'step': case 'min': case 'max': $this->$name = (float) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see FormField::setup() * @since 3.2 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // It is better not to force any default limits if none is specified $this->max = isset($this->element['max']) ? (float) $this->element['max'] : null; $this->min = isset($this->element['min']) ? (float) $this->element['min'] : null; $this->step = isset($this->element['step']) ? (float) $this->element['step'] : 1; } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { if ($this->element['useglobal']) { $component = Factory::getApplication()->getInput()->getCmd('option'); // Get correct component for menu items if ($component === 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new Uri($link); $component = $uri->getVar('option', 'com_menus'); } $params = ComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (\is_null($value)) { $value = Factory::getApplication()->get($this->fieldname); } // Try with menu configuration if (\is_null($value) && Factory::getApplication()->getInput()->getCmd('option') === 'com_menus') { $value = ComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!\is_null($value)) { $value = (string) $value; $this->hint = Text::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } } // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = [ 'max' => $this->max, 'min' => $this->min, 'step' => $this->step, 'value' => $this->value, ]; return array_merge($data, $extraData); } } PK1A#]x,!>mm&src/Form/Field/RedirectStatusField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Redirect Status field. * * @since 3.8.0 */ class RedirectStatusField extends PredefinedlistField { /** * The form field type. * * @var string * @since 3.8.0 */ public $type = 'Redirect_Status'; /** * Available statuses * * @var string[] * @since 3.8.0 */ protected $predefinedOptions = [ '-2' => 'JTRASHED', '0' => 'JDISABLED', '1' => 'JENABLED', '2' => 'JARCHIVED', '*' => 'JALL', ]; } PK1A#]�o���'src/Form/Field/AccessiblemediaField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Field; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The Field to load the form inside current form * * @since 4.0.0 */ class AccessiblemediaField extends SubformField { /** * The form field type. * * @var string * @since 4.0.0 */ protected $type = 'Accessiblemedia'; /** * The preview. * * @var string * @since 4.0.0 */ protected $preview; /** * The directory. * * @var string * @since 4.0.0 */ protected $directory; /** * The previewWidth. * * @var integer * @since 4.0.0 */ protected $previewWidth; /** * The previewHeight. * * @var integer * @since 4.0.0 */ protected $previewHeight; /** * Layout to render * * @var string * @since 4.0.0 */ protected $layout; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 4.0.0 */ public function __get($name) { switch ($name) { case 'directory': case 'preview': case 'previewHeight': case 'previewWidth': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 4.0.0 */ public function __set($name, $value) { switch ($name) { case 'directory': case 'preview': $this->$name = (string) $value; break; case 'previewHeight': case 'previewWidth': $this->$name = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the <field /> tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. * * @return boolean True on success. * * @since 4.0.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { /** * When you have subforms which are not repeatable (i.e. a subform custom field with the * repeat attribute set to 0) you get an array here since the data comes from decoding the * JSON into an associative array, including the media subfield's data. * * However, this method expects an object or a string, not an array. Typecasting the array * to an object solves the data format discrepancy. */ $value = is_array($value) ? (object) $value : $value; /** * If the value is not a string, it is * most likely within a custom field of type subform * and the value is a stdClass with properties * imagefile and alt_text. So it is fine. */ if (\is_string($value)) { json_decode($value); // Check if value is a valid JSON string. if ($value !== '' && json_last_error() !== JSON_ERROR_NONE) { /** * If the value is not empty and is not a valid JSON string, * it is most likely a custom field created in Joomla 3 and * the value is a string that contains the file name. */ if (is_file(JPATH_ROOT . '/' . $value)) { $value = '{"imagefile":"' . $value . '","alt_text":""}'; } else { $value = ''; } } } elseif ( !is_object($value) || !property_exists($value, 'imagefile') || !property_exists($value, 'alt_text') ) { return false; } if (!parent::setup($element, $value, $group)) { return false; } $this->directory = (string) $this->element['directory']; $this->preview = (string) $this->element['preview']; $this->previewHeight = isset($this->element['preview_height']) ? (int) $this->element['preview_height'] : 200; $this->previewWidth = isset($this->element['preview_width']) ? (int) $this->element['preview_width'] : 200; $xml = <<<XML <?xml version="1.0" encoding="utf-8"?> <form> <fieldset name="accessiblemedia" label="JLIB_FORM_FIELD_PARAM_ACCESSIBLEMEDIA_LABEL" > <field name="imagefile" type="media" label="JLIB_FORM_FIELD_PARAM_ACCESSIBLEMEDIA_PARAMS_IMAGEFILE_LABEL" directory="$this->directory" preview="$this->preview" preview_width="$this->previewWidth" preview_height="$this->previewHeight" schemes="http,https,ftp,ftps,data,file" validate="url" relative="true" /> <field name="alt_text" type="text" label="JLIB_FORM_FIELD_PARAM_ACCESSIBLEMEDIA_PARAMS_ALT_TEXT_LABEL" /> <field name="alt_empty" type="checkbox" label="JLIB_FORM_FIELD_PARAM_ACCESSIBLEMEDIA_PARAMS_ALT_EMPTY_LABEL" description="JLIB_FORM_FIELD_PARAM_ACCESSIBLEMEDIA_PARAMS_ALT_EMPTY_DESC" /> </fieldset> </form> XML; $this->formsource = $xml; $this->layout = 'joomla.form.field.media.accessiblemedia'; return true; } } PK1A#]u���"src/Form/FormFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a FormFactoryInterface Aware Class. * * @since 4.0.0 */ trait FormFactoryAwareTrait { /** * FormFactoryInterface * * @var FormFactoryInterface * @since 4.0.0 */ private $formFactory; /** * Get the FormFactoryInterface. * * @return FormFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the FormFactory has not been set. */ public function getFormFactory(): FormFactoryInterface { if ($this->formFactory) { return $this->formFactory; } throw new \UnexpectedValueException('FormFactory not set in ' . __CLASS__); } /** * Set the form factory to use. * * @param ?FormFactoryInterface $formFactory The form factory to use. * * @return $this * * @since 4.0.0 */ public function setFormFactory(FormFactoryInterface $formFactory = null) { $this->formFactory = $formFactory; return $this; } } PK1A#]��"�sssrc/Form/Form.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Class for the Joomla Platform. * * This class implements a robust API for constructing, populating, filtering, and validating forms. * It uses XML definitions to construct form fields and a variety of field and rule classes to * render and validate the form. * * @link https://www.w3.org/TR/html4/interact/forms.html * @link https://html.spec.whatwg.org/multipage/forms.html * @since 1.7.0 */ class Form { use DatabaseAwareTrait; /** * The Registry data store for form fields during display. * * @var Registry * @since 1.7.0 */ protected $data; /** * The form object errors array. * * @var \Exception[] * @since 1.7.0 */ protected $errors = []; /** * The name of the form instance. * * @var string * @since 1.7.0 */ protected $name; /** * The form object options for use in rendering and validation. * * @var array * @since 1.7.0 */ protected $options = []; /** * The form XML definition. * * @var \SimpleXMLElement * @since 1.7.0 */ protected $xml; /** * Form instances. * * @var Form[] * @since 1.7.0 */ protected static $forms = []; /** * Allows extensions to implement repeating elements * * @var boolean * @since 3.2 */ public $repeat = false; /** * Method to instantiate the form object. * * @param string $name The name of the form. * @param array $options An array of form options. * * @since 1.7.0 */ public function __construct($name, array $options = []) { // Set the name for the form. $this->name = $name; // Initialise the Registry data. $this->data = new Registry(); // Set the options if specified. $this->options['control'] = $options['control'] ?? false; } /** * Method to bind data to the form. * * @param mixed $data An array or object of data to bind to the form. * * @return boolean True on success. * * @since 1.7.0 */ public function bind($data) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // The data must be an object or array. if (!\is_object($data) && !\is_array($data)) { return false; } $this->bindLevel(null, $data); return true; } /** * Method to bind data to the form for the group level. * * @param string $group The dot-separated form group path on which to bind the data. * @param mixed $data An array or object of data to bind to the form for the group level. * * @return void * * @since 1.7.0 */ protected function bindLevel($group, $data) { // Ensure the input data is an array. if (\is_object($data)) { if ($data instanceof Registry) { // Handle a Registry. $data = $data->toArray(); } elseif ($data instanceof CMSObject) { // Handle a CMSObject. $data = $data->getProperties(); } else { // Handle other types of objects. $data = (array) $data; } } // Process the input data. foreach ($data as $k => $v) { $level = $group ? $group . '.' . $k : $k; if ($this->findField($k, $group)) { // If the field exists set the value. $this->data->set($level, $v); } elseif (\is_object($v) || ArrayHelper::isAssociative($v)) { // If the value is an object or an associative array, hand it off to the recursive bind level method. $this->bindLevel($level, $v); } } } /** * Return Exceptions thrown during the form validation process. * * @return \Exception[] * * @since 1.7.0 */ public function getErrors() { return $this->errors; } /** * Method to get a form field represented as a FormField object. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return FormField|boolean The FormField object for the field or boolean false on error. * * @since 1.7.0 */ public function getField($name, $group = null, $value = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Attempt to find the field by name and group. $element = $this->findField($name, $group); // If the field element was not found return false. if (!$element) { return false; } return $this->loadField($element, $group, $value); } /** * Method to get an attribute value from a field XML element. If the attribute doesn't exist or * is null then the optional default value will be used. * * @param string $name The name of the form field for which to get the attribute value. * @param string $attribute The name of the attribute for which to get a value. * @param mixed $default The optional default value to use if no attribute value exists. * @param string $group The optional dot-separated form group path on which to find the field. * * @return mixed The attribute value for the field. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function getFieldAttribute($name, $attribute, $default = null, $group = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Find the form field element from the definition. $element = $this->findField($name, $group); // If the element exists and the attribute exists for the field return the attribute value. if (($element instanceof \SimpleXMLElement) && \strlen((string) $element[$attribute])) { return (string) $element[$attribute]; } else { // Otherwise return the given default value. return $default; } } /** * Method to get an array of FormField objects in a given fieldset by name. If no name is * given then all fields are returned. * * @param string $set The optional name of the fieldset. * * @return FormField[] The array of FormField objects in the fieldset. * * @since 1.7.0 */ public function getFieldset($set = null) { $fields = []; // Get all of the field elements in the fieldset. if ($set) { $elements = $this->findFieldsByFieldset($set); } else { // Get all fields. $elements = $this->findFieldsByGroup(); } // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // Get the field groups for the element. $attrs = $element->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); $group = implode('.', $groups); // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } return $fields; } /** * Method to get an array of fieldset objects optionally filtered over a given field group. * * @param string $group The dot-separated form group path on which to filter the fieldsets. * * @return object[] The array of fieldset objects. * * @since 1.7.0 */ public function getFieldsets($group = null) { $fieldsets = []; $sets = []; // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); foreach ($elements as &$element) { // Get an array of <fieldset /> elements and fieldset attributes within the fields element. if ($tmp = $element->xpath('descendant::fieldset[@name] | descendant::field[@fieldset]/@fieldset')) { $sets = array_merge($sets, (array) $tmp); } } } else { // Get an array of <fieldset /> elements and fieldset attributes. $sets = $this->xml->xpath('//fieldset[@name and not(ancestor::field/form/*)] | //field[@fieldset and not(ancestor::field/form/*)]/@fieldset'); } // If no fieldsets are found return empty. if (empty($sets)) { return $fieldsets; } // Process each found fieldset. foreach ($sets as $set) { if ((string) $set['hidden'] === 'true') { continue; } // Are we dealing with a fieldset element? if ((string) $set['name']) { // Only create it if it doesn't already exist. if (empty($fieldsets[(string) $set['name']])) { // Build the fieldset object. $fieldset = (object) ['name' => '', 'label' => '', 'description' => '']; foreach ($set->attributes() as $name => $value) { $fieldset->$name = (string) $value; } // Add the fieldset object to the list. $fieldsets[$fieldset->name] = $fieldset; } } else { // Must be dealing with a fieldset attribute. // Only create it if it doesn't already exist. if (empty($fieldsets[(string) $set])) { // Attempt to get the fieldset element for data (throughout the entire form document). $tmp = $this->xml->xpath('//fieldset[@name="' . (string) $set . '"]'); // If no element was found, build a very simple fieldset object. if (empty($tmp)) { $fieldset = (object) ['name' => (string) $set, 'label' => '', 'description' => '']; } else { // Build the fieldset object from the element. $fieldset = (object) ['name' => '', 'label' => '', 'description' => '']; foreach ($tmp[0]->attributes() as $name => $value) { $fieldset->$name = (string) $value; } } // Add the fieldset object to the list. $fieldsets[$fieldset->name] = $fieldset; } } } return $fieldsets; } /** * Method to get the form control. This string serves as a container for all form fields. For * example, if there is a field named 'foo' and a field named 'bar' and the form control is * empty the fields will be rendered like: `<input name="foo" />` and `<input name="bar" />`. If * the form control is set to 'joomla' however, the fields would be rendered like: * `<input name="joomla[foo]" />` and `<input name="joomla[bar]" />`. * * @return string The form control string. * * @since 1.7.0 */ public function getFormControl() { return (string) $this->options['control']; } /** * Method to get an array of FormField objects in a given field group by name. * * @param string $group The dot-separated form group path for which to get the form fields. * @param boolean $nested True to also include fields in nested groups that are inside of the * group for which to find fields. * * @return FormField[] The array of FormField objects in the field group. * * @since 1.7.0 */ public function getGroup($group, $nested = false) { $fields = []; // Get all of the field elements in the field group. $elements = $this->findFieldsByGroup($group, $nested); // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // Get the field groups for the element. $attrs = $element->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); $group = implode('.', $groups); // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } return $fields; } /** * Method to get a form field markup for the field input. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return string The form field markup. * * @since 1.7.0 */ public function getInput($name, $group = null, $value = null) { // Attempt to get the form field. if ($field = $this->getField($name, $group, $value)) { return $field->input; } return ''; } /** * Method to get the label for a field input. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * * @return string The form field label. * * @since 1.7.0 */ public function getLabel($name, $group = null) { // Attempt to get the form field. if ($field = $this->getField($name, $group)) { return $field->label; } return ''; } /** * Method to get the form name. * * @return string The name of the form. * * @since 1.7.0 */ public function getName() { return $this->name; } /** * Method to get the value of a field. * * @param string $name The name of the field for which to get the value. * @param string $group The optional dot-separated form group path on which to get the value. * @param mixed $default The optional default value of the field value is empty. * * @return mixed The value of the field or the default value if empty. * * @since 1.7.0 */ public function getValue($name, $group = null, $default = null) { // If a group is set use it. if ($group) { $return = $this->data->get($group . '.' . $name, $default); } else { $return = $this->data->get($name, $default); } return $return; } /** * Method to get a control group with label and input. * * @param string $name The name of the field for which to get the value. * @param string $group The optional dot-separated form group path on which to get the value. * @param mixed $default The optional default value of the field value is empty. * @param array $options Any options to be passed into the rendering of the field * * @return string A string containing the html for the control group * * @since 3.2.3 */ public function renderField($name, $group = null, $default = null, $options = []) { $field = $this->getField($name, $group, $default); if ($field) { return $field->renderField($options); } return ''; } /** * Method to get all control groups with label and input of a fieldset. * * @param string $name The name of the fieldset for which to get the values. * @param array $options Any options to be passed into the rendering of the field * * @return string A string containing the html for the control groups * * @since 3.2.3 */ public function renderFieldset($name, $options = []) { $fields = $this->getFieldset($name); $html = []; foreach ($fields as $field) { $html[] = $field->renderField($options); } return implode('', $html); } /** * Method to load the form description from an XML string or object. * * The replace option works per field. If a field being loaded already exists in the current * form definition then the behavior or load will vary depending upon the replace flag. If it * is set to true, then the existing field will be replaced in its exact location by the new * field being loaded. If it is false, then the new field being loaded will be ignored and the * method will move on to the next field to load. * * @param string|\SimpleXMLElement $data The name of an XML string or object. * @param boolean $replace Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param string $xpath An optional xpath to search for the fields. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function load($data, $replace = true, $xpath = null) { // If the data to load isn't already an XML element or string return false. if (!($data instanceof \SimpleXMLElement) && !\is_string($data)) { return false; } // Attempt to load the XML if a string. if (\is_string($data)) { try { $data = new \SimpleXMLElement($data); } catch (\Exception $e) { return false; } } // If we have no XML definition at this point let's make sure we get one. if (empty($this->xml)) { // If no XPath query is set to search for fields, and we have a <form />, set it and return. if (!$xpath && ($data->getName() === 'form')) { $this->xml = $data; // Synchronize any paths found in the load. $this->syncPaths(); return true; } else { // Create a root element for the form. $this->xml = new \SimpleXMLElement('<form></form>'); } } // Get the XML elements to load. $elements = []; if ($xpath) { $elements = $data->xpath($xpath); } elseif ($data->getName() === 'form') { $elements = $data->children(); } // If there is nothing to load return true. if (empty($elements)) { return true; } // Load the found form elements. foreach ($elements as $element) { // Get an array of fields with the correct name. $fields = $element->xpath('descendant-or-self::field'); foreach ($fields as $field) { // Get the group names as strings for ancestor fields elements. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); // Check to see if the field exists in the current form. if ($current = $this->findField((string) $field['name'], implode('.', $groups))) { // If set to replace found fields, replace the data and remove the field so we don't add it twice. if ($replace) { $olddom = dom_import_simplexml($current); $loadeddom = dom_import_simplexml($field); $addeddom = $olddom->ownerDocument->importNode($loadeddom, true); $olddom->parentNode->replaceChild($addeddom, $olddom); $loadeddom->parentNode->removeChild($loadeddom); } else { unset($field); } } } // Merge the new field data into the existing XML document. self::addNode($this->xml, $element); } // Synchronize any paths found in the load. $this->syncPaths(); return true; } /** * Method to load the form description from an XML file. * * The reset option works on a group basis. If the XML file references * groups that have already been created they will be replaced with the * fields in the new XML file unless the $reset parameter has been set * to false. * * @param string $file The filesystem path of an XML file. * @param boolean $reset Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param string $xpath An optional xpath to search for the fields. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function loadFile($file, $reset = true, $xpath = null) { // Check to see if the path is an absolute path. if (!is_file($file)) { // Not an absolute path so let's attempt to find one using JPath. $file = Path::find(self::addFormPath(), strtolower($file) . '.xml'); // If unable to find the file return false. if (!$file) { return false; } } // Attempt to load the XML file. $xml = simplexml_load_file($file); return $this->load($xml, $reset, $xpath); } /** * Method to remove a field from the form definition. * * @param string $name The name of the form field for which remove. * @param string $group The optional dot-separated form group path on which to find the field. * * @return boolean True on success, false otherwise. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function removeField($name, $group = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Find the form field element from the definition. $element = $this->findField($name, $group); // If the element exists remove it from the form definition. if ($element instanceof \SimpleXMLElement) { $dom = dom_import_simplexml($element); $dom->parentNode->removeChild($dom); return true; } return false; } /** * Method to remove a group from the form definition. * * @param string $group The dot-separated form group path for the group to remove. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function removeGroup($group) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Get the fields elements for a given group. $elements = &$this->findGroup($group); foreach ($elements as &$element) { $dom = dom_import_simplexml($element); $dom->parentNode->removeChild($dom); } return true; } /** * Method to reset the form data store and optionally the form XML definition. * * @param boolean $xml True to also reset the XML form definition. * * @return boolean True on success. * * @since 1.7.0 */ public function reset($xml = false) { unset($this->data); $this->data = new Registry(); if ($xml) { unset($this->xml); $this->xml = new \SimpleXMLElement('<form></form>'); } return true; } /** * Method to set a field XML element to the form definition. If the replace flag is set then * the field will be set whether it already exists or not. If it isn't set, then the field * will not be replaced if it already exists. * * @param \SimpleXMLElement $element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to set the field. * @param boolean $replace True to replace an existing field if one already exists. * @param string $fieldset The name of the fieldset we are adding the field to. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function setField(\SimpleXMLElement $element, $group = null, $replace = true, $fieldset = 'default') { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Find the form field element from the definition. $old = $this->findField((string) $element['name'], $group); // If an existing field is found and replace flag is false do nothing and return true. if (!$replace && !empty($old)) { return true; } // If an existing field is found and replace flag is true remove the old field. if ($replace && !empty($old) && ($old instanceof \SimpleXMLElement)) { $dom = dom_import_simplexml($old); // Get the parent element, this should be the fieldset $parent = $dom->parentNode; $fieldset = $parent->getAttribute('name'); $parent->removeChild($dom); } // Create the search path $path = '//'; if (!empty($group)) { $path .= 'fields[@name="' . $group . '"]/'; } $path .= 'fieldset[@name="' . $fieldset . '"]'; $fs = $this->xml->xpath($path); if (isset($fs[0]) && ($fs[0] instanceof \SimpleXMLElement)) { // Add field to the form. self::addNode($fs[0], $element); // Synchronize any paths found in the load. $this->syncPaths(); return true; } // We couldn't find a fieldset to add the field. Now we are checking, if we have set only a group if (!empty($group)) { $fields = &$this->findGroup($group); // If an appropriate fields element was found for the group, add the element. if (isset($fields[0]) && ($fields[0] instanceof \SimpleXMLElement)) { self::addNode($fields[0], $element); } // Synchronize any paths found in the load. $this->syncPaths(); return true; } // We couldn't find a parent so we are adding it at root level // Add field to the form. self::addNode($this->xml, $element); // Synchronize any paths found in the load. $this->syncPaths(); return true; } /** * Method to set an attribute value for a field XML element. * * @param string $name The name of the form field for which to set the attribute value. * @param string $attribute The name of the attribute for which to set a value. * @param mixed $value The value to set for the attribute. * @param string $group The optional dot-separated form group path on which to find the field. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function setFieldAttribute($name, $attribute, $value, $group = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Find the form field element from the definition. $element = $this->findField($name, $group); // If the element doesn't exist return false. if (!($element instanceof \SimpleXMLElement)) { return false; } else { // Otherwise set the attribute and return true. $element[$attribute] = $value; // Synchronize any paths found in the load. $this->syncPaths(); return true; } } /** * Method to set some field XML elements to the form definition. If the replace flag is set then * the fields will be set whether they already exists or not. If it isn't set, then the fields * will not be replaced if they already exist. * * @param \SimpleXMLElement[] &$elements The array of XML element object representations of the form fields. * @param string $group The optional dot-separated form group path on which to set the fields. * @param boolean $replace True to replace existing fields if they already exist. * @param string $fieldset The name of the fieldset we are adding the field to. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function setFields(&$elements, $group = null, $replace = true, $fieldset = 'default') { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Make sure the elements to set are valid. foreach ($elements as $element) { if (!($element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } } // Set the fields. $return = true; foreach ($elements as $element) { if (!$this->setField($element, $group, $replace, $fieldset)) { $return = false; } } // Synchronize any paths found in the load. $this->syncPaths(); return $return; } /** * Method to set the value of a field. If the field does not exist in the form then the method * will return false. * * @param string $name The name of the field for which to set the value. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The value to set for the field. * * @return boolean True on success. * * @since 1.7.0 */ public function setValue($name, $group = null, $value = null) { // If the field does not exist return false. if (!$this->findField($name, $group)) { return false; } // If a group is set use it. if ($group) { $this->data->set($group . '.' . $name, $value); } else { $this->data->set($name, $value); } return true; } /** * Method to process the form data. * * @param array $data An array of field values to filter. * @param string $group The dot-separated form group path on which to filter the fields. * * @return mixed Array or false. * * @since 4.0.0 */ public function process($data, $group = null) { $data = $this->filter($data, $group); $valid = $this->validate($data, $group); if (!$valid) { return $valid; } return $this->postProcess($data, $group); } /** * Method to filter the form data. * * @param array $data An array of field values to filter. * @param string $group The dot-separated form group path on which to filter the fields. * * @return mixed Array or false. * * @since 4.0.0 */ public function filter($data, $group = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } $input = new Registry($data); $output = new Registry(); // Get the fields for which to filter the data. $fields = $this->findFieldsByGroup($group); if (!$fields) { // PANIC! return false; } // Filter the fields. foreach ($fields as $field) { $name = (string) $field['name']; // Get the field groups for the element. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); $attrGroup = implode('.', $groups); $key = $attrGroup ? $attrGroup . '.' . $name : $name; // Filter the value if it exists. if ($input->exists($key)) { $fieldObj = $this->loadField($field, $group); // Only set into the output if the field was supposed to render on the page (i.e. setup returned true) if ($fieldObj) { $output->set($key, $fieldObj->filter($input->get($key, (string) $field['default']), $group, $input)); } } } return $output->toArray(); } /** * Method to validate form data. * * Validation warnings will be pushed into Form::$errors and should be * retrieved with Form::getErrors() when validate returns boolean false. * * @param array $data An array of field values to validate. * @param string $group The optional dot-separated form group path on which to filter the * fields to be validated. * * @return boolean True on success. * * @since 1.7.0 */ public function validate($data, $group = null) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } $return = true; // Create an input registry object from the data to validate. $input = new Registry($data); // Get the fields for which to validate the data. $fields = $this->findFieldsByGroup($group); if (!$fields) { // PANIC! return false; } // Validate the fields. foreach ($fields as $field) { $name = (string) $field['name']; // Define field name for messages if ($field['label']) { $fieldLabel = $field['label']; // Try to translate label if not set to false $translate = (string) $field['translateLabel']; if (!($translate === 'false' || $translate === 'off' || $translate === '0')) { $fieldLabel = Text::_($fieldLabel); } } else { $fieldLabel = Text::_($name); } $disabled = ((string) $field['disabled'] === 'true' || (string) $field['disabled'] === 'disabled'); $fieldExistsInRequestData = $input->exists($name) || $input->exists($group . '.' . $name); // If the field is disabled but it is passed in the request this is invalid as disabled fields are not added to the request if ($disabled && $fieldExistsInRequestData) { throw new \RuntimeException(Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $fieldLabel)); } // Get the field groups for the element. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); $attrGroup = implode('.', $groups); $key = $attrGroup ? $attrGroup . '.' . $name : $name; $fieldObj = $this->loadField($field, $attrGroup); if ($fieldObj) { $valid = $fieldObj->validate($input->get($key), $attrGroup, $input); // Check for an error. if ($valid instanceof \Exception) { $this->errors[] = $valid; $return = false; } } elseif ($input->exists($key)) { // The field returned false from setup and shouldn't be included in the page body - yet we received // a value for it. This is probably some sort of injection attack and should be rejected $this->errors[] = new \RuntimeException(Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $key)); $return = false; } } return $return; } /** * Method to post-process form data. * * @param array $data An array of field values to post-process. * @param string $group The optional dot-separated form group path on which to filter the * fields to be validated. * * @return mixed Array or false. * * @since 4.0.0 */ public function postProcess($data, $group = null) { // Make sure there is a valid SimpleXMLElement if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } $input = new Registry($data); $output = new Registry(); // Get the fields for which to postProcess the data. $fields = $this->findFieldsByGroup($group); if (!$fields) { // PANIC! return false; } // Filter the fields. foreach ($fields as $field) { $name = (string) $field['name']; // Get the field groups for the element. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ?: []); $attrGroup = implode('.', $groups); $key = $attrGroup ? $attrGroup . '.' . $name : $name; // Filter the value if it exists. if ($input->exists($key)) { $fieldobj = $this->loadField($field, $group); $output->set($key, $fieldobj->postProcess($input->get($key, (string) $field['default']), $group, $input)); } } return $output->toArray(); } /** * Method to get a form field represented as an XML element object. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * * @return \SimpleXMLElement|boolean The XML element object for the field or boolean false on error. * * @since 1.7.0 */ protected function findField($name, $group = null) { $element = false; $fields = []; // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Let's get the appropriate field element based on the method arguments. if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); // Get all of the field elements with the correct name for the fields elements. foreach ($elements as $el) { // If there are matching field elements add them to the fields array. if ($tmp = $el->xpath('descendant::field[@name="' . $name . '" and not(ancestor::field/form/*)]')) { $fields = array_merge($fields, $tmp); } } // Make sure something was found. if (!$fields) { return false; } // Use the first correct match in the given group. $groupNames = explode('.', $group); foreach ($fields as &$field) { // Get the group names as strings for ancestor fields elements. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $names = array_map('strval', $attrs ?: []); // If the field is in the exact group use it and break out of the loop. if ($names == (array) $groupNames) { $element = &$field; break; } } } else { // Get an array of fields with the correct name. $fields = $this->xml->xpath('//field[@name="' . $name . '" and not(ancestor::field/form/*)]'); // Make sure something was found. if (!$fields) { return false; } // Search through the fields for the right one. foreach ($fields as &$field) { // If we find an ancestor fields element with a group name then it isn't what we want. if ($field->xpath('ancestor::fields[@name]')) { continue; } else { // Found it! $element = &$field; break; } } } return $element; } /** * Method to get an array of `<field>` elements from the form XML document which are in a specified fieldset by name. * * @param string $name The name of the fieldset. * * @return \SimpleXMLElement[]|boolean Boolean false on error or array of SimpleXMLElement objects. * * @since 1.7.0 */ protected function &findFieldsByFieldset($name) { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } /* * Get an array of <field /> elements that are underneath a <fieldset /> element * with the appropriate name attribute, and also any <field /> elements with * the appropriate fieldset attribute. To allow repeatable elements only fields * which are not descendants of other fields are selected. */ $fields = $this->xml->xpath('(//fieldset[@name="' . $name . '"]//field | //field[@fieldset="' . $name . '"])[not(ancestor::field)]'); return $fields; } /** * Method to get an array of `<field>` elements from the form XML document which are in a control group by name. * * @param mixed $group The optional dot-separated form group path on which to find the fields. * Null will return all fields. False will return fields not in a group. * @param boolean $nested True to also include fields in nested groups that are inside of the * group for which to find fields. * * @return \SimpleXMLElement[]|boolean Boolean false on error or array of SimpleXMLElement objects. * * @since 1.7.0 */ protected function &findFieldsByGroup($group = null, $nested = false) { $fields = []; // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Get only fields in a specific group? if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); // Get all of the field elements for the fields elements. foreach ($elements as $element) { // If there are field elements add them to the return result. if ($tmp = $element->xpath('descendant::field')) { // If we also want fields in nested groups then just merge the arrays. if ($nested) { $fields = array_merge($fields, $tmp); } else { // If we want to exclude nested groups then we need to check each field. $groupNames = explode('.', $group); foreach ($tmp as $field) { // Get the names of the groups that the field is in. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $names = array_map('strval', $attrs ?: []); // If the field is in the specific group then add it to the return list. if ($names == (array) $groupNames) { $fields = array_merge($fields, [$field]); } } } } } } elseif ($group === false) { // Get only field elements not in a group. $fields = $this->xml->xpath('descendant::fields[not(@name)]/field | descendant::fields[not(@name)]/fieldset/field '); } else { // Get an array of all the <field /> elements. $fields = $this->xml->xpath('//field[not(ancestor::field/form/*)]'); } return $fields; } /** * Method to get a form field group represented as an XML element object. * * @param string $group The dot-separated form group path on which to find the group. * * @return \SimpleXMLElement[]|boolean An array of XML element objects for the group or boolean false on error. * * @since 1.7.0 */ protected function &findGroup($group) { $groups = []; $tmp = []; // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Make sure there is actually a group to find. $group = explode('.', $group); if (count($group)) { // Get any fields elements with the correct group name. $elements = $this->xml->xpath('//fields[@name="' . (string) $group[0] . '" and not(ancestor::field/form/*)]'); // Check to make sure that there are no parent groups for each element. foreach ($elements as $element) { if (!$element->xpath('ancestor::fields[@name]')) { $tmp[] = $element; } } // Iterate through the nested groups to find any matching form field groups. for ($i = 1, $n = \count($group); $i < $n; $i++) { // Initialise some loop variables. $validNames = \array_slice($group, 0, $i + 1); $current = $tmp; $tmp = []; // Check to make sure that there are no parent groups for each element. foreach ($current as $element) { // Get any fields elements with the correct group name. $children = $element->xpath('descendant::fields[@name="' . (string) $group[$i] . '"]'); // For the found fields elements validate that they are in the correct groups. foreach ($children as $fields) { // Get the group names as strings for ancestor fields elements. $attrs = $fields->xpath('ancestor-or-self::fields[@name]/@name'); $names = array_map('strval', $attrs ?: []); // If the group names for the fields element match the valid names at this // level add the fields element. if ($validNames == $names) { $tmp[] = $fields; } } } } // Only include valid XML objects. foreach ($tmp as $element) { if ($element instanceof \SimpleXMLElement) { $groups[] = $element; } } } return $groups; } /** * Method to load, setup and return a FormField object based on field data. * * @param string|\SimpleXMLElement $element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return FormField|boolean The FormField object for the field or boolean false on error. * * @since 1.7.0 */ protected function loadField($element, $group = null, $value = null) { // Make sure there is a valid SimpleXMLElement. if (!($element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Get the field type. $type = $element['type'] ? (string) $element['type'] : 'text'; // Load the FormField object for the field. $field = FormHelper::loadFieldType($type); if ($field instanceof DatabaseAwareInterface) { try { $field->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $field->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } } // If the object could not be loaded, get a text field object. if ($field === false) { $field = FormHelper::loadFieldType('text'); } /* * Get the value for the form field if not set. * Default to the translated version of the 'default' attribute * if 'translate_default' attribute if set to 'true' or '1' * else the value of the 'default' attribute for the field. */ if ($value === null) { $default = (string) ($element['default'] ? $element['default'] : $element->default); if (($translate = $element['translate_default']) && ((string) $translate === 'true' || (string) $translate === '1')) { $lang = Factory::getLanguage(); if ($lang->hasKey($default)) { $debug = $lang->setDebug(false); $default = Text::_($default); $lang->setDebug($debug); } else { $default = Text::_($default); } } $value = $this->getValue((string) $element['name'], $group, $default); } // Setup the FormField object. $field->setForm($this); if ($field->setup($element, $value, $group)) { return $field; } else { return false; } } /** * Method to synchronize any field, form or rule paths contained in the XML document. * * @return boolean True on success. * * @since 1.7.0 * @todo Maybe we should receive all addXXXpaths attributes at once? */ protected function syncPaths() { // Make sure there is a valid Form XML document. if (!($this->xml instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::%s `xml` is not an instance of SimpleXMLElement', \get_class($this), __METHOD__)); } // Get any addfieldpath attributes from the form definition. $paths = $this->xml->xpath('//*[@addfieldpath]/@addfieldpath'); $paths = array_map('strval', $paths ?: []); // Add the field paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addFieldPath($path); } // Get any addformpath attributes from the form definition. $paths = $this->xml->xpath('//*[@addformpath]/@addformpath'); $paths = array_map('strval', $paths ?: []); // Add the form paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addFormPath($path); } // Get any addrulepath attributes from the form definition. $paths = $this->xml->xpath('//*[@addrulepath]/@addrulepath'); $paths = array_map('strval', $paths ?: []); // Add the rule paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addRulePath($path); } // Get any addrulepath attributes from the form definition. $paths = $this->xml->xpath('//*[@addfilterpath]/@addfilterpath'); $paths = array_map('strval', $paths ?: []); // Add the rule paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addFilterPath($path); } // Get any addfieldprefix attributes from the form definition. $prefixes = $this->xml->xpath('//*[@addfieldprefix]/@addfieldprefix'); $prefixes = array_map('strval', $prefixes ?: []); // Add the field prefixes. foreach ($prefixes as $prefix) { FormHelper::addFieldPrefix($prefix); } // Get any addformprefix attributes from the form definition. $prefixes = $this->xml->xpath('//*[@addformprefix]/@addformprefix'); $prefixes = array_map('strval', $prefixes ?: []); // Add the field prefixes. foreach ($prefixes as $prefix) { FormHelper::addFormPrefix($prefix); } // Get any addruleprefix attributes from the form definition. $prefixes = $this->xml->xpath('//*[@addruleprefix]/@addruleprefix'); $prefixes = array_map('strval', $prefixes ?: []); // Add the field prefixes. foreach ($prefixes as $prefix) { FormHelper::addRulePrefix($prefix); } // Get any addruleprefix attributes from the form definition. $prefixes = $this->xml->xpath('//*[@addfilterprefix]/@addfilterprefix'); $prefixes = array_map('strval', $prefixes ?: []); // Add the field prefixes. foreach ($prefixes as $prefix) { FormHelper::addFilterPrefix($prefix); } return true; } /** * Proxy for {@link FormHelper::addFieldPath()}. * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 1.7.0 */ public static function addFieldPath($new = null) { return FormHelper::addFieldPath($new); } /** * Proxy for FormHelper::addFormPath(). * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @see FormHelper::addFormPath() * @since 1.7.0 */ public static function addFormPath($new = null) { return FormHelper::addFormPath($new); } /** * Proxy for FormHelper::addRulePath(). * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @see FormHelper::addRulePath() * @since 1.7.0 */ public static function addRulePath($new = null) { return FormHelper::addRulePath($new); } /** * Proxy for FormHelper::addFilterPath(). * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @see FormHelper::addFilterPath() * @since 4.0.0 */ public static function addFilterPath($new = null) { return FormHelper::addFilterPath($new); } /** * Method to get an instance of a form. * * @param string $name The name of the form. * @param string $data The name of an XML file or string to load as the form definition. * @param array $options An array of form options. * @param boolean $replace Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param string|boolean $xpath An optional xpath to search for the fields. * * @return Form Form instance. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the FormFactory service from the container * Example: Factory::getContainer()->get(FormFactoryInterface::class)->createForm($name, $options); * * @throws \InvalidArgumentException if no data provided. * @throws \RuntimeException if the form could not be loaded. */ public static function getInstance($name, $data = null, $options = [], $replace = true, $xpath = false) { // Reference to array with form instances $forms = &self::$forms; // Only instantiate the form if it does not already exist. if (!isset($forms[$name])) { $data = trim($data); if (empty($data)) { throw new \InvalidArgumentException(sprintf('%1$s(%2$s, *%3$s*)', __METHOD__, $name, \gettype($data))); } // Instantiate the form. $forms[$name] = Factory::getContainer()->get(FormFactoryInterface::class)->createForm($name, $options); // Load the data. if (substr($data, 0, 1) === '<') { if ($forms[$name]->load($data, $replace, $xpath) == false) { throw new \RuntimeException(sprintf('%s() could not load form', __METHOD__)); } } else { if ($forms[$name]->loadFile($data, $replace, $xpath) == false) { throw new \RuntimeException(sprintf('%s() could not load file', __METHOD__)); } } } return $forms[$name]; } /** * Adds a new child SimpleXMLElement node to the source. * * @param \SimpleXMLElement $source The source element on which to append. * @param \SimpleXMLElement $new The new element to append. * * @return void * * @since 1.7.0 */ protected static function addNode(\SimpleXMLElement $source, \SimpleXMLElement $new) { // Add the new child node. $node = $source->addChild($new->getName(), htmlspecialchars(trim($new))); // Add the attributes of the child node. foreach ($new->attributes() as $name => $value) { $node->addAttribute($name, $value); } // Add any children of the new node. foreach ($new->children() as $child) { self::addNode($node, $child); } } /** * Update the attributes of a child node * * @param \SimpleXMLElement $source The source element on which to append the attributes * @param \SimpleXMLElement $new The new element to append * * @return void * * @since 1.7.0 */ protected static function mergeNode(\SimpleXMLElement $source, \SimpleXMLElement $new) { // Update the attributes of the child node. foreach ($new->attributes() as $name => $value) { if (isset($source[$name])) { $source[$name] = (string) $value; } else { $source->addAttribute($name, $value); } } } /** * Merges new elements into a source `<fields>` element. * * @param \SimpleXMLElement $source The source element. * @param \SimpleXMLElement $new The new element to merge. * * @return void * * @since 1.7.0 */ protected static function mergeNodes(\SimpleXMLElement $source, \SimpleXMLElement $new) { // The assumption is that the inputs are at the same relative level. // So we just have to scan the children and deal with them. // Update the attributes of the child node. foreach ($new->attributes() as $name => $value) { if (isset($source[$name])) { $source[$name] = (string) $value; } else { $source->addAttribute($name, $value); } } foreach ($new->children() as $child) { $type = $child->getName(); $name = $child['name']; // Does this node exist? $fields = $source->xpath($type . '[@name="' . $name . '"]'); if (empty($fields)) { // This node does not exist, so add it. self::addNode($source, $child); } else { // This node does exist. switch ($type) { case 'field': self::mergeNode($fields[0], $child); break; default: self::mergeNodes($fields[0], $child); break; } } } } /** * Returns the value of an attribute of the form itself * * @param string $name Name of the attribute to get * @param mixed $default Optional value to return if attribute not found * * @return mixed Value of the attribute / default * * @since 3.2 */ public function getAttribute($name, $default = null) { if ($this->xml instanceof \SimpleXMLElement) { $value = $this->xml->attributes()->$name; if ($value !== null) { return (string) $value; } } return $default; } /** * Getter for the form data * * @return Registry Object with the data * * @since 3.2 */ public function getData() { return $this->data; } /** * Method to get the XML form object * * @return \SimpleXMLElement The form XML object * * @since 3.2 */ public function getXml() { return $this->xml; } /** * Method to get a form field represented as an XML element object. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * * @return \SimpleXMLElement|boolean The XML element object for the field or boolean false on error. * * @since 3.7.0 */ public function getFieldXml($name, $group = null) { return $this->findField($name, $group); } } PK1A#]P�<��src/Form/FormRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * * Remove phpcs exception with deprecated constant JCOMPAT_UNICODE_PROPERTIES * @phpcs:disable PSR1.Files.SideEffects */ namespace Joomla\CMS\Form; \defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; // Detect if we have full UTF-8 and unicode PCRE support. if (!\defined('JCOMPAT_UNICODE_PROPERTIES')) { /** * Flag indicating UTF-8 and PCRE support is present * * @const boolean * @since 1.6 * * @deprecated 4.0 will be removed in 6.0 * Will be removed without replacement (Also remove phpcs exception) */ \define('JCOMPAT_UNICODE_PROPERTIES', (bool) @preg_match('/\pL/u', 'a')); } /** * Form Rule class for the Joomla Platform. * * @since 1.6 */ class FormRule { /** * The regular expression to use in testing a form field value. * * @var string * @since 1.6 */ protected $regex; /** * The regular expression modifiers to use when testing a form field value. * * @var string * @since 1.6 */ protected $modifiers = ''; /** * Method to test the value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.6 * @throws \UnexpectedValueException if rule is invalid. */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Check for a valid regex. if (empty($this->regex)) { throw new \UnexpectedValueException(sprintf('%s has invalid regex.', \get_class($this))); } // Detect if we have full UTF-8 and unicode PCRE support. static $unicodePropertiesSupport = null; if ($unicodePropertiesSupport === null) { $unicodePropertiesSupport = (bool) @\preg_match('/\pL/u', 'a'); } // Add unicode property support if available. if ($unicodePropertiesSupport) { $this->modifiers = (strpos($this->modifiers, 'u') !== false) ? $this->modifiers : $this->modifiers . 'u'; } // Test the value against the regular expression. if (preg_match(\chr(1) . $this->regex . \chr(1) . $this->modifiers, $value)) { return true; } return false; } } PK1A#]FA�� src/Form/FormFilterInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a filter class. * * @since 4.0.0 */ interface FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null); } PK1A#]~*;>A>Asrc/Form/FormHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; use Joomla\CMS\Filesystem\Path; use Joomla\String\Normalise; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form's helper class. * Provides a storage for filesystem's paths where Form's entities reside and methods for creating those entities. * Also stores objects with entities' prototypes for further reusing. * * @since 1.7.0 */ class FormHelper { /** * Array with paths where entities(field, rule, form) can be found. * * Array's structure: * * paths: * {ENTITY_NAME}: * - /path/1 * - /path/2 * * @var array[] * @since 1.7.0 */ protected static $paths; /** * The class namespaces. * * @var array[] * @since 3.8.0 */ protected static $prefixes = ['field' => [], 'form' => [], 'rule' => [], 'filter' => []]; /** * Static array of Form's entity objects for re-use. * Prototypes for all fields and rules are here. * * Array's structure: * entities: * {ENTITY_NAME}: * {KEY}: {OBJECT} * * @var array[] * @since 1.7.0 */ protected static $entities = ['field' => [], 'form' => [], 'rule' => [], 'filter' => []]; /** * Method to load a form field object given a type. * * @param string $type The field type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return FormField|boolean FormField object on success, false otherwise. * * @since 1.7.0 */ public static function loadFieldType($type, $new = true) { return self::loadType('field', $type, $new); } /** * Method to load a form rule object given a type. * * @param string $type The rule type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return FormRule|boolean FormRule object on success, false otherwise. * * @since 1.7.0 */ public static function loadRuleType($type, $new = true) { return self::loadType('rule', $type, $new); } /** * Method to load a form filter object given a type. * * @param string $type The rule type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return FormFilterInterface|boolean FormRule object on success, false otherwise. * * @since 4.0.0 */ public static function loadFilterType($type, $new = true) { return self::loadType('filter', $type, $new); } /** * Method to load a form entity object given a type. * Each type is loaded only once and then used as a prototype for other objects of same type. * Please, use this method only with those entities which support types (forms don't support them). * * @param string $entity The entity. * @param string $type The entity type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return mixed Entity object on success, false otherwise. * * @since 1.7.0 */ protected static function loadType($entity, $type, $new = true) { // Reference to an array with current entity's type instances $types = &self::$entities[$entity]; $key = md5($type); // Return an entity object if it already exists and we don't need a new one. if (isset($types[$key]) && $new === false) { return $types[$key]; } $class = self::loadClass($entity, $type); if ($class === false) { return false; } // Instantiate a new type object. $types[$key] = new $class(); return $types[$key]; } /** * Attempt to import the FormField class file if it isn't already imported. * You can use this method outside of Form for loading a field for inheritance or composition. * * @param string $type Type of a field whose class should be loaded. * * @return string|boolean Class name on success or false otherwise. * * @since 1.7.0 */ public static function loadFieldClass($type) { return self::loadClass('field', $type); } /** * Attempt to import the FormRule class file if it isn't already imported. * You can use this method outside of Form for loading a rule for inheritance or composition. * * @param string $type Type of a rule whose class should be loaded. * * @return string|boolean Class name on success or false otherwise. * * @since 1.7.0 */ public static function loadRuleClass($type) { return self::loadClass('rule', $type); } /** * Attempt to import the FormFilter class file if it isn't already imported. * You can use this method outside of Form for loading a filter for inheritance or composition. * * @param string $type Type of a filter whose class should be loaded. * * @return string|boolean Class name on success or false otherwise. * * @since 4.0.0 */ public static function loadFilterClass($type) { return self::loadClass('filter', $type); } /** * Load a class for one of the form's entities of a particular type. * Currently, it makes sense to use this method for the "field" and "rule" entities * (but you can support more entities in your subclass). * * @param string $entity One of the form entities (field or rule). * @param string $type Type of an entity. * * @return string|boolean Class name on success or false otherwise. * * @since 1.7.0 */ protected static function loadClass($entity, $type) { // Check if there is a class in the registered namespaces foreach (self::addPrefix($entity) as $prefix) { // Treat underscores as namespace $name = Normalise::toSpaceSeparated($type); $name = str_ireplace(' ', '\\', ucwords($name)); $subPrefix = ''; if (strpos($name, '.')) { list($subPrefix, $name) = explode('.', $name); $subPrefix = ucfirst($subPrefix) . '\\'; } // Compile the classname $class = rtrim($prefix, '\\') . '\\' . $subPrefix . ucfirst($name) . ucfirst($entity); // Check if the class exists if (class_exists($class)) { return $class; } } $prefix = 'J'; if (strpos($type, '.')) { list($prefix, $type) = explode('.', $type); } $class = StringHelper::ucfirst($prefix, '_') . 'Form' . StringHelper::ucfirst($entity, '_') . StringHelper::ucfirst($type, '_'); if (class_exists($class)) { return $class; } // Get the field search path array. $paths = self::addPath($entity); // If the type is complex, add the base type to the paths. if ($pos = strpos($type, '_')) { // Add the complex type prefix to the paths. for ($i = 0, $n = \count($paths); $i < $n; $i++) { // Derive the new path. $path = $paths[$i] . '/' . strtolower(substr($type, 0, $pos)); // If the path does not exist, add it. if (!\in_array($path, $paths)) { $paths[] = $path; } } // Break off the end of the complex type. $type = substr($type, $pos + 1); } // Try to find the class file. $type = strtolower($type) . '.php'; foreach ($paths as $path) { $file = Path::find($path, $type); if (!$file) { continue; } require_once $file; if (class_exists($class)) { break; } } // Check for all if the class exists. return class_exists($class) ? $class : false; } /** * Method to add a path to the list of field include paths. * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 1.7.0 */ public static function addFieldPath($new = null) { return self::addPath('field', $new); } /** * Method to add a path to the list of form include paths. * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 1.7.0 */ public static function addFormPath($new = null) { return self::addPath('form', $new); } /** * Method to add a path to the list of rule include paths. * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 1.7.0 */ public static function addRulePath($new = null) { return self::addPath('rule', $new); } /** * Method to add a path to the list of filter include paths. * * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 4.0.0 */ public static function addFilterPath($new = null) { return self::addPath('filter', $new); } /** * Method to add a path to the list of include paths for one of the form's entities. * Currently supported entities: field, rule and form. You are free to support your own in a subclass. * * @param string $entity Form's entity name for which paths will be added. * @param string|string[] $new A path or array of paths to add. * * @return string[] The list of paths that have been added. * * @since 1.7.0 */ protected static function addPath($entity, $new = null) { if (!isset(self::$paths[$entity])) { self::$paths[$entity] = []; } // Reference to an array with paths for current entity $paths = &self::$paths[$entity]; // Force the new path(s) to an array. settype($new, 'array'); // Add the new paths to the stack if not already there. foreach ($new as $path) { $path = \trim($path); if (!\in_array($path, $paths)) { \array_unshift($paths, $path); } } return $paths; } /** * Method to add a namespace prefix to the list of field lookups. * * @param string|string[] $new A namespaces or array of namespaces to add. * * @return string[] The list of namespaces that have been added. * * @since 3.8.0 */ public static function addFieldPrefix($new = null) { return self::addPrefix('field', $new); } /** * Method to add a namespace to the list of form lookups. * * @param string|string[] $new A namespace or array of namespaces to add. * * @return string[] The list of namespaces that have been added. * * @since 3.8.0 */ public static function addFormPrefix($new = null) { return self::addPrefix('form', $new); } /** * Method to add a namespace to the list of rule lookups. * * @param string|string[] $new A namespace or array of namespaces to add. * * @return string[] The list of namespaces that have been added. * * @since 3.8.0 */ public static function addRulePrefix($new = null) { return self::addPrefix('rule', $new); } /** * Method to add a namespace to the list of filter lookups. * * @param string|string[] $new A namespace or array of namespaces to add. * * @return string[] The list of namespaces that have been added. * * @since 4.0.0 */ public static function addFilterPrefix($new = null) { return self::addPrefix('filter', $new); } /** * Method to add a namespace to the list of namespaces for one of the form's entities. * Currently supported entities: field, rule and form. You are free to support your own in a subclass. * * @param string $entity Form's entity name for which paths will be added. * @param string|string[] $new A namespace or array of namespaces to add. * * @return string[] The list of namespaces that have been added. * * @since 3.8.0 */ protected static function addPrefix($entity, $new = null) { // Reference to an array with namespaces for current entity $prefixes = &self::$prefixes[$entity]; // Add the default entity's search namespace if not set. if (empty($prefixes)) { $prefixes[] = __NAMESPACE__ . '\\' . ucfirst($entity); } // Force the new namespace(s) to an array. settype($new, 'array'); // Add the new paths to the stack if not already there. foreach ($new as $prefix) { $prefix = trim($prefix); if (\in_array($prefix, $prefixes)) { continue; } array_unshift($prefixes, $prefix); } return $prefixes; } /** * Parse the show on conditions * * @param string $showOn Show on conditions. * @param string $formControl Form name. * @param string $group The dot-separated form group path. * * @return array[] Array with show on conditions. * * @since 3.7.0 */ public static function parseShowOnConditions($showOn, $formControl = null, $group = null) { // Process the showon data. if (!$showOn) { return []; } $formPath = $formControl ?: ''; if ($group) { $groups = explode('.', $group); // An empty formControl leads to invalid shown property // Use the 1st part of the group instead to avoid. if (empty($formPath) && isset($groups[0])) { $formPath = $groups[0]; array_shift($groups); } foreach ($groups as $group) { $formPath .= '[' . $group . ']'; } } $showOnData = []; $showOnParts = preg_split('#(\[AND\]|\[OR\])#', $showOn, -1, PREG_SPLIT_DELIM_CAPTURE); $op = ''; foreach ($showOnParts as $showOnPart) { if (($showOnPart === '[AND]') || $showOnPart === '[OR]') { $op = trim($showOnPart, '[]'); continue; } $compareEqual = strpos($showOnPart, '!:') === false; $showOnPartBlocks = explode(($compareEqual ? ':' : '!:'), $showOnPart, 2); $dotPos = strpos($showOnPartBlocks[0], '.'); if ($dotPos === false) { $field = $formPath ? $formPath . '[' . $showOnPartBlocks[0] . ']' : $showOnPartBlocks[0]; } else { if ($dotPos === 0) { $fieldName = substr($showOnPartBlocks[0], 1); $field = $formControl ? $formControl . '[' . $fieldName . ']' : $fieldName; } else { if ($formControl) { $field = $formControl . ('[' . str_replace('.', '][', $showOnPartBlocks[0]) . ']'); } else { $groupParts = explode('.', $showOnPartBlocks[0]); $field = array_shift($groupParts) . '[' . join('][', $groupParts) . ']'; } } } $showOnData[] = [ 'field' => $field, 'values' => explode(',', $showOnPartBlocks[1]), 'sign' => $compareEqual === true ? '=' : '!=', 'op' => $op, ]; if ($op !== '') { $op = ''; } } return $showOnData; } } PK1A#]Y:�~ ~ src/Form/Rule/EqualsRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class EqualsRule extends FormRule { /** * Method to test if two values are equal. To use this rule, the form * XML needs a validate attribute of equals and a field attribute * that is equal to the field to test against. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $field = (string) $element['field']; // Check that a validation field is set. if (!$field) { throw new \UnexpectedValueException(sprintf('$field empty in %s::test', \get_class($this))); } if (\is_null($form)) { throw new \InvalidArgumentException(sprintf('The value for $form must not be null in %s', \get_class($this))); } if (\is_null($input)) { throw new \InvalidArgumentException(sprintf('The value for $input must not be null in %s', \get_class($this))); } $test = $input->get($field); if (isset($group) && $group !== '') { $test = $input->get($group . '.' . $field); } // Test the two values against each other. return $value == $test; } } PK1A#]�]G� � src/Form/Rule/UserIdRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 4.0.0 */ class UserIdRule extends FormRule implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * Method to test the validity of a Joomla User. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param ?string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 4.0.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Check if the field is required. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); // If the value is empty, null or has the value 0 and the field is not required return true else return false if (($value === '' || $value === null || (string) $value === '0')) { return !$required; } // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Build the query. $query->select('COUNT(*)') ->from($db->quoteName('#__users')) ->where($db->quoteName('id') . ' = :userId') ->bind(':userId', $value, ParameterType::INTEGER); // Set and query the database. return (bool) $db->setQuery($query)->loadResult(); } } PK1A#]�c�f��src/Form/Rule/OptionsRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * Requires the value entered be one of the options in a field of type="list" * * @since 1.7.0 */ class OptionsRule extends FormRule { /** * Method to test the value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Check if the field is required. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); // Check if the value is empty. $blank = empty($value) && $value !== '0' && $value !== 0 && $value !== 0.0; if (!$required && $blank) { return true; } // Make an array of all available option values. $options = []; // Create the field $field = null; if ($form) { $field = $form->getField((string) $element->attributes()->name, $group); } // When the field exists, the real options are fetched. // This is needed for fields which do have dynamic options like from a database. if ($field && \is_array($field->options)) { foreach ($field->options as $opt) { $options[] = $opt->value; } } else { foreach ($element->option as $opt) { $options[] = $opt->attributes()->value; } } // There may be multiple values in the form of an array (if the element is checkboxes, for example). if (\is_array($value)) { // If all values are in the $options array, $diff will be empty and the options valid. $diff = array_diff($value, $options); return empty($diff); } else { // In this case value must be a string return \in_array((string) $value, $options); } } } PK1A#]6���� � src/Form/Rule/UsernameRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class UsernameRule extends FormRule implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * Method to test the username for uniqueness. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Get the extra field check attribute. $userId = ($form instanceof Form) ? (int) $form->getValue('id') : 0; // Build the query. $query->select('COUNT(*)') ->from($db->quoteName('#__users')) ->where( [ $db->quoteName('username') . ' = :username', $db->quoteName('id') . ' <> :userId', ] ) ->bind(':username', $value) ->bind(':userId', $userId, ParameterType::INTEGER); // Set and query the database. $db->setQuery($query); $duplicate = (bool) $db->loadResult(); if ($duplicate) { return false; } return true; } } PK1A#]��nc\\src/Form/Rule/BooleanRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\FormRule; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class BooleanRule extends FormRule { /** * The regular expression to use in testing a form field value. * * @var string * @since 1.7.0 */ protected $regex = '^(?:[01]|true|false)$'; /** * The regular expression modifiers to use when testing a form field value. * * @var string * @since 1.7.0 */ protected $modifiers = 'i'; } PK1A#]'MZ�2 2 src/Form/Rule/ExistsRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form rule class to determine if a value exists in a database table. * * @since 3.9.0 */ class ExistsRule extends FormRule implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * Method to test the username for uniqueness. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.9.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $value = trim($value); $existsTable = (string) $element['exists_table']; $existsColumn = (string) $element['exists_column']; // We cannot validate without a table name if ($existsTable === '') { return true; } // Assume a default column name of `id` if ($existsColumn === '') { $existsColumn = 'id'; } $db = $this->getDatabase(); // Set and query the database. $exists = $db->setQuery( $db->getQuery(true) ->select('COUNT(*)') ->from($db->quoteName($existsTable)) ->where($db->quoteName($existsColumn) . ' = ' . $db->quote($value)) )->loadResult(); return (int) $exists > 0; } } PK1A#]N`�__src/Form/Rule/TimeRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\CMS\Language\Text; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 4.0.0 */ class TimeRule extends FormRule { /** * Method to test the range for a number value using min and max attributes. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 4.0.0 * * @throws \Exception */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null): bool { // Check if the field is required. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); // If the value is empty and the field is not required return True. if (($value === '' || $value === null) && !$required) { return true; } $stringValue = (string) $value; // If the length of a field is smaller than 5 return error message if (strlen($stringValue) !== 5 && !isset($element['step'])) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If the third symbol isn't a ':' return error message if ($stringValue[2] !== ':') { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If the are other symbols except of numbers and ':' return error message if (!preg_match('#^[0-9:]+$#', $stringValue)) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If min and max is set if (isset($element['min']) && isset($element['max'])) { $min = $element['min'][0] . $element['min'][1]; $max = $element['max'][0] . $element['max'][1]; // If the input is smaller than the set min return error message if (intval($min) > intval($stringValue[0] . $stringValue[1])) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_MIN_TIME', $min), 'warning' ); return false; } // If the input is greater than the set max return error message if (intval($max) < intval($stringValue[0] . $stringValue[1])) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_MAX_TIME'), 'warning' ); return false; } // If the hour input is equal to the set max but the minutes input is greater than zero return error message if (intval($max) === intval($stringValue[0] . $stringValue[1])) { if (intval($element['min'][3] . $element['min'][4]) !== 0) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_MAX_TIME'), 'warning' ); return false; } } } // If the first symbol is greater than 2 return error message if (intval($stringValue[0]) > 2) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If the first symbol is greater than 2 and the second symbol is greater than 3 return error message if (intval($stringValue[0]) === 2 && intval($stringValue[1]) > 3) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If the fourth symbol is greater than 5 return error message if (intval($stringValue[3]) > 5) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT'), 'warning' ); return false; } // If the step is set return same error messages as above but taking into a count that there 8 and not 5 symbols if (isset($element['step'])) { if ( strlen($stringValue) !== 8 || intval($stringValue[5]) !== ':' || intval($stringValue[6]) > 5 ) { Factory::getApplication()->enqueueMessage( Text::_('JLIB_FORM_FIELD_INVALID_TIME_INPUT_SECONDS'), 'warning' ); return false; } } return true; } } PK1A#]�GS��src/Form/Rule/CalendarRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Date\Date; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform * * @since 3.7.0 */ class CalendarRule extends FormRule { /** * Method to test the calendar value for a valid parts. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } if (strtolower($value) === 'now') { return true; } try { return Factory::getDate($value) instanceof Date; } catch (\Exception $e) { return false; } } } PK1A#]�1a�E E #src/Form/Rule/CssIdentifierRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 4.0.0 */ class CssIdentifierRule extends FormRule { /** * Method to test if the file path is valid * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 4.0.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value) && $value !== '0') { return true; } // Make sure we allow multiple classes to be added $cssIdentifiers = explode(' ', $value); foreach ($cssIdentifiers as $i => $identifier) { /** * The following regex rules are based on the Html::cleanCssIdentifier method from Drupal * https://github.com/drupal/drupal/blob/8.8.5/core/lib/Drupal/Component/Utility/Html.php#L116-L130 * * with the addition for Joomla that we allow the colon (U+003A) and the @ (U+0040). */ /** * Valid characters in a CSS identifier are: * - the hyphen (U+002D) * - a-z (U+0030 - U+0039) * - A-Z (U+0041 - U+005A) * - the underscore (U+005F) * - the colon (U+003A) * - the @-sign (U+0040) * - 0-9 (U+0061 - U+007A) * - ISO 10646 characters U+00A1 and higher */ if (preg_match('/[^\\x{002D}\\x{0030}-\\x{0039}\\x{0040}-\\x{005A}\\x{005F}\\x{003A}\\x{0061}-\\x{007A}\\x{00A1}-\\x{FFFF}]/u', $identifier)) { return false; } /** * Full identifiers cannot start with a digit, two hyphens, or a hyphen followed by a digit. */ if (preg_match('/^[0-9]/', $identifier) || preg_match('/^(-[0-9])|^(--)/', $identifier)) { return false; } } return true; } } PK1A#]��_��src/Form/Rule/SubformRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Field\SubformField; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form rule to validate subforms field-wise. * * @since 3.9.7 */ class SubformRule extends FormRule { /** * Method to test given values for a subform.. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.9.7 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Get the form field object. $field = $form->getField($element['name'], $group); if (!($field instanceof SubformField)) { throw new \UnexpectedValueException(sprintf('%s is no subform field.', $element['name'])); } if ($value === null) { return true; } $subForm = $field->loadSubForm(); // Multiple values: Validate every row. if ($field->multiple) { foreach ($value as $row) { if ($subForm->validate($row) === false) { // Pass the first error that occurred on the subform validation. $errors = $subForm->getErrors(); if (!empty($errors[0])) { return $errors[0]; } return false; } } } else { // Single value. if ($subForm->validate($value) === false) { // Pass the first error that occurred on the subform validation. $errors = $subForm->getErrors(); if (!empty($errors[0])) { return $errors[0]; } return false; } } return true; } } PK1A#]cN<�� � src/Form/Rule/NumberRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 3.5 */ class NumberRule extends FormRule { /** * Method to test the range for a number value using min and max attributes. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.5 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Check if the field is required. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); // If the value is empty and the field is not required return True. if (($value === '' || $value === null) && ! $required) { return true; } $float_value = (float) $value; if (isset($element['min'])) { $min = (float) $element['min']; if ($min > $float_value) { return false; } } if (isset($element['max'])) { $max = (float) $element['max']; if ($max < $float_value) { return false; } } return true; } } PK1A#]�2B:ggsrc/Form/Rule/PasswordRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\CMS\Language\Text; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 3.1.2 */ class PasswordRule extends FormRule { /** * Method to test if two values are not equal. To use this rule, the form * XML needs a validate attribute of equals and a field attribute * that is equal to the field to test against. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.1.2 * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $meter = isset($element['strengthmeter']) ? ' meter="0"' : '1'; $threshold = isset($element['threshold']) ? (int) $element['threshold'] : 66; $minimumLength = isset($element['minimum_length']) ? (int) $element['minimum_length'] : 12; $minimumIntegers = isset($element['minimum_integers']) ? (int) $element['minimum_integers'] : 0; $minimumSymbols = isset($element['minimum_symbols']) ? (int) $element['minimum_symbols'] : 0; $minimumUppercase = isset($element['minimum_uppercase']) ? (int) $element['minimum_uppercase'] : 0; $minimumLowercase = isset($element['minimum_lowercase']) ? (int) $element['minimum_lowercase'] : 0; // In the installer we don't have any access to the // database yet so use the hard coded default settings if ( !Factory::getApplication()->isClient('installation') && !Factory::getApplication()->isClient('cli_installation') ) { // If we have parameters from com_users, use those instead. // Some of these may be empty for legacy reasons. $params = ComponentHelper::getParams('com_users'); if (!empty($params)) { $minimumLengthp = $params->get('minimum_length', 12); $minimumIntegersp = $params->get('minimum_integers', 0); $minimumSymbolsp = $params->get('minimum_symbols', 0); $minimumUppercasep = $params->get('minimum_uppercase', 0); $minimumLowercasep = $params->get('minimum_lowercase', 0); $meterp = $params->get('meter'); $thresholdp = $params->get('threshold', 66); empty($minimumLengthp) ?: $minimumLength = (int) $minimumLengthp; empty($minimumIntegersp) ?: $minimumIntegers = (int) $minimumIntegersp; empty($minimumSymbolsp) ?: $minimumSymbols = (int) $minimumSymbolsp; empty($minimumUppercasep) ?: $minimumUppercase = (int) $minimumUppercasep; empty($minimumLowercasep) ?: $minimumLowercase = (int) $minimumLowercasep; empty($meterp) ?: $meter = $meterp; empty($thresholdp) ?: $threshold = $thresholdp; } } // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } $valueLength = \strlen($value); // We set a maximum length to prevent abuse since it is unfiltered. if ($valueLength > 4096) { Factory::getApplication()->enqueueMessage(Text::_('JFIELD_PASSWORD_TOO_LONG'), 'error'); } // We don't allow white space inside passwords $valueTrim = trim($value); // Set a variable to check if any errors are made in password $validPassword = true; if (\strlen($valueTrim) !== $valueLength) { Factory::getApplication()->enqueueMessage( Text::_('JFIELD_PASSWORD_SPACES_IN_PASSWORD'), 'error' ); $validPassword = false; } // Minimum number of integers required if (!empty($minimumIntegers)) { $nInts = preg_match_all('/[0-9]/', $value, $imatch); if ($nInts < $minimumIntegers) { Factory::getApplication()->enqueueMessage( Text::plural('JFIELD_PASSWORD_NOT_ENOUGH_INTEGERS_N', $minimumIntegers), 'error' ); $validPassword = false; } } // Minimum number of symbols required if (!empty($minimumSymbols)) { $nsymbols = preg_match_all('[\W]', $value, $smatch); if ($nsymbols < $minimumSymbols) { Factory::getApplication()->enqueueMessage( Text::plural('JFIELD_PASSWORD_NOT_ENOUGH_SYMBOLS_N', $minimumSymbols), 'error' ); $validPassword = false; } } // Minimum number of upper case ASCII characters required if (!empty($minimumUppercase)) { $nUppercase = preg_match_all('/[A-Z]/', $value, $umatch); if ($nUppercase < $minimumUppercase) { Factory::getApplication()->enqueueMessage( Text::plural('JFIELD_PASSWORD_NOT_ENOUGH_UPPERCASE_LETTERS_N', $minimumUppercase), 'error' ); $validPassword = false; } } // Minimum number of lower case ASCII characters required if (!empty($minimumLowercase)) { $nLowercase = preg_match_all('/[a-z]/', $value, $umatch); if ($nLowercase < $minimumLowercase) { Factory::getApplication()->enqueueMessage( Text::plural('JFIELD_PASSWORD_NOT_ENOUGH_LOWERCASE_LETTERS_N', $minimumLowercase), 'error' ); $validPassword = false; } } // Minimum length option if (!empty($minimumLength)) { if (\strlen((string) $value) < $minimumLength) { Factory::getApplication()->enqueueMessage( Text::plural('JFIELD_PASSWORD_TOO_SHORT_N', $minimumLength), 'error' ); $validPassword = false; } } // If valid has violated any rules above return false. if (!$validPassword) { return false; } return true; } } PK1A#]8TV src/Form/Rule/ColorRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class ColorRule extends FormRule { /** * Method to test for a valid color in hexadecimal. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $value = trim($value); // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } if ($value[0] != '#') { return false; } // Remove the leading # if present to validate the numeric part $value = ltrim($value, '#'); // The value must be 6 or 3 characters long if (!((\strlen($value) == 6 || \strlen($value) == 3) && ctype_xdigit($value))) { return false; } return true; } } PK1A#]�G�� � src/Form/Rule/NotequalsRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class NotequalsRule extends FormRule { /** * Method to test if two values are not equal. To use this rule, the form * XML needs a validate attribute of equals and a field attribute * that is equal to the field to test against. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $field = (string) $element['field']; // Check that a validation field is set. if (!$field) { throw new \UnexpectedValueException(sprintf('$field empty in %s::test', \get_class($this))); } if ($input === null) { throw new \InvalidArgumentException(sprintf('The value for $input must not be null in %s', \get_class($this))); } // Test the two values against each other. if ($value != $input->get($field)) { return true; } return false; } } PK1A#]�)asrc/Form/Rule/RulesRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Access\Access; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class RulesRule extends FormRule { /** * Method to test the value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // Get the possible field actions and the ones posted to validate them. $fieldActions = self::getFieldActions($element); $valueActions = self::getValueActions($value); // Make sure that all posted actions are in the list of possible actions for the field. foreach ($valueActions as $action) { if (!\in_array($action, $fieldActions)) { return false; } } return true; } /** * Method to get the list of permission action names from the form field value. * * @param mixed $value The form field value to validate. * * @return string[] A list of permission action names from the form field value. * * @since 1.7.0 */ protected function getValueActions($value) { $actions = []; // Iterate over the asset actions and add to the actions. foreach ((array) $value as $name => $rules) { $actions[] = $name; } return $actions; } /** * Method to get the list of possible permission action names for the form field. * * @param \SimpleXMLElement $element The \SimpleXMLElement object representing the `<field>` tag for the form field object. * * @return string[] A list of permission action names from the form field element definition. * * @since 1.7.0 */ protected function getFieldActions(\SimpleXMLElement $element) { $actions = []; // Initialise some field attributes. $section = $element['section'] ? (string) $element['section'] : ''; $component = $element['component'] ? (string) $element['component'] : ''; // Get the asset actions for the element. $elActions = Access::getActionsFromFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml', "/access/section[@name='" . $section . "']/" ); if ($elActions) { // Iterate over the asset actions and add to the actions. foreach ($elActions as $item) { $actions[] = $item->name; } } // Iterate over the children and add to the actions. foreach ($element->children() as $el) { if ($el->getName() === 'action') { $actions[] = (string) $el['name']; } } return $actions; } } PK1A#]w�� &src/Form/Rule/FolderPathExistsRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla CMS. * * @since 4.0.0 */ class FolderPathExistsRule extends FilePathRule { /** * Method to test if the folder path is valid and points to an existing folder below the Joomla root * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid and points to an existing folder below the Joomla root, false otherwise. * * @since 4.0.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { if (!parent::test($element, $value, $group, $input, $form)) { return false; } // If the field is empty and not required so the previous test hasn't failed, the field is valid. if ($value === '' || $value === null) { return true; } // Spaces only would result in Joomla root which is not allowed if (!trim($value)) { return false; } $pathCleaned = rtrim(Path::clean(JPATH_ROOT . '/' . $value), \DIRECTORY_SEPARATOR); $rootCleaned = rtrim(Path::clean(JPATH_ROOT), \DIRECTORY_SEPARATOR); // JPATH_ROOT is not allowed if ($pathCleaned === $rootCleaned) { return false; } return Folder::exists($pathCleaned); } } PK1A#]�3�}xxsrc/Form/Rule/TelRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform * * @since 1.7.0 */ class TelRule extends FormRule { /** * Method to test the url for a valid parts. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } /* * @link http://www.nanpa.com/ * @link http://tools.ietf.org/html/rfc4933 * @link http://www.itu.int/rec/T-REC-E.164/en * * Regex by Steve Levithan * @link http://blog.stevenlevithan.com/archives/validate-phone-number * @note that valid ITU-T and EPP must begin with +. */ $regexarray = [ 'NANP' => '/^(?:\+?1[-. ]?)?\(?([2-9][0-8][0-9])\)?[-. ]?([2-9][0-9]{2})[-. ]?([0-9]{4})$/', 'ITU-T' => '/^\+(?:[0-9] ?){6,14}[0-9]$/', 'EPP' => '/^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/', ]; if (isset($element['plan'])) { $plan = (string) $element['plan']; if ($plan === 'northamerica' || $plan === 'us') { $plan = 'NANP'; } elseif ($plan === 'International' || $plan === 'int' || $plan === 'missdn' || !$plan) { $plan = 'ITU-T'; } elseif ($plan === 'IETF') { $plan = 'EPP'; } $regex = $regexarray[$plan]; // Test the value against the regular expression. if (preg_match($regex, $value) == false) { return false; } } else { /* * If the rule is set but no plan is selected just check that there are between * 7 and 15 digits inclusive and no illegal characters (but common number separators * are allowed). */ $cleanvalue = preg_replace('/[+. \-(\)]/', '', $value); $regex = '/^[0-9]{7,15}?$/'; if (preg_match($regex, $cleanvalue) == true) { return true; } else { return false; } } return true; } } PK1A#]��㙩�src/Form/Rule/EmailRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\CMS\Language\Text; use Joomla\CMS\String\PunycodeHelper; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class EmailRule extends FormRule implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * The regular expression to use in testing a form field value. * * @var string * @since 1.7.0 * @link https://www.w3.org/TR/html/sec-forms.html#email-state-typeemail */ protected $regex = "^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])" . "?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"; /** * Method to test the email address and optionally check for uniqueness. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed Boolean true if field value is valid. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } // If the tld attribute is present, change the regular expression to require at least 2 characters for it. $tld = ((string) $element['tld'] === 'tld' || (string) $element['tld'] === 'required'); if ($tld) { $this->regex = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])" . '?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$'; } // Determine if the multiple attribute is present $multiple = ((string) $element['multiple'] === 'true' || (string) $element['multiple'] === 'multiple'); if (!$multiple) { // Handle idn email addresses by converting to punycode. $value = PunycodeHelper::emailToPunycode($value); // Test the value against the regular expression. if (!parent::test($element, $value, $group, $input, $form)) { throw new \UnexpectedValueException(Text::_('JLIB_DATABASE_ERROR_VALID_MAIL')); } } else { $values = explode(',', $value); foreach ($values as $value) { // Handle idn email addresses by converting to punycode. $value = PunycodeHelper::emailToPunycode($value); // Test the value against the regular expression. if (!parent::test($element, $value, $group, $input, $form)) { throw new \UnexpectedValueException(Text::_('JLIB_DATABASE_ERROR_VALID_MAIL')); } } } /** * validDomains value should consist of component name and the name of domain list field in component's configuration, separated by a dot. * This allows different components and contexts to use different lists. * If value is incomplete, com_users.domains is used as fallback. */ $validDomains = (string) $element['validDomains'] !== '' && (string) $element['validDomains'] !== 'false'; if ($validDomains && !$multiple) { $config = explode('.', $element['validDomains'], 2); if (\count($config) > 1) { $domains = ComponentHelper::getParams($config[0])->get($config[1]); } else { $domains = ComponentHelper::getParams('com_users')->get('domains'); } if ($domains) { $emailDomain = explode('@', $value); $emailDomain = $emailDomain[1]; $emailParts = array_reverse(explode('.', $emailDomain)); $emailCount = \count($emailParts); $allowed = true; foreach ($domains as $domain) { $domainParts = array_reverse(explode('.', $domain->name)); $status = 0; // Don't run if the email has less segments than the rule. if ($emailCount < \count($domainParts)) { continue; } foreach ($emailParts as $key => $emailPart) { if (!isset($domainParts[$key]) || $domainParts[$key] == $emailPart || $domainParts[$key] == '*') { $status++; } } // All segments match, check whether to allow the domain or not. if ($status === $emailCount) { if ($domain->rule == 0) { $allowed = false; } else { $allowed = true; } } } // If domain is not allowed, fail validation. Otherwise continue. if (!$allowed) { throw new \UnexpectedValueException(Text::sprintf('JGLOBAL_EMAIL_DOMAIN_NOT_ALLOWED', $emailDomain)); } } } // Check if we should test for uniqueness. This only can be used if multiple is not true $unique = ((string) $element['unique'] === 'true' || (string) $element['unique'] === 'unique'); if ($unique && !$multiple) { // Get the database object and a new query object. $db = $this->getDatabase(); $query = $db->getQuery(true); // Get the extra field check attribute. $userId = ($form instanceof Form) ? (int) $form->getValue('id') : 0; // Build the query. $query->select('COUNT(*)') ->from($db->quoteName('#__users')) ->where( [ $db->quoteName('email') . ' = :email', $db->quoteName('id') . ' <> :userId', ] ) ->bind(':email', $value) ->bind(':userId', $userId, ParameterType::INTEGER); // Set and query the database. $db->setQuery($query); $duplicate = (bool) $db->loadResult(); if ($duplicate) { throw new \UnexpectedValueException(Text::_('JLIB_DATABASE_ERROR_EMAIL_INUSE')); } } return true; } } PK1A#]�"}��src/Form/Rule/FilePathRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 3.9.21 */ class FilePathRule extends FormRule { /** * Method to test if the file path is valid * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.9.21 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $value = trim($value); // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] == 'true' || (string) $element['required'] == 'required'); if (!$required && empty($value)) { return true; } // Get the exclude setting from the xml $exclude = (array) explode('|', (string) $element['exclude']); // Exclude current folder '.' to be safe from full path disclosure $exclude[] = '.'; // Check the exclude setting $path = preg_split('/[\/\\\\]/', $value); if (in_array(strtolower($path[0]), $exclude) || empty($path[0])) { return false; } // Prepend the root path $value = JPATH_ROOT . '/' . $value; // Check if $value is a valid path, which includes not allowing to break out of the current path try { Path::check($value); } catch (\Exception $e) { // When there is an exception in the check path this is not valid return false; } // When there are no exception this rule should pass. // See: https://github.com/joomla/joomla-cms/issues/30500#issuecomment-683290162 return true; } } PK1A#]%�.��,src/Form/Rule/CssIdentifierSubstringRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 3.10.7 */ class CssIdentifierSubstringRule extends FormRule { /** * Method to test if a string is a valid CSS identifier substring * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 3.10.7 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value) && $value !== '0') { return true; } /** * The following regex rules are based on the Html::cleanCssIdentifier method from Drupal * https://github.com/drupal/drupal/blob/8.8.5/core/lib/Drupal/Component/Utility/Html.php#L116-L130 * * with the addition for Joomla that we allow the colon (U+003A) and the @ (U+0040). */ /** * Valid characters in a CSS identifier are: * - the hyphen (U+002D) * - a-z (U+0030 - U+0039) * - A-Z (U+0041 - U+005A) * - the underscore (U+005F) * - the colon (U+003A) * - the @ sign (U+0040) * - 0-9 (U+0061 - U+007A) * - ISO 10646 characters U+00A1 and higher */ // Make sure we allow multiple classes to be added $cssIdentifiers = explode(' ', $value); foreach ($cssIdentifiers as $identifier) { if (preg_match('/[^\\x{002D}\\x{0030}-\\x{0039}\\x{0040}-\\x{005A}\\x{005F}\\x{003A}\\x{0061}-\\x{007A}\\x{00A1}-\\x{FFFF}]/u', $identifier)) { return false; } } return true; } } PK1A#]�}� � src/Form/Rule/CaptchaRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Captcha\Captcha; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Framework. * * @since 2.5 */ class CaptchaRule extends FormRule { /** * Method to test if the Captcha is correct. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 2.5 */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $app = Factory::getApplication(); $default = $app->get('captcha'); if ($app->isClient('site')) { $default = $app->getParams()->get('captcha', $default); } $plugin = $element['plugin'] ? (string) $element['plugin'] : $default; $namespace = $element['namespace'] ?: $form->getName(); // Use 0 for none if ($plugin === 0 || $plugin === '0') { return true; } try { $captcha = Captcha::getInstance((string) $plugin, ['namespace' => (string) $namespace]); return $captcha->checkAnswer($value); } catch (\RuntimeException $e) { $app->enqueueMessage($e->getMessage(), 'error'); } return false; } } PK1A#]����"src/Form/Rule/ModuleLayoutRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Form\FormRule; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 3.9.26 */ class ModuleLayoutRule extends FormRule { /** * The regular expression to use in testing a module layout field value. * * A valid module layout field value consists of * - optionally a template name with only characters, numbers, hyphens and * underscores, which can also be just "_" for layouts provided by the * module, followed by a colon. * - the base name of the layout file, not starting with a dot and with * only characters, numbers, dots and hyphens but no underscores (see * method "getInput" of the "ModuleLayout" field). * * @var string * @since 3.9.26 */ protected $regex = '^([A-Za-z0-9_-]+:)?[A-Za-z0-9-][A-Za-z0-9\.-]*$'; } PK1A#]* �Rsrc/Form/Rule/UrlRule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Rule; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormRule; use Joomla\CMS\Language\Text; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\Uri\UriHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Rule class for the Joomla Platform. * * @since 1.7.0 */ class UrlRule extends FormRule { /** * Method to test an external or internal url for all valid parts. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return boolean True if the value is valid, false otherwise. * * @since 1.7.0 * @link https://www.w3.org/Addressing/URL/url-spec.txt * @see \Joomla\String\StringHelper */ public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { // If the field is empty and not required, the field is valid. $required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required'); if (!$required && empty($value)) { return true; } // Check the value for XSS payloads if ((string) $element['disableXssCheck'] !== 'true' && InputFilter::checkAttribute(['href', $value])) { $element->addAttribute('message', Text::sprintf('JLIB_FORM_VALIDATE_FIELD_URL_INJECTION_DETECTED', $element['name'])); return false; } $urlParts = UriHelper::parse_url($value); // See https://www.w3.org/Addressing/URL/url-spec.txt // Use the full list or optionally specify a list of permitted schemes. if ($element['schemes'] == '') { $scheme = ['http', 'https', 'ftp', 'ftps', 'gopher', 'mailto', 'news', 'prospero', 'telnet', 'rlogin', 'sftp', 'tn3270', 'wais', 'mid', 'cid', 'nntp', 'tel', 'urn', 'ldap', 'file', 'fax', 'modem', 'git', ]; } else { $scheme = explode(',', $element['schemes']); } /* * Note that parse_url() does not always parse accurately without a scheme, * but at least the path should be set always. Note also that parse_url() * returns False for seriously malformed URLs instead of an associative array. * @link https://www.php.net/manual/en/function.parse-url.php */ if ($urlParts === false || !\array_key_exists('scheme', $urlParts)) { /* * The function parse_url() returned false (seriously malformed URL) or no scheme * was found and the relative option is not set: in both cases the field is not valid. */ if ($urlParts === false || !$element['relative']) { $element->addAttribute('message', Text::sprintf('JLIB_FORM_VALIDATE_FIELD_URL_SCHEMA_MISSING', $value, implode(', ', $scheme))); return false; } // The best we can do for the rest is make sure that the path exists and is valid UTF-8. if (!\array_key_exists('path', $urlParts) || !StringHelper::valid((string) $urlParts['path'])) { return false; } // The internal URL seems to be good. return true; } // Scheme found, check all parts found. $urlScheme = (string) $urlParts['scheme']; $urlScheme = strtolower($urlScheme); if (\in_array($urlScheme, $scheme) == false) { return false; } // For some schemes here must be two slashes. $scheme = ['http', 'https', 'ftp', 'ftps', 'gopher', 'wais', 'prospero', 'sftp', 'telnet', 'git']; if (\in_array($urlScheme, $scheme) && substr($value, \strlen($urlScheme), 3) !== '://') { return false; } // The best we can do for the rest is make sure that the strings are valid UTF-8 // and the port is an integer. if (\array_key_exists('host', $urlParts) && !StringHelper::valid((string) $urlParts['host'])) { return false; } if (\array_key_exists('port', $urlParts) && 0 === (int) $urlParts['port']) { return false; } if (\array_key_exists('path', $urlParts) && !StringHelper::valid((string) $urlParts['path'])) { return false; } return true; } } PK1A#]ٲ0�yysrc/Form/Filter/UnsetFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class to unset * * @since 4.0.0 */ class UnsetFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { return null; } } PK1A#]4�R���src/Form/Filter/RulesFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for rules * * @since 4.0.0 */ class RulesFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $return = []; foreach ((array) $value as $action => $ids) { // Build the rules array. $return[$action] = []; foreach ($ids as $id => $p) { if ($p !== '') { $return[$action][$id] = ($p == '1' || $p === 'true'); } } } return $return; } } PK1A#]&R &cc"src/Form/Filter/SafehtmlFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for safe HTML * * @since 4.0.0 */ class SafehtmlFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { return InputFilter::getInstance( [], [], InputFilter::ONLY_BLOCK_DEFINED_TAGS, InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES )->clean($value, 'html'); } } PK1A#]��r�src/Form/Filter/RawFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for raw values * * @since 4.0.0 */ class RawFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { return $value; } } PK1A#]��� GGsrc/Form/Filter/UrlFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\CMS\String\PunycodeHelper; use Joomla\CMS\Uri\Uri; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for URLs * * @since 4.0.0 */ class UrlFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { if (empty($value)) { return false; } // This cleans some of the more dangerous characters but leaves special characters that are valid. $value = InputFilter::getInstance()->clean($value, 'html'); $value = trim($value); // <>" are never valid in a uri see https://www.ietf.org/rfc/rfc1738.txt $value = str_replace(['<', '>', '"'], '', $value); // Check for a protocol $protocol = parse_url($value, PHP_URL_SCHEME); // If there is no protocol and the relative option is not specified, // we assume that it is an external URL and prepend http:// if ( ((string) $element['type'] === 'url' && !$protocol && !$element['relative']) || ((string) $element['type'] !== 'url' && !$protocol) ) { $protocol = 'http'; // If it looks like an internal link, then add the root. if (substr($value, 0, 9) === 'index.php') { $value = Uri::root() . $value; } else { // Otherwise we treat it as an external link. // Put the url back together. $value = $protocol . '://' . $value; } } elseif (!$protocol && $element['relative']) { // If relative URLS are allowed we assume that URLs without protocols are internal. $host = Uri::getInstance('SERVER')->getHost(); // If it starts with the host string, just prepend the protocol. if (substr($value, 0) === $host) { $value = 'http://' . $value; } elseif (substr($value, 0, 1) !== '/') { // Otherwise if it doesn't start with "/" prepend the prefix of the current site. $value = Uri::root(true) . '/' . $value; } } $value = PunycodeHelper::urlToPunycode($value); return $value; } } PK1A#]4 ��BBsrc/Form/Filter/TelFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for phone numbers * * @since 4.0.0 */ class TelFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { $value = trim($value); // Does it match the NANP pattern? if (preg_match('/^(?:\+?1[-. ]?)?\(?([2-9][0-8][0-9])\)?[-. ]?([2-9][0-9]{2})[-. ]?([0-9]{4})$/', $value) == 1) { $number = (string) preg_replace('/[^\d]/', '', $value); if (substr($number, 0, 1) === '1') { $number = substr($number, 1); } if (substr($number, 0, 2) === '+1') { $number = substr($number, 2); } $result = '1.' . $number; } elseif (preg_match('/^\+(?:[0-9] ?){6,14}[0-9]$/', $value) == 1) { // If not, does it match ITU-T? $countrycode = substr($value, 0, strpos($value, ' ')); $countrycode = (string) preg_replace('/[^\d]/', '', $countrycode); $number = strstr($value, ' '); $number = (string) preg_replace('/[^\d]/', '', $number); $result = $countrycode . '.' . $number; } elseif (preg_match('/^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/', $value) == 1) { // If not, does it match EPP? if (strstr($value, 'x')) { $xpos = strpos($value, 'x'); $value = substr($value, 0, $xpos); } $result = str_replace('+', '', $value); } elseif (preg_match('/[0-9]{1,3}\.[0-9]{4,14}$/', $value) == 1) { // Maybe it is already ccc.nnnnnnn? $result = $value; } else { // If not, can we make it a string of digits? $value = (string) preg_replace('/[^\d]/', '', $value); if ($value != null && \strlen($value) <= 15) { $length = \strlen($value); // If it is fewer than 13 digits assume it is a local number if ($length <= 12) { $result = '.' . $value; } else { // If it has 13 or more digits let's make a country code. $cclen = $length - 12; $result = substr($value, 0, $cclen) . '.' . substr($value, $cclen); } } else { // If not let's not save anything. $result = ''; } } return $result; } } PK1A#]D�MM"src/Form/Filter/IntarrayFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form\Filter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFilterInterface; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Form Filter class for integer arrays * * @since 4.0.0 */ class IntarrayFilter implements FormFilterInterface { /** * Method to filter a field value. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form. * @param ?Form $form The form object for which the field is being tested. * * @return mixed The filtered value. * * @since 4.0.0 */ public function filter(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) { if (strtoupper((string) $element['filter']) === 'INT_ARRAY') { @trigger_error('`INT_ARRAY` form filter is deprecated and will be removed in 5.0. Use `Intarray` instead', E_USER_DEPRECATED); } if (\is_object($value)) { $value = get_object_vars($value); } $value = \is_array($value) ? $value : [$value]; $value = ArrayHelper::toInteger($value); return $value; } } PK1A#],���!src/Form/FormFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create Form objects * * @since 4.0.0 */ interface FormFactoryInterface { /** * Method to get an instance of a form. * * @param string $name The name of the form. * @param array $options An array of form options. * * @return Form * * @since 4.0.0 */ public function createForm(string $name, array $options = []): Form; } PK1A#]?%��&src/Form/FormFactoryAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a form factory. * * @since 4.0.0 */ interface FormFactoryAwareInterface { /** * Set the form factory to use. * * @param FormFactoryInterface $factory The form factory to use. * * @return FormFactoryAwareInterface This method is chainable. * * @since 4.0.0 */ public function setFormFactory(FormFactoryInterface $factory); } PK1A#]B[�f��src/Form/FormFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; use Joomla\Database\DatabaseAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating Form objects * * @since 4.0.0 */ class FormFactory implements FormFactoryInterface { use DatabaseAwareTrait; /** * Method to get an instance of a form. * * @param string $name The name of the form. * @param array $options An array of form options. * * @return Form * * @since 4.0.0 */ public function createForm(string $name, array $options = []): Form { $form = new Form($name, $options); $form->setDatabase($this->getDatabase()); return $form; } } PK1A#]��������src/Form/FormField.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Form; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Field\SubformField; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Log\Log; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Registry\Registry; use Joomla\String\Normalise; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract Form Field class for the Joomla Platform. * * @since 1.7.0 */ abstract class FormField implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * The description text for the form field. Usually used in tooltips. * * @var string * @since 1.7.0 */ protected $description; /** * The hint text for the form field used to display hint inside the field. * * @var string * @since 3.2 */ protected $hint; /** * The autocomplete state for the form field. If 'off' element will not be automatically * completed by browser. * * @var mixed * @since 3.2 */ protected $autocomplete = 'on'; /** * The spellcheck state for the form field. * * @var boolean * @since 3.2 */ protected $spellcheck = true; /** * The autofocus request for the form field. If true element will be automatically * focused on document load. * * @var boolean * @since 3.2 */ protected $autofocus = false; /** * The SimpleXMLElement object of the `<field>` XML element that describes the form field. * * @var \SimpleXMLElement * @since 1.7.0 */ protected $element; /** * The Form object of the form attached to the form field. * * @var Form * @since 1.7.0 */ protected $form; /** * The form control prefix for field names from the Form object attached to the form field. * * @var string * @since 1.7.0 */ protected $formControl; /** * The hidden state for the form field. * * @var boolean * @since 1.7.0 */ protected $hidden = false; /** * Should the label be hidden when rendering the form field? This may be useful if you have the * label rendering in a legend in your form field itself for radio buttons in a fieldset etc. * If you use this flag you should ensure you display the label in your form (for a11y etc.) * * @var boolean * @since 4.0.0 */ protected $hiddenLabel = false; /** * Should the description be hidden when rendering the form field? This may be useful if you have the * description rendering in your form field itself for e.g. note fields. * * @var boolean * @since 4.0.0 */ protected $hiddenDescription = false; /** * True to translate the field label string. * * @var boolean * @since 1.7.0 */ protected $translateLabel = true; /** * True to translate the field description string. * * @var boolean * @since 1.7.0 */ protected $translateDescription = true; /** * True to translate the field hint string. * * @var boolean * @since 3.2 */ protected $translateHint = true; /** * The document id for the form field. * * @var string * @since 1.7.0 */ protected $id; /** * The input for the form field. * * @var string * @since 1.7.0 */ protected $input; /** * The label for the form field. * * @var string * @since 1.7.0 */ protected $label; /** * The multiple state for the form field. If true then multiple values are allowed for the * field. Most often used for list field types. * * @var boolean * @since 1.7.0 */ protected $multiple = false; /** * Allows extensions to create repeat elements * * @var mixed * @since 3.2 */ public $repeat = false; /** * The pattern (Reg Ex) of value of the form field. * * @var string * @since 1.7.0 */ protected $pattern; /** * The validation text of invalid value of the form field. * * @var string * @since 4.0.0 */ protected $validationtext; /** * The name of the form field. * * @var string * @since 1.7.0 */ protected $name; /** * The name of the field. * * @var string * @since 1.7.0 */ protected $fieldname; /** * The group of the field. * * @var string * @since 1.7.0 */ protected $group; /** * The required state for the form field. If true then there must be a value for the field to * be considered valid. * * @var boolean * @since 1.7.0 */ protected $required = false; /** * The disabled state for the form field. If true then the field will be disabled and user can't * interact with the field. * * @var boolean * @since 3.2 */ protected $disabled = false; /** * The readonly state for the form field. If true then the field will be readonly. * * @var boolean * @since 3.2 */ protected $readonly = false; /** * The form field type. * * @var string * @since 1.7.0 */ protected $type; /** * The validation method for the form field. This value will determine which method is used * to validate the value for a field. * * @var string * @since 1.7.0 */ protected $validate; /** * The value of the form field. * * @var mixed * @since 1.7.0 */ protected $value; /** * The default value of the form field. * * @var mixed * @since 1.7.0 */ protected $default; /** * The size of the form field. * * @var integer * @since 3.2 */ protected $size; /** * The class of the form field * * @var mixed * @since 3.2 */ protected $class; /** * The label's CSS class of the form field * * @var mixed * @since 1.7.0 */ protected $labelclass; /** * The javascript onchange of the form field. * * @var string * @since 3.2 */ protected $onchange; /** * The javascript onclick of the form field. * * @var string * @since 3.2 */ protected $onclick; /** * The conditions to show/hide the field. * * @var string * @since 3.7.0 */ protected $showon; /** * The parent class of the field * * @var string * @since 4.0.0 */ protected $parentclass; /** * The count value for generated name field * * @var integer * @since 1.7.0 */ protected static $count = 0; /** * The string used for generated fields names * * @var string * @since 1.7.0 */ protected static $generated_fieldname = '__field'; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout; /** * Layout to render the form field * * @var string */ protected $renderLayout = 'joomla.form.renderfield'; /** * Layout to render the label * * @var string */ protected $renderLabelLayout = 'joomla.form.renderlabel'; /** * The data-attribute name and values of the form field. * For example, data-action-type="click" data-action-type="change" * * @var array * * @since 4.0.0 */ protected $dataAttributes = []; /** * Method to instantiate the form field object. * * @param Form $form The form to attach to the form field object. * * @since 1.7.0 */ public function __construct($form = null) { // If there is a form passed into the constructor set the form and form control properties. if ($form instanceof Form) { $this->form = $form; $this->formControl = $form->getFormControl(); } // Detect the field type if not set if (!isset($this->type)) { $parts = Normalise::fromCamelCase(\get_called_class(), true); if ($parts[0] === 'J') { $this->type = StringHelper::ucfirst($parts[\count($parts) - 1], '_'); } else { $this->type = StringHelper::ucfirst($parts[0], '_') . StringHelper::ucfirst($parts[\count($parts) - 1], '_'); } } } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 1.7.0 */ public function __get($name) { switch ($name) { case 'description': case 'hint': case 'formControl': case 'hidden': case 'id': case 'multiple': case 'name': case 'required': case 'type': case 'validate': case 'value': case 'class': case 'layout': case 'labelclass': case 'size': case 'onchange': case 'onclick': case 'fieldname': case 'group': case 'disabled': case 'readonly': case 'autofocus': case 'autocomplete': case 'spellcheck': case 'validationtext': case 'showon': case 'parentclass': return $this->$name; case 'input': // If the input hasn't yet been generated, generate it. if (empty($this->input)) { $this->input = $this->getInput(); } return $this->input; case 'label': // If the label hasn't yet been generated, generate it. if (empty($this->label)) { $this->label = $this->getLabel(); } return $this->label; case 'title': return $this->getTitle(); default: // Check for data attribute if (strpos($name, 'data-') === 0 && array_key_exists($name, $this->dataAttributes)) { return $this->dataAttributes[$name]; } } } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'class': // Removes spaces from left & right and extra spaces from middle $value = preg_replace('/\s+/', ' ', trim((string) $value)); // No break case 'description': case 'hint': case 'value': case 'labelclass': case 'layout': case 'onchange': case 'onclick': case 'validate': case 'pattern': case 'validationtext': case 'group': case 'showon': case 'parentclass': case 'default': case 'autocomplete': $this->$name = (string) $value; break; case 'id': $this->id = $this->getId((string) $value, $this->fieldname); break; case 'fieldname': $this->fieldname = $this->getFieldName((string) $value); break; case 'name': $this->fieldname = $this->getFieldName((string) $value); $this->name = $this->getName($this->fieldname); break; case 'multiple': // Allow for field classes to force the multiple values option. $value = (string) $value; $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value; // No break case 'required': case 'disabled': case 'readonly': case 'autofocus': case 'hidden': $value = (string) $value; $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; case 'spellcheck': case 'translateLabel': case 'translateDescription': case 'translateHint': $value = (string) $value; $this->$name = !($value === 'false' || $value === 'off' || $value === '0'); break; case 'translate_label': $value = (string) $value; $this->translateLabel = $this->translateLabel && !($value === 'false' || $value === 'off' || $value === '0'); break; case 'translate_description': $value = (string) $value; $this->translateDescription = $this->translateDescription && !($value === 'false' || $value === 'off' || $value === '0'); break; case 'size': $this->$name = (int) $value; break; default: // Detect data attribute(s) if (strpos($name, 'data-') === 0) { $this->dataAttributes[$name] = $value; } else { if (property_exists(__CLASS__, $name)) { Log::add("Cannot access protected / private property $name of " . __CLASS__); } else { $this->$name = $value; } } } } /** * Method to attach a Form object to the field. * * @param Form $form The Form object to attach to the form field. * * @return FormField The form field object so that the method can be used in a chain. * * @since 1.7.0 */ public function setForm(Form $form) { $this->form = $form; $this->formControl = $form->getFormControl(); return $this; } /** * Method to attach a Form object to the field. * * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @since 1.7.0 */ public function setup(\SimpleXMLElement $element, $value, $group = null) { // Make sure there is a valid FormField XML element. if ((string) $element->getName() !== 'field') { return false; } // Reset the input and label values. $this->input = null; $this->label = null; // Set the XML element object. $this->element = $element; // Set the group of the field. $this->group = $group; $attributes = [ 'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext', 'default', 'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel', 'translate_label', 'translateDescription', 'translate_description', 'size', 'showon', ]; $this->default = isset($element['value']) ? (string) $element['value'] : $this->default; // Set the field default value. if ($element['multiple'] && \is_string($value) && \is_array(json_decode($value, true))) { $this->value = (array) json_decode($value); } else { $this->value = $value; } // Lets detect miscellaneous data attribute. For eg, data-* foreach ($this->element->attributes() as $key => $value) { if (strpos($key, 'data-') === 0) { // Data attribute key value pair $this->dataAttributes[$key] = $value; } } foreach ($attributes as $attributeName) { $this->__set($attributeName, $element[$attributeName]); } // Allow for repeatable elements $repeat = (string) $element['repeat']; $this->repeat = ($repeat === 'true' || $repeat === 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1)); // Set the visibility. $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden'); $this->layout = !empty($this->element['layout']) ? (string) $this->element['layout'] : $this->layout; $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass; // Add required to class list if field is required. if ($this->required) { $this->class = trim($this->class . ' required'); } return true; } /** * Simple method to set the value * * @param mixed $value Value to set * * @return void * * @since 3.2 */ public function setValue($value) { $this->value = $value; } /** * Method to get the id used for the field input tag. * * @param string $fieldId The field element id. * @param string $fieldName The field element name. * * @return string The id to be used for the field input tag. * * @since 1.7.0 */ protected function getId($fieldId, $fieldName) { $id = ''; // If there is a form control set for the attached form add it first. if ($this->formControl) { $id .= $this->formControl; } // If the field is in a group add the group control to the field id. if ($this->group) { // If we already have an id segment add the group control as another level. if ($id) { $id .= '_' . str_replace('.', '_', $this->group); } else { $id .= str_replace('.', '_', $this->group); } } // If we already have an id segment add the field id/name as another level. if ($id) { $id .= '_' . ($fieldId ?: $fieldName); } else { $id .= ($fieldId ?: $fieldName); } // Clean up any invalid characters. $id = preg_replace('#\W#', '_', $id); // If this is a repeatable element, add the repeat count to the ID if ($this->repeat) { $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter; $id .= '-' . $repeatCounter; if (strtolower($this->type) === 'radio') { $id .= '-'; } } return $id; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the field title. * * @return string The field title. * * @since 1.7.0 */ protected function getTitle() { $title = ''; if ($this->hidden) { return $title; } // Get the label text from the XML element, defaulting to the element name. $title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; $title = $this->translateLabel ? Text::_($title) : $title; return $title; } /** * Method to get the field label markup. * * @return string The field label markup. * * @since 1.7.0 */ protected function getLabel() { if ($this->hidden) { return ''; } $data = $this->getLayoutData(); // Forcing the Alias field to display the tip below $position = ((string) $this->element['name']) === 'alias' ? ' data-bs-placement="bottom" ' : ''; // Here mainly for B/C with old layouts. This can be done in the layouts directly $extraData = [ 'text' => $data['label'], 'for' => $this->id, 'classes' => explode(' ', $data['labelclass']), 'position' => $position, ]; return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData)); } /** * Method to get the name used for the field input tag. * * @param string $fieldName The field element name. * * @return string The name to be used for the field input tag. * * @since 1.7.0 */ protected function getName($fieldName) { // To support repeated element, extensions can set this in plugin->onRenderSettings $name = ''; // If there is a form control set for the attached form add it first. if ($this->formControl) { $name .= $this->formControl; } // If the field is in a group add the group control to the field name. if ($this->group) { // If we already have a name segment add the group control as another level. $groups = explode('.', $this->group); if ($name) { foreach ($groups as $group) { $name .= '[' . $group . ']'; } } else { $name .= array_shift($groups); foreach ($groups as $group) { $name .= '[' . $group . ']'; } } } // If we already have a name segment add the field name as another level. if ($name) { $name .= '[' . $fieldName . ']'; } else { $name .= $fieldName; } // If the field should support multiple values add the final array segment. if ($this->multiple) { switch (strtolower((string) $this->element['type'])) { case 'text': case 'textarea': case 'email': case 'password': case 'radio': case 'calendar': case 'editor': case 'hidden': break; default: $name .= '[]'; } } return $name; } /** * Method to get the field name used. * * @param string $fieldName The field element name. * * @return string The field name * * @since 1.7.0 */ protected function getFieldName($fieldName) { if ($fieldName) { return $fieldName; } else { self::$count = self::$count + 1; return self::$generated_fieldname . self::$count; } } /** * Method to get an attribute of the field * * @param string $name Name of the attribute to get * @param mixed $default Optional value to return if attribute not found * * @return mixed Value of the attribute / default * * @since 3.2 */ public function getAttribute($name, $default = null) { if ($this->element instanceof \SimpleXMLElement) { $attributes = $this->element->attributes(); // Ensure that the attribute exists if ($attributes->$name !== null) { return (string) $attributes->$name; } } return $default; } /** * Method to get data attributes. For example, data-user-type * * @return array list of data attribute(s) * * @since 4.0.0 */ public function getDataAttributes() { return $this->dataAttributes; } /** * Method to render data attributes to html. * * @return string A HTML Tag Attribute string of data attribute(s) * * @since 4.0.0 */ public function renderDataAttributes() { $dataAttribute = ''; $dataAttributes = $this->getDataAttributes(); if (!empty($dataAttributes)) { foreach ($dataAttributes as $key => $attrValue) { $dataAttribute .= ' ' . $key . '="' . htmlspecialchars($attrValue, ENT_COMPAT, 'UTF-8') . '"'; } } return $dataAttribute; } /** * Render a layout of this field * * @param string $layoutId Layout identifier * @param array $data Optional data for the layout * * @return string * * @since 3.5 */ public function render($layoutId, $data = []) { $data = array_merge($this->getLayoutData(), $data); return $this->getRenderer($layoutId)->render($data); } /** * Method to get a control group with label and input. * * @param array $options Options to be passed into the rendering of the field * * @return string A string containing the html for the control group * * @since 3.2 */ public function renderField($options = []) { if ($this->hidden) { return $this->getInput(); } if (!isset($options['class'])) { $options['class'] = ''; } $options['rel'] = ''; if (empty($options['hiddenLabel'])) { if ($this->getAttribute('hiddenLabel')) { $options['hiddenLabel'] = $this->getAttribute('hiddenLabel') == 'true'; } else { $options['hiddenLabel'] = $this->hiddenLabel; } } if (empty($options['hiddenDescription'])) { if ($this->getAttribute('hiddenDescription')) { $options['hiddenDescription'] = $this->getAttribute('hiddenDescription') == 'true'; } else { $options['hiddenDescription'] = $this->hiddenDescription; } } $options['inlineHelp'] = isset($this->form, $this->form->getXml()->config->inlinehelp['button']) ? ((string) $this->form->getXml()->config->inlinehelp['button'] == 'show' ?: false) : false; // Check if the field has showon in nested option $hasOptionShowOn = false; if (!empty((array) $this->element->xpath('option'))) { foreach ($this->element->xpath('option') as $option) { if ((string) $option['showon']) { $hasOptionShowOn = true; break; } } } if ($this->showon || $hasOptionShowOn) { $options['rel'] = ' data-showon=\'' . json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\''; $options['showonEnabled'] = true; } $data = [ 'input' => $this->getInput(), 'label' => $this->getLabel(), 'options' => $options, ]; $data = array_merge($this->getLayoutData(), $data); return $this->getRenderer($this->renderLayout)->render($data); } /** * Method to filter a field value. * * @param mixed $value The optional value to use as the default for the field. * @param string $group The optional dot-separated form group path on which to find the field. * @param ?Registry $input An optional Registry object with the entire data set to filter * against the entire form. * * @return mixed The filtered value. * * @since 4.0.0 * @throws \UnexpectedValueException */ public function filter($value, $group = null, Registry $input = null) { // Make sure there is a valid SimpleXMLElement. if (!($this->element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); } // Get the field filter type. $filter = (string) $this->element['filter']; if ($filter !== '') { $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); if (($value === '' || $value === null) && !$required) { return ''; } // Check for a callback filter if (strpos($filter, '::') !== false && \is_callable(explode('::', $filter))) { return \call_user_func(explode('::', $filter), $value); } // Load the FormRule object for the field. FormRule objects take precedence over PHP functions $obj = FormHelper::loadFilterType($filter); // Run the filter rule. if ($obj) { return $obj->filter($this->element, $value, $group, $input, $this->form); } if (\function_exists($filter)) { return \call_user_func($filter, $value); } if ($this instanceof SubformField) { $subForm = $this->loadSubForm(); // Subform field may have a default value, that is a JSON string if ($value && is_string($value)) { $value = json_decode($value, true); // The string is invalid json if (!$value) { return null; } } if ($this->multiple) { $return = []; if ($value) { foreach ($value as $key => $val) { $return[$key] = $subForm->filter($val); } } } else { $return = $subForm->filter($value); } return $return; } } return InputFilter::getInstance()->clean($value, $filter); } /** * Method to validate a FormField object based on field data. * * @param mixed $value The optional value to use as the default for the field. * @param string $group The optional dot-separated form group path on which to find the field. * @param ?Registry $input An optional Registry object with the entire data set to validate * against the entire form. * * @return boolean|\Exception Boolean true if field value is valid, Exception on failure. * * @since 4.0.0 * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ public function validate($value, $group = null, Registry $input = null) { // Make sure there is a valid SimpleXMLElement. if (!($this->element instanceof \SimpleXMLElement)) { throw new \UnexpectedValueException(sprintf('%s::validate `element` is not an instance of SimpleXMLElement', \get_class($this))); } $valid = true; // Check if the field is required. $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); if ($this->element['label']) { $fieldLabel = $this->element['label']; // Try to translate label if not set to false $translate = (string) $this->element['translateLabel']; if (!($translate === 'false' || $translate === 'off' || $translate === '0')) { $fieldLabel = Text::_($fieldLabel); } } else { $fieldLabel = Text::_($this->element['name']); } // If the field is required and the value is empty return an error message. if ($required && (($value === '') || ($value === null))) { $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $fieldLabel); return new \RuntimeException($message); } // Get the field validation rule. if ($type = (string) $this->element['validate']) { // Load the FormRule object for the field. $rule = FormHelper::loadRuleType($type); // If the object could not be loaded return an error message. if ($rule === false) { throw new \UnexpectedValueException(sprintf('%s::validate() rule `%s` missing.', \get_class($this), $type)); } if ($rule instanceof DatabaseAwareInterface) { try { $rule->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } } try { // Run the field validation rule test. $valid = $rule->test($this->element, $value, $group, $input, $this->form); } catch (\Exception $e) { return $e; } } if ($valid !== false && $this instanceof SubformField) { // Load the subform validation rule. $rule = FormHelper::loadRuleType('Subform'); if ($rule instanceof DatabaseAwareInterface) { try { $rule->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } } try { // Run the field validation rule test. $valid = $rule->test($this->element, $value, $group, $input, $this->form); } catch (\Exception $e) { return $e; } } // Check if the field is valid. if ($valid === false) { // Does the field have a defined error message? $message = (string) $this->element['message']; if ($message) { $message = Text::_($this->element['message']); } else { $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $fieldLabel); } return new \UnexpectedValueException($message); } return $valid; } /** * Method to post-process a field value. * * @param mixed $value The optional value to use as the default for the field. * @param string $group The optional dot-separated form group path on which to find the field. * @param ?Registry $input An optional Registry object with the entire data set to filter * against the entire form. * * @return mixed The processed value. * * @since 4.0.0 */ public function postProcess($value, $group = null, Registry $input = null) { return $value; } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; $label = $label && $this->translateLabel ? Text::_($label) : $label; $description = !empty($this->description) ? $this->description : null; $description = !empty($description) && $this->translateDescription ? Text::_($description) : $description; $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); $options = [ 'autocomplete' => $this->autocomplete, 'autofocus' => $this->autofocus, 'class' => $this->class, 'description' => $description, 'disabled' => $this->disabled, 'field' => $this, 'group' => $this->group, 'hidden' => $this->hidden, 'hint' => $this->translateHint ? Text::alt($this->hint, $alt) : $this->hint, 'id' => $this->id, 'label' => $label, 'labelclass' => $this->labelclass, 'multiple' => $this->multiple, 'name' => $this->name, 'onchange' => $this->onchange, 'onclick' => $this->onclick, 'pattern' => $this->pattern, 'validationtext' => $this->validationtext, 'readonly' => $this->readonly, 'repeat' => $this->repeat, 'required' => (bool) $this->required, 'size' => $this->size, 'spellcheck' => $this->spellcheck, 'validate' => $this->validate, 'value' => $this->value, 'dataAttribute' => $this->renderDataAttributes(), 'dataAttributes' => $this->dataAttributes, 'parentclass' => $this->parentclass, ]; return $options; } /** * Allow to override renderer include paths in child fields * * @return array * * @since 3.5 */ protected function getLayoutPaths() { $renderer = new FileLayout('default'); return $renderer->getDefaultIncludePaths(); } /** * Get the renderer * * @param string $layoutId Id to load * * @return FileLayout * * @since 3.5 */ protected function getRenderer($layoutId = 'default') { $renderer = new FileLayout($layoutId); $renderer->setDebug($this->isDebugEnabled()); $layoutPaths = $this->getLayoutPaths(); if ($layoutPaths) { $renderer->setIncludePaths($layoutPaths); } return $renderer; } /** * Is debug enabled for this field * * @return boolean * * @since 3.5 */ protected function isDebugEnabled() { return $this->getAttribute('debug', 'false') === 'true'; } } PK1A#]3��n��src/Adapter/AdapterInstance.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Adapter; use Joomla\CMS\Factory; use Joomla\CMS\Object\CMSObject; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Adapter Instance Class * * @since 1.6 * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ class AdapterInstance extends CMSObject { /** * Parent * * @var Adapter * @since 1.6 */ protected $parent = null; /** * Database * * @var DatabaseDriver * @since 1.6 */ protected $db = null; /** * Constructor * * @param Adapter $parent Parent object * @param DatabaseDriver $db Database object * @param array $options Configuration Options * * @since 1.6 */ public function __construct(Adapter $parent, DatabaseDriver $db, array $options = []) { // Set the properties from the options array that is passed in $this->setProperties($options); // Set the parent and db in case $options for some reason overrides it. $this->parent = $parent; // Pull in the global dbo in case something happened to it. $this->db = $db ?: Factory::getDbo(); } /** * Retrieves the parent object * * @return Adapter * * @since 1.6 */ public function getParent() { return $this->parent; } } PK1A#]T��e��src/Adapter/Adapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Adapter; use Joomla\CMS\Factory; use Joomla\CMS\Object\CMSObject; use Joomla\Database\DatabaseAwareInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Adapter Class * Retains common adapter pattern functions * Class harvested from joomla.installer.installer * * @since 1.6 * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ class Adapter extends CMSObject { /** * Associative array of adapters * * @var static[] * @since 1.6 */ protected $_adapters = []; /** * Adapter Folder * * @var string * @since 1.6 */ protected $_adapterfolder = 'adapters'; /** * Adapter Class Prefix * * @var string * @since 1.6 */ protected $_classprefix = 'J'; /** * Base Path for the adapter instance * * @var string * @since 1.6 */ protected $_basepath = null; /** * Database Connector Object * * @var \Joomla\Database\DatabaseDriver * @since 1.6 */ protected $_db; /** * Constructor * * @param string $basepath Base Path of the adapters * @param string $classprefix Class prefix of adapters * @param string $adapterfolder Name of folder to append to base path * * @since 1.6 */ public function __construct($basepath, $classprefix = null, $adapterfolder = null) { $this->_basepath = $basepath; $this->_classprefix = $classprefix ?: 'J'; $this->_adapterfolder = $adapterfolder ?: 'adapters'; $this->_db = Factory::getDbo(); // Ensure BC, when removed in 5, then the db must be set with setDatabase explicitly if ($this instanceof DatabaseAwareInterface) { $this->setDatabase($this->_db); } } /** * Get the database connector object * * @return \Joomla\Database\DatabaseDriver Database connector object * * @since 1.6 */ public function getDbo() { return $this->_db; } /** * Return an adapter. * * @param string $name Name of adapter to return * @param array $options Adapter options * * @return static|boolean Adapter of type 'name' or false * * @since 1.6 */ public function getAdapter($name, $options = []) { if (array_key_exists($name, $this->_adapters)) { return $this->_adapters[$name]; } if ($this->setAdapter($name, $options)) { return $this->_adapters[$name]; } return false; } /** * Set an adapter by name * * @param string $name Adapter name * @param object $adapter Adapter object * @param array $options Adapter options * * @return boolean True if successful * * @since 1.6 */ public function setAdapter($name, &$adapter = null, $options = []) { if (is_object($adapter)) { $this->_adapters[$name] = &$adapter; return true; } $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name); if (class_exists($class)) { $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name) . 'Adapter'; if (class_exists($class)) { $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } $fullpath = $this->_basepath . '/' . $this->_adapterfolder . '/' . strtolower($name) . '.php'; if (!is_file($fullpath)) { return false; } // Try to load the adapter object $class = $this->_classprefix . ucfirst($name); \JLoader::register($class, $fullpath); if (!class_exists($class)) { return false; } $this->_adapters[$name] = new $class($this, $this->_db, $options); return true; } /** * Loads all adapters. * * @param array $options Adapter options * * @return void * * @since 1.6 */ public function loadAllAdapters($options = []) { $files = new \DirectoryIterator($this->_basepath . '/' . $this->_adapterfolder); /** @type $file \DirectoryIterator */ foreach ($files as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } // Try to load the adapter object require_once $this->_basepath . '/' . $this->_adapterfolder . '/' . $fileName; // Derive the class name from the filename. $name = str_ireplace('.php', '', ucfirst(trim($fileName))); $class = $this->_classprefix . ucfirst($name); if (!class_exists($class)) { // Skip to next one continue; } $adapter = new $class($this, $this->_db, $options); $this->_adapters[$name] = clone $adapter; } } } PK1A#]���c��.src/MVC/Factory/MVCFactoryServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a MVC factory. * * @since 4.0.0 */ interface MVCFactoryServiceInterface { /** * Get the factory. * * @return MVCFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the factory has not been set. */ public function getMVCFactory(): MVCFactoryInterface; } PK1A#]���*src/MVC/Factory/MVCFactoryServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a MVC factory service class. * * @since 4.0.0 */ trait MVCFactoryServiceTrait { /** * The MVC Factory. * * @var MVCFactoryInterface */ private $mvcFactory; /** * Get the factory. * * @return MVCFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the factory has not been set. */ public function getMVCFactory(): MVCFactoryInterface { if (!$this->mvcFactory) { throw new \UnexpectedValueException('MVC factory not set in ' . __CLASS__); } return $this->mvcFactory; } /** * The MVC Factory. * * @param MVCFactoryInterface $mvcFactory The factory * * @return void * * @since 4.0.0 */ public function setMVCFactory(MVCFactoryInterface $mvcFactory) { $this->mvcFactory = $mvcFactory; } } PK1A#]%<�ww!src/MVC/Factory/LegacyFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\MVC\Model\ModelInterface; use Joomla\CMS\Table\Table; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory to create MVC objects in legacy mode. * Uses the static getInstance function on the classes itself. Behavior of the old none * namespaced extension set up. * * @since 3.10.0 */ class LegacyFactory implements MVCFactoryInterface { /** * Method to load and return a controller object. * * @param string $name The name of the controller * @param string $prefix The controller prefix * @param array $config The configuration array for the controller * @param CMSApplicationInterface $app The app * @param Input $input The input * * @return \Joomla\CMS\MVC\Controller\ControllerInterface * * @since 4.0.0 * @throws \Exception */ public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input) { throw new \BadFunctionCallException('Legacy controller creation not supported.'); } /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Optional configuration array for the model. * * @return ModelInterface The model object * * @since 3.10.0 * @throws \Exception */ public function createModel($name, $prefix = '', array $config = []) { // Clean the model name $modelName = preg_replace('/[^A-Z0-9_]/i', '', $name); $classPrefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); return BaseDatabaseModel::getInstance($modelName, $classPrefix, $config); } /** * Method to load and return a view object. * * @param string $name The name of the view. * @param string $prefix Optional view prefix. * @param string $type Optional type of view. * @param array $config Optional configuration array for the view. * * @return \Joomla\CMS\MVC\View\ViewInterface The view object * * @since 3.10.0 * @throws \Exception */ public function createView($name, $prefix = '', $type = '', array $config = []) { // Clean the view name $viewName = preg_replace('/[^A-Z0-9_]/i', '', $name); $classPrefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); $viewType = preg_replace('/[^A-Z0-9_]/i', '', $type); // Build the view class name $viewClass = $classPrefix . $viewName; if (!class_exists($viewClass)) { $path = Path::find($config['paths'], BaseController::createFileName('view', ['name' => $viewName, 'type' => $viewType])); if (!$path) { return null; } \JLoader::register($viewClass, $path); if (!class_exists($viewClass)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_VIEW_CLASS_NOT_FOUND', $viewClass, $path), 500); } } return new $viewClass($config); } /** * Method to load and return a table object. * * @param string $name The name of the table. * @param string $prefix Optional table prefix. * @param array $config Optional configuration array for the table. * * @return \Joomla\CMS\Table\Table The table object * * @since 3.10.0 * @throws \Exception */ public function createTable($name, $prefix = 'Table', array $config = []) { // Clean the model name $name = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); return Table::getInstance($name, $prefix, $config); } } PK1A#]}���(src/MVC/Factory/MVCFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * MVCFactory aware trait. * * @since 4.0.0 */ trait MVCFactoryAwareTrait { /** * The mvc factory. * * @var MVCFactoryInterface * @since 4.0.0 */ private $mvcFactory; /** * Returns the MVC factory. * * @return MVCFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException */ protected function getMVCFactory(): MVCFactoryInterface { if ($this->mvcFactory) { return $this->mvcFactory; } throw new \UnexpectedValueException('MVC Factory not set in ' . __CLASS__); } /** * Set the MVC factory. * * @param MVCFactoryInterface $mvcFactory The MVC factory * * @return void * * @since 4.0.0 */ public function setMVCFactory(MVCFactoryInterface $mvcFactory) { $this->mvcFactory = $mvcFactory; } } PK1A#]g�7� � 'src/MVC/Factory/MVCFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\MVC\Model\ModelInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory to create MVC objects. * * @since 3.10.0 */ interface MVCFactoryInterface { /** * Method to load and return a controller object. * * @param string $name The name of the controller * @param string $prefix The controller prefix * @param array $config The configuration array for the controller * @param CMSApplicationInterface $app The app * @param Input $input The input * * @return \Joomla\CMS\MVC\Controller\ControllerInterface * * @since 4.0.0 * @throws \Exception */ public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input); /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Optional configuration array for the model. * * @return ModelInterface The model object * * @since 3.10.0 * @throws \Exception */ public function createModel($name, $prefix = '', array $config = []); /** * Method to load and return a view object. * * @param string $name The name of the view. * @param string $prefix Optional view prefix. * @param string $type Optional type of view. * @param array $config Optional configuration array for the view. * * @return \Joomla\CMS\MVC\View\ViewInterface The view object * * @since 3.10.0 * @throws \Exception */ public function createView($name, $prefix = '', $type = '', array $config = []); /** * Method to load and return a table object. * * @param string $name The name of the table. * @param string $prefix Optional table prefix. * @param array $config Optional configuration array for the table. * * @return \Joomla\CMS\Table\Table The table object * * @since 3.10.0 * @throws \Exception */ public function createTable($name, $prefix = '', array $config = []); } PK1A#]�Ƿٙ�!src/MVC/Factory/ApiMVCFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory to create MVC objects based on a namespace. Note that in an API Application model and table objects will be * created from their administrator counterparts. * * @since 4.0.0 */ final class ApiMVCFactory extends MVCFactory { /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Optional configuration array for the model. * * @return \Joomla\CMS\MVC\Model\ModelInterface The model object * * @since 4.0.0 * @throws \Exception */ public function createModel($name, $prefix = '', array $config = []) { $model = parent::createModel($name, $prefix, $config); if (!$model) { $model = parent::createModel($name, 'Administrator', $config); } return $model; } /** * Method to load and return a table object. * * @param string $name The name of the table. * @param string $prefix Optional table prefix. * @param array $config Optional configuration array for the table. * * @return \Joomla\CMS\Table\Table The table object * * @since 4.0.0 * @throws \Exception */ public function createTable($name, $prefix = '', array $config = []) { $table = parent::createTable($name, $prefix, $config); if (!$table) { $table = parent::createTable($name, 'Administrator', $config); } return $table; } } PK1A#]�x��00src/MVC/Factory/MVCFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Factory; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormFactoryAwareInterface; use Joomla\CMS\Form\FormFactoryAwareTrait; use Joomla\CMS\Mail\MailerFactoryAwareInterface; use Joomla\CMS\Mail\MailerFactoryAwareTrait; use Joomla\CMS\MVC\Model\ModelInterface; use Joomla\CMS\Router\SiteRouterAwareInterface; use Joomla\CMS\Router\SiteRouterAwareTrait; use Joomla\CMS\User\UserFactoryAwareInterface; use Joomla\CMS\User\UserFactoryAwareTrait; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory to create MVC objects based on a namespace. * * @since 3.10.0 */ class MVCFactory implements MVCFactoryInterface, FormFactoryAwareInterface, SiteRouterAwareInterface, UserFactoryAwareInterface, MailerFactoryAwareInterface { use FormFactoryAwareTrait; use DispatcherAwareTrait; use DatabaseAwareTrait; use SiteRouterAwareTrait; use CacheControllerFactoryAwareTrait; use UserFactoryAwareTrait; use MailerFactoryAwareTrait; /** * The namespace to create the objects from. * * @var string * @since 4.0.0 */ private $namespace; /** * The namespace must be like: * Joomla\Component\Content * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct($namespace) { $this->namespace = $namespace; } /** * Method to load and return a controller object. * * @param string $name The name of the controller * @param string $prefix The controller prefix * @param array $config The configuration array for the controller * @param CMSApplicationInterface $app The app * @param Input $input The input * * @return \Joomla\CMS\MVC\Controller\ControllerInterface * * @since 3.10.0 * @throws \Exception */ public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input) { // Clean the parameters $name = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); $className = $this->getClassName('Controller\\' . ucfirst($name) . 'Controller', $prefix); if (!$className) { return null; } $controller = new $className($config, $this, $app, $input); $this->setFormFactoryOnObject($controller); $this->setDispatcherOnObject($controller); $this->setRouterOnObject($controller); $this->setCacheControllerOnObject($controller); $this->setUserFactoryOnObject($controller); $this->setMailerFactoryOnObject($controller); return $controller; } /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Optional configuration array for the model. * * @return ModelInterface The model object * * @since 3.10.0 * @throws \Exception */ public function createModel($name, $prefix = '', array $config = []) { // Clean the parameters $name = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); if (!$prefix) { @trigger_error( sprintf( 'Calling %s() without a prefix is deprecated.', __METHOD__ ), E_USER_DEPRECATED ); $prefix = Factory::getApplication()->getName(); } $className = $this->getClassName('Model\\' . ucfirst($name) . 'Model', $prefix); if (!$className) { return null; } $model = new $className($config, $this); $this->setFormFactoryOnObject($model); $this->setDispatcherOnObject($model); $this->setRouterOnObject($model); $this->setCacheControllerOnObject($model); $this->setUserFactoryOnObject($model); $this->setMailerFactoryOnObject($model); if ($model instanceof DatabaseAwareInterface) { try { $model->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $model->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } } return $model; } /** * Method to load and return a view object. * * @param string $name The name of the view. * @param string $prefix Optional view prefix. * @param string $type Optional type of view. * @param array $config Optional configuration array for the view. * * @return \Joomla\CMS\MVC\View\ViewInterface The view object * * @since 3.10.0 * @throws \Exception */ public function createView($name, $prefix = '', $type = '', array $config = []) { // Clean the parameters $name = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); $type = preg_replace('/[^A-Z0-9_]/i', '', $type); if (!$prefix) { @trigger_error( sprintf( 'Calling %s() without a prefix is deprecated.', __METHOD__ ), E_USER_DEPRECATED ); $prefix = Factory::getApplication()->getName(); } $className = $this->getClassName('View\\' . ucfirst($name) . '\\' . ucfirst($type) . 'View', $prefix); if (!$className) { return null; } $view = new $className($config); $this->setFormFactoryOnObject($view); $this->setDispatcherOnObject($view); $this->setRouterOnObject($view); $this->setCacheControllerOnObject($view); $this->setUserFactoryOnObject($view); return $view; } /** * Method to load and return a table object. * * @param string $name The name of the table. * @param string $prefix Optional table prefix. * @param array $config Optional configuration array for the table. * * @return \Joomla\CMS\Table\Table The table object * * @since 3.10.0 * @throws \Exception */ public function createTable($name, $prefix = '', array $config = []) { // Clean the parameters $name = preg_replace('/[^A-Z0-9_]/i', '', $name); $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix); if (!$prefix) { @trigger_error( sprintf( 'Calling %s() without a prefix is deprecated.', __METHOD__ ), E_USER_DEPRECATED ); $prefix = Factory::getApplication()->getName(); } $className = $this->getClassName('Table\\' . ucfirst($name) . 'Table', $prefix) ?: $this->getClassName('Table\\' . ucfirst($name) . 'Table', 'Administrator'); if (!$className) { return null; } try { $db = \array_key_exists('dbo', $config) ? $config['dbo'] : $this->getDatabase(); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); $db = Factory::getContainer()->get(DatabaseInterface::class); } $table = new $className($db); $this->setUserFactoryOnObject($table); return $table; } /** * Returns a standard classname, if the class doesn't exist null is returned. * * @param string $suffix The suffix * @param string $prefix The prefix * * @return string|null The class name * * @since 3.10.0 */ protected function getClassName(string $suffix, string $prefix) { if (!$prefix) { $prefix = Factory::getApplication(); } $className = trim($this->namespace, '\\') . '\\' . ucfirst($prefix) . '\\' . $suffix; if (!class_exists($className)) { return null; } return $className; } /** * Sets the internal form factory on the given object. * * @param object $object The object * * @return void * * @since 4.0.0 */ private function setFormFactoryOnObject($object) { if (!$object instanceof FormFactoryAwareInterface) { return; } try { $object->setFormFactory($this->getFormFactory()); } catch (\UnexpectedValueException $e) { // Ignore it } } /** * Sets the internal event dispatcher on the given object. * * @param object $object The object * * @return void * * @since 4.2.0 */ private function setDispatcherOnObject($object) { if (!$object instanceof DispatcherAwareInterface) { return; } try { $object->setDispatcher($this->getDispatcher()); } catch (\UnexpectedValueException $e) { // Ignore it } } /** * Sets the internal router on the given object. * * @param object $object The object * * @return void * * @since 4.2.0 */ private function setRouterOnObject($object): void { if (!$object instanceof SiteRouterAwareInterface) { return; } try { $object->setSiteRouter($this->getSiteRouter()); } catch (\UnexpectedValueException $e) { // Ignore it } } /** * Sets the internal cache controller on the given object. * * @param object $object The object * * @return void * * @since 4.2.0 */ private function setCacheControllerOnObject($object): void { if (!$object instanceof CacheControllerFactoryAwareInterface) { return; } try { $object->setCacheControllerFactory($this->getCacheControllerFactory()); } catch (\UnexpectedValueException $e) { // Ignore it } } /** * Sets the internal user factory on the given object. * * @param object $object The object * * @return void * * @since 4.4.0 */ private function setUserFactoryOnObject($object): void { if (!$object instanceof UserFactoryAwareInterface) { return; } try { $object->setUserFactory($this->getUserFactory()); } catch (\UnexpectedValueException $e) { // Ignore it } } /** * Sets the internal mailer factory on the given object. * * @param object $object The object * * @return void * * @since 4.4.0 */ private function setMailerFactoryOnObject($object): void { if (!$object instanceof MailerFactoryAwareInterface) { return; } try { $object->setMailerFactory($this->getMailerFactory()); } catch (\UnexpectedValueException $e) { // Ignore it } } } PK1A#]Ӛ-�~~ src/MVC/Model/ModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a base model. * * @since 4.0.0 */ interface ModelInterface { /** * Method to get the model name. * * @return string The name of the model * * @since 4.0.0 * @throws \Exception */ public function getName(); } PK1A#]��Δ--#src/MVC/Model/FormBehaviorTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Form\FormField; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which supports form behavior. * * @since 4.0.0 */ trait FormBehaviorTrait { /** * Array of form objects. * * @var Form[] * @since 4.0.0 */ protected $_forms = []; /** * Method to get a form object. * * @param string $name The name of the form. * @param string $source The form source. Can be XML string if file flag is set to false. * @param array $options Optional array of options for the form creation. * @param boolean $clear Optional argument to force load a new form. * @param string $xpath An optional xpath to search for the fields. * * @return Form * * @see Form * @since 4.0.0 * @throws \Exception */ protected function loadForm($name, $source = null, $options = [], $clear = false, $xpath = null) { // Handle the optional arguments. $options['control'] = ArrayHelper::getValue((array) $options, 'control', false); // Create a signature hash. But make sure, that loading the data does not create a new instance $sigoptions = $options; if (isset($sigoptions['load_data'])) { unset($sigoptions['load_data']); } $hash = md5($source . serialize($sigoptions)); // Check if we can use a previously loaded form. if (!$clear && isset($this->_forms[$hash])) { return $this->_forms[$hash]; } // Get the form. Form::addFormPath(JPATH_COMPONENT . '/forms'); Form::addFormPath(JPATH_COMPONENT . '/models/forms'); Form::addFieldPath(JPATH_COMPONENT . '/models/fields'); Form::addFormPath(JPATH_COMPONENT . '/model/form'); Form::addFieldPath(JPATH_COMPONENT . '/model/field'); try { $formFactory = $this->getFormFactory(); } catch (\UnexpectedValueException $e) { $formFactory = Factory::getContainer()->get(FormFactoryInterface::class); } $form = $formFactory->createForm($name, $options); // Load the data. if (substr($source, 0, 1) === '<') { if ($form->load($source, false, $xpath) == false) { throw new \RuntimeException('Form::loadForm could not load form'); } } else { if ($form->loadFile($source, false, $xpath) == false) { throw new \RuntimeException('Form::loadForm could not load file'); } } if (isset($options['load_data']) && $options['load_data']) { // Get the data for the form. $data = $this->loadFormData(); } else { $data = []; } // Allow for additional modification of the form, and events to be triggered. // We pass the data because plugins may require it. $this->preprocessForm($form, $data); // Load the data into the form after the plugins have operated. $form->bind($data); // Store the form for later. $this->_forms[$hash] = $form; return $form; } /** * Method to get the data that should be injected in the form. * * @return array The default data is an empty array. * * @since 4.0.0 */ protected function loadFormData() { return []; } /** * Method to allow derived classes to preprocess the data. * * @param string $context The context identifier. * @param mixed &$data The data to be processed. It gets altered directly. * @param string $group The name of the plugin group to import (defaults to "content"). * * @return void * * @since 4.0.0 */ protected function preprocessData($context, &$data, $group = 'content') { // Get the dispatcher and load the users plugins. PluginHelper::importPlugin($group); // Trigger the data preparation event. Factory::getApplication()->triggerEvent('onContentPrepareData', [$context, &$data]); } /** * Method to allow derived classes to preprocess the form. * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * @param string $group The name of the plugin group to import (defaults to "content"). * * @return void * * @see FormField * @since 4.0.0 * @throws \Exception if there is an error in the form event. */ protected function preprocessForm(Form $form, $data, $group = 'content') { // Import the appropriate plugin group. PluginHelper::importPlugin($group); // Trigger the form preparation event. Factory::getApplication()->triggerEvent('onContentPrepareForm', [$form, $data]); } /** * Get the FormFactoryInterface. * * @return FormFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the FormFactory has not been set. */ abstract public function getFormFactory(): FormFactoryInterface; } PK1A#]D(1 (src/MVC/Model/LegacyModelLoaderTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Extension\LegacyComponent; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Factory\MVCFactoryServiceInterface; use Joomla\CMS\Table\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which contains the legacy getInstance functionality * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ trait LegacyModelLoaderTrait { /** * Create the filename for a resource * * @param string $type The resource type to create the filename for. * @param array $parts An associative array of filename information. * * @return string The filename * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ protected static function _createFileName($type, $parts = []) { return $type === 'model' ? strtolower($parts['name']) . '.php' : ''; } /** * Returns a Model object, always creating it * * @param string $type The model type to instantiate * @param string $prefix Prefix for the model class name. Optional. * @param array $config Configuration array for model. Optional. * * @return self|boolean A \JModelLegacy instance or false on failure * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement. Get the model through the MVCFactory instead * Example: Factory::getApplication()->bootComponent('com_xxx')->getMVCFactory()->createModel($type, $prefix, $config); */ public static function getInstance($type, $prefix = '', $config = []) { @trigger_error( sprintf( '%1$s::getInstance() is deprecated. Load it through the MVC factory.', self::class ), E_USER_DEPRECATED ); $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); if ($model = self::createModelFromComponent($type, $prefix, $config)) { return $model; } $modelClass = $prefix . ucfirst($type); if (!class_exists($modelClass)) { $path = Path::find(self::addIncludePath(null, $prefix), self::_createFileName('model', ['name' => $type])); if (!$path) { $path = Path::find(self::addIncludePath(null, ''), self::_createFileName('model', ['name' => $type])); } if (!$path) { return false; } require_once $path; if (!class_exists($modelClass)) { Log::add(Text::sprintf('JLIB_APPLICATION_ERROR_MODELCLASS_NOT_FOUND', $modelClass), Log::WARNING, 'jerror'); return false; } } return new $modelClass($config); } /** * Adds to the stack of model table paths in LIFO order. * * @param mixed $path The directory as a string or directories as an array to add. * * @return void * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement. Get the model through the MVCFactory instead */ public static function addTablePath($path) { Table::addIncludePath($path); } /** * Returns a Model object by loading the component from the prefix. * * @param string $type The model type to instantiate * @param string $prefix Prefix for the model class name. Optional. * @param array $config Configuration array for model. Optional. * * @return ModelInterface|null A ModelInterface instance or null on failure * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ private static function createModelFromComponent($type, $prefix = '', $config = []): ?ModelInterface { // Do nothing when prefix is not given if (!$prefix) { return null; } // Boot the component $componentName = 'com_' . str_replace('model', '', strtolower($prefix)); $component = Factory::getApplication()->bootComponent($componentName); // When it is a legacy component or not a MVCFactoryService then ignore if ($component instanceof LegacyComponent || !$component instanceof MVCFactoryServiceInterface) { return null; } // Setup the client $client = Factory::getApplication()->getName(); // Detect the client based on the include paths $adminPath = Path::clean(JPATH_ADMINISTRATOR . '/components/' . $componentName); $sitePath = Path::clean(JPATH_SITE . '/components/' . $componentName); foreach (self::addIncludePath() as $path) { if (strpos($path, $adminPath) !== false) { $client = 'Administrator'; break; } if (strpos($path, $sitePath) !== false) { $client = 'Site'; break; } } // Create the model $model = $component->getMVCFactory()->createModel($type, $client, $config); // When the model can't be loaded, then return null if (!$model) { return null; } // Return the model instance return $model; } } PK1A#]�X� � $src/MVC/Model/StateBehaviorTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which supports state behavior * * @since 4.0.0 */ trait StateBehaviorTrait { /** * Indicates if the internal state has been set * * @var boolean * @since 4.0.0 */ protected $__state_set = null; /** * A state object * * @var CMSObject * @since 4.0.0 */ protected $state = null; /** * Method to get state variables. * * @param string $property Optional parameter name * @param mixed $default Optional default value * * @return mixed The property where specified, the state object where omitted * * @since 4.0.0 */ public function getState($property = null, $default = null) { if ($this->state === null) { $this->state = new CMSObject(); } if (!$this->__state_set) { // Protected method to auto-populate the state $this->populateState(); // Set the state set flag to true. $this->__state_set = true; } return $property === null ? $this->state : $this->state->get($property, $default); } /** * Method to set state variables. * * @param string $property The name of the property * @param mixed $value The value of the property to set or null * * @return mixed The previous value of the property or null if not set * * @since 4.0.0 */ public function setState($property, $value = null) { if ($this->state === null) { $this->state = new CMSObject(); } return $this->state->set($property, $value); } /** * Method to auto-populate the state. * * This method should only be called once per instantiation and is designed * to be called on the first call to the getState() method unless the * configuration flag to ignore the request is set. * * @return void * * @note Calling getState in this method will result in recursion. * @since 4.0.0 */ protected function populateState() { } } PK1A#]�o����src/MVC/Model/AdminModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Event\Model\BeforeBatchEvent; use Joomla\CMS\Factory; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Language\Associations; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Route; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\Tag\TaggableTableInterface; use Joomla\CMS\UCM\UCMType; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Prototype admin model. * * @since 1.6 */ abstract class AdminModel extends FormModel { /** * The type alias for this content type (for example, 'com_content.article'). * * @var string * @since 3.8.6 */ public $typeAlias; /** * The prefix to use with controller messages. * * @var string * @since 1.6 */ protected $text_prefix = null; /** * The event to trigger after deleting the data. * * @var string * @since 1.6 */ protected $event_after_delete = null; /** * The event to trigger after saving the data. * * @var string * @since 1.6 */ protected $event_after_save = null; /** * The event to trigger before deleting the data. * * @var string * @since 1.6 */ protected $event_before_delete = null; /** * The event to trigger before saving the data. * * @var string * @since 1.6 */ protected $event_before_save = null; /** * The event to trigger before changing the published state of the data. * * @var string * @since 4.0.0 */ protected $event_before_change_state = null; /** * The event to trigger after changing the published state of the data. * * @var string * @since 1.6 */ protected $event_change_state = null; /** * The event to trigger before batch. * * @var string * @since 4.0.0 */ protected $event_before_batch = null; /** * Batch copy/move command. If set to false, * the batch copy/move command is not supported * * @var string * @since 3.4 */ protected $batch_copymove = 'category_id'; /** * Allowed batch commands * * @var array * @since 3.4 */ protected $batch_commands = [ 'assetgroup_id' => 'batchAccess', 'language_id' => 'batchLanguage', 'tag' => 'batchTag', ]; /** * The context used for the associations table * * @var string * @since 3.4.4 */ protected $associationsContext = null; /** * A flag to indicate if member variables for batch actions (and saveorder) have been initialized * * @var ?bool * @since 3.8.2 */ protected $batchSet = null; /** * The user performing the actions (re-usable in batch methods & saveorder(), initialized via initBatch()) * * @var object * @since 3.8.2 */ protected $user = null; /** * A JTable instance (of appropriate type) to manage the DB records (re-usable in batch methods & saveorder(), initialized via initBatch()) * * @var Table * @since 3.8.2 */ protected $table = null; /** * The class name of the JTable instance managing the DB records (re-usable in batch methods & saveorder(), initialized via initBatch()) * * @var string * @since 3.8.2 */ protected $tableClassName = null; /** * UCM Type corresponding to the current model class (re-usable in batch action methods, initialized via initBatch()) * * @var object * @since 3.8.2 */ protected $contentType = null; /** * DB data of UCM Type corresponding to the current model class (re-usable in batch action methods, initialized via initBatch()) * * @var object * @since 3.8.2 */ protected $type = null; /** * Constructor. * * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). * @param ?MVCFactoryInterface $factory The factory. * @param ?FormFactoryInterface $formFactory The form factory. * * @since 1.6 * @throws \Exception */ public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) { parent::__construct($config, $factory, $formFactory); if (isset($config['event_after_delete'])) { $this->event_after_delete = $config['event_after_delete']; } elseif (empty($this->event_after_delete)) { $this->event_after_delete = 'onContentAfterDelete'; } if (isset($config['event_after_save'])) { $this->event_after_save = $config['event_after_save']; } elseif (empty($this->event_after_save)) { $this->event_after_save = 'onContentAfterSave'; } if (isset($config['event_before_delete'])) { $this->event_before_delete = $config['event_before_delete']; } elseif (empty($this->event_before_delete)) { $this->event_before_delete = 'onContentBeforeDelete'; } if (isset($config['event_before_save'])) { $this->event_before_save = $config['event_before_save']; } elseif (empty($this->event_before_save)) { $this->event_before_save = 'onContentBeforeSave'; } if (isset($config['event_before_change_state'])) { $this->event_before_change_state = $config['event_before_change_state']; } elseif (empty($this->event_before_change_state)) { $this->event_before_change_state = 'onContentBeforeChangeState'; } if (isset($config['event_change_state'])) { $this->event_change_state = $config['event_change_state']; } elseif (empty($this->event_change_state)) { $this->event_change_state = 'onContentChangeState'; } if (isset($config['event_before_batch'])) { $this->event_before_batch = $config['event_before_batch']; } elseif (empty($this->event_before_batch)) { $this->event_before_batch = 'onBeforeBatch'; } $config['events_map'] = $config['events_map'] ?? []; $this->events_map = array_merge( [ 'delete' => 'content', 'save' => 'content', 'change_state' => 'content', 'validate' => 'content', ], $config['events_map'] ); // Guess the \Text message prefix. Defaults to the option. if (isset($config['text_prefix'])) { $this->text_prefix = strtoupper($config['text_prefix']); } elseif (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } } /** * Method to perform batch operations on an item or a set of items. * * @param array $commands An array of commands to perform. * @param array $pks An array of item ids. * @param array $contexts An array of item contexts. * * @return boolean Returns true on success, false on failure. * * @since 1.7 */ public function batch($commands, $pks, $contexts) { // Sanitize ids. $pks = array_unique($pks); $pks = ArrayHelper::toInteger($pks); // Remove any values of zero. if (array_search(0, $pks, true)) { unset($pks[array_search(0, $pks, true)]); } if (empty($pks)) { $this->setError(Text::_('JGLOBAL_NO_ITEM_SELECTED')); return false; } $done = false; // Initialize re-usable member properties $this->initBatch(); if ($this->batch_copymove && !empty($commands[$this->batch_copymove])) { $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); if ($cmd === 'c') { $result = $this->batchCopy($commands[$this->batch_copymove], $pks, $contexts); if (\is_array($result)) { foreach ($result as $old => $new) { $contexts[$new] = $contexts[$old]; } $pks = array_values($result); } else { return false; } } elseif ($cmd === 'm' && !$this->batchMove($commands[$this->batch_copymove], $pks, $contexts)) { return false; } $done = true; } foreach ($this->batch_commands as $identifier => $command) { if (!empty($commands[$identifier])) { if (!$this->$command($commands[$identifier], $pks, $contexts)) { return false; } $done = true; } } if (!$done) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); return false; } // Clear the cache $this->cleanCache(); return true; } /** * Batch access level changes for a group of rows. * * @param integer $value The new value matching an Asset Group ID. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 1.7 */ protected function batchAccess($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $this->table->access = (int) $value; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'access'] ); $this->dispatchEvent($event); // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } } else { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch copy items to a new category or current. * * @param integer $value The new category. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return array|boolean An array of new IDs on success, boolean false on failure. * * @since 1.7 */ protected function batchCopy($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $categoryId = $value; if (!$this->checkCategoryId($categoryId)) { return false; } $newIds = []; $db = $this->getDbo(); // Parent exists so let's proceed while (!empty($pks)) { // Pop the first ID off the stack $pk = array_shift($pks); $this->table->reset(); // Check that the row actually exists if (!$this->table->load($pk)) { if ($error = $this->table->getError()) { // Fatal error $this->setError($error); return false; } else { // Not fatal error $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); continue; } } // Check for asset_id if ($this->table->hasField($this->table->getColumnAlias('asset_id'))) { $oldAssetId = $this->table->asset_id; } $this->generateTitle($categoryId, $this->table); // Reset the ID because we are making a copy $this->table->id = 0; // Unpublish because we are making a copy if (isset($this->table->published)) { $this->table->published = 0; } elseif (isset($this->table->state)) { $this->table->state = 0; } $hitsAlias = $this->table->getColumnAlias('hits'); if (isset($this->table->$hitsAlias)) { $this->table->$hitsAlias = 0; } // New category ID $this->table->catid = $categoryId; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'copy'] ); $this->dispatchEvent($event); // @todo: Deal with ordering? // $this->table->ordering = 1; // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } // Store the row. if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } // Get the new item ID $newId = $this->table->get('id'); if (!empty($oldAssetId)) { $dbType = strtolower($db->getServerType()); // Copy rules $query = $db->getQuery(true); $query->clear() ->update($db->quoteName('#__assets', 't')); if ($dbType === 'mysql') { $query->set($db->quoteName('t.rules') . ' = ' . $db->quoteName('s.rules')); } else { $query->set($db->quoteName('rules') . ' = ' . $db->quoteName('s.rules')); } $query->join( 'INNER', $db->quoteName('#__assets', 's'), $db->quoteName('s.id') . ' = :oldassetid' ) ->where($db->quoteName('t.id') . ' = :assetid') ->bind(':oldassetid', $oldAssetId, ParameterType::INTEGER) ->bind(':assetid', $this->table->asset_id, ParameterType::INTEGER); $db->setQuery($query)->execute(); } $this->cleanupPostBatchCopy($this->table, $newId, $pk); // Add the new ID to the array $newIds[$pk] = $newId; } // Clean the cache $this->cleanCache(); return $newIds; } /** * Function that can be overridden to do any data cleanup after batch copying data * * @param TableInterface $table The table object containing the newly created item * @param integer $newId The id of the new item * @param integer $oldId The original item id * * @return void * * @since 3.8.12 */ protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId) { } /** * Batch language changes for a group of rows. * * @param string $value The new value matching a language. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 2.5 */ protected function batchLanguage($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $this->table->language = $value; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'language'] ); $this->dispatchEvent($event); // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } } else { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch move items to a new category * * @param integer $value The new category ID. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 1.7 */ protected function batchMove($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $categoryId = (int) $value; if (!$this->checkCategoryId($categoryId)) { return false; } // Parent exists so we proceed foreach ($pks as $pk) { if (!$this->user->authorise('core.edit', $contexts[$pk])) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } // Check that the row actually exists if (!$this->table->load($pk)) { if ($error = $this->table->getError()) { // Fatal error $this->setError($error); return false; } else { // Not fatal error $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); continue; } } // Set the new category ID $this->table->catid = $categoryId; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'move'] ); $this->dispatchEvent($event); // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } // Store the row. if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch tag a list of item. * * @param integer $value The value of the new tag. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 3.1 */ protected function batchTag($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $tags = [$value]; foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $setTagsEvent = \Joomla\CMS\Event\AbstractEvent::create( 'onTableSetNewTags', [ 'subject' => $this->table, 'newTags' => $tags, 'replaceTags' => false, ] ); try { $this->table->getDispatcher()->dispatch('onTableSetNewTags', $setTagsEvent); } catch (\RuntimeException $e) { $this->setError($e->getMessage()); return false; } } else { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Method to test whether a record can be deleted. * * @param object $record A record object. * * @return boolean True if allowed to delete the record. Defaults to the permission for the component. * * @since 1.6 */ protected function canDelete($record) { return Factory::getUser()->authorise('core.delete', $this->option); } /** * Method to test whether a record can have its state changed. * * @param object $record A record object. * * @return boolean True if allowed to change the state of the record. Defaults to the permission for the component. * * @since 1.6 */ protected function canEditState($record) { return Factory::getUser()->authorise('core.edit.state', $this->option); } /** * Method override to check-in a record or an array of record * * @param mixed $pks The ID of the primary key or an array of IDs * * @return integer|boolean Boolean false if there is an error, otherwise the count of records checked in. * * @since 1.6 */ public function checkin($pks = []) { $pks = (array) $pks; $table = $this->getTable(); $count = 0; if (empty($pks)) { $pks = [(int) $this->getState($this->getName() . '.id')]; } $checkedOutField = $table->getColumnAlias('checked_out'); // Check in all items. foreach ($pks as $pk) { if ($table->load($pk)) { if ($table->{$checkedOutField} > 0) { if (!parent::checkin($pk)) { return false; } $count++; } } else { $this->setError($table->getError()); return false; } } return $count; } /** * Method override to check-out a record. * * @param integer $pk The ID of the primary key. * * @return boolean True if successful, false if an error occurs. * * @since 1.6 */ public function checkout($pk = null) { $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id'); return parent::checkout($pk); } /** * Method to delete one or more records. * * @param array &$pks An array of record primary keys. * * @return boolean True if successful, false if an error occurs. * * @since 1.6 */ public function delete(&$pks) { $pks = ArrayHelper::toInteger((array) $pks); $table = $this->getTable(); // Include the plugins for the delete events. PluginHelper::importPlugin($this->events_map['delete']); // Iterate the items to delete each one. foreach ($pks as $i => $pk) { if ($table->load($pk)) { if ($this->canDelete($table)) { $context = $this->option . '.' . $this->name; // Trigger the before delete event. $result = Factory::getApplication()->triggerEvent($this->event_before_delete, [$context, $table]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Multilanguage: if associated, delete the item in the _associations table if ($this->associationsContext && Associations::isEnabled()) { $db = $this->getDbo(); $query = $db->getQuery(true) ->select( [ 'COUNT(*) AS ' . $db->quoteName('count'), $db->quoteName('as1.key'), ] ) ->from($db->quoteName('#__associations', 'as1')) ->join('LEFT', $db->quoteName('#__associations', 'as2'), $db->quoteName('as1.key') . ' = ' . $db->quoteName('as2.key')) ->where( [ $db->quoteName('as1.context') . ' = :context', $db->quoteName('as1.id') . ' = :pk', ] ) ->bind(':context', $this->associationsContext) ->bind(':pk', $pk, ParameterType::INTEGER) ->group($db->quoteName('as1.key')); $db->setQuery($query); $row = $db->loadAssoc(); if (!empty($row['count'])) { $query = $db->getQuery(true) ->delete($db->quoteName('#__associations')) ->where( [ $db->quoteName('context') . ' = :context', $db->quoteName('key') . ' = :key', ] ) ->bind(':context', $this->associationsContext) ->bind(':key', $row['key']); if ($row['count'] > 2) { $query->where($db->quoteName('id') . ' = :pk') ->bind(':pk', $pk, ParameterType::INTEGER); } $db->setQuery($query); $db->execute(); } } if (!$table->delete($pk)) { $this->setError($table->getError()); return false; } // Trigger the after event. Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]); } else { // Prune items that you can't change. unset($pks[$i]); $error = $this->getError(); if ($error) { Log::add($error, Log::WARNING, 'jerror'); return false; } else { Log::add(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), Log::WARNING, 'jerror'); return false; } } } else { $this->setError($table->getError()); return false; } } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to change the title & alias. * * @param integer $categoryId The id of the category. * @param string $alias The alias. * @param string $title The title. * * @return array Contains the modified title and alias. * * @since 1.7 */ protected function generateNewTitle($categoryId, $alias, $title) { // Alter the title & alias $table = $this->getTable(); $aliasField = $table->getColumnAlias('alias'); $catidField = $table->getColumnAlias('catid'); $titleField = $table->getColumnAlias('title'); while ($table->load([$aliasField => $alias, $catidField => $categoryId])) { if ($title === $table->$titleField) { $title = StringHelper::increment($title); } $alias = StringHelper::increment($alias, 'dash'); } return [$title, $alias]; } /** * Method to get a single record. * * @param integer $pk The id of the primary key. * * @return CMSObject|boolean Object on success, false on failure. * * @since 1.6 */ public function getItem($pk = null) { $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id'); $table = $this->getTable(); if ($pk > 0) { // Attempt to load the row. $return = $table->load($pk); // Check for a table object error. if ($return === false) { // If there was no underlying error, then the false means there simply was not a row in the db for this $pk. if (!$table->getError()) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST')); } else { $this->setError($table->getError()); } return false; } } // Convert to the CMSObject before adding other data. $properties = $table->getProperties(1); $item = ArrayHelper::toObject($properties, CMSObject::class); if (property_exists($item, 'params')) { $registry = new Registry($item->params); $item->params = $registry->toArray(); } return $item; } /** * A protected method to get a set of ordering conditions. * * @param Table $table A Table object. * * @return string[] An array of conditions to add to ordering queries. * * @since 1.6 */ protected function getReorderConditions($table) { return []; } /** * Stock method to auto-populate the model state. * * @return void * * @since 1.6 */ protected function populateState() { $table = $this->getTable(); $key = $table->getKeyName(); // Get the pk of the record from the request. $pk = Factory::getApplication()->getInput()->getInt($key); $this->setState($this->getName() . '.id', $pk); // Load the parameters. $value = ComponentHelper::getParams($this->option); $this->setState('params', $value); } /** * Prepare and sanitise the table data prior to saving. * * @param Table $table A reference to a Table object. * * @return void * * @since 1.6 */ protected function prepareTable($table) { // Derived class will provide its own implementation if required. } /** * Method to change the published state of one or more records. * * @param array &$pks A list of the primary keys to change. * @param integer $value The value of the published state. * * @return boolean True on success. * * @since 1.6 */ public function publish(&$pks, $value = 1) { $user = Factory::getUser(); $table = $this->getTable(); $pks = (array) $pks; $context = $this->option . '.' . $this->name; // Include the plugins for the change of state event. PluginHelper::importPlugin($this->events_map['change_state']); // Access checks. foreach ($pks as $i => $pk) { $table->reset(); if ($table->load($pk)) { if (!$this->canEditState($table)) { // Prune items that you can't change. unset($pks[$i]); Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror'); return false; } // If the table is checked out by another user, drop it and report to the user trying to change its state. if ($table->hasField('checked_out') && $table->checked_out && ($table->checked_out != $user->id)) { Log::add(Text::_('JLIB_APPLICATION_ERROR_CHECKIN_USER_MISMATCH'), Log::WARNING, 'jerror'); // Prune items that you can't change. unset($pks[$i]); return false; } /** * Prune items that are already at the given state. Note: Only models whose table correctly * sets 'published' column alias (if different than published) will benefit from this */ $publishedColumnName = $table->getColumnAlias('published'); if (property_exists($table, $publishedColumnName) && $table->get($publishedColumnName, $value) == $value) { unset($pks[$i]); } } } // Check if there are items to change if (!\count($pks)) { return true; } // Trigger the before change state event. $result = Factory::getApplication()->triggerEvent($this->event_before_change_state, [$context, $pks, $value]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Attempt to change the state of the records. if (!$table->publish($pks, $value, $user->get('id'))) { $this->setError($table->getError()); return false; } // Trigger the change state event. $result = Factory::getApplication()->triggerEvent($this->event_change_state, [$context, $pks, $value]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to adjust the ordering of a row. * * Returns NULL if the user did not have edit * privileges for any of the selected primary keys. * * @param integer $pks The ID of the primary key to move. * @param integer $delta Increment, usually +1 or -1 * * @return boolean|null False on failure or error, true on success, null if the $pk is empty (no items selected). * * @since 1.6 */ public function reorder($pks, $delta = 0) { $table = $this->getTable(); $pks = (array) $pks; $result = true; $allowed = true; foreach ($pks as $i => $pk) { $table->reset(); if ($table->load($pk) && $this->checkout($pk)) { // Access checks. if (!$this->canEditState($table)) { // Prune items that you can't change. unset($pks[$i]); $this->checkin($pk); Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror'); $allowed = false; continue; } $where = $this->getReorderConditions($table); if (!$table->move($delta, $where)) { $this->setError($table->getError()); unset($pks[$i]); $result = false; } $this->checkin($pk); } else { $this->setError($table->getError()); unset($pks[$i]); $result = false; } } if ($allowed === false && empty($pks)) { $result = null; } // Clear the component's cache if ($result == true) { $this->cleanCache(); } return $result; } /** * Method to save the form data. * * @param array $data The form data. * * @return boolean True on success, False on error. * * @since 1.6 */ public function save($data) { $table = $this->getTable(); $context = $this->option . '.' . $this->name; $app = Factory::getApplication(); if (\array_key_exists('tags', $data) && \is_array($data['tags'])) { $table->newTags = $data['tags']; } $key = $table->getKeyName(); $pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id'); $isNew = true; // Include the plugins for the save events. PluginHelper::importPlugin($this->events_map['save']); // Allow an exception to be thrown. try { // Load the row if saving an existing record. if ($pk > 0) { $table->load($pk); $isNew = false; } // Bind the data. if (!$table->bind($data)) { $this->setError($table->getError()); return false; } // Prepare the row for saving $this->prepareTable($table); // Check the data. if (!$table->check()) { $this->setError($table->getError()); return false; } // Trigger the before save event. $result = $app->triggerEvent($this->event_before_save, [$context, $table, $isNew, $data]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Store the data. if (!$table->store()) { $this->setError($table->getError()); return false; } // Clean the cache. $this->cleanCache(); // Trigger the after save event. $app->triggerEvent($this->event_after_save, [$context, $table, $isNew, $data]); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } if (isset($table->$key)) { $this->setState($this->getName() . '.id', $table->$key); } $this->setState($this->getName() . '.new', $isNew); if ($this->associationsContext && Associations::isEnabled() && !empty($data['associations'])) { $associations = $data['associations']; // Unset any invalid associations $associations = ArrayHelper::toInteger($associations); // Unset any invalid associations foreach ($associations as $tag => $id) { if (!$id) { unset($associations[$tag]); } } // Show a warning if the item isn't assigned to a language but we have associations. if ($associations && $table->language === '*') { $app->enqueueMessage( Text::_(strtoupper($this->option) . '_ERROR_ALL_LANGUAGE_ASSOCIATED'), 'warning' ); } // Get associationskey for edited item $db = $this->getDbo(); $id = (int) $table->$key; $query = $db->getQuery(true) ->select($db->quoteName('key')) ->from($db->quoteName('#__associations')) ->where($db->quoteName('context') . ' = :context') ->where($db->quoteName('id') . ' = :id') ->bind(':context', $this->associationsContext) ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); $oldKey = $db->loadResult(); if ($associations || $oldKey !== null) { // Deleting old associations for the associated items $query = $db->getQuery(true) ->delete($db->quoteName('#__associations')) ->where($db->quoteName('context') . ' = :context') ->bind(':context', $this->associationsContext); $where = []; if ($associations) { $where[] = $db->quoteName('id') . ' IN (' . implode(',', $query->bindArray(array_values($associations))) . ')'; } if ($oldKey !== null) { $where[] = $db->quoteName('key') . ' = :oldKey'; $query->bind(':oldKey', $oldKey); } $query->extendWhere('AND', $where, 'OR'); $db->setQuery($query); $db->execute(); } // Adding self to the association if ($table->language !== '*') { $associations[$table->language] = (int) $table->$key; } if (\count($associations) > 1) { // Adding new association for these items $key = md5(json_encode($associations)); $query = $db->getQuery(true) ->insert($db->quoteName('#__associations')) ->columns( [ $db->quoteName('id'), $db->quoteName('context'), $db->quoteName('key'), ] ); foreach ($associations as $id) { $query->values( implode( ',', $query->bindArray( [$id, $this->associationsContext, $key], [ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING] ) ) ); } $db->setQuery($query); $db->execute(); } } if ($app->getInput()->get('task') == 'editAssociations') { return $this->redirectToAssociations($data); } return true; } /** * Saves the manually set order of records. * * @param array $pks An array of primary key ids. * @param integer $order +1 or -1 * * @return boolean Boolean true on success, false on failure * * @since 1.6 */ public function saveorder($pks = [], $order = null) { // Initialize re-usable member properties $this->initBatch(); $conditions = []; if (empty($pks)) { Factory::getApplication()->enqueueMessage(Text::_($this->text_prefix . '_ERROR_NO_ITEMS_SELECTED'), 'error'); return false; } $orderingField = $this->table->getColumnAlias('ordering'); // Update ordering values foreach ($pks as $i => $pk) { $this->table->load((int) $pk); // We don't want to modify tags on reorder, not removing the tagsHelper removes all associated tags if ($this->table instanceof TaggableTableInterface) { $this->table->clearTagsHelper(); } // Access checks. if (!$this->canEditState($this->table)) { // Prune items that you can't change. unset($pks[$i]); Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror'); } elseif ($this->table->$orderingField != $order[$i]) { $this->table->$orderingField = $order[$i]; if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } // Remember to reorder within position and client_id $condition = $this->getReorderConditions($this->table); $found = false; foreach ($conditions as $cond) { if ($cond[1] == $condition) { $found = true; break; } } if (!$found) { $key = $this->table->getKeyName(); $conditions[] = [$this->table->$key, $condition]; } } } // Execute reorder for each category. foreach ($conditions as $cond) { $this->table->load($cond[0]); $this->table->reorder($cond[1]); } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to check the validity of the category ID for batch copy and move * * @param integer $categoryId The category ID to check * * @return boolean * * @since 3.2 */ protected function checkCategoryId($categoryId) { // Check that the category exists if ($categoryId) { $categoryTable = Table::getInstance('Category'); if (!$categoryTable->load($categoryId)) { if ($error = $categoryTable->getError()) { // Fatal error $this->setError($error); return false; } else { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } } } if (empty($categoryId)) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } // Check that the user has create permission for the component $extension = Factory::getApplication()->getInput()->get('option', ''); $user = Factory::getUser(); if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE')); return false; } return true; } /** * A method to preprocess generating a new title in order to allow tables with alternative names * for alias and title to use the batch move and copy methods * * @param integer $categoryId The target category id * @param Table $table The Table within which move or copy is taking place * * @return void * * @since 3.2 */ public function generateTitle($categoryId, $table) { // Alter the title & alias $titleField = $table->getColumnAlias('title'); $aliasField = $table->getColumnAlias('alias'); $data = $this->generateNewTitle($categoryId, $table->$aliasField, $table->$titleField); $table->$titleField = $data['0']; $table->$aliasField = $data['1']; } /** * Method to initialize member variables used by batch methods and other methods like saveorder() * * @return void * * @since 3.8.2 */ public function initBatch() { if ($this->batchSet === null) { $this->batchSet = true; // Get current user $this->user = Factory::getUser(); // Get table $this->table = $this->getTable(); // Get table class name $tc = explode('\\', \get_class($this->table)); $this->tableClassName = end($tc); // Get UCM Type data $this->contentType = new UCMType(); $this->type = $this->contentType->getTypeByTable($this->tableClassName) ?: $this->contentType->getTypeByAlias($this->typeAlias); } } /** * Method to load an item in com_associations. * * @param array $data The form data. * * @return boolean True if successful, false otherwise. * * @since 3.9.0 * * @deprecated 4.3 will be removed in 6.0 * It is handled by regular save method now. */ public function editAssociations($data) { // Save the item return $this->save($data); } /** * Method to load an item in com_associations. * * @param array $data The form data. * * @return boolean True if successful, false otherwise. * * @throws \Exception * @since 3.9.17 */ protected function redirectToAssociations($data) { $app = Factory::getApplication(); $id = $data['id']; // Deal with categories associations if ($this->text_prefix === 'COM_CATEGORIES') { $extension = $app->getInput()->get('extension', 'com_content'); $this->typeAlias = $extension . '.category'; $component = strtolower($this->text_prefix); $view = 'category'; } else { $aliasArray = explode('.', $this->typeAlias); $component = $aliasArray[0]; $view = $aliasArray[1]; $extension = ''; } // Menu item redirect needs admin client $client = $component === 'com_menus' ? '&client_id=0' : ''; if ($id == 0) { $app->enqueueMessage(Text::_('JGLOBAL_ASSOCIATIONS_NEW_ITEM_WARNING'), 'error'); $app->redirect( Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false) ); return false; } if ($data['language'] === '*') { $app->enqueueMessage(Text::_('JGLOBAL_ASSOC_NOT_POSSIBLE'), 'notice'); $app->redirect( Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false) ); return false; } $languages = LanguageHelper::getContentLanguages([0, 1]); $target = ''; /** * If the site contains only 2 languages and an association exists for the item * load directly the associated target item in the side by side view * otherwise select already the target language */ if (count($languages) === 2) { $lang_code = []; foreach ($languages as $language) { $lang_code[] = $language->lang_code; } $refLang = [$data['language']]; $targetLang = array_diff($lang_code, $refLang); $targetLang = implode(',', $targetLang); $targetId = $data['associations'][$targetLang]; if ($targetId) { $target = '&target=' . $targetLang . '%3A' . $targetId . '%3Aedit'; } else { $target = '&target=' . $targetLang . '%3A0%3Aadd'; } } $app->redirect( Route::_( 'index.php?option=com_associations&view=association&layout=edit&itemtype=' . $this->typeAlias . '&task=association.edit&id=' . $id . $target, false ) ); return true; } } PK1A#]��Y�,�,'src/MVC/Model/WorkflowBehaviorTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Form\Form; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Workflow\Workflow; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which supports state behavior * * @since 4.0.0 */ trait WorkflowBehaviorTrait { /** * The name of the component. * * @var string * @since 4.0.0 */ protected $extension = null; /** * The section of the component. * * @var string * @since 4.0.0 */ protected $section = ''; /** * Is workflow for this component enabled? * * @var boolean * @since 4.0.0 */ protected $workflowEnabled = false; /** * The workflow object * * @var Workflow * @since 4.0.0 */ protected $workflow; /** * Set Up the workflow * * @param string $extension The option and section separated by. * * @return void * * @since 4.0.0 */ public function setUpWorkflow($extension) { $parts = explode('.', $extension); $this->extension = array_shift($parts); if (count($parts)) { $this->section = array_shift($parts); } if (method_exists($this, 'getDatabase')) { $db = $this->getDatabase(); } else { @trigger_error('From 6.0 implementing the getDatabase method will be mandatory.', E_USER_DEPRECATED); $db = Factory::getContainer()->get(DatabaseDriver::class); } $this->workflow = new Workflow($extension, Factory::getApplication(), $db); $params = ComponentHelper::getParams($this->extension); $this->workflowEnabled = $params->get('workflow_enabled'); $this->enableWorkflowBatch(); } /** * Add the workflow batch to the command list. Can be overwritten by the child class * * @return void * * @since 4.0.0 */ protected function enableWorkflowBatch() { // Enable batch if ($this->workflowEnabled && property_exists($this, 'batch_commands')) { $this->batch_commands['workflowstage_id'] = 'batchWorkflowStage'; } } /** * Method to allow derived classes to preprocess the form. * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * * @return void * * @since 4.0.0 * @see FormField */ public function workflowPreprocessForm(Form $form, $data) { $this->addTransitionField($form, $data); if (!$this->workflowEnabled) { return; } // Import the workflow plugin group to allow form manipulation. $this->importWorkflowPlugins(); } /** * Let plugins access stage change events * * @return void * * @since 4.0.0 */ public function workflowBeforeStageChange() { if (!$this->workflowEnabled) { return; } $this->importWorkflowPlugins(); } /** * Preparation of workflow data/plugins * * @return void * * @since 4.0.0 */ public function workflowBeforeSave() { if (!$this->workflowEnabled) { return; } $this->importWorkflowPlugins(); } /** * Executing of relevant workflow methods * * @return void * * @since 4.0.0 */ public function workflowAfterSave($data) { // Regardless if workflow is active or not, we have to set the default stage // So we can work with the workflow, when the user activates it later $id = $this->getState($this->getName() . '.id'); $isNew = $this->getState($this->getName() . '.new'); // We save the first stage if ($isNew) { // We have to add the paths, because it could be called outside of the extension context $path = JPATH_BASE . '/components/' . $this->extension; $path = Path::check($path); Form::addFormPath($path . '/forms'); Form::addFormPath($path . '/models/forms'); Form::addFieldPath($path . '/models/fields'); Form::addFormPath($path . '/model/form'); Form::addFieldPath($path . '/model/field'); $form = $this->getForm(); $stage_id = $this->getStageForNewItem($form, $data); $this->workflow->createAssociation($id, $stage_id); } if (!$this->workflowEnabled) { return; } // Execute transition if (!empty($data['transition'])) { $this->executeTransition([$id], $data['transition']); } } /** * Batch change workflow stage or current. * * @param integer $value The workflow stage ID. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return mixed An array of new IDs on success, boolean false on failure. * * @since 4.0.0 */ public function batchWorkflowStage(int $value, array $pks, array $contexts) { $user = Factory::getApplication()->getIdentity(); $workflow = Factory::getApplication()->bootComponent('com_workflow'); if (!$user->authorise('core.admin', $this->option)) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EXECUTE_TRANSITION')); } // Get workflow stage information $stage = $workflow->getMVCFactory()->createTable('Stage', 'Administrator'); if (empty($value) || !$stage->load($value)) { Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_BATCH_WORKFLOW_STAGE_ROW_NOT_FOUND'), 'error'); return false; } if (empty($pks)) { Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_BATCH_WORKFLOW_STAGE_ROW_NOT_FOUND'), 'error'); return false; } // Update workflow associations return $this->workflow->updateAssociations($pks, $value); } /** * Batch change workflow stage or current. * * @param integer $oldId The ID of the item copied from * @param integer $newId The ID of the new item * * @return null * * @since 4.0.0 */ public function workflowCleanupBatchMove($oldId, $newId) { // Trigger workflow plugins only if enable (will be triggered from parent class) if ($this->workflowEnabled) { $this->importWorkflowPlugins(); } // We always need an association, so create one $table = $this->getTable(); $table->load($newId); $catKey = $table->getColumnAlias('catid'); $stage_id = $this->workflow->getDefaultStageByCategory($table->$catKey); if (empty($stage_id)) { return; } $this->workflow->createAssociation((int) $newId, (int) $stage_id); } /** * Runs transition for item. * * @param array $pks Id of items to execute the transition * @param integer $transitionId Id of transition * * @return boolean * * @since 4.0.0 */ public function executeTransition(array $pks, int $transitionId) { $result = $this->workflow->executeTransition($pks, $transitionId); if (!$result) { $app = Factory::getApplication(); $app->enqueueMessage(Text::_('COM_CONTENT_ERROR_UPDATE_STAGE', $app::MSG_WARNING)); return false; } return true; } /** * Import the Workflow plugins. * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * * @return void */ protected function importWorkflowPlugins() { PluginHelper::importPlugin('workflow'); } /** * Adds a transition field to the form. Can be overwritten by the child class if not needed * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * * @return void * @since 4.0.0 */ protected function addTransitionField(Form $form, $data) { $extension = $this->extension . ($this->section ? '.' . $this->section : ''); $field = new \SimpleXMLElement('<field></field>'); $field->addAttribute('name', 'transition'); $field->addAttribute('type', $this->workflowEnabled ? 'transition' : 'hidden'); $field->addAttribute('label', 'COM_CONTENT_WORKFLOW_STAGE'); $field->addAttribute('extension', $extension); $form->setField($field); $table = $this->getTable(); $key = $table->getKeyName(); $id = isset($data->$key) ? $data->$key : $form->getValue($key); if ($id) { // Transition field $assoc = $this->workflow->getAssociation($id); if (!empty($assoc->stage_id)) { $form->setFieldAttribute('transition', 'workflow_stage', (int) $assoc->stage_id); } } else { $stage_id = $this->getStageForNewItem($form, $data); if (!empty($stage_id)) { $form->setFieldAttribute('transition', 'workflow_stage', (int) $stage_id); } } } /** * Try to load a workflow stage for newly created items * which does not have a workflow assigned yet. If the category is not the * carrier, overwrite it on your model and deliver your own carrier. * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * * @return boolean|integer An integer, holding the stage ID or false * @since 4.0.0 */ protected function getStageForNewItem(Form $form, $data) { $table = $this->getTable(); $hasKey = $table->hasField('catid'); if (!$hasKey) { return false; } $catKey = $table->getColumnAlias('catid'); $field = $form->getField($catKey); if (!$field) { return false; } $catId = isset(((object) $data)->$catKey) ? ((object) $data)->$catKey : $form->getValue($catKey); // Try to get the category from the html code of the field if (empty($catId)) { $catId = $field->getAttribute('default', null); if (!$catId) { // Choose the first category available $catOptions = $field->options; if ($catOptions && !empty($catOptions[0]->value)) { $catId = (int) $catOptions[0]->value; } } } if (empty($catId)) { return false; } return $this->workflow->getDefaultStageByCategory($catId); } } PK1A#]�껑�$src/MVC/Model/ListModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a list model. * * @since 4.0.0 */ interface ListModelInterface { /** * Method to get an array of data items. * * @return mixed An array of data items * * @since 4.0.0 * * @throws \Exception */ public function getItems(); } PK1A#]�bm��3src/MVC/Model/Exception/ModelExceptionInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface that all exceptions stemming from the model should implement for processing by the controller. * It is expected that the controller should catch all exceptions that implement this interface and then * make a decision as to whether the exception can be recovered from or not. * * @since 4.4.0 */ interface ModelExceptionInterface extends \Throwable { } PK1A#]���2�Y�Ysrc/MVC/Model/ListModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFactoryAwareInterface; use Joomla\CMS\Form\FormFactoryAwareTrait; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Pagination\Pagination; use Joomla\Database\DatabaseQuery; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Model class for handling lists of items. * * @since 1.6 */ class ListModel extends BaseDatabaseModel implements FormFactoryAwareInterface, ListModelInterface { use FormBehaviorTrait; use FormFactoryAwareTrait; /** * Internal memory based cache array of data. * * @var array * @since 1.6 */ protected $cache = []; /** * Context string for the model type. This is used to handle uniqueness * when dealing with the getStoreId() method and caching data structures. * * @var string * @since 1.6 */ protected $context = null; /** * Valid filter fields or ordering. * * @var array * @since 1.6 */ protected $filter_fields = []; /** * An internal cache for the last query used. * * @var DatabaseQuery|string * @since 1.6 */ protected $query = []; /** * The cache ID used when last populating $this->query * * @var null|string * @since 3.10.4 */ protected $lastQueryStoreId = null; /** * Name of the filter form to load * * @var string * @since 3.2 */ protected $filterFormName = null; /** * Associated HTML form * * @var string * @since 3.2 */ protected $htmlFormName = 'adminForm'; /** * A list of filter variables to not merge into the model's state * * @var array * @since 3.4.5 * @deprecated 4.0 will be removed in 6.0 * Use $filterForbiddenList instead */ protected $filterBlacklist = []; /** * A list of forbidden filter variables to not merge into the model's state * * @var array * @since 4.0.0 */ protected $filterForbiddenList = []; /** * A list of forbidden variables to not merge into the model's state * * @var array * @since 3.4.5 * @deprecated 4.0 will be removed in 6.0 * Use $listForbiddenList instead */ protected $listBlacklist = ['select']; /** * A list of forbidden variables to not merge into the model's state * * @var array * @since 4.0.0 */ protected $listForbiddenList = ['select']; /** * Constructor * * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). * @param ?MVCFactoryInterface $factory The factory. * * @since 1.6 * @throws \Exception */ public function __construct($config = [], MVCFactoryInterface $factory = null) { parent::__construct($config, $factory); // Add the ordering filtering fields allowed list. if (isset($config['filter_fields'])) { $this->filter_fields = $config['filter_fields']; } // Guess the context as Option.ModelName. if (empty($this->context)) { $this->context = strtolower($this->option . '.' . $this->getName()); } /** * @deprecated 4.0 will be removed in 6.0 * Use $this->filterForbiddenList instead */ if (!empty($this->filterBlacklist)) { $this->filterForbiddenList = array_merge($this->filterBlacklist, $this->filterForbiddenList); } /** * @deprecated 4.0 will be removed in 6.0 * Use $this->listForbiddenList instead */ if (!empty($this->listBlacklist)) { $this->listForbiddenList = array_merge($this->listBlacklist, $this->listForbiddenList); } } /** * Provide a query to be used to evaluate if this is an Empty State, can be overridden in the model to provide granular control. * * @return DatabaseQuery * * @since 4.0.0 */ protected function getEmptyStateQuery() { $query = clone $this->_getListQuery(); if ($query instanceof DatabaseQuery) { $query->clear('bounded') ->clear('group') ->clear('having') ->clear('join') ->clear('values') ->clear('where'); } return $query; } /** * Is this an empty state, I.e: no items of this type regardless of the searched for states. * * @return boolean * * @throws \Exception * * @since 4.0.0 */ public function getIsEmptyState(): bool { return $this->_getListCount($this->getEmptyStateQuery()) === 0; } /** * Method to cache the last query constructed. * * This method ensures that the query is constructed only once for a given state of the model. * * @return DatabaseQuery A DatabaseQuery object * * @since 1.6 */ protected function _getListQuery() { // Compute the current store id. $currentStoreId = $this->getStoreId(); // If the last store id is different from the current, refresh the query. if ($this->lastQueryStoreId !== $currentStoreId || empty($this->query)) { $this->lastQueryStoreId = $currentStoreId; $this->query = $this->getListQuery(); } return $this->query; } /** * Function to get the active filters * * @return array Associative array in the format: array('filter_published' => 0) * * @since 3.2 */ public function getActiveFilters() { $activeFilters = []; if (!empty($this->filter_fields)) { foreach ($this->filter_fields as $filter) { $filterName = 'filter.' . $filter; if (property_exists($this->state, $filterName) && (!empty($this->state->{$filterName}) || is_numeric($this->state->{$filterName}))) { $activeFilters[$filter] = $this->state->get($filterName); } } } return $activeFilters; } /** * Method to get an array of data items. * * @return mixed An array of data items on success, false on failure. * * @since 1.6 */ public function getItems() { // Get a storage key. $store = $this->getStoreId(); // Try to load the data from internal storage. if (isset($this->cache[$store])) { return $this->cache[$store]; } try { // Load the list items and add the items to the internal cache. $this->cache[$store] = $this->_getList($this->_getListQuery(), $this->getStart(), $this->getState('list.limit')); } catch (\RuntimeException $e) { $this->setError($e->getMessage()); return false; } return $this->cache[$store]; } /** * Method to get a DatabaseQuery object for retrieving the data set from a database. * * @return DatabaseQuery|string A DatabaseQuery object to retrieve the data set. * * @since 1.6 */ protected function getListQuery() { return $this->getDbo()->getQuery(true); } /** * Method to get a \JPagination object for the data set. * * @return Pagination A Pagination object for the data set. * * @since 1.6 */ public function getPagination() { // Get a storage key. $store = $this->getStoreId('getPagination'); // Try to load the data from internal storage. if (isset($this->cache[$store])) { return $this->cache[$store]; } $limit = (int) $this->getState('list.limit') - (int) $this->getState('list.links'); // Create the pagination object and add the object to the internal cache. $this->cache[$store] = new Pagination($this->getTotal(), $this->getStart(), $limit); return $this->cache[$store]; } /** * Method to get a store id based on the model configuration state. * * This is necessary because the model is used by the component and * different modules that might need different sets of data or different * ordering requirements. * * @param string $id An identifier string to generate the store id. * * @return string A store id. * * @since 1.6 */ protected function getStoreId($id = '') { // Add the list state to the store id. $id .= ':' . $this->getState('list.start'); $id .= ':' . $this->getState('list.limit'); $id .= ':' . $this->getState('list.ordering'); $id .= ':' . $this->getState('list.direction'); return md5($this->context . ':' . $id); } /** * Method to get the total number of items for the data set. * * @return integer The total number of items available in the data set. * * @since 1.6 */ public function getTotal() { // Get a storage key. $store = $this->getStoreId('getTotal'); // Try to load the data from internal storage. if (isset($this->cache[$store])) { return $this->cache[$store]; } try { // Load the total and add the total to the internal cache. $this->cache[$store] = (int) $this->_getListCount($this->_getListQuery()); } catch (\RuntimeException $e) { $this->setError($e->getMessage()); return false; } return $this->cache[$store]; } /** * Method to get the starting number of items for the data set. * * @return integer The starting number of items available in the data set. * * @since 1.6 */ public function getStart() { $store = $this->getStoreId('getstart'); // Try to load the data from internal storage. if (isset($this->cache[$store])) { return $this->cache[$store]; } $start = $this->getState('list.start'); if ($start > 0) { $limit = $this->getState('list.limit'); $total = $this->getTotal(); if ($start > $total - $limit) { $start = max(0, (int) (ceil($total / $limit) - 1) * $limit); } } // Add the total to the internal cache. $this->cache[$store] = $start; return $this->cache[$store]; } /** * Get the filter form * * @param array $data data * @param boolean $loadData load current data * * @return Form|null The \JForm object or null if the form can't be found * * @since 3.2 */ public function getFilterForm($data = [], $loadData = true) { // Try to locate the filter form automatically. Example: ContentModelArticles => "filter_articles" if (empty($this->filterFormName)) { $classNameParts = explode('Model', \get_called_class()); if (\count($classNameParts) >= 2) { $this->filterFormName = 'filter_' . str_replace('\\', '', strtolower($classNameParts[1])); } } if (empty($this->filterFormName)) { return null; } try { // Get the form. return $this->loadForm($this->context . '.filter', $this->filterFormName, ['control' => '', 'load_data' => $loadData]); } catch (\RuntimeException $e) { } return null; } /** * Method to get the data that should be injected in the form. * * @return mixed The data for the form. * * @since 3.2 */ protected function loadFormData() { // Check the session for previously entered form data. $data = Factory::getApplication()->getUserState($this->context, new \stdClass()); // Pre-fill the list options if (!property_exists($data, 'list')) { $data->list = [ 'direction' => $this->getState('list.direction'), 'limit' => $this->getState('list.limit'), 'ordering' => $this->getState('list.ordering'), 'start' => $this->getState('list.start'), ]; } return $data; } /** * Method to auto-populate the model state. * * This method should only be called once per instantiation and is designed * to be called on the first call to the getState() method unless the model * configuration flag to ignore the request is set. * * Note. Calling getState in this method will result in recursion. * * @param string $ordering An optional ordering field. * @param string $direction An optional direction (asc|desc). * * @return void * * @since 1.6 */ protected function populateState($ordering = null, $direction = null) { // If the context is set, assume that stateful lists are used. if ($this->context) { $app = Factory::getApplication(); $inputFilter = InputFilter::getInstance(); // Receive & set filters if ($filters = $app->getUserStateFromRequest($this->context . '.filter', 'filter', [], 'array')) { foreach ($filters as $name => $value) { // Exclude if forbidden if (!\in_array($name, $this->filterForbiddenList)) { $this->setState('filter.' . $name, $value); } } } $limit = 0; // Receive & set list options if ($list = $app->getUserStateFromRequest($this->context . '.list', 'list', [], 'array')) { foreach ($list as $name => $value) { // Exclude if forbidden if (!\in_array($name, $this->listForbiddenList)) { // Extra validations switch ($name) { case 'fullordering': $orderingParts = explode(' ', $value); if (\count($orderingParts) >= 2) { // Latest part will be considered the direction $fullDirection = end($orderingParts); if (\in_array(strtoupper($fullDirection), ['ASC', 'DESC', ''])) { $this->setState('list.direction', $fullDirection); } else { $this->setState('list.direction', $direction); // Fallback to the default value $value = $ordering . ' ' . $direction; } unset($orderingParts[\count($orderingParts) - 1]); // The rest will be the ordering $fullOrdering = implode(' ', $orderingParts); if (\in_array($fullOrdering, $this->filter_fields)) { $this->setState('list.ordering', $fullOrdering); } else { $this->setState('list.ordering', $ordering); // Fallback to the default value $value = $ordering . ' ' . $direction; } } else { $this->setState('list.ordering', $ordering); $this->setState('list.direction', $direction); // Fallback to the default value $value = $ordering . ' ' . $direction; } break; case 'ordering': if (!\in_array($value, $this->filter_fields)) { $value = $ordering; } break; case 'direction': if ($value && (!\in_array(strtoupper($value), ['ASC', 'DESC', '']))) { $value = $direction; } break; case 'limit': $value = $inputFilter->clean($value, 'int'); $limit = $value; break; case 'select': $explodedValue = explode(',', $value); foreach ($explodedValue as &$field) { $field = $inputFilter->clean($field, 'cmd'); } $value = implode(',', $explodedValue); break; } $this->setState('list.' . $name, $value); } } } else { // Keep B/C for components previous to jform forms for filters // Pre-fill the limits $limit = $app->getUserStateFromRequest('global.list.limit', 'limit', $app->get('list_limit'), 'uint'); $this->setState('list.limit', $limit); // Check if the ordering field is in the allowed list, otherwise use the incoming value. $value = $app->getUserStateFromRequest($this->context . '.ordercol', 'filter_order', $ordering); if (!\in_array($value, $this->filter_fields)) { $value = $ordering; $app->setUserState($this->context . '.ordercol', $value); } $this->setState('list.ordering', $value); // Check if the ordering direction is valid, otherwise use the incoming value. $value = $app->getUserStateFromRequest($this->context . '.orderdirn', 'filter_order_Dir', $direction); if (!$value || !\in_array(strtoupper($value), ['ASC', 'DESC', ''])) { $value = $direction; $app->setUserState($this->context . '.orderdirn', $value); } $this->setState('list.direction', $value); } // Support old ordering field $oldOrdering = $app->getInput()->get('filter_order'); if (!empty($oldOrdering) && \in_array($oldOrdering, $this->filter_fields)) { $this->setState('list.ordering', $oldOrdering); } // Support old direction field $oldDirection = $app->getInput()->get('filter_order_Dir'); if (!empty($oldDirection) && \in_array(strtoupper($oldDirection), ['ASC', 'DESC', ''])) { $this->setState('list.direction', $oldDirection); } $value = $app->getUserStateFromRequest($this->context . '.limitstart', 'limitstart', 0, 'int'); $limitstart = ($limit != 0 ? (floor($value / $limit) * $limit) : 0); $this->setState('list.start', $limitstart); } else { $this->setState('list.start', 0); $this->setState('list.limit', 0); } } /** * Gets the value of a user state variable and sets it in the session * * This is the same as the method in Application except that this also can optionally * force you back to the first page when a filter has changed * * @param string $key The key of the user state variable. * @param string $request The name of the variable passed in a request. * @param string $default The default value for the variable if not found. Optional. * @param string $type Filter for the variable, for valid values see {@link InputFilter::clean()}. Optional. * @param boolean $resetPage If true, the limitstart in request is set to zero * * @return mixed The request user state. * * @since 1.6 */ public function getUserStateFromRequest($key, $request, $default = null, $type = 'none', $resetPage = true) { $app = Factory::getApplication(); $input = $app->getInput(); $old_state = $app->getUserState($key); $cur_state = $old_state ?? $default; $new_state = $input->get($request, null, $type); // BC for Search Tools which uses different naming if ($new_state === null && strpos($request, 'filter_') === 0) { $name = substr($request, 7); $filters = $app->getInput()->get('filter', [], 'array'); if (isset($filters[$name])) { $new_state = $filters[$name]; } } if ($cur_state != $new_state && $new_state !== null && $resetPage) { $input->set('limitstart', 0); } // Save the new value only if it is set in this request. if ($new_state !== null) { $app->setUserState($key, $new_state); } else { $new_state = $cur_state; } return $new_state; } /** * Parse and transform the search string into a string fit for regex-ing arbitrary strings against * * @param string $search The search string * @param string $regexDelimiter The regex delimiter to use for the quoting * * @return string Search string escaped for regex * * @since 3.4 */ protected function refineSearchStringToRegex($search, $regexDelimiter = '/') { $searchArr = explode('|', trim($search, ' |')); foreach ($searchArr as $key => $searchString) { if (trim($searchString) === '') { unset($searchArr[$key]); continue; } $searchArr[$key] = str_replace(' ', '.*', preg_quote(trim($searchString), $regexDelimiter)); } return implode('|', $searchArr); } } PK1A#]r"&�m m (src/MVC/Model/WorkflowModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Form\Form; use Joomla\CMS\Table\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a workflow model. * * @since 4.0.0 */ interface WorkflowModelInterface { /** * Set Up the workflow * * @param string $extension The option and section separated by. * * @return void * * @since 4.0.0 */ public function setUpWorkflow($extension); /** * Method to allow derived classes to preprocess the form. * * @param Form $form A Form object. * @param mixed $data The data expected for the form. * * @return void * * @see FormField * @since 4.0.0 * @throws \Exception if there is an error in the form event. */ public function workflowPreprocessForm(Form $form, $data); /** * Let plugins access stage change events * * @return void * * @since 4.0.0 */ public function workflowBeforeStageChange(); /** * Preparation of workflow data/plugins * * @return void * * @since 4.0.0 */ public function workflowBeforeSave(); /** * Executing of relevant workflow methods * * @return void * * @since 4.0.0 */ public function workflowAfterSave($data); /** * Batch change workflow stage or current. * * @param integer $oldId The ID of the item copied from * @param integer $newId The ID of the new item * * @return null * * @since 4.0.0 */ public function workflowCleanupBatchMove($oldId, $newId); /** * Runs transition for item. * * @param array $pks Id of items to execute the transition * @param integer $transitionId Id of transition * * @return boolean * * @since 4.0.0 */ public function executeTransition(array $pks, int $transitionId); /** * Method to get state variables. * * @param string $property Optional parameter name * @param mixed $default Optional default value * * @return mixed The property where specified, the state object where omitted * * @since 4.0.0 */ public function getState($property = null, $default = null); /** * Method to get the model name * * The model name. By default parsed using the classname or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @since 4.0.0 * @throws \Exception */ public function getName(); /** * Method to get a table object, load it if necessary. * * @param string $name The table name. Optional. * @param string $prefix The class prefix. Optional. * @param array $options Configuration array for model. Optional. * * @return Table A Table object * * @since 3.0 * @throws \Exception */ public function getTable($name = '', $prefix = '', $options = []); } PK1A#]}��b��src/MVC/Model/ItemModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Prototype item model. * * @since 1.6 */ abstract class ItemModel extends BaseDatabaseModel implements ItemModelInterface { /** * An item. * * @var array * @since 1.6 */ protected $_item = null; /** * Model context string. * * @var string * @since 1.6 */ protected $_context = 'group.type'; /** * Method to get a store id based on model configuration state. * * This is necessary because the model is used by the component and * different modules that might need different sets of data or different * ordering requirements. * * @param string $id A prefix for the store id. * * @return string A store id. * * @since 1.6 */ protected function getStoreId($id = '') { // Compile the store id. return md5($id); } } PK1A#]� ���6�6#src/MVC/Model/BaseDatabaseModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Extension\ComponentInterface; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Factory\LegacyFactory; use Joomla\CMS\MVC\Factory\MVCFactoryAwareTrait; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Factory\MVCFactoryServiceInterface; use Joomla\CMS\Table\Table; use Joomla\CMS\User\CurrentUserInterface; use Joomla\CMS\User\CurrentUserTrait; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\DatabaseQuery; use Joomla\Database\Exception\DatabaseNotFoundException; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Event\Event; use Joomla\Event\EventInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a database aware Joomla Model * * Acts as a Factory class for application specific objects and provides many supporting API functions. * * @since 2.5.5 */ abstract class BaseDatabaseModel extends BaseModel implements DatabaseModelInterface, DispatcherAwareInterface, CurrentUserInterface, CacheControllerFactoryAwareInterface, DatabaseAwareInterface { use DatabaseAwareTrait; use MVCFactoryAwareTrait; use DispatcherAwareTrait; use CurrentUserTrait; use CacheControllerFactoryAwareTrait; /** * The URL option for the component. * * @var string * @since 3.0 */ protected $option = null; /** * The event to trigger when cleaning cache. * * @var string * @since 3.0 */ protected $event_clean_cache = null; /** * Constructor * * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). * @param ?MVCFactoryInterface $factory The factory. * * @since 3.0 * @throws \Exception */ public function __construct($config = [], MVCFactoryInterface $factory = null) { parent::__construct($config); // Guess the option from the class name (Option)Model(View). if (empty($this->option)) { $r = null; if (!preg_match('/(.*)Model/i', \get_class($this), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } $this->option = ComponentHelper::getComponentName($this, $r[1]); } /** * @deprecated 4.3 will be Removed in 6.0 * Database instance is injected through the setter function, * subclasses should not use the db instance in constructor anymore */ $db = \array_key_exists('dbo', $config) ? $config['dbo'] : Factory::getDbo(); if ($db) { @trigger_error(sprintf('Database is not available in constructor in 6.0.'), E_USER_DEPRECATED); $this->setDatabase($db); // Is needed, when models use the deprecated MVC DatabaseAwareTrait, as the trait is overriding the local functions $this->setDbo($db); } // Set the default view search path if (\array_key_exists('table_path', $config)) { $this->addTablePath($config['table_path']); } elseif (\defined('JPATH_COMPONENT_ADMINISTRATOR')) { $this->addTablePath(JPATH_COMPONENT_ADMINISTRATOR . '/tables'); $this->addTablePath(JPATH_COMPONENT_ADMINISTRATOR . '/table'); } // Set the clean cache event if (isset($config['event_clean_cache'])) { $this->event_clean_cache = $config['event_clean_cache']; } elseif (empty($this->event_clean_cache)) { $this->event_clean_cache = 'onContentCleanCache'; } if ($factory) { $this->setMVCFactory($factory); return; } $component = Factory::getApplication()->bootComponent($this->option); if ($component instanceof MVCFactoryServiceInterface) { $this->setMVCFactory($component->getMVCFactory()); } } /** * Gets an array of objects from the results of database query. * * @param DatabaseQuery|string $query The query. * @param integer $limitstart Offset. * @param integer $limit The number of records. * * @return object[] An array of results. * * @since 3.0 * @throws \RuntimeException */ protected function _getList($query, $limitstart = 0, $limit = 0) { if (\is_string($query)) { $query = $this->getDbo()->getQuery(true)->setQuery($query); } $query->setLimit($limit, $limitstart); $this->getDbo()->setQuery($query); return $this->getDbo()->loadObjectList(); } /** * Returns a record count for the query. * * Note: Current implementation of this method assumes that getListQuery() returns a set of unique rows, * thus it uses SELECT COUNT(*) to count the rows. In cases that getListQuery() uses DISTINCT * then either this method must be overridden by a custom implementation at the derived Model Class * or a GROUP BY clause should be used to make the set unique. * * @param DatabaseQuery|string $query The query. * * @return integer Number of rows for query. * * @since 3.0 */ protected function _getListCount($query) { // Use fast COUNT(*) on DatabaseQuery objects if there is no GROUP BY or HAVING clause: if ( $query instanceof DatabaseQuery && $query->type === 'select' && $query->group === null && $query->merge === null && $query->querySet === null && $query->having === null ) { $query = clone $query; $query->clear('select')->clear('order')->clear('limit')->clear('offset')->select('COUNT(*)'); $this->getDbo()->setQuery($query); return (int) $this->getDbo()->loadResult(); } // Otherwise fall back to inefficient way of counting all results. // Remove the limit, offset and order parts if it's a DatabaseQuery object if ($query instanceof DatabaseQuery) { $query = clone $query; $query->clear('limit')->clear('offset')->clear('order'); } $this->getDbo()->setQuery($query); $this->getDbo()->execute(); return (int) $this->getDbo()->getNumRows(); } /** * Method to load and return a table object. * * @param string $name The name of the view * @param string $prefix The class prefix. Optional. * @param array $config Configuration settings to pass to Table::getInstance * * @return Table|boolean Table object or boolean false if failed * * @since 3.0 * @see \JTable::getInstance() */ protected function _createTable($name, $prefix = 'Table', $config = []) { // Make sure we are returning a DBO object if (!\array_key_exists('dbo', $config)) { $config['dbo'] = $this->getDbo(); } $table = $this->getMVCFactory()->createTable($name, $prefix, $config); if ($table instanceof CurrentUserInterface) { $table->setCurrentUser($this->getCurrentUser()); } return $table; } /** * Method to get a table object, load it if necessary. * * @param string $name The table name. Optional. * @param string $prefix The class prefix. Optional. * @param array $options Configuration array for model. Optional. * * @return Table A Table object * * @since 3.0 * @throws \Exception */ public function getTable($name = '', $prefix = '', $options = []) { if (empty($name)) { $name = $this->getName(); } // We need this ugly code to deal with non-namespaced MVC code if (empty($prefix) && $this->getMVCFactory() instanceof LegacyFactory) { $prefix = 'Table'; } if ($table = $this->_createTable($name, $prefix, $options)) { return $table; } throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_TABLE_NAME_NOT_SUPPORTED', $name), 0); } /** * Method to check if the given record is checked out by the current user * * @param \stdClass $item The record to check * * @return bool */ public function isCheckedOut($item) { $table = $this->getTable(); $checkedOutField = $table->getColumnAlias('checked_out'); if (property_exists($item, $checkedOutField) && $item->{$checkedOutField} != $this->getCurrentUser()->id) { return true; } return false; } /** * Clean the cache * * @param string $group The cache group * * @return void * * @since 3.0 */ protected function cleanCache($group = null) { $app = Factory::getApplication(); $options = [ 'defaultgroup' => $group ?: ($this->option ?? $app->getInput()->get('option')), 'cachebase' => $app->get('cache_path', JPATH_CACHE), 'result' => true, ]; try { /** @var CallbackController $cache */ $cache = $this->getCacheControllerFactory()->createCacheController('callback', $options); $cache->clean(); } catch (CacheExceptionInterface $exception) { $options['result'] = false; } // Trigger the onContentCleanCache event. $this->dispatchEvent(new Event($this->event_clean_cache, $options)); } /** * Boots the component with the given name. * * @param string $component The component name, eg. com_content. * * @return ComponentInterface The service container * * @since 4.0.0 */ protected function bootComponent($component): ComponentInterface { return Factory::getApplication()->bootComponent($component); } /** * Get the event dispatcher. * * The override was made to keep a backward compatibility for legacy component. * TODO: Remove the override in 6.0 * * @return DispatcherInterface * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ public function getDispatcher() { if (!$this->dispatcher) { @trigger_error( sprintf('Dispatcher for %s should be set through MVC factory. It will throw an exception in 6.0', __CLASS__), E_USER_DEPRECATED ); return Factory::getContainer()->get(DispatcherInterface::class); } return $this->dispatcher; } /** * Dispatches the given event on the internal dispatcher, does a fallback to the global one. * * @param EventInterface $event The event * * @return void * * @since 4.1.0 * * @deprecated 4.4 will be removed in 6.0. Use $this->getDispatcher() directly. */ protected function dispatchEvent(EventInterface $event) { $this->getDispatcher()->dispatch($event->getName(), $event); @trigger_error( sprintf( 'Method %s is deprecated and will be removed in 6.0. Use getDispatcher()->dispatch() directly.', __METHOD__ ), E_USER_DEPRECATED ); } /** * Get the database driver. * * @return DatabaseInterface The database driver. * * @since 4.2.0 * @throws \UnexpectedValueException * * @deprecated 4.3 will be removed in 6.0 * Use getDatabase() instead * Example: $model->getDatabase(); */ public function getDbo() { try { return $this->getDatabase(); } catch (DatabaseNotFoundException $e) { throw new \UnexpectedValueException('Database driver not set in ' . __CLASS__); } } /** * Set the database driver. * * @param ?DatabaseInterface $db The database driver. * * @return void * * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Use setDatabase() instead * Example: $model->setDatabase($db); */ public function setDbo(DatabaseInterface $db = null) { if ($db === null) { return; } $this->setDatabase($db); } /** * Proxy for _db variable. * * @param string $name The name of the element * * @return mixed The value of the element if set, null otherwise * * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Use getDatabase() instead of directly accessing _db */ public function __get($name) { if ($name === '_db') { return $this->getDbo(); } // Default the variable if (!isset($this->$name)) { $this->$name = null; } return $this->$name; } } PK1A#]$�/"��(src/MVC/Model/DatabaseModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\Database\DatabaseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a database model. * * @since 4.0.0 */ interface DatabaseModelInterface { /** * Method to get the database driver object. * * @return DatabaseInterface * * @since 4.0.0 */ public function getDbo(); } PK1A#]#.�I��$src/MVC/Model/ItemModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for an item model. * * @since 4.0.0 */ interface ItemModelInterface { /** * Method to get an item. * * @param integer $pk The id of the item * * @return object * * @since 4.0.0 * @throws \Exception */ public function getItem($pk = null); } PK1A#]h��LL$src/MVC/Model/FormModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Form\Form; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a form model. * * @since 4.0.0 */ interface FormModelInterface { /** * Method for getting a form. * * @param array $data Data for the form. * @param boolean $loadData True if the form is to load its own data (default case), false if not. * * @return Form * * @since 4.0.0 * * @throws \Exception */ public function getForm($data = [], $loadData = true); } PK1A#]��n��$src/MVC/Model/DatabaseAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\Database\DatabaseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Database aware trait. * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the trait from the database package * Example: \Joomla\Database\DatabaseAwareTrait */ trait DatabaseAwareTrait { /** * The database driver. * * @var DatabaseInterface * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the trait from the database package * Example: \Joomla\Database\DatabaseAwareTrait::$databaseAwareTraitDatabase */ protected $_db; /** * Get the database driver. * * @return DatabaseInterface The database driver. * * @since 4.0.0 * @throws \UnexpectedValueException * * @deprecated 4.3 will be removed in 6.0 * Use the trait from the database package * Example: \Joomla\Database\DatabaseAwareTrait::getDatabase() */ public function getDbo() { if ($this->_db) { return $this->_db; } throw new \UnexpectedValueException('Database driver not set in ' . __CLASS__); } /** * Set the database driver. * * @param ?DatabaseInterface $db The database driver. * * @return void * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Use the trait from the database package * Example: \Joomla\Database\DatabaseAwareTrait::setDatabase() */ public function setDbo(DatabaseInterface $db = null) { $this->_db = $db; } } PK1A#]�O=���(src/MVC/Model/StatefulModelInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a stateful model. * * @since 4.0.0 */ interface StatefulModelInterface { /** * Method to get model state variables. * * @param string $property Optional parameter name * @param mixed $default Optional default value * * @return mixed The property where specified, the state object where omitted * * @since 4.0.0 */ public function getState($property = null, $default = null); /** * Method to set model state variables. * * @param string $property The name of the property. * @param mixed $value The value of the property to set or null. * * @return mixed The previous value of the property or null if not set. * * @since 4.0.0 */ public function setState($property, $value = null); } PK1A#] �oosrc/MVC/Model/BaseModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Model * * @since 4.0.0 */ abstract class BaseModel extends CMSObject implements ModelInterface, StatefulModelInterface { use StateBehaviorTrait; use LegacyModelLoaderTrait; /** * The model (base) name * * @var string * @since 4.0.0 */ protected $name; /** * The include paths * * @var array * @since 4.0.0 */ protected static $paths; /** * Constructor * * @param array $config An array of configuration options (name, state, ignore_request). * * @since 4.0.0 * @throws \Exception */ public function __construct($config = []) { // Set the view name if (empty($this->name)) { if (\array_key_exists('name', $config)) { $this->name = $config['name']; } else { $this->name = $this->getName(); } } // Set the model state if (\array_key_exists('state', $config)) { $this->state = $config['state']; } // Set the internal state marker - used to ignore setting state from the request if (!empty($config['ignore_request'])) { $this->__state_set = true; } } /** * Add a directory where \JModelLegacy should search for models. You may * either pass a string or an array of directories. * * @param mixed $path A path or array[sting] of paths to search. * @param string $prefix A prefix for models. * * @return array An array with directory elements. If prefix is equal to '', all directories are returned. * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement. Get the model through the MVCFactory + namespace instead * * @see LegacyModelLoaderTrait::getInstance(...) */ public static function addIncludePath($path = '', $prefix = '') { if (!isset(self::$paths)) { self::$paths = []; } if (!isset(self::$paths[$prefix])) { self::$paths[$prefix] = []; } if (!isset(self::$paths[''])) { self::$paths[''] = []; } if (!empty($path)) { foreach ((array) $path as $includePath) { if (!\in_array($includePath, self::$paths[$prefix])) { array_unshift(self::$paths[$prefix], Path::clean($includePath)); } if (!\in_array($includePath, self::$paths[''])) { array_unshift(self::$paths[''], Path::clean($includePath)); } } } return self::$paths[$prefix]; } /** * Method to get the model name * * The model name. By default parsed using the classname or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @since 4.0.0 * @throws \Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/Model(.*)/i', \get_class($this), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } $this->name = str_replace(['\\', 'model'], '', strtolower($r[1])); } return $this->name; } } PK1A#]�����src/MVC/Model/FormModel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Model; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\Form\FormFactoryAwareInterface; use Joomla\CMS\Form\FormFactoryAwareTrait; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Form\FormRule; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Plugin\PluginHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Prototype form model. * * @see Form * @see FormField * @see FormRule * @since 1.6 */ abstract class FormModel extends BaseDatabaseModel implements FormFactoryAwareInterface, FormModelInterface { use FormBehaviorTrait; use FormFactoryAwareTrait; /** * Maps events to plugin groups. * * @var array * @since 3.6 */ protected $events_map = null; /** * Constructor * * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). * @param ?MVCFactoryInterface $factory The factory. * @param ?FormFactoryInterface $formFactory The form factory. * * @since 3.6 * @throws \Exception */ public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) { $config['events_map'] = $config['events_map'] ?? []; $this->events_map = array_merge( ['validate' => 'content'], $config['events_map'] ); parent::__construct($config, $factory); $this->setFormFactory($formFactory); } /** * Method to checkin a row. * * @param integer $pk The numeric id of the primary key. * * @return boolean False on failure or error, true otherwise. * * @since 1.6 */ public function checkin($pk = null) { // Only attempt to check the row in if it exists. if ($pk) { $user = $this->getCurrentUser(); // Get an instance of the row to checkin. $table = $this->getTable(); if (!$table->load($pk)) { $this->setError($table->getError()); return false; } // If there is no checked_out or checked_out_time field, just return true. if (!$table->hasField('checked_out') || !$table->hasField('checked_out_time')) { return true; } $checkedOutField = $table->getColumnAlias('checked_out'); // Check if this is the user having previously checked out the row. if ($table->$checkedOutField > 0 && $table->$checkedOutField != $user->get('id') && !$user->authorise('core.manage', 'com_checkin')) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_CHECKIN_USER_MISMATCH')); return false; } // Attempt to check the row in. if (!$table->checkIn($pk)) { $this->setError($table->getError()); return false; } } return true; } /** * Method to check-out a row for editing. * * @param integer $pk The numeric id of the primary key. * * @return boolean False on failure or error, true otherwise. * * @since 1.6 */ public function checkout($pk = null) { // Only attempt to check the row in if it exists. if ($pk) { // Get an instance of the row to checkout. $table = $this->getTable(); if (!$table->load($pk)) { if ($table->getError() === false) { // There was no error returned, but false indicates that the row did not exist in the db, so probably previously deleted. $this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST')); } else { $this->setError($table->getError()); } return false; } // If there is no checked_out or checked_out_time field, just return true. if (!$table->hasField('checked_out') || !$table->hasField('checked_out_time')) { return true; } $user = $this->getCurrentUser(); // When the user is a guest, don't do a checkout if (!$user->id) { return false; } $checkedOutField = $table->getColumnAlias('checked_out'); // Check if this is the user having previously checked out the row. if ($table->$checkedOutField > 0 && $table->$checkedOutField != $user->get('id')) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_CHECKOUT_USER_MISMATCH')); return false; } // Attempt to check the row out. if (!$table->checkOut($user->get('id'), $pk)) { $this->setError($table->getError()); return false; } } return true; } /** * Method to validate the form data. * * @param Form $form The form to validate against. * @param array $data The data to validate. * @param string $group The name of the field group to validate. * * @return array|boolean Array of filtered data if valid, false otherwise. * * @see FormRule * @see InputFilter * @since 1.6 */ public function validate($form, $data, $group = null) { // Include the plugins for the delete events. PluginHelper::importPlugin($this->events_map['validate']); $dispatcher = Factory::getContainer()->get('dispatcher'); if (!empty($dispatcher->getListeners('onUserBeforeDataValidation'))) { @trigger_error( 'The `onUserBeforeDataValidation` event is deprecated and will be removed in 5.0.' . 'Use the `onContentValidateData` event instead.', E_USER_DEPRECATED ); Factory::getApplication()->triggerEvent('onUserBeforeDataValidation', [$form, &$data]); } Factory::getApplication()->triggerEvent('onContentBeforeValidateData', [$form, &$data]); // Filter and validate the form data. $return = $form->process($data, $group); // Check for an error. if ($return instanceof \Exception) { $this->setError($return->getMessage()); return false; } // Check the validation results. if ($return === false) { // Get the validation messages from the form. foreach ($form->getErrors() as $message) { $this->setError($message); } return false; } $data = $return; // Tags B/C break at 3.1.2 if (!isset($data['tags']) && isset($data['metadata']['tags'])) { $data['tags'] = $data['metadata']['tags']; } return $data; } } PK1A#]a�Ő7�7&src/MVC/Controller/AdminController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\MVC\Model\WorkflowModelInterface; use Joomla\CMS\Router\Route; use Joomla\Input\Input; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Administrator Controller * * Controller (controllers are where you put all the actual code) Provides basic * functionality, such as rendering views (aka displaying templates). * * @since 1.6 */ class AdminController extends BaseController { /** * The URL option for the component. * * @var string * @since 1.6 */ protected $option; /** * The prefix to use with controller messages. * * @var string * @since 1.6 */ protected $text_prefix; /** * The URL view list variable. * * @var string * @since 1.6 */ protected $view_list; /** * Constructor. * * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'default_task', 'model_path', and * 'view_path' (this list is not meant to be comprehensive). * @param ?MVCFactoryInterface $factory The factory. * @param ?CMSApplication $app The Application for the dispatcher * @param ?Input $input The Input object for the request * * @since 3.0 */ public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { parent::__construct($config, $factory, $app, $input); // Define standard task mappings. // Value = 0 $this->registerTask('unpublish', 'publish'); // Value = 2 $this->registerTask('archive', 'publish'); // Value = -2 $this->registerTask('trash', 'publish'); // Value = -3 $this->registerTask('report', 'publish'); $this->registerTask('orderup', 'reorder'); $this->registerTask('orderdown', 'reorder'); // Guess the option as com_NameOfController. if (empty($this->option)) { $this->option = ComponentHelper::getComponentName($this, $this->getName()); } // Guess the \Text message prefix. Defaults to the option. if (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } // Guess the list view as the suffix, eg: OptionControllerSuffix. if (empty($this->view_list)) { $reflect = new \ReflectionClass($this); $r = [0 => '', 1 => '', 2 => $reflect->getShortName()]; if ($reflect->getNamespaceName()) { $r[2] = str_replace('Controller', '', $r[2]); } elseif (!preg_match('/(.*)Controller(.*)/i', $reflect->getShortName(), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } $this->view_list = strtolower($r[2]); } } /** * Removes an item. * * @return void * * @since 1.6 */ public function delete() { // Check for request forgeries $this->checkToken(); // Get items to remove from the request. $cid = (array) $this->input->get('cid', [], 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); if (empty($cid)) { $this->app->getLogger()->warning(Text::_($this->text_prefix . '_NO_ITEM_SELECTED'), ['category' => 'jerror']); } else { // Get the model. $model = $this->getModel(); // Remove the items. if ($model->delete($cid)) { $this->setMessage(Text::plural($this->text_prefix . '_N_ITEMS_DELETED', \count($cid))); } else { $this->setMessage($model->getError(), 'error'); } // Invoke the postDelete method to allow for the child class to access the model. $this->postDeleteHook($model, $cid); } $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); } /** * Function that allows child controller access to model data * after the item has been deleted. * * @param BaseDatabaseModel $model The data model object. * @param integer[] $id An array of deleted IDs. * * @return void * * @since 3.1 */ protected function postDeleteHook(BaseDatabaseModel $model, $id = null) { } /** * Method to publish a list of items * * @return void * * @since 1.6 */ public function publish() { // Check for request forgeries $this->checkToken(); // Get items to publish from the request. $cid = (array) $this->input->get('cid', [], 'int'); $data = ['publish' => 1, 'unpublish' => 0, 'archive' => 2, 'trash' => -2, 'report' => -3]; $task = $this->getTask(); $value = ArrayHelper::getValue($data, $task, 0, 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); if (empty($cid)) { $this->app->getLogger()->warning(Text::_($this->text_prefix . '_NO_ITEM_SELECTED'), ['category' => 'jerror']); } else { // Get the model. $model = $this->getModel(); // Publish the items. try { $model->publish($cid, $value); $errors = $model->getErrors(); $ntext = null; if ($value === 1) { if ($errors) { $this->app->enqueueMessage(Text::plural($this->text_prefix . '_N_ITEMS_FAILED_PUBLISHING', \count($cid)), 'error'); } else { $ntext = $this->text_prefix . '_N_ITEMS_PUBLISHED'; } } elseif ($value === 0) { $ntext = $this->text_prefix . '_N_ITEMS_UNPUBLISHED'; } elseif ($value === 2) { $ntext = $this->text_prefix . '_N_ITEMS_ARCHIVED'; } else { $ntext = $this->text_prefix . '_N_ITEMS_TRASHED'; } if (\count($cid)) { $this->setMessage(Text::plural($ntext, \count($cid))); } } catch (\Exception $e) { $this->setMessage($e->getMessage(), 'error'); } } $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); } /** * Changes the order of one or more records. * * @return boolean True on success * * @since 1.6 */ public function reorder() { // Check for request forgeries. $this->checkToken(); $ids = (array) $this->input->post->get('cid', [], 'int'); $inc = $this->getTask() === 'orderup' ? -1 : 1; // Remove zero values resulting from input filter $ids = array_filter($ids); $model = $this->getModel(); $return = $model->reorder($ids, $inc); $redirect = Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false); if ($return === false) { // Reorder failed. $message = Text::sprintf('JLIB_APPLICATION_ERROR_REORDER_FAILED', $model->getError()); $this->setRedirect($redirect, $message, 'error'); return false; } else { // Reorder succeeded. $message = Text::_('JLIB_APPLICATION_SUCCESS_ITEM_REORDERED'); $this->setRedirect($redirect, $message); return true; } } /** * Method to save the submitted ordering values for records. * * @return boolean True on success * * @since 1.6 */ public function saveorder() { // Check for request forgeries. $this->checkToken(); // Get the input $pks = (array) $this->input->post->get('cid', [], 'int'); $order = (array) $this->input->post->get('order', [], 'int'); // Remove zero PKs and corresponding order values resulting from input filter for PK foreach ($pks as $i => $pk) { if ($pk === 0) { unset($pks[$i]); unset($order[$i]); } } // Get the model $model = $this->getModel(); // Save the ordering $return = $model->saveorder($pks, $order); $redirect = Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false); if ($return === false) { // Reorder failed $message = Text::sprintf('JLIB_APPLICATION_ERROR_REORDER_FAILED', $model->getError()); $this->setRedirect($redirect, $message, 'error'); return false; } else { // Reorder succeeded. $this->setMessage(Text::_('JLIB_APPLICATION_SUCCESS_ORDERING_SAVED')); $this->setRedirect($redirect); return true; } } /** * Check in of one or more records. * * @return boolean True on success * * @since 1.6 */ public function checkin() { // Check for request forgeries. $this->checkToken(); $ids = (array) $this->input->post->get('cid', [], 'int'); // Remove zero values resulting from input filter $ids = array_filter($ids); $model = $this->getModel(); $return = $model->checkin($ids); if ($return === false) { // Checkin failed. $message = Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ), $message, 'error' ); return false; } else { // Checkin succeeded. $message = Text::plural($this->text_prefix . '_N_ITEMS_CHECKED_IN', \count($ids)); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ), $message ); return true; } } /** * Method to save the submitted ordering values for records via AJAX. * * @return void * * @since 3.0 */ public function saveOrderAjax() { // Check for request forgeries. $this->checkToken(); // Get the input $pks = (array) $this->input->post->get('cid', [], 'int'); $order = (array) $this->input->post->get('order', [], 'int'); // Remove zero PKs and corresponding order values resulting from input filter for PK foreach ($pks as $i => $pk) { if ($pk === 0) { unset($pks[$i]); unset($order[$i]); } } // Get the model $model = $this->getModel(); // Save the ordering $return = $model->saveorder($pks, $order); if ($return) { echo '1'; } // Close the application $this->app->close(); } /** * Method to run Transition by id of item. * * @return boolean Indicates whether the transition was successful. * * @since 4.0.0 */ public function runTransition() { // Check for request forgeries $this->checkToken(); // Get the input $pks = (array) $this->input->post->get('cid', [], 'int'); // Remove zero values resulting from input filter $pks = array_filter($pks); if (!\count($pks)) { return false; } $transitionId = (int) $this->input->post->getInt('transition_id'); // Get the model $model = $this->getModel(); if (!$model instanceof WorkflowModelInterface) { return false; } $return = $model->executeTransition($pks, $transitionId); $redirect = Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false); if ($return === false) { // Transition change failed. $message = Text::sprintf('JLIB_APPLICATION_ERROR_RUN_TRANSITION', $model->getError()); $this->setRedirect($redirect, $message, 'error'); return false; } // Transition change succeeded. $message = Text::_('JLIB_APPLICATION_SUCCESS_RUN_TRANSITION'); $this->setRedirect($redirect, $message); return true; } /** * Gets the URL arguments to append to a list redirect. * * @return string The arguments to append to the redirect URL. * * @since 4.0.0 */ protected function getRedirectToListAppend() { return ''; } } PK1A#]�Hz~��*src/MVC/Controller/Exception/SendEmail.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Send email Exception * * @since 4.0.0 */ class SendEmail extends \RuntimeException { } PK1A#]!^�v1src/MVC/Controller/Exception/ResourceNotFound.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining a resource not found exception * * @since 4.0.0 */ class ResourceNotFound extends \RuntimeException { } PK1A#]���0src/MVC/Controller/Exception/CheckinCheckout.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Checkin/checkout Exception * * @since 4.0.0 */ class CheckinCheckout extends \RuntimeException { } PK1A#]i�LV��%src/MVC/Controller/Exception/Save.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception saving data * * @since 4.0.0 */ class Save extends \RuntimeException { } PK1A#]�� ? ?$src/MVC/Controller/ApiController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; use Joomla\CMS\Access\Exception\NotAllowed; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Form\Form; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\ListModel; use Joomla\CMS\MVC\View\JsonApiView; use Joomla\CMS\Object\CMSObject; use Joomla\Input\Input; use Joomla\String\Inflector; use Tobscure\JsonApi\Exception\InvalidParameterException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla API Controller * * Controller (controllers are where you put all the actual code) Provides basic * functionality, such as rendering views (aka displaying templates). * * @since 4.0.0 */ class ApiController extends BaseController { /** * The content type of the item. * * @var string * @since 4.0.0 */ protected $contentType; /** * The URL option for the component. * * @var string * @since 4.0.0 */ protected $option; /** * The prefix to use with controller messages. * * @var string * @since 4.0.0 */ protected $text_prefix; /** * The context for storing internal data, e.g. record. * * @var string * @since 4.0.0 */ protected $context; /** * Items on a page * * @var integer */ protected $itemsPerPage = 20; /** * The model state to inject * * @var CMSObject */ protected $modelState; /** * Constructor. * * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'default_task', 'model_path', and * 'view_path' (this list is not meant to be comprehensive). * @param ?MVCFactoryInterface $factory The factory. * @param ?CMSApplication $app The Application for the dispatcher * @param ?Input $input Input * * @since 4.0.0 * @throws \Exception */ public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { $this->modelState = new CMSObject(); parent::__construct($config, $factory, $app, $input); // Guess the option as com_NameOfController if (empty($this->option)) { $this->option = ComponentHelper::getComponentName($this, $this->getName()); } // Guess the \Text message prefix. Defaults to the option. if (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } // Guess the context as the suffix, eg: OptionControllerContent. if (empty($this->context)) { $r = null; if (!preg_match('/(.*)Controller(.*)/i', \get_class($this), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } $this->context = str_replace('\\', '', strtolower($r[2])); } } /** * Basic display of an item view * * @param integer $id The primary key to display. Leave empty if you want to retrieve data from the request * * @return static A \JControllerLegacy object to support chaining. * * @since 4.0.0 */ public function displayItem($id = null) { if ($id === null) { $id = $this->input->get('id', 0, 'int'); } $viewType = $this->app->getDocument()->getType(); $viewName = $this->input->get('view', $this->default_view); $viewLayout = $this->input->get('layout', 'default', 'string'); try { /** @var JsonApiView $view */ $view = $this->getView( $viewName, $viewType, '', ['base_path' => $this->basePath, 'layout' => $viewLayout, 'contentType' => $this->contentType] ); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } $modelName = $this->input->get('model', Inflector::singularize($this->contentType)); // Create the model, ignoring request data so we can safely set the state in the request from the controller $model = $this->getModel($modelName, '', ['ignore_request' => true, 'state' => $this->modelState]); if (!$model) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); } try { $modelName = $model->getName(); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } $model->setState($modelName . '.id', $id); // Push the model into the view (as default) $view->setModel($model, true); $view->document = $this->app->getDocument(); $view->displayItem(); return $this; } /** * Basic display of a list view * * @return static A \JControllerLegacy object to support chaining. * * @since 4.0.0 */ public function displayList() { // Assemble pagination information (using recommended JsonApi pagination notation for offset strategy) $paginationInfo = $this->input->get('page', [], 'array'); $limit = null; $offset = null; if (\array_key_exists('offset', $paginationInfo)) { $offset = $paginationInfo['offset']; $this->modelState->set($this->context . '.limitstart', $offset); } if (\array_key_exists('limit', $paginationInfo)) { $limit = $paginationInfo['limit']; $this->modelState->set($this->context . '.list.limit', $limit); } $viewType = $this->app->getDocument()->getType(); $viewName = $this->input->get('view', $this->default_view); $viewLayout = $this->input->get('layout', 'default', 'string'); try { /** @var JsonApiView $view */ $view = $this->getView( $viewName, $viewType, '', ['base_path' => $this->basePath, 'layout' => $viewLayout, 'contentType' => $this->contentType] ); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } $modelName = $this->input->get('model', $this->contentType); /** @var ListModel $model */ $model = $this->getModel($modelName, '', ['ignore_request' => true, 'state' => $this->modelState]); if (!$model) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); } // Push the model into the view (as default) $view->setModel($model, true); if ($offset) { $model->setState('list.start', $offset); } /** * Sanity check we don't have too much data being requested as regularly in html we automatically set it back to * the last page of data. If there isn't a limit start then set */ if ($limit) { $model->setState('list.limit', $limit); } else { $model->setState('list.limit', $this->itemsPerPage); } if (!is_null($offset) && $offset > $model->getTotal()) { throw new Exception\ResourceNotFound(); } $view->document = $this->app->getDocument(); $view->displayList(); return $this; } /** * Removes an item. * * @param integer $id The primary key to delete item. * * @return void * * @since 4.0.0 */ public function delete($id = null) { if (!$this->app->getIdentity()->authorise('core.delete', $this->option)) { throw new NotAllowed('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED', 403); } if ($id === null) { $id = $this->input->get('id', 0, 'int'); } $modelName = $this->input->get('model', Inflector::singularize($this->contentType)); /** @var \Joomla\CMS\MVC\Model\AdminModel $model */ $model = $this->getModel($modelName, '', ['ignore_request' => true]); if (!$model) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); } // Remove the item. if (!$model->delete($id)) { if ($model->getError() !== false) { throw new \RuntimeException($model->getError(), 500); } throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_DELETE'), 500); } $this->app->setHeader('status', 204); } /** * Method to add a new record. * * @return void * * @since 4.0.0 * @throws NotAllowed * @throws \RuntimeException */ public function add() { // Access check. if (!$this->allowAdd()) { throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403); } $recordId = $this->save(); $this->displayItem($recordId); } /** * Method to edit an existing record. * * @return static A \JControllerLegacy object to support chaining. * * @since 4.0.0 */ public function edit() { /** @var \Joomla\CMS\MVC\Model\AdminModel $model */ $model = $this->getModel(Inflector::singularize($this->contentType)); if (!$model) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); } try { $table = $model->getTable(); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } $recordId = $this->input->getInt('id'); if (!$recordId) { throw new Exception\ResourceNotFound(Text::_('JLIB_APPLICATION_ERROR_RECORD'), 404); } $key = $table->getKeyName(); // Access check. if (!$this->allowEdit([$key => $recordId], $key)) { throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403); } // Attempt to check-out the new record for editing and redirect. if ($table->hasField('checked_out') && !$model->checkout($recordId)) { // Check-out failed, display a notice but allow the user to see the record. throw new Exception\CheckinCheckout(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKOUT_FAILED', $model->getError())); } $this->save($recordId); $this->displayItem($recordId); return $this; } /** * Method to save a record. * * @param integer $recordKey The primary key of the item (if exists) * * @return integer The record ID on success, false on failure * * @since 4.0.0 */ protected function save($recordKey = null) { /** @var \Joomla\CMS\MVC\Model\AdminModel $model */ $model = $this->getModel(Inflector::singularize($this->contentType)); if (!$model) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_MODEL_CREATE')); } try { $table = $model->getTable(); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } $key = $table->getKeyName(); $data = $this->input->get('data', json_decode($this->input->json->getRaw(), true), 'array'); $checkin = property_exists($table, $table->getColumnAlias('checked_out')); $data[$key] = $recordKey; if ($this->input->getMethod() === 'PATCH') { if ($recordKey && $table->load($recordKey)) { $fields = $table->getFields(); foreach ($fields as $field) { if (array_key_exists($field->Field, $data)) { continue; } $data[$field->Field] = $table->{$field->Field}; } } } $data = $this->preprocessSaveData($data); // @todo: Not the cleanest thing ever but it works... Form::addFormPath(JPATH_COMPONENT_ADMINISTRATOR . '/forms'); // Needs to be set because com_fields needs the data in jform to determine the assigned catid $this->input->set('jform', $data); // Validate the posted data. $form = $model->getForm($data, false); if (!$form) { throw new \RuntimeException(Text::_('JLIB_APPLICATION_ERROR_FORM_CREATE')); } // Test whether the data is valid. $validData = $model->validate($form, $data); // Check for validation errors. if ($validData === false) { $errors = $model->getErrors(); $messages = []; // Push up to three validation messages out to the user. for ($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) { if ($errors[$i] instanceof \Exception) { $messages[] = "{$errors[$i]->getMessage()}"; } else { $messages[] = "{$errors[$i]}"; } } throw new InvalidParameterException(implode("\n", $messages)); } if (!isset($validData['tags'])) { $validData['tags'] = []; } // Attempt to save the data. if (!$model->save($validData)) { throw new Exception\Save(Text::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError())); } try { $modelName = $model->getName(); } catch (\Exception $e) { throw new \RuntimeException($e->getMessage()); } // Ensure we have the record ID in case we created a new article $recordId = $model->getState($modelName . '.id'); if ($recordId === null) { throw new Exception\CheckinCheckout(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError())); } // Save succeeded, so check-in the record. if ($checkin && $model->checkin($recordId) === false) { throw new Exception\CheckinCheckout(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError())); } return $recordId; } /** * Method to check if you can edit an existing record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key; default is id. * * @return boolean * * @since 4.0.0 */ protected function allowEdit($data = [], $key = 'id') { return $this->app->getIdentity()->authorise('core.edit', $this->option); } /** * Method to check if you can add a new record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * * @return boolean * * @since 4.0.0 */ protected function allowAdd($data = []) { $user = $this->app->getIdentity(); return $user->authorise('core.create', $this->option) || \count($user->getAuthorisedCategories($this->option, 'core.create')); } /** * Method to allow extended classes to manipulate the data to be saved for an extension. * * @param array $data An array of input data. * * @return array * * @since 4.0.0 */ protected function preprocessSaveData(array $data): array { return $data; } } PK1A#]5Pz�����%src/MVC/Controller/BaseController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Document\DocumentAwareInterface; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\LanguageAwareInterface; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Factory\LegacyFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\MVC\Model\BaseModel; use Joomla\CMS\MVC\View\ViewInterface; use Joomla\CMS\Session\Session; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\CurrentUserInterface; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Controller * * Controller (Controllers are where you put all the actual code.) Provides basic * functionality, such as rendering views (aka displaying templates). * * @since 2.5.5 */ class BaseController implements ControllerInterface, DispatcherAwareInterface { use DispatcherAwareTrait; /** * The base path of the controller * * @var string * @since 3.0 */ protected $basePath; /** * The default view for the display method. * * @var string * @since 3.0 */ protected $default_view; /** * The mapped task that was performed. * * @var string * @since 3.0 */ protected $doTask; /** * Redirect message. * * @var string * @since 3.0 */ protected $message; /** * Redirect message type. * * @var string * @since 3.0 */ protected $messageType; /** * Array of class methods * * @var array * @since 3.0 */ protected $methods; /** * The name of the controller * * @var string * @since 3.0 */ protected $name; /** * The prefix of the models * * @var string * @since 3.0 */ protected $model_prefix; /** * The set of search directories for resources (views). * * @var array * @since 3.0 */ protected $paths; /** * URL for redirection. * * @var string * @since 3.0 */ protected $redirect; /** * Current or most recently performed task. * * @var string * @since 3.0 */ protected $task; /** * Array of class methods to call for a given task. * * @var array * @since 3.0 */ protected $taskMap; /** * Hold a JInput object for easier access to the input variables. * * @var Input * @since 3.0 */ protected $input; /** * The factory. * * @var MVCFactoryInterface * @since 3.10.0 */ protected $factory; /** * Instance container. * * @var static * @since 3.0 */ protected static $instance; /** * Instance container containing the views. * * @var ViewInterface[] * @since 3.4 */ protected static $views; /** * The Application * * @var CMSApplication|null * @since 4.0.0 */ protected $app; /** * Adds to the stack of model paths in LIFO order. * * @param mixed $path The directory (string), or list of directories (array) to add. * @param string $prefix A prefix for models * * @return void * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement. Get the model through the MVCFactory instead */ public static function addModelPath($path, $prefix = '') { BaseModel::addIncludePath($path, $prefix); } /** * Create the filename for a resource. * * @param string $type The resource type to create the filename for. * @param array $parts An associative array of filename information. Optional. * * @return string The filename. * * @since 3.0 */ public static function createFileName($type, $parts = []) { $filename = ''; switch ($type) { case 'controller': if (!empty($parts['format'])) { if ($parts['format'] === 'html') { $parts['format'] = ''; } else { $parts['format'] = '.' . $parts['format']; } } else { $parts['format'] = ''; } $filename = strtolower($parts['name'] . $parts['format'] . '.php'); break; case 'view': if (!empty($parts['type'])) { $parts['type'] = '.' . $parts['type']; } else { $parts['type'] = ''; } $filename = strtolower($parts['name'] . '/view' . $parts['type'] . '.php'); break; } return $filename; } /** * Method to get a singleton controller instance. * * @param string $prefix The prefix for the controller. * @param array $config An array of optional constructor options. * * @return static * * @since 3.0 * * @deprecated 4.3 will be removed in 6.0 * Get the controller through the MVCFactory instead * Example: Factory::getApplication()->bootComponent($option)->getMVCFactory()->createController(...); * * @throws \Exception if the controller cannot be loaded. */ public static function getInstance($prefix, $config = []) { if (\is_object(self::$instance)) { return self::$instance; } @trigger_error( sprintf( '%1$s::getInstance() is deprecated. Load it through the MVC factory.', self::class ), E_USER_DEPRECATED ); $app = Factory::getApplication(); $input = $app->getInput(); // Get the environment configuration. $basePath = \array_key_exists('base_path', $config) ? $config['base_path'] : JPATH_COMPONENT; $format = $input->getWord('format'); $command = $input->get('task', 'display'); // Check for array format. $filter = InputFilter::getInstance(); if (\is_array($command)) { $keys = array_keys($command); $command = $filter->clean(array_pop($keys), 'cmd'); } else { $command = $filter->clean($command, 'cmd'); } // Check for a controller.task command. if (strpos($command, '.') !== false) { // Explode the controller.task command. list($type, $task) = explode('.', $command); // Define the controller filename and path. $file = self::createFileName('controller', ['name' => $type, 'format' => $format]); $path = $basePath . '/controllers/' . $file; $backuppath = $basePath . '/controller/' . $file; // Reset the task without the controller context. $input->set('task', $task); } else { // Base controller. $type = ''; // Define the controller filename and path. $file = self::createFileName('controller', ['name' => 'controller', 'format' => $format]); $path = $basePath . '/' . $file; $backupfile = self::createFileName('controller', ['name' => 'controller']); $backuppath = $basePath . '/' . $backupfile; } // Get the controller class name. $class = ucfirst($prefix) . 'Controller' . ucfirst($type); // Include the class if not present. if (!class_exists($class)) { // If the controller file path exists, include it. if (is_file($path)) { require_once $path; } elseif (isset($backuppath) && is_file($backuppath)) { require_once $backuppath; } else { throw new \InvalidArgumentException(Text::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER', $type, $format)); } } // Instantiate the class. if (!class_exists($class)) { throw new \InvalidArgumentException(Text::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER_CLASS', $class)); } // Check for a possible service from the container otherwise manually instantiate the class if (Factory::getContainer()->has($class)) { self::$instance = Factory::getContainer()->get($class); } else { self::$instance = new $class($config, null, $app, $input); } return self::$instance; } /** * Constructor. * * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'default_task', 'model_path', and * 'view_path' (this list is not meant to be comprehensive). * @param ?MVCFactoryInterface $factory The factory. * @param ?CMSApplication $app The Application for the dispatcher * @param ?Input $input Input * * @since 3.0 */ public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { $this->methods = []; $this->message = null; $this->messageType = 'message'; $this->paths = []; $this->redirect = null; $this->taskMap = []; $this->app = $app ?: Factory::getApplication(); $this->input = $input ?: $this->app->getInput(); if (\defined('JDEBUG') && JDEBUG) { Log::addLogger(['text_file' => 'jcontroller.log.php'], Log::ALL, ['controller']); } // Determine the methods to exclude from the base class. $xMethods = get_class_methods('\\Joomla\\CMS\\MVC\\Controller\\BaseController'); // Get the public methods in this class using reflection. $r = new \ReflectionClass($this); $rMethods = $r->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($rMethods as $rMethod) { $mName = $rMethod->getName(); // Add default display method if not explicitly declared. if ($mName === 'display' || !\in_array($mName, $xMethods)) { $this->methods[] = strtolower($mName); // Auto register the methods as tasks. $this->taskMap[strtolower($mName)] = $mName; } } // Set the view name if (empty($this->name)) { if (\array_key_exists('name', $config)) { $this->name = $config['name']; } else { $this->name = $this->getName(); } } // Set a base path for use by the controller if (\array_key_exists('base_path', $config)) { $this->basePath = $config['base_path']; } else { $this->basePath = JPATH_COMPONENT; } // If the default task is set, register it as such if (\array_key_exists('default_task', $config)) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask('display'); } // Set the models prefix if (empty($this->model_prefix)) { if (\array_key_exists('model_prefix', $config)) { // User-defined prefix $this->model_prefix = $config['model_prefix']; } else { $this->model_prefix = ucfirst($this->name) . 'Model'; } } // Set the default model search path if (\array_key_exists('model_path', $config)) { // User-defined dirs $this->addModelPath($config['model_path'], $this->model_prefix); } else { $this->addModelPath($this->basePath . '/models', $this->model_prefix); } // Set the default view search path if (\array_key_exists('view_path', $config)) { // User-defined dirs $this->setPath('view', $config['view_path']); } else { $this->setPath('view', $this->basePath . '/views'); } // Set the default view. if (\array_key_exists('default_view', $config)) { $this->default_view = $config['default_view']; } elseif (empty($this->default_view)) { $this->default_view = $this->getName(); } $this->factory = $factory ?: new LegacyFactory(); } /** * Adds to the search path for templates and resources. * * @param string $type The path type (e.g. 'model', 'view'). * @param mixed $path The directory string or stream array to search. * * @return static A \JControllerLegacy object to support chaining. * * @since 3.0 */ protected function addPath($type, $path) { if (!isset($this->paths[$type])) { $this->paths[$type] = []; } // Loop through the path directories foreach ((array) $path as $dir) { // No surrounding spaces allowed! $dir = rtrim(Path::check($dir), '/') . '/'; // Add to the top of the search dirs array_unshift($this->paths[$type], $dir); } return $this; } /** * Add one or more view paths to the controller's stack, in LIFO order. * * @param mixed $path The directory (string) or list of directories (array) to add. * * @return static This object to support chaining. * * @since 3.0 */ public function addViewPath($path) { return $this->addPath('view', $path); } /** * Method to check whether an ID is in the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return boolean True if the ID is in the edit list. * * @since 3.0 */ protected function checkEditId($context, $id) { if ($id) { $values = (array) $this->app->getUserState($context . '.id'); $result = \in_array((int) $id, $values); if (\defined('JDEBUG') && JDEBUG) { $this->app->getLogger()->info( sprintf( 'Checking edit ID %s.%s: %d %s', $context, $id, (int) $result, str_replace("\n", ' ', print_r($values, 1)) ), ['category' => 'controller'] ); } return $result; } // No id for a new item. return true; } /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Configuration array for the model. Optional. * * @return BaseDatabaseModel|boolean Model object on success; otherwise false on failure. * * @since 3.0 */ protected function createModel($name, $prefix = '', $config = []) { $model = $this->factory->createModel($name, $prefix, $config); if ($model === null) { return false; } if ($model instanceof CurrentUserInterface && $this->app->getIdentity()) { $model->setCurrentUser($this->app->getIdentity()); } return $model; } /** * Method to load and return a view object. This method first looks in the * current template directory for a match and, failing that, uses a default * set path to load the view class file. * * Note the "name, prefix, type" order of parameters, which differs from the * "name, type, prefix" order used in related public methods. * * @param string $name The name of the view. * @param string $prefix Optional prefix for the view class name. * @param string $type The type of view. * @param array $config Configuration array for the view. Optional. * * @return ViewInterface|null View object on success; null or error result on failure. * * @since 3.0 * @throws \Exception */ protected function createView($name, $prefix = '', $type = '', $config = []) { $config['paths'] = $this->paths['view']; $view = $this->factory->createView($name, $prefix, $type, $config); if ($view instanceof CurrentUserInterface && $this->app->getIdentity()) { $view->setCurrentUser($this->app->getIdentity()); } if ($view instanceof LanguageAwareInterface && $this->app->getLanguage()) { $view->setLanguage($this->app->getLanguage()); } return $view; } /** * Typical view method for MVC based architecture * * This function is provide as a default implementation, in most cases * you will need to override it in your own controllers. * * @param boolean $cachable If true, the view output will be cached * @param array $urlparams An array of safe url parameters and their variable types, for valid values see {@link InputFilter::clean()}. * * @return static A \JControllerLegacy object to support chaining. * * @since 3.0 * @throws \Exception */ public function display($cachable = false, $urlparams = []) { $document = $this->app->getDocument(); $viewType = $document->getType(); $viewName = $this->input->get('view', $this->default_view); $viewLayout = $this->input->get('layout', 'default', 'string'); $view = $this->getView($viewName, $viewType, '', ['base_path' => $this->basePath, 'layout' => $viewLayout]); // Get/Create the model if ($model = $this->getModel($viewName, '', ['base_path' => $this->basePath])) { // Push the model into the view (as default) $view->setModel($model, true); } if ($view instanceof DocumentAwareInterface && $document) { $view->setDocument($this->app->getDocument()); } else { @trigger_error( 'View should implement document aware interface.', E_USER_DEPRECATED ); $view->document = $document; } // Display the view if ($cachable && $viewType !== 'feed' && $this->app->get('caching') >= 1) { $option = $this->input->get('option'); if (\is_array($urlparams)) { if (!empty($this->app->registeredurlparams)) { $registeredurlparams = $this->app->registeredurlparams; } else { $registeredurlparams = new \stdClass(); } foreach ($urlparams as $key => $value) { // Add your safe URL parameters with variable type as value {@see InputFilter::clean()}. $registeredurlparams->$key = $value; } $this->app->registeredurlparams = $registeredurlparams; } try { /** @var \Joomla\CMS\Cache\Controller\ViewController $cache */ $cache = Factory::getCache($option, 'view'); $cache->get($view, 'display'); } catch (CacheExceptionInterface $exception) { $view->display(); } } else { $view->display(); } return $this; } /** * Execute a task by triggering a method in the derived class. * * @param string $task The task to perform. If no matching task is found, the '__default' task is executed, if defined. * * @return mixed The value returned by the called method. * * @since 3.0 * @throws \Exception */ public function execute($task) { $this->task = $task; $task = strtolower((string) $task); if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } // Record the actual task being fired $this->doTask = $doTask; return $this->$doTask(); } /** * Method to get a model object, loading it if required. * * @param string $name The model name. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for model. Optional. * * @return BaseDatabaseModel|boolean Model object on success; otherwise false on failure. * * @since 3.0 */ public function getModel($name = '', $prefix = '', $config = []) { if (empty($name)) { $name = $this->getName(); } if (!$prefix) { if ($this->factory instanceof LegacyFactory) { $prefix = $this->model_prefix; } elseif (!empty($config['base_path']) && strpos(Path::clean($config['base_path']), JPATH_ADMINISTRATOR) === 0) { // When the frontend uses an administrator model $prefix = 'Administrator'; } else { $prefix = $this->app->getName(); } } if ($model = $this->createModel($name, $prefix, $config)) { // Task is a reserved state $model->setState('task', $this->task); // We don't have the concept on a menu tree in the api app, so skip setting it's information and // return early if ($this->app->isClient('api')) { return $model; } // Let's get the application object and set menu information if it's available $menu = $this->app->getMenu(); if (\is_object($menu) && $item = $menu->getActive()) { $params = $menu->getParams($item->id); // Set default state data $model->setState('parameters.menu', $params); } } return $model; } /** * Method to get the controller name * * The dispatcher name is set by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the dispatcher * * @since 3.0 * @throws \Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)Controller/i', \get_class($this), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } $this->name = strtolower($r[1]); } return $this->name; } /** * Get the last task that is being performed or was most recently performed. * * @return string The task that is being performed or was most recently performed. * * @since 3.0 */ public function getTask() { return $this->task; } /** * Gets the available tasks in the controller. * * @return array Array[i] of task names. * * @since 3.0 */ public function getTasks() { return $this->methods; } /** * Method to get a reference to the current view and load it if necessary. * * @param string $name The view name. Optional, defaults to the controller name. * @param string $type The view type. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for view. Optional. * * @return ViewInterface Reference to the view or an error. * * @since 3.0 * @throws \Exception */ public function getView($name = '', $type = '', $prefix = '', $config = []) { // @note We use self so we only access stuff in this class rather than in all classes. if (!isset(self::$views)) { self::$views = []; } if (empty($name)) { $name = $this->getName(); } if (!$prefix) { if ($this->factory instanceof LegacyFactory) { $prefix = $this->getName() . 'View'; } elseif (!empty($config['base_path']) && strpos(Path::clean($config['base_path']), JPATH_ADMINISTRATOR) === 0) { // When the front uses an administrator view $prefix = 'Administrator'; } else { $prefix = $this->app->getName(); } } if (empty(self::$views[$name][$type][$prefix])) { if ($view = $this->createView($name, $prefix, $type, $config)) { self::$views[$name][$type][$prefix] = &$view; } else { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_VIEW_NOT_FOUND', $name, $type, $prefix), 404); } } return self::$views[$name][$type][$prefix]; } /** * Method to add a record ID to the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return void * * @since 3.0 */ protected function holdEditId($context, $id) { $values = (array) $this->app->getUserState($context . '.id'); // Add the id to the list if non-zero. if (!empty($id)) { $values[] = (int) $id; $values = array_unique($values); $this->app->setUserState($context . '.id', $values); if (\defined('JDEBUG') && JDEBUG) { $this->app->getLogger()->info( sprintf( 'Holding edit ID %s.%s %s', $context, $id, str_replace("\n", ' ', print_r($values, 1)) ), ['category' => 'controller'] ); } } } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. * * @since 3.0 */ public function redirect() { if ($this->redirect) { // Enqueue the redirect message $this->app->enqueueMessage($this->message, $this->messageType); // Execute the redirect $this->app->redirect($this->redirect); } return false; } /** * Register the default task to perform if a mapping is not found. * * @param string $method The name of the method in the derived class to perform if a named task is not found. * * @return static A \JControllerLegacy object to support chaining. * * @since 3.0 */ public function registerDefaultTask($method) { $this->registerTask('__default', $method); return $this; } /** * Register (map) a task to a method in the class. * * @param string $task The task. * @param string $method The name of the method in the derived class to perform for this task. * * @return static A \JControllerLegacy object to support chaining. * * @since 3.0 */ public function registerTask($task, $method) { if (\in_array(strtolower($method), $this->methods)) { $this->taskMap[strtolower($task)] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return static This object to support chaining. * * @since 3.0 */ public function unregisterTask($task) { unset($this->taskMap[strtolower($task)]); return $this; } /** * Method to check whether an ID is in the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return void * * @since 3.0 */ protected function releaseEditId($context, $id) { $values = (array) $this->app->getUserState($context . '.id'); // Do a strict search of the edit list values. $index = array_search((int) $id, $values, true); if (\is_int($index)) { unset($values[$index]); $this->app->setUserState($context . '.id', $values); if (\defined('JDEBUG') && JDEBUG) { $this->app->getLogger()->info( sprintf( 'Releasing edit ID %s.%s %s', $context, $id, str_replace("\n", ' ', print_r($values, 1)) ), ['category' => 'controller'] ); } } } /** * Sets the internal message that is passed with a redirect * * @param string $text Message to display on redirect. * @param string $type Message type. Optional, defaults to 'message'. * * @return string Previous message * * @since 3.0 */ public function setMessage($text, $type = 'message') { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Sets an entire array of search paths for resources. * * @param string $type The type of path to set, typically 'view' or 'model'. * @param string $path The new set of search paths. If null or false, resets to the current directory only. * * @return void * * @since 3.0 */ protected function setPath($type, $path) { // Clear out the prior search dirs $this->paths[$type] = []; // Actually add the user-specified directories $this->addPath($type, $path); } /** * Checks for a form token in the request. * * Use in conjunction with HTMLHelper::_('form.token') or Session::getFormToken. * * @param string $method The request method in which to look for the token key. * @param boolean $redirect Whether to implicitly redirect user to the referrer page on failure or simply return false. * * @return boolean True if found and valid, otherwise return false or redirect to referrer page. * * @since 3.7.0 * @see Session::checkToken() */ public function checkToken($method = 'post', $redirect = true) { $valid = Session::checkToken($method); if (!$valid && $redirect) { $referrer = $this->input->server->getString('HTTP_REFERER'); if (\is_null($referrer) || !Uri::isInternal($referrer)) { $referrer = 'index.php'; } $this->app->enqueueMessage(Text::_('JINVALID_TOKEN_NOTICE'), 'warning'); $this->app->redirect($referrer); } return $valid; } /** * Set a URL for browser redirection. * * @param string $url URL to redirect to. * @param string $msg Message to display on redirect. Optional, defaults to value set internally by controller, if any. * @param string $type Message type. Optional, defaults to 'message' or the type set by a previous call to setMessage. * * @return static This object to support chaining. * * @since 3.0 */ public function setRedirect($url, $msg = null, $type = null) { $this->redirect = $url; if ($msg !== null) { // Controller may have set this directly $this->message = $msg; } // Ensure the type is not overwritten by a previous call to setMessage. if (empty($type)) { if (empty($this->messageType)) { $this->messageType = 'message'; } } else { // If the type is explicitly set, set it. $this->messageType = $type; } return $this; } /** * Get the event dispatcher. * * The override was made to keep a backward compatibility for legacy component. * TODO: Remove the override in 6.0 * * @return DispatcherInterface * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ public function getDispatcher() { if (!$this->dispatcher) { @trigger_error( sprintf('Dispatcher for %s should be set through MVC factory. It will throw an exception in 6.0', __CLASS__), E_USER_DEPRECATED ); return $this->app->getDispatcher(); } return $this->dispatcher; } } PK1A#]��<��q�q%src/MVC/Controller/FormController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; use Doctrine\Inflector\InflectorFactory; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Form\FormFactoryAwareInterface; use Joomla\CMS\Form\FormFactoryAwareTrait; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Controller tailored to suit most form-based admin operations. * * @since 1.6 * @todo Add ability to set redirect manually to better cope with frontend usage. */ class FormController extends BaseController implements FormFactoryAwareInterface { use FormFactoryAwareTrait; /** * The context for storing internal data, e.g. record. * * @var string * @since 1.6 */ protected $context; /** * The URL option for the component. * * @var string * @since 1.6 */ protected $option; /** * The URL view item variable. * * @var string * @since 1.6 */ protected $view_item; /** * The URL view list variable. * * @var string * @since 1.6 */ protected $view_list; /** * The prefix to use with controller messages. * * @var string * @since 1.6 */ protected $text_prefix; /** * Constructor. * * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'default_task', 'model_path', and * 'view_path' (this list is not meant to be comprehensive). * @param ?MVCFactoryInterface $factory The factory. * @param ?CMSApplication $app The Application for the dispatcher * @param ?Input $input Input * @param ?FormFactoryInterface $formFactory The form factory. * * @since 3.0 */ public function __construct( $config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, FormFactoryInterface $formFactory = null ) { parent::__construct($config, $factory, $app, $input); // Guess the option as com_NameOfController if (empty($this->option)) { $this->option = ComponentHelper::getComponentName($this, $this->getName()); } // Guess the \Text message prefix. Defaults to the option. if (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } // Guess the context as the suffix, eg: OptionControllerContent. if (empty($this->context)) { $r = null; $match = 'Controller'; // If there is a namespace append a backslash if (strpos(\get_class($this), '\\')) { $match .= '\\\\'; } if (!preg_match('/(.*)' . $match . '(.*)/i', \get_class($this), $r)) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_GET_NAME', __METHOD__), 500); } // Remove the backslashes and the suffix controller $this->context = str_replace(['\\', 'controller'], '', strtolower($r[2])); } // Guess the item view as the context. if (empty($this->view_item)) { $this->view_item = $this->context; } // Guess the list view as the plural of the item view. if (empty($this->view_list)) { $this->view_list = InflectorFactory::create()->build()->pluralize($this->view_item); } $this->setFormFactory($formFactory); // Apply, Save & New, and Save As copy should be standard on forms. $this->registerTask('apply', 'save'); $this->registerTask('save2menu', 'save'); $this->registerTask('save2new', 'save'); $this->registerTask('save2copy', 'save'); $this->registerTask('editAssociations', 'save'); } /** * Method to add a new record. * * @return boolean True if the record can be added, false if not. * * @since 1.6 */ public function add() { $context = "$this->option.edit.$this->context"; // Access check. if (!$this->allowAdd()) { // Set the internal error and also the redirect error. $this->setMessage(Text::_('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED'), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Clear the record edit information from the session. $this->app->setUserState($context . '.data', null); // Redirect to the edit screen. $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend(), false ) ); return true; } /** * Method to check if you can add a new record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * * @return boolean * * @since 1.6 */ protected function allowAdd($data = []) { $user = $this->app->getIdentity(); return $user->authorise('core.create', $this->option) || \count($user->getAuthorisedCategories($this->option, 'core.create')); } /** * Method to check if you can edit an existing record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key; default is id. * * @return boolean * * @since 1.6 */ protected function allowEdit($data = [], $key = 'id') { return $this->app->getIdentity()->authorise('core.edit', $this->option); } /** * Method to check if you can save a new or existing record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key. * * @return boolean * * @since 1.6 */ protected function allowSave($data, $key = 'id') { $recordId = $data[$key] ?? '0'; if ($recordId) { return $this->allowEdit($data, $key); } else { return $this->allowAdd($data); } } /** * Method to run batch operations. * * @param BaseDatabaseModel $model The model of the component being processed. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 1.7 */ public function batch($model) { $vars = $this->input->post->get('batch', [], 'array'); $cid = (array) $this->input->post->get('cid', [], 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); // Build an array of item contexts to check $contexts = []; $option = $this->extension ?? $this->option; foreach ($cid as $id) { // If we're coming from com_categories, we need to use extension vs. option $contexts[$id] = $option . '.' . $this->context . '.' . $id; } // Attempt to run the batch operation. if ($model->batch($vars, $cid, $contexts)) { $this->setMessage(Text::_('JLIB_APPLICATION_SUCCESS_BATCH')); return true; } else { $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_FAILED', $model->getError()), 'warning'); return false; } } /** * Method to cancel an edit. * * @param string $key The name of the primary key of the URL variable. * * @return boolean True if access level checks pass, false otherwise. * * @since 1.6 */ public function cancel($key = null) { $this->checkToken(); $model = $this->getModel(); $table = $model->getTable(); $context = "$this->option.edit.$this->context"; if (empty($key)) { $key = $table->getKeyName(); } $recordId = $this->input->getInt($key); // Attempt to check-in the current record. if ($recordId && $table->hasField('checked_out') && $model->checkin($recordId) === false) { // Check-in failed, go back to the record and display a notice. $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $key), false ) ); return false; } // Clean the session data and redirect. $this->releaseEditId($context, $recordId); $this->app->setUserState($context . '.data', null); $url = 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); if (!\is_null($return) && Uri::isInternal(base64_decode($return))) { $url = base64_decode($return); } // Redirect to the list screen. $this->setRedirect(Route::_($url, false)); return true; } /** * Method to edit an existing record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key * (sometimes required to avoid router collisions). * * @return boolean True if access level check and checkout passes, false otherwise. * * @since 1.6 */ public function edit($key = null, $urlVar = null) { // Do not cache the response to this, its a redirect, and mod_expires and google chrome browser bugs cache it forever! $this->app->allowCache(false); $model = $this->getModel(); $table = $model->getTable(); $cid = (array) $this->input->post->get('cid', [], 'int'); $context = "$this->option.edit.$this->context"; // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } // Get the previous record id (if any) and the current record id. $recordId = (int) (\count($cid) ? $cid[0] : $this->input->getInt($urlVar)); $checkin = $table->hasField('checked_out'); // Access check. if (!$this->allowEdit([$key => $recordId], $key)) { $this->setMessage(Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Attempt to check-out the new record for editing and redirect. if ($checkin && !$model->checkout($recordId)) { // Check-out failed, display a notice but allow the user to see the record. $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKOUT_FAILED', $model->getError()), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } else { // Check-out succeeded, push the new record id into the session. $this->holdEditId($context, $recordId); $this->app->setUserState($context . '.data', null); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return true; } } /** * Method to get a model object, loading it if required. * * @param string $name The model name. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for model. Optional. * * @return BaseDatabaseModel The model. * * @since 1.6 */ public function getModel($name = '', $prefix = '', $config = ['ignore_request' => true]) { if (empty($name)) { $name = $this->context; } return parent::getModel($name, $prefix, $config); } /** * Gets the URL arguments to append to an item redirect. * * @param integer $recordId The primary key id for the item. * @param string $urlVar The name of the URL variable for the id. * * @return string The arguments to append to the redirect URL. * * @since 1.6 */ protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') { $append = ''; // Setup redirect info. if ($tmpl = $this->input->get('tmpl', '', 'string')) { $append .= '&tmpl=' . $tmpl; } if ($layout = $this->input->get('layout', 'edit', 'string')) { $append .= '&layout=' . $layout; } if ($forcedLanguage = $this->input->get('forcedLanguage', '', 'cmd')) { $append .= '&forcedLanguage=' . $forcedLanguage; } if ($recordId) { $append .= '&' . $urlVar . '=' . $recordId; } $return = $this->input->get('return', null, 'base64'); if ($return) { $append .= '&return=' . $return; } return $append; } /** * Gets the URL arguments to append to a list redirect. * * @return string The arguments to append to the redirect URL. * * @since 1.6 */ protected function getRedirectToListAppend() { $append = ''; // Setup redirect info. if ($tmpl = $this->input->get('tmpl', '', 'string')) { $append .= '&tmpl=' . $tmpl; } if ($forcedLanguage = $this->input->get('forcedLanguage', '', 'cmd')) { $append .= '&forcedLanguage=' . $forcedLanguage; } return $append; } /** * Function that allows child controller access to model data * after the data has been saved. * * @param BaseDatabaseModel $model The data model object. * @param array $validData The validated data. * * @return void * * @since 1.6 */ protected function postSaveHook(BaseDatabaseModel $model, $validData = []) { } /** * Method to save a record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). * * @return boolean True if successful, false otherwise. * * @since 1.6 */ public function save($key = null, $urlVar = null) { // Check for request forgeries. $this->checkToken(); $app = $this->app; $model = $this->getModel(); $table = $model->getTable(); $data = $this->input->post->get('jform', [], 'array'); $checkin = $table->hasField('checked_out'); $context = "$this->option.edit.$this->context"; $task = $this->getTask(); // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } $recordId = $this->input->getInt($urlVar); // Populate the row id from the session. $data[$key] = $recordId; // The save2copy task needs to be handled slightly differently. if ($task === 'save2copy') { // Check-in the original row. if ($checkin && $model->checkin($data[$key]) === false) { // Check-in failed. Go back to the item and display a notice. $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } // Reset the ID, the multilingual associations and then treat the request as for Apply. $data[$key] = 0; $data['associations'] = []; $task = 'apply'; } // Access check. if (!$this->allowSave($data, $key)) { $this->setMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Validate the posted data. // Sometimes the form needs some posted data, such as for plugins and modules. $form = $model->getForm($data, false); if (!$form) { $app->enqueueMessage($model->getError(), 'error'); return false; } // Send an object which can be modified through the plugin event $objData = (object) $data; $app->triggerEvent( 'onContentNormaliseRequestData', [$this->option . '.' . $this->context, $objData, $form] ); $data = (array) $objData; // Test whether the data is valid. $validData = $model->validate($form, $data); // Check for validation errors. if ($validData === false) { // Get the validation messages. $errors = $model->getErrors(); // Push up to three validation messages out to the user. for ($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) { if ($errors[$i] instanceof \Exception) { $app->enqueueMessage($errors[$i]->getMessage(), 'warning'); } else { $app->enqueueMessage($errors[$i], 'warning'); } } /** * We need the filtered value of calendar fields because the UTC normalisation is * done in the filter and on output. This would apply the Timezone offset on * reload. We set the calendar values we save to the processed date. */ $filteredData = $form->filter($data); foreach ($form->getFieldset() as $field) { if ($field->type === 'Calendar') { $fieldName = $field->fieldname; if (isset($filteredData[$fieldName])) { $data[$fieldName] = $filteredData[$fieldName]; } } } // Save the data in the session. $app->setUserState($context . '.data', $data); // Redirect back to the edit screen. $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } if (!isset($validData['tags'])) { $validData['tags'] = []; } // Attempt to save the data. if (!$model->save($validData)) { // Save the data in the session. $app->setUserState($context . '.data', $validData); // Redirect back to the edit screen. $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } // Save succeeded, so check-in the record. if ($checkin && $model->checkin($validData[$key]) === false) { // Save the data in the session. $app->setUserState($context . '.data', $validData); // Check-in failed, so go back to the record and display a notice. $this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()), 'error'); $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } $langKey = $this->text_prefix . ($recordId === 0 && $app->isClient('site') ? '_SUBMIT' : '') . '_SAVE_SUCCESS'; $prefix = $this->app->getLanguage()->hasKey($langKey) ? $this->text_prefix : 'JLIB_APPLICATION'; $this->setMessage(Text::_($prefix . ($recordId === 0 && $app->isClient('site') ? '_SUBMIT' : '') . '_SAVE_SUCCESS')); // Redirect the user and adjust session state based on the chosen task. switch ($task) { case 'apply': // Set the record data in the session. $recordId = $model->getState($model->getName() . '.id'); $this->holdEditId($context, $recordId); $app->setUserState($context . '.data', null); $model->checkout($recordId); // Redirect back to the edit screen. $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); break; case 'save2new': // Clear the record id and data from the session. $this->releaseEditId($context, $recordId); $app->setUserState($context . '.data', null); // Redirect back to the edit screen. $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend(null, $urlVar), false ) ); break; default: // Clear the record id and data from the session. $this->releaseEditId($context, $recordId); $app->setUserState($context . '.data', null); $url = 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); if (!\is_null($return) && Uri::isInternal(base64_decode($return))) { $url = base64_decode($return); } // Redirect to the list screen. $this->setRedirect(Route::_($url, false)); break; } // Invoke the postSave method to allow for the child class to access the model. $this->postSaveHook($model, $validData); return true; } /** * Method to reload a record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). * * @return void * * @since 3.7.4 */ public function reload($key = null, $urlVar = null) { // Check for request forgeries. $this->checkToken(); $app = $this->app; $model = $this->getModel(); $data = $this->input->post->get('jform', [], 'array'); // Determine the name of the primary key for the data. if (empty($key)) { $key = $model->getTable()->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } $recordId = $this->input->getInt($urlVar); // Populate the row id from the session. $data[$key] = $recordId; // Check if it is allowed to edit or create the data if (($recordId && !$this->allowEdit($data, $key)) || (!$recordId && !$this->allowAdd($data))) { $this->setRedirect( Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); $this->redirect(); } // The redirect url $redirectUrl = Route::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ); /** @var \Joomla\CMS\Form\Form $form */ $form = $model->getForm($data, false); /** * We need the filtered value of calendar fields because the UTC normalisation is * done in the filter and on output. This would apply the Timezone offset on * reload. We set the calendar values we save to the processed date. */ $filteredData = $form->filter($data); foreach ($form->getFieldset() as $field) { if ($field->type === 'Calendar') { $fieldName = $field->fieldname; if ($field->group) { if (isset($filteredData[$field->group][$fieldName])) { $data[$field->group][$fieldName] = $filteredData[$field->group][$fieldName]; } } else { if (isset($filteredData[$fieldName])) { $data[$fieldName] = $filteredData[$fieldName]; } } } } // Save the data in the session. $app->setUserState($this->option . '.edit.' . $this->context . '.data', $data); $this->setRedirect($redirectUrl); $this->redirect(); } /** * Load item to edit associations in com_associations * * @return void * * @since 3.9.0 * * @deprecated 4.3 will be removed in 6.0 * It is handled by regular save method now. */ public function editAssociations() { // Initialise variables. $app = $this->app; $input = $app->getInput(); $model = $this->getModel(); $data = $input->get('jform', [], 'array'); $model->editAssociations($data); } } PK1A#]$��O *src/MVC/Controller/ControllerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform CMS Interface * * @since 4.0.0 */ interface ControllerInterface { /** * Execute a controller task. * * @param string $task The task to perform. * * @return mixed The value returned by the called method. * * @since 4.0.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function execute($task); } PK1A#]WQ�wP!P!src/MVC/View/JsonApiView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Document\JsonapiDocument; use Joomla\CMS\Factory; use Joomla\CMS\MVC\View\Event\OnGetApiFields; use Joomla\CMS\Router\Exception\RouteNotFoundException; use Joomla\CMS\Serializer\JoomlaSerializer; use Joomla\CMS\Uri\Uri; use Tobscure\JsonApi\AbstractSerializer; use Tobscure\JsonApi\Collection; use Tobscure\JsonApi\Resource; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Json List View * * Class holding methods for displaying presentation data. * * @since 4.0.0 */ abstract class JsonApiView extends JsonView { /** * The active document object (Redeclared for typehinting) * * @var JsonapiDocument * @since 3.0 */ public $document; /** * The content type * * @var string */ protected $type; /** * Item relationship * * @var array * * @since 4.0.0 */ protected $relationship = []; /** * Serializer data * * @var AbstractSerializer * @since 4.0.0 */ protected $serializer; /** * The fields to render item in the documents * * @var array * @since 4.0.0 */ protected $fieldsToRenderItem = []; /** * The fields to render items in the documents * * @var array * @since 4.0.0 */ protected $fieldsToRenderList = []; /** * Constructor. * * @param array $config A named configuration array for object construction. * contentType: the name (optional) of the content type to use for the serialization * * @since 4.0.0 */ public function __construct($config = []) { if (\array_key_exists('contentType', $config)) { $this->type = $config['contentType']; } if ($this->serializer === null) { $this->serializer = new JoomlaSerializer($this->type); } parent::__construct($config); } /** * Execute and display a template script. * * @param array|null $items Array of items * * @return string * * @since 4.0.0 */ public function displayList(array $items = null) { /** @var \Joomla\CMS\MVC\Model\ListModel $model */ $model = $this->getModel(); // Get page query $currentUrl = Uri::getInstance(); $currentPageDefaultInformation = ['offset' => 0, 'limit' => 20]; $currentPageQuery = $currentUrl->getVar('page', $currentPageDefaultInformation); if ($items === null) { $items = []; foreach ($model->getItems() as $item) { $items[] = $this->prepareItem($item); } } $pagination = $model->getPagination(); // Check for errors. if (\count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); } if ($this->type === null) { throw new \RuntimeException('Content type missing'); } // Set up links for pagination $totalItemsCount = ($pagination->pagesTotal * $pagination->limit); $this->getDocument()->addMeta('total-pages', $pagination->pagesTotal) ->addLink('self', (string) $currentUrl); // Check for first and previous pages if ($pagination->limitstart > 0) { $firstPage = clone $currentUrl; $firstPageQuery = $currentPageQuery; $firstPageQuery['offset'] = 0; $firstPage->setVar('page', $firstPageQuery); $previousPage = clone $currentUrl; $previousPageQuery = $currentPageQuery; $previousOffset = $currentPageQuery['offset'] - $pagination->limit; $previousPageQuery['offset'] = $previousOffset >= 0 ? $previousOffset : 0; $previousPage->setVar('page', $previousPageQuery); $this->getDocument()->addLink('first', $this->queryEncode((string) $firstPage)) ->addLink('previous', $this->queryEncode((string) $previousPage)); } // Check for next and last pages if ($pagination->limitstart + $pagination->limit < $totalItemsCount) { $nextPage = clone $currentUrl; $nextPageQuery = $currentPageQuery; $nextOffset = $currentPageQuery['offset'] + $pagination->limit; $nextPageQuery['offset'] = ($nextOffset > ($pagination->pagesTotal * $pagination->limit)) ? $pagination->pagesTotal - $pagination->limit : $nextOffset; $nextPage->setVar('page', $nextPageQuery); $lastPage = clone $currentUrl; $lastPageQuery = $currentPageQuery; $lastPageQuery['offset'] = ($pagination->pagesTotal - 1) * $pagination->limit; $lastPage->setVar('page', $lastPageQuery); $this->getDocument()->addLink('next', $this->queryEncode((string) $nextPage)) ->addLink('last', $this->queryEncode((string) $lastPage)); } $eventData = ['type' => OnGetApiFields::LIST, 'fields' => $this->fieldsToRenderList, 'context' => $this->type]; $event = new OnGetApiFields('onApiGetFields', $eventData); /** @var OnGetApiFields $eventResult */ $eventResult = Factory::getApplication()->getDispatcher()->dispatch('onApiGetFields', $event); $collection = (new Collection($items, $this->serializer)) ->fields([$this->type => $eventResult->getAllPropertiesToRender()]); if (!empty($this->relationship)) { $collection->with($this->relationship); } // Set the data into the document and render it $this->getDocument()->setData($collection); return $this->getDocument()->render(); } /** * Execute and display a template script. * * @param object $item Item * * @return string * * @since 4.0.0 */ public function displayItem($item = null) { if ($item === null) { /** @var \Joomla\CMS\MVC\Model\AdminModel $model */ $model = $this->getModel(); $item = $this->prepareItem($model->getItem()); } if ($item->id === null) { throw new RouteNotFoundException('Item does not exist'); } // Check for errors. if (\count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); } if ($this->type === null) { throw new \RuntimeException('Content type missing'); } $eventData = [ 'type' => OnGetApiFields::ITEM, 'fields' => $this->fieldsToRenderItem, 'relations' => $this->relationship, 'context' => $this->type, ]; $event = new OnGetApiFields('onApiGetFields', $eventData); /** @var OnGetApiFields $eventResult */ $eventResult = Factory::getApplication()->getDispatcher()->dispatch('onApiGetFields', $event); $element = (new Resource($item, $this->serializer)) ->fields([$this->type => $eventResult->getAllPropertiesToRender()]); if (!empty($this->relationship)) { $element->with($eventResult->getAllRelationsToRender()); } $this->getDocument()->setData($element); $this->getDocument()->addLink('self', Uri::current()); return $this->getDocument()->render(); } /** * Prepare item before render. * * @param object $item The model item * * @return object * * @since 4.0.0 */ protected function prepareItem($item) { return $item; } /** * Encode square brackets in the URI query, according to JSON API specification. * * @param string $query The URI query * * @return string * * @since 4.0.0 */ protected function queryEncode($query) { return str_replace(['[', ']'], ['%5B', '%5D'], $query); } } PK1A#]̥֨::!src/MVC/View/CategoryFeedView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Document\Feed\FeedItem; use Joomla\CMS\Factory; use Joomla\CMS\Helper\RouteHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\UCM\UCMType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base feed View class for a category * * @since 3.2 */ class CategoryFeedView extends HtmlView { /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @since 3.2 * @throws \Exception */ public function display($tpl = null) { $app = Factory::getApplication(); $document = $this->getDocument(); $extension = $app->getInput()->getString('option'); $contentType = $extension . '.' . $this->viewName; $ucmType = new UCMType(); $ucmRow = $ucmType->getTypeByAlias($contentType); $ucmMapCommon = json_decode($ucmRow->field_mappings)->common; $createdField = null; $titleField = null; if (\is_object($ucmMapCommon)) { $createdField = $ucmMapCommon->core_created_time; $titleField = $ucmMapCommon->core_title; } elseif (\is_array($ucmMapCommon)) { $createdField = $ucmMapCommon[0]->core_created_time; $titleField = $ucmMapCommon[0]->core_title; } $document->link = Route::_(RouteHelper::getCategoryRoute($app->getInput()->getInt('id'), $language = 0, $extension)); $app->getInput()->set('limit', $app->get('feed_limit')); $siteEmail = $app->get('mailfrom'); $fromName = $app->get('fromname'); $feedEmail = $app->get('feed_email', 'none'); $document->editor = $fromName; if ($feedEmail !== 'none') { $document->editorEmail = $siteEmail; } // Get some data from the model $items = $this->get('Items'); $category = $this->get('Category'); // Don't display feed if category id missing or non existent if ($category == false || $category->alias === 'root') { throw new \Exception(Text::_('JGLOBAL_CATEGORY_NOT_FOUND'), 404); } foreach ($items as $item) { $this->reconcileNames($item); // Strip html from feed item title if ($titleField) { $title = $this->escape($item->$titleField); $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); } else { $title = ''; } // URL link to article $router = new RouteHelper(); $link = Route::_($router->getRoute($item->id, $contentType, null, null, $item->catid)); // Strip HTML from feed item description text. $description = $item->description; $author = $item->created_by_alias ?: $item->author; $categoryTitle = isset($item->category_title) ? $item->category_title : $category->title; if ($createdField) { $date = isset($item->$createdField) ? date('r', strtotime($item->$createdField)) : ''; } else { $date = ''; } // Load individual item creator class. $feeditem = new FeedItem(); $feeditem->title = $title; $feeditem->link = $link; $feeditem->description = $description; $feeditem->date = $date; $feeditem->category = $categoryTitle; $feeditem->author = $author; // We don't have the author email so we have to use site in both cases. if ($feedEmail === 'site') { $feeditem->authorEmail = $siteEmail; } elseif ($feedEmail === 'author') { $feeditem->authorEmail = $item->author_email; } // Loads item information into RSS array $document->addItem($feeditem); } } /** * Method to reconcile non standard names from components to usage in this class. * Typically overridden in the component feed view class. * * @param object $item The item for a feed, an element of the $items array. * * @return void * * @since 3.2 */ protected function reconcileNames($item) { if (!property_exists($item, 'title') && property_exists($item, 'name')) { $item->title = $item->name; } } } PK1A#]����src/MVC/View/ListView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Doctrine\Inflector\InflectorFactory; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Toolbar\Toolbar; use Joomla\CMS\Toolbar\ToolbarHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla List View * * Class holding methods for displaying presentation data. * * @since 2.5.5 */ class ListView extends HtmlView { /** * An array of items * * @var array */ protected $items; /** * The pagination object * * @var \Joomla\CMS\Pagination\Pagination */ protected $pagination; /** * The model state * * @var CMSObject */ protected $state; /** * The actions the user is authorised to perform * * @var CMSObject */ protected $canDo; /** * Form object for search filters * * @var \Joomla\CMS\Form\Form */ public $filterForm; /** * The active search filters * * @var array */ public $activeFilters; /** * The sidebar markup * * @var string */ protected $sidebar; /** * The toolbar title * * @var string */ protected $toolbarTitle; /** * The toolbar icon * * @var string */ protected $toolbarIcon; /** * The flag which determine whether we want to show batch button * * @var boolean */ protected $supportsBatch = true; /** * The help link for the view * * @var string */ protected $helpLink; /** * Constructor * * @param array $config An optional associative array of configuration settings. */ public function __construct(array $config) { parent::__construct($config); // Set class properties from config data passed in constructor if (isset($config['toolbar_title'])) { $this->toolbarTitle = $config['toolbar_title']; } else { $this->toolbarTitle = strtoupper($this->option . '_MANAGER_' . $this->getName()); } if (isset($config['toolbar_icon'])) { $this->toolbarIcon = $config['toolbar_icon']; } else { $this->toolbarIcon = strtolower($this->getName()); } if (isset($config['supports_batch'])) { $this->supportsBatch = $config['supports_batch']; } if (isset($config['help_link'])) { $this->helpLink = $config['help_link']; } // Set default value for $canDo to avoid fatal error if child class doesn't set value for this property $this->canDo = new CMSObject(); } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @throws \Exception */ public function display($tpl = null) { // Prepare view data $this->initializeView(); // Check for errors. if (\count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); } // Build toolbar $this->addToolbar(); parent::display($tpl); } /** * Prepare view data * * @return void */ protected function initializeView() { $componentName = substr($this->option, 4); $helperClass = ucfirst($componentName . 'Helper'); // Include the component helpers. \JLoader::register($helperClass, JPATH_COMPONENT . '/helpers/' . $componentName . '.php'); if ($this->getLayout() !== 'modal') { if (\is_callable($helperClass . '::addSubmenu')) { \call_user_func([$helperClass, 'addSubmenu'], $this->getName()); } $this->sidebar = \JHtmlSidebar::render(); } $this->items = $this->get('Items'); $this->pagination = $this->get('Pagination'); $this->state = $this->get('State'); $this->filterForm = $this->get('FilterForm'); $this->activeFilters = $this->get('ActiveFilters'); } /** * Add the page title and toolbar. * * @return void * * @since 1.6 */ protected function addToolbar() { $canDo = $this->canDo; $user = Factory::getUser(); // Get the toolbar object instance $bar = Toolbar::getInstance('toolbar'); $viewName = $this->getName(); $singularViewName = InflectorFactory::create()->build()->singularize($viewName); ToolbarHelper::title(Text::_($this->toolbarTitle), $this->toolbarIcon); if ($canDo->get('core.create')) { ToolbarHelper::addNew($singularViewName . '.add'); } if (($canDo->get('core.edit')) || ($canDo->get('core.edit.own'))) { ToolbarHelper::editList($singularViewName . '.edit'); } if ($canDo->get('core.edit.state')) { ToolbarHelper::publish($viewName . '.publish', 'JTOOLBAR_PUBLISH', true); ToolbarHelper::unpublish($viewName . '.unpublish', 'JTOOLBAR_UNPUBLISH', true); if (isset($this->items[0]->featured)) { ToolbarHelper::custom($viewName . '.featured', 'featured', '', 'JFEATURE', true); ToolbarHelper::custom($viewName . '.unfeatured', 'unfeatured', '', 'JUNFEATURE', true); } ToolbarHelper::archiveList($viewName . '.archive'); ToolbarHelper::checkin($viewName . '.checkin'); } // Add a batch button if ( $this->supportsBatch && $user->authorise('core.create', $this->option) && $user->authorise('core.edit', $this->option) && $user->authorise('core.edit.state', $this->option) ) { $title = Text::_('JTOOLBAR_BATCH'); // Instantiate a new LayoutFile instance and render the popup button $layout = new FileLayout('joomla.toolbar.popup'); $dhtml = $layout->render(['title' => $title]); $bar->appendButton('Custom', $dhtml, 'batch'); } if ( $canDo->get('core.delete') && ( $this->state->get('filter.state') == -2 || $this->state->get('filter.published') == -2 ) ) { ToolbarHelper::deleteList('JGLOBAL_CONFIRM_DELETE', $viewName . '.delete', 'JTOOLBAR_EMPTY_TRASH'); } elseif ($canDo->get('core.edit.state')) { ToolbarHelper::trash($viewName . '.trash'); } if ($user->authorise('core.admin', $this->option) || $user->authorise('core.options', $this->option)) { ToolbarHelper::preferences($this->option); } if ($this->helpLink) { ToolbarHelper::help($this->helpLink); } } } PK1A#]+gFgFsrc/MVC/View/HtmlView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\CurrentUserInterface; use Joomla\CMS\User\CurrentUserTrait; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Html View * * Class holding methods for displaying presentation data. * * @since 2.5.5 */ class HtmlView extends AbstractView implements CurrentUserInterface { use CurrentUserTrait; /** * The base path of the view * * @var string * @since 3.0 */ protected $_basePath = null; /** * Layout name * * @var string * @since 3.0 */ protected $_layout = 'default'; /** * Layout extension * * @var string * @since 3.0 */ protected $_layoutExt = 'php'; /** * Layout template * * @var string * @since 3.0 */ protected $_layoutTemplate = '_'; /** * The set of search directories for resources (templates) * * @var array * @since 3.0 */ protected $_path = ['template' => [], 'helper' => []]; /** * The name of the default template source file. * * @var string * @since 3.0 */ protected $_template = null; /** * The output of the template script. * * @var string * @since 3.0 */ protected $_output = null; /** * Charset to use in escaping mechanisms; defaults to urf8 (UTF-8) * * @var string * @since 3.0 */ protected $_charset = 'UTF-8'; /** * Constructor * * @param array $config A named configuration array for object construction. * name: the name (optional) of the view (defaults to the view class name suffix). * charset: the character set to use for display * escape: the name (optional) of the function to use for escaping strings * base_path: the parent path (optional) of the views directory (defaults to the component folder) * template_plath: the path (optional) of the layout directory (defaults to base_path + /views/ + view name * helper_path: the path (optional) of the helper files (defaults to base_path + /helpers/) * layout: the layout (optional) to use to display the view * * @since 3.0 */ public function __construct($config = []) { parent::__construct($config); // Set the charset (used by the variable escaping functions) if (\array_key_exists('charset', $config)) { @trigger_error( 'Setting a custom charset for escaping is deprecated. Override \JViewLegacy::escape() instead.', E_USER_DEPRECATED ); $this->_charset = $config['charset']; } // Set a base path for use by the view if (\array_key_exists('base_path', $config)) { $this->_basePath = $config['base_path']; } else { $this->_basePath = JPATH_COMPONENT; } // Set the default template search path if (\array_key_exists('template_path', $config)) { // User-defined dirs $this->_setPath('template', $config['template_path']); } elseif (is_dir($this->_basePath . '/tmpl/' . $this->getName())) { $this->_setPath('template', $this->_basePath . '/tmpl/' . $this->getName()); } elseif (is_dir($this->_basePath . '/View/' . $this->getName() . '/tmpl')) { $this->_setPath('template', $this->_basePath . '/View/' . $this->getName() . '/tmpl'); } elseif (is_dir($this->_basePath . '/view/' . $this->getName() . '/tmpl')) { $this->_setPath('template', $this->_basePath . '/view/' . $this->getName() . '/tmpl'); } elseif (is_dir($this->_basePath . '/views/' . $this->getName() . '/tmpl')) { $this->_setPath('template', $this->_basePath . '/views/' . $this->getName() . '/tmpl'); } else { $this->_setPath('template', $this->_basePath . '/views/' . $this->getName()); } // Set the default helper search path if (\array_key_exists('helper_path', $config)) { // User-defined dirs $this->_setPath('helper', $config['helper_path']); } else { $this->_setPath('helper', $this->_basePath . '/helpers'); } // Set the layout if (\array_key_exists('layout', $config)) { $this->setLayout($config['layout']); } else { $this->setLayout('default'); } $this->baseurl = Uri::base(true); } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @throws \Exception * @see \JViewLegacy::loadTemplate() * @since 3.0 */ public function display($tpl = null) { $app = Factory::getApplication(); if ($this->option) { $component = $this->option; } else { $component = ApplicationHelper::getComponentName(); } $context = $component . '.' . $this->getName(); $app->getDispatcher()->dispatch( 'onBeforeDisplay', AbstractEvent::create( 'onBeforeDisplay', [ 'eventClass' => 'Joomla\CMS\Event\View\DisplayEvent', 'subject' => $this, 'extension' => $context, ] ) ); $result = $this->loadTemplate($tpl); $eventResult = $app->getDispatcher()->dispatch( 'onAfterDisplay', AbstractEvent::create( 'onAfterDisplay', [ 'eventClass' => 'Joomla\CMS\Event\View\DisplayEvent', 'subject' => $this, 'extension' => $context, 'source' => $result, ] ) ); $eventResult->getArgument('used', false); echo $result; } /** * Escapes a value for output in a view script. * * If escaping mechanism is htmlspecialchars, use * {@link $_charset} setting. * * @param mixed $var The output to escape. * * @return mixed The escaped value. * * @note the ENT_COMPAT flag was replaced by ENT_QUOTES in Joomla 4.0 to also escape single quotes * * @since 3.0 */ public function escape($var) { if ($var === null) { return ''; } return htmlspecialchars($var, ENT_QUOTES, $this->_charset); } /** * Get the layout. * * @return string The layout name * * @since 3.0 */ public function getLayout() { return $this->_layout; } /** * Get the layout template. * * @return string The layout template name * * @since 3.0 */ public function getLayoutTemplate() { return $this->_layoutTemplate; } /** * Sets the layout name to use * * @param string $layout The layout name or a string in format <template>:<layout file> * * @return string Previous value. * * @since 3.0 */ public function setLayout($layout) { $previous = $this->_layout; if (strpos($layout, ':') === false) { $this->_layout = $layout; } else { // Convert parameter to array based on : $temp = explode(':', $layout); $this->_layout = $temp[1]; // Set layout template $this->_layoutTemplate = $temp[0]; } return $previous; } /** * Allows a different extension for the layout files to be used * * @param string $value The extension. * * @return string Previous value * * @since 3.0 */ public function setLayoutExt($value) { $previous = $this->_layoutExt; if ($value = preg_replace('#[^A-Za-z0-9]#', '', trim($value))) { $this->_layoutExt = $value; } return $previous; } /** * Adds to the stack of view script paths in LIFO order. * * @param mixed $path A directory path or an array of paths. * * @return void * * @since 3.0 */ public function addTemplatePath($path) { $this->_addPath('template', $path); } /** * Adds to the stack of helper script paths in LIFO order. * * @param mixed $path A directory path or an array of paths. * * @return void * * @since 3.0 */ public function addHelperPath($path) { $this->_addPath('helper', $path); } /** * Load a template file -- first look in the templates folder for an override * * @param string $tpl The name of the template source file; automatically searches the template paths and compiles as needed. * * @return string The output of the template script. * * @since 3.0 * @throws \Exception */ public function loadTemplate($tpl = null) { // Clear prior output $this->_output = null; $template = Factory::getApplication()->getTemplate(true); $layout = $this->getLayout(); $layoutTemplate = $this->getLayoutTemplate(); // Create the template file name based on the layout $file = isset($tpl) ? $layout . '_' . $tpl : $layout; // Clean the file name $file = preg_replace('/[^A-Z0-9_\.-]/i', '', $file); $tpl = isset($tpl) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $tpl) : $tpl; try { // Load the language file for the template $lang = $this->getLanguage(); } catch (\UnexpectedValueException $e) { $lang = Factory::getApplication()->getLanguage(); } $lang->load('tpl_' . $template->template, JPATH_BASE) || $lang->load('tpl_' . $template->parent, JPATH_THEMES . '/' . $template->parent) || $lang->load('tpl_' . $template->template, JPATH_THEMES . '/' . $template->template); // Change the template folder if alternative layout is in different template if (isset($layoutTemplate) && $layoutTemplate !== '_' && $layoutTemplate != $template->template) { $this->_path['template'] = str_replace( JPATH_THEMES . DIRECTORY_SEPARATOR . $template->template . DIRECTORY_SEPARATOR, JPATH_THEMES . DIRECTORY_SEPARATOR . $layoutTemplate . DIRECTORY_SEPARATOR, $this->_path['template'] ); } // Load the template script $filetofind = $this->_createFileName('template', ['name' => $file]); $this->_template = Path::find($this->_path['template'], $filetofind); // If alternate layout can't be found, fall back to default layout if ($this->_template == false) { $filetofind = $this->_createFileName('', ['name' => 'default' . (isset($tpl) ? '_' . $tpl : $tpl)]); $this->_template = Path::find($this->_path['template'], $filetofind); } if ($this->_template != false) { // Unset so as not to introduce into template scope unset($tpl, $file); // Never allow a 'this' property if (isset($this->this)) { unset($this->this); } // Start capturing output into a buffer ob_start(); // Include the requested template filename in the local scope // (this will execute the view logic). include $this->_template; // Done with the requested template; get the buffer and // clear it. $this->_output = ob_get_contents(); ob_end_clean(); return $this->_output; } throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $file), 500); } /** * Load a helper file * * @param string $hlp The name of the helper source file automatically searches the helper paths and compiles as needed. * * @return void * * @since 3.0 */ public function loadHelper($hlp = null) { // Clean the file name $file = preg_replace('/[^A-Z0-9_\.-]/i', '', $hlp); // Load the template script $helper = Path::find($this->_path['helper'], $this->_createFileName('helper', ['name' => $file])); if ($helper != false) { // Include the requested template filename in the local scope include_once $helper; } } /** * Sets an entire array of search paths for templates or resources. * * @param string $type The type of path to set, typically 'template'. * @param mixed $path The new search path, or an array of search paths. If null or false, resets to the current directory only. * * @return void * * @since 3.0 */ protected function _setPath($type, $path) { if ($this->option) { $component = $this->option; } else { $component = ApplicationHelper::getComponentName(); } $app = Factory::getApplication(); // Clear out the prior search dirs $this->_path[$type] = []; // Actually add the user-specified directories $this->_addPath($type, $path); // Get the active template object $template = $app->getTemplate(true); // Always add the fallback directories as last resort switch (strtolower($type)) { case 'template': // Set the alternative template search dir if (isset($app)) { if ($component) { $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $component); } $name = $this->getName(); if (!empty($template->parent)) { // Parent template's overrides $this->_addPath('template', JPATH_THEMES . '/' . $template->parent . '/html/' . $component . '/' . $name); // Child template's overrides $this->_addPath('template', JPATH_THEMES . '/' . $template->template . '/html/' . $component . '/' . $name); break; } $this->_addPath('template', JPATH_THEMES . '/' . $template->template . '/html/' . $component . '/' . $name); } break; } } /** * Adds to the search path for templates and resources. * * @param string $type The type of path to add. * @param mixed $path The directory or stream, or an array of either, to search. * * @return void * * @since 3.0 */ protected function _addPath($type, $path) { // Loop through the path directories foreach ((array) $path as $dir) { // Clean up the path $dir = Path::clean($dir); // Add trailing separators as needed if (substr($dir, -1) !== DIRECTORY_SEPARATOR) { // Directory $dir .= DIRECTORY_SEPARATOR; } // Add to the top of the search dirs array_unshift($this->_path[$type], $dir); } } /** * Create the filename for a resource * * @param string $type The resource type to create the filename for * @param array $parts An associative array of filename information * * @return string The filename * * @since 3.0 */ protected function _createFileName($type, $parts = []) { switch ($type) { case 'template': $filename = strtolower($parts['name']) . '.' . $this->_layoutExt; break; default: $filename = strtolower($parts['name']) . '.php'; break; } return $filename; } /** * Returns the form object * * @return mixed A \JForm object on success, false on failure * * @since 3.2 */ public function getForm() { if (!\is_object($this->form)) { $this->form = $this->get('Form'); } return $this->form; } /** * Sets the document title according to Global Configuration options * * @param string $title The page title * * @return void * * @since 3.6 */ public function setDocumentTitle($title) { $app = Factory::getApplication(); // Check for empty title and add site name if param is set if (empty($title)) { $title = $app->get('sitename'); } elseif ($app->get('sitename_pagetitles', 0) == 1) { $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); } elseif ($app->get('sitename_pagetitles', 0) == 2) { $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); } $this->getDocument()->setTitle($title); } } PK1A#]�`Z� src/MVC/View/ViewInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\MVC\Model\BaseDatabaseModel; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform CMS Interface * * @since 4.0.0 */ interface ViewInterface { /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @since 4.0.0 */ public function display($tpl = null); /** * Method to get the model object * * @param string $name The name of the model (optional) * * @return BaseDatabaseModel The model object * * @since 3.0 */ public function getModel($name = null); } PK1A#]��e���src/MVC/View/JsonView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Json View * * Class holding methods for displaying presentation data. * * @since 4.0.0 */ class JsonView extends AbstractView { /** * The base path of the view * * @var string * @since 4.0.0 */ protected $_basePath = null; /** * Charset to use in escaping mechanisms; defaults to urf8 (UTF-8) * * @var string * @since 4.0.0 */ protected $_charset = 'UTF-8'; /** * The output of the view. * * @var array * @since 4.0.0 */ protected $_output = []; /** * Constructor * * @param array $config A named configuration array for object construction. * name: the name (optional) of the view (defaults to the view class name suffix). * charset: the character set to use for display * escape: the name (optional) of the function to use for escaping strings * base_path: the parent path (optional) of the views directory (defaults to the component folder) * template_plath: the path (optional) of the layout directory (defaults to base_path + /views/ + view name * helper_path: the path (optional) of the helper files (defaults to base_path + /helpers/) * layout: the layout (optional) to use to display the view * * @since 4.0.0 */ public function __construct($config = []) { parent::__construct($config); // Set the charset (used by the variable escaping functions) if (\array_key_exists('charset', $config)) { @trigger_error( 'Setting a custom charset for escaping is deprecated. Override \JViewLegacy::escape() instead.', E_USER_DEPRECATED ); $this->_charset = $config['charset']; } // Set a base path for use by the view if (\array_key_exists('base_path', $config)) { $this->_basePath = $config['base_path']; } else { $this->_basePath = JPATH_COMPONENT; } } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @since 4.0.0 */ public function display($tpl = null) { // Serializing the output $result = json_encode($this->_output); // Pushing output to the document $this->getDocument()->setBuffer($result); } } PK1A#]{�E��%src/MVC/View/Event/OnGetApiFields.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View\Event; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event for getting extra API Fields and Relations to render with an entity * * @since 4.0.0 */ final class OnGetApiFields extends AbstractImmutableEvent { /** * List of types of view supported * * @since 4.0.0 */ public const LIST = 'list'; /** * List of types of view supported * * @since 4.0.0 */ public const ITEM = 'item'; /** * List of names of properties that will be rendered as relations * * @var string[] * @since 4.0.0 */ private $extraRelations = []; /** * List of names of properties that will be rendered as data * * @var string[] * @since 4.0.0 */ private $extraAttributes = []; /** * Constructor. * * Mandatory arguments: * type string The type of the field. Should be a constant from static::VIEW_TYPE * fields fields The list of fields that will be rendered in the API. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('type', $arguments)) { throw new \BadMethodCallException("Argument 'type' is required for event $name"); } if (!\array_key_exists('fields', $arguments)) { throw new \BadMethodCallException("Argument 'fields' is required for event $name"); } if (!\array_key_exists('context', $arguments)) { throw new \BadMethodCallException("Argument 'context' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the type argument * * @param integer $value The constant from VIEW_TYPE * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type */ protected function setType($value) { if (!in_array($value, [static::ITEM, static::LIST])) { throw new \BadMethodCallException("Argument 'type' of event {$this->name} must be a valid value"); } return $value; } /** * Setter for the fields argument * * @param mixed $value The value to set * * @return array * * @throws \BadMethodCallException if the argument is not a non-empty array */ protected function setFields($value) { if (!\is_array($value) || is_array($value) && empty($value)) { throw new \BadMethodCallException("Argument 'fields' of event {$this->name} must be be an array and not empty"); } return $value; } /** * Setter for the relations argument * * @param mixed $value The value to set * * @return array * * @throws \BadMethodCallException if the argument is not a non-empty array */ protected function setRelations($value) { if (!\is_array($value)) { throw new \BadMethodCallException("Argument 'relations' of event {$this->name} must be be an array"); } return $value; } /** * Allows the user to add names of properties that will be interpreted as relations * Note that if there is an existing data property it will also be displayed as well * as the relation due to the internal implementation (this behaviour is not part of this API * however and should not be guaranteed) * * @param string[] $fields The array of additional fields to add to the data of the attribute * * @return void */ public function addFields(array $fields): void { $this->extraAttributes = array_merge($this->extraAttributes, $fields); } /** * Allows the user to add names of properties that will be interpreted as relations * Note that if there is an existing data property it will also be displayed as well * as the relation due to the internal implementation (this behaviour is not part of this API * however and should not be guaranteed) * * @param string[] $fields The array of additional fields to add as relations * * @return void */ public function addRelations(array $fields): void { $this->extraRelations = array_merge($this->extraRelations, $fields); } /** * Get properties to render. * * @return array */ public function getAllPropertiesToRender(): array { return array_merge($this->getArgument('fields'), $this->extraAttributes); } /** * Get properties to render. * * @return array */ public function getAllRelationsToRender(): array { return array_merge($this->getArgument('relations'), $this->extraRelations); } } PK1A#](ݫ6��src/MVC/View/CategoriesView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Categories view base class. * * @since 3.2 */ class CategoriesView extends HtmlView { /** * State data * * @var \Joomla\Registry\Registry * @since 3.2 */ protected $state; /** * Category items data * * @var array * @since 3.2 */ protected $items; /** * Language key for default page heading * * @var string * @since 3.2 */ protected $pageHeading; /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void|boolean * * @since 3.2 * @throws \Exception */ public function display($tpl = null) { $state = $this->get('State'); $items = $this->get('Items'); $parent = $this->get('Parent'); $app = Factory::getApplication(); // Check for errors. if (\count($errors = $this->get('Errors'))) { $app->enqueueMessage($errors, 'error'); return false; } if ($items === false) { $app->enqueueMessage(Text::_('JGLOBAL_CATEGORY_NOT_FOUND'), 'error'); return false; } if ($parent == false) { $app->enqueueMessage(Text::_('JGLOBAL_CATEGORY_NOT_FOUND'), 'error'); return false; } $params = &$state->params; $items = [$parent->id => $items]; // Escape strings for HTML output $this->pageclass_sfx = htmlspecialchars($params->get('pageclass_sfx', ''), ENT_COMPAT, 'UTF-8'); $this->maxLevelcat = $params->get('maxLevelcat', -1) < 0 ? PHP_INT_MAX : $params->get('maxLevelcat', PHP_INT_MAX); $this->params = &$params; $this->parent = &$parent; $this->items = &$items; $this->prepareDocument(); parent::display($tpl); } /** * Prepares the document * * @return void * * @since 3.2 */ protected function prepareDocument() { // Because the application sets a default page title, we need to get it from the menu item itself $menu = Factory::getApplication()->getMenu()->getActive(); if ($menu) { $this->params->def('page_heading', $this->params->get('page_title', $menu->title)); } else { $this->params->def('page_heading', Text::_($this->pageHeading)); } $this->setDocumentTitle($this->params->get('page_title', '')); if ($this->params->get('menu-meta_description')) { $this->getDocument()->setDescription($this->params->get('menu-meta_description')); } if ($this->params->get('robots')) { $this->getDocument()->setMetaData('robots', $this->params->get('robots')); } } } PK1A#]��<%src/MVC/View/GenericDataException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an error getting data from a model into a view * * @since 4.0.0 */ class GenericDataException extends \RuntimeException { } PK1A#]�,HJ�$�$src/MVC/View/AbstractView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\DocumentAwareInterface; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageAwareInterface; use Joomla\CMS\Language\LanguageAwareTrait; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Object\CMSObject; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\Event\EventInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla View * * Class holding methods for displaying presentation data. * * @since 2.5.5 */ abstract class AbstractView extends CMSObject implements ViewInterface, DispatcherAwareInterface, DocumentAwareInterface, LanguageAwareInterface { use DispatcherAwareTrait; use LanguageAwareTrait; /** * The active document object * * @var Document * @since 3.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use $this->getDocument() instead */ public $document; /** * The URL option for the component. It is usually passed by controller while it creates the view * * @var string * @since 3.0 */ protected $option = null; /** * The name of the view * * @var string * @since 3.0 */ protected $_name = null; /** * Registered models * * @var array * @since 3.0 */ protected $_models = []; /** * The default model * * @var string * @since 3.0 */ protected $_defaultModel = null; /** * Constructor * * @param array $config A named configuration array for object construction. * name: the name (optional) of the view (defaults to the view class name suffix). * charset: the character set to use for display * escape: the name (optional) of the function to use for escaping strings * base_path: the parent path (optional) of the views directory (defaults to the component folder) * template_plath: the path (optional) of the layout directory (defaults to base_path + /views/ + view name * helper_path: the path (optional) of the helper files (defaults to base_path + /helpers/) * layout: the layout (optional) to use to display the view * * @since 3.0 */ public function __construct($config = []) { // Set the view name if (empty($this->_name)) { if (\array_key_exists('name', $config)) { $this->_name = $config['name']; } else { $this->_name = $this->getName(); } } // Set the component name if passed if (!empty($config['option'])) { $this->option = $config['option']; } } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @since 3.0 */ abstract public function display($tpl = null); /** * Method to get data from a registered model or a property of the view * * @param string $property The name of the method to call on the model or the property to get * @param string $default The name of the model to reference or the default value [optional] * * @return mixed The return value of the method * * @since 3.0 */ public function get($property, $default = null) { // If $model is null we use the default model if ($default === null) { $model = $this->_defaultModel; } else { $model = strtolower($default); } // First check to make sure the model requested exists if (isset($this->_models[$model])) { // Model exists, let's build the method name $method = 'get' . ucfirst($property); // Does the method exist? if (method_exists($this->_models[$model], $method)) { // The method exists, let's call it and return what we get return $this->_models[$model]->$method(); } } // Degrade to CMSObject::get return parent::get($property, $default); } /** * Method to get the model object * * @param string $name The name of the model (optional) * * @return BaseDatabaseModel The model object * * @since 3.0 */ public function getModel($name = null) { if ($name === null) { $name = $this->_defaultModel; } return $this->_models[strtolower($name)]; } /** * Method to add a model to the view. We support a multiple model single * view system by which models are referenced by classname. A caveat to the * classname referencing is that any classname prepended by \JModel will be * referenced by the name without \JModel, eg. \JModelCategory is just * Category. * * @param BaseDatabaseModel $model The model to add to the view. * @param boolean $default Is this the default model? * * @return BaseDatabaseModel The added model. * * @since 3.0 */ public function setModel($model, $default = false) { $name = strtolower($model->getName()); $this->_models[$name] = $model; if ($default) { $this->_defaultModel = $name; } return $model; } /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @since 3.0 * @throws \Exception */ public function getName() { if (empty($this->_name)) { $reflection = new \ReflectionClass($this); if ($viewNamespace = $reflection->getNamespaceName()) { $pos = strrpos($viewNamespace, '\\'); if ($pos !== false) { $this->_name = strtolower(substr($viewNamespace, $pos + 1)); } } else { $className = \get_class($this); $viewPos = strpos($className, 'View'); if ($viewPos != false) { $this->_name = strtolower(substr($className, $viewPos + 4)); } } if (empty($this->_name)) { throw new \Exception(sprintf($this->text('JLIB_APPLICATION_ERROR_GET_NAME'), __METHOD__), 500); } } return $this->_name; } /** * Get the Document. * * @return Document * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the document has not been set. */ protected function getDocument(): Document { if ($this->document) { return $this->document; } throw new \UnexpectedValueException('Document not set in ' . __CLASS__); } /** * Set the document to use. * * @param Document $document The document to use * * @return void * * @since 4.4.0 */ public function setDocument(Document $document): void { $this->document = $document; } /** * Get the event dispatcher. * * The override was made to keep a backward compatibility for legacy component. * TODO: Remove the override in 6.0 * * @return DispatcherInterface * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ public function getDispatcher() { if (!$this->dispatcher) { @trigger_error( sprintf('Dispatcher for %s should be set through MVC factory. It will throw an exception in 6.0', __CLASS__), E_USER_DEPRECATED ); return Factory::getContainer()->get(DispatcherInterface::class); } return $this->dispatcher; } /** * Dispatches the given event on the internal dispatcher, does a fallback to the global one. * * @param EventInterface $event The event * * @return void * * @since 4.1.0 * * @deprecated 4.4 will be removed in 6.0. Use $this->getDispatcher() directly. */ protected function dispatchEvent(EventInterface $event) { $this->getDispatcher()->dispatch($event->getName(), $event); @trigger_error( sprintf( 'Method %s is deprecated and will be removed in 6.0. Use getDispatcher()->dispatch() directly.', __METHOD__ ), E_USER_DEPRECATED ); } } PK1A#]����$�$src/MVC/View/CategoryView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Categories\CategoryNode; use Joomla\CMS\Factory; use Joomla\CMS\Helper\TagsHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Router\Route; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base HTML View class for the a Category list * * @since 3.2 */ class CategoryView extends HtmlView { /** * State data * * @var \Joomla\Registry\Registry * @since 3.2 */ protected $state; /** * Category items data * * @var array * @since 3.2 */ protected $items; /** * The category model object for this category * * @var CategoryNode * @since 3.2 */ protected $category; /** * The list of other categories for this extension. * * @var array * @since 3.2 */ protected $categories; /** * Pagination object * * @var \Joomla\CMS\Pagination\Pagination * @since 3.2 */ protected $pagination; /** * Child objects * * @var array * @since 3.2 */ protected $children; /** * The name of the extension for the category * * @var string * @since 3.2 */ protected $extension; /** * The name of the view to link individual items to * * @var string * @since 3.2 */ protected $viewName; /** * Default title to use for page title * * @var string * @since 3.2 */ protected $defaultPageTitle; /** * Whether to run the standard Joomla plugin events. * Off by default for b/c * * @var boolean * @since 3.5 */ protected $runPlugins = false; /** * The flag to mark if the active menu item is linked to the category being displayed * * @var bool * @since 4.0.0 */ protected $menuItemMatchCategory = false; /** * Method with common display elements used in category list displays * * @return void * * @since 3.2 */ public function commonCategoryDisplay() { $app = Factory::getApplication(); $user = Factory::getUser(); $params = $app->getParams(); // Get some data from the models $model = $this->getModel(); $paramsModel = $model->getState('params'); $paramsModel->set('check_access_rights', 0); $model->setState('params', $paramsModel); $state = $this->get('State'); $category = $this->get('Category'); $children = $this->get('Children'); $parent = $this->get('Parent'); if ($category == false) { throw new \InvalidArgumentException(Text::_('JGLOBAL_CATEGORY_NOT_FOUND'), 404); } if ($parent == false) { throw new \InvalidArgumentException(Text::_('JGLOBAL_CATEGORY_NOT_FOUND'), 404); } // Check whether category access level allows access. $groups = $user->getAuthorisedViewLevels(); if (!\in_array($category->access, $groups)) { throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403); } $items = $this->get('Items'); $pagination = $this->get('Pagination'); // Check for errors. if (\count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); } // Setup the category parameters. $cparams = $category->getParams(); $category->params = clone $params; $category->params->merge($cparams); $children = [$category->id => $children]; // Escape strings for HTML output $this->pageclass_sfx = htmlspecialchars($params->get('pageclass_sfx', '')); if ($this->runPlugins) { PluginHelper::importPlugin('content'); foreach ($items as $itemElement) { $itemElement = (object) $itemElement; $itemElement->event = new \stdClass(); // For some plugins. !empty($itemElement->description) ? $itemElement->text = $itemElement->description : $itemElement->text = ''; Factory::getApplication()->triggerEvent('onContentPrepare', [$this->extension . '.category', &$itemElement, &$itemElement->params, 0]); $results = Factory::getApplication()->triggerEvent( 'onContentAfterTitle', [$this->extension . '.category', &$itemElement, &$itemElement->core_params, 0] ); $itemElement->event->afterDisplayTitle = trim(implode("\n", $results)); $results = Factory::getApplication()->triggerEvent( 'onContentBeforeDisplay', [$this->extension . '.category', &$itemElement, &$itemElement->core_params, 0] ); $itemElement->event->beforeDisplayContent = trim(implode("\n", $results)); $results = Factory::getApplication()->triggerEvent( 'onContentAfterDisplay', [$this->extension . '.category', &$itemElement, &$itemElement->core_params, 0] ); $itemElement->event->afterDisplayContent = trim(implode("\n", $results)); if ($itemElement->text) { $itemElement->description = $itemElement->text; } } } $maxLevel = $params->get('maxLevel', -1) < 0 ? PHP_INT_MAX : $params->get('maxLevel', PHP_INT_MAX); $this->maxLevel = &$maxLevel; $this->state = &$state; $this->items = &$items; $this->category = &$category; $this->children = &$children; $this->params = &$params; $this->parent = &$parent; $this->pagination = &$pagination; $this->user = &$user; // Check for layout override only if this is not the active menu item // If it is the active menu item, then the view and category id will match $active = $app->getMenu()->getActive(); if ( $active && $active->component == $this->extension && isset($active->query['view'], $active->query['id']) && $active->query['view'] === 'category' && $active->query['id'] == $this->category->id ) { if (isset($active->query['layout'])) { $this->setLayout($active->query['layout']); } $this->menuItemMatchCategory = true; } elseif ($layout = $category->params->get('category_layout')) { $this->setLayout($layout); } $this->category->tags = new TagsHelper(); $this->category->tags->getItemTags($this->extension . '.category', $this->category->id); } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @since 3.2 * @throws \Exception */ public function display($tpl = null) { $this->prepareDocument(); parent::display($tpl); } /** * Method to prepares the document * * @return void * * @since 3.2 */ protected function prepareDocument() { $app = Factory::getApplication(); $this->pathway = $app->getPathway(); // Because the application sets a default page title, we need to get it from the menu item itself $this->menu = $app->getMenu()->getActive(); if ($this->menu) { $this->params->def('page_heading', $this->params->get('page_title', $this->menu->title)); } else { $this->params->def('page_heading', Text::_($this->defaultPageTitle)); } $this->setDocumentTitle($this->params->get('page_title', '')); if ($this->params->get('menu-meta_description')) { $this->getDocument()->setDescription($this->params->get('menu-meta_description')); } if ($this->params->get('robots')) { $this->getDocument()->setMetaData('robots', $this->params->get('robots')); } } /** * Method to add an alternative feed link to a category layout. * * @return void * * @since 3.2 */ protected function addFeed() { if ($this->params->get('show_feed_link', 1) == 1) { $link = '&format=feed&limitstart='; $attribs = ['type' => 'application/rss+xml', 'title' => htmlspecialchars($this->getDocument()->getTitle())]; $this->getDocument()->addHeadLink(Route::_($link . '&type=rss'), 'alternate', 'rel', $attribs); $attribs = ['type' => 'application/atom+xml', 'title' => htmlspecialchars($this->getDocument()->getTitle())]; $this->getDocument()->addHeadLink(Route::_($link . '&type=atom'), 'alternate', 'rel', $attribs); } } } PK1A#] �ttsrc/MVC/View/FormView.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\View; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\Toolbar\ToolbarHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Form View * * Class holding methods for displaying presentation data. * * @since 2.5.5 */ class FormView extends HtmlView { /** * The \JForm object * * @var \Joomla\CMS\Form\Form */ protected $form; /** * The active item * * @var object */ protected $item; /** * The item primary key name * * @var string */ protected $keyName; /** * The model state * * @var object */ protected $state; /** * The actions the user is authorised to perform * * @var CMSObject */ protected $canDo; /** * The toolbar title * * @var string */ protected $toolbarTitle; /** * The toolbar icon * * @var string */ protected $toolbarIcon; /** * The preview link * * @var string */ protected $previewLink; /** * The help link * * @var string */ protected $helpLink; /** * Constructor * * @param array $config An optional associative array of configuration settings. */ public function __construct(array $config) { parent::__construct($config); if (isset($config['help_link'])) { $this->helpLink = $config['help_link']; } if (isset($config['toolbar_icon'])) { $this->toolbarIcon = $config['toolbar_icon']; } else { $this->toolbarIcon = 'pencil-2 ' . $this->getName() . '-add'; } // Set default value for $canDo to avoid fatal error if child class doesn't set value for this property $this->canDo = new CMSObject(); } /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void * * @throws \Exception */ public function display($tpl = null) { // Prepare view data $this->initializeView(); // Check for errors. if (\count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); } // Build toolbar $this->addToolbar(); parent::display($tpl); } /** * Prepare view data * * @return void */ protected function initializeView() { $this->form = $this->get('Form'); $this->item = $this->get('Item'); $this->state = $this->get('State'); $table = $this->get('Table'); $this->keyName = $table instanceof TableInterface ? $table->getKeyName() : 'id'; $action = empty($this->item->{$this->keyName}) ? '_NEW' : '_EDIT'; // Set default toolbar title $this->toolbarTitle = Text::_(strtoupper($this->option . '_MANAGER_' . $this->getName() . $action)); } /** * Add the page title and toolbar. * * @return void * * @since 1.6 */ protected function addToolbar() { Factory::getApplication()->getInput()->set('hidemainmenu', true); $user = Factory::getUser(); $userId = $user->id; $isNew = empty($this->item->{$this->keyName}); $viewName = $this->getName(); $checkedOut = $this->getModel()->isCheckedOut($this->item); $canDo = $this->canDo; ToolbarHelper::title( $this->toolbarTitle, $this->toolbarIcon ); // For new records, check the create permission. if ($isNew && $canDo->get('core.create')) { ToolbarHelper::saveGroup( [ ['apply', $viewName . '.apply'], ['save', $viewName . '.save'], ['save2new', $viewName . '.save2new'], ], 'btn-success' ); ToolbarHelper::cancel($viewName . '.cancel'); } else { // Since it's an existing record, check the edit permission, or fall back to edit own if the owner. if (property_exists($this->item, 'created_by')) { $itemEditable = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId); } else { $itemEditable = $canDo->get('core.edit'); } $toolbarButtons = []; // Can't save the record if it's checked out and editable if (!$checkedOut && $itemEditable) { $toolbarButtons[] = ['apply', $viewName . '.apply']; $toolbarButtons[] = ['save', $viewName . '.save']; // We can save this record, but check the create permission to see if we can return to make a new one. if ($canDo->get('core.create')) { $toolbarButtons[] = ['save2new', $viewName . '.save2new']; } } // If checked out, we can still save if ($canDo->get('core.create')) { $toolbarButtons[] = ['save2copy', $viewName . '.save2copy']; } ToolbarHelper::saveGroup( $toolbarButtons, 'btn-success' ); if (ComponentHelper::isEnabled('com_contenthistory') && $this->state->params->get('save_history', 0) && $itemEditable) { ToolbarHelper::versions($this->option . '.' . $viewName, $this->item->id); } if (!$isNew && $this->previewLink) { ToolbarHelper::preview($this->previewLink, Text::_('JGLOBAL_PREVIEW'), 'eye', 80, 90); } ToolbarHelper::cancel($viewName . '.cancel', 'JTOOLBAR_CLOSE'); } ToolbarHelper::divider(); if ($this->helpLink) { ToolbarHelper::help($this->helpLink); } } } PK1A#]�ٔ�src/Autoload/ClassLoader.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Autoload; use Composer\Autoload\ClassLoader as ComposerClassLoader; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Decorate Composer ClassLoader for Joomla! * * For backward compatibility due to class aliasing in the CMS, the loadClass() method was modified to call * the JLoader::applyAliasFor() method. * * @since 3.4 */ class ClassLoader { /** * The Composer class loader * * @var ComposerClassLoader * @since 3.4 */ private $loader; /** * Constructor * * @param ComposerClassLoader $loader Composer autoloader * * @since 3.4 */ public function __construct(ComposerClassLoader $loader) { $this->loader = $loader; } /** * Loads the given class or interface. * * @param string $class The name of the class * * @return boolean|null True if loaded, null otherwise * * @since 3.4 */ public function loadClass($class) { if ($result = $this->loader->loadClass($class)) { \JLoader::applyAliasFor($class); } return $result; } } PK1A#]��p>��src/Editor/Editor.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Editor; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Event\AbstractEvent; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Editor class to handle WYSIWYG editors * * @since 1.5 */ class Editor implements DispatcherAwareInterface { use DispatcherAwareTrait; /** * Editor Plugin object * * @var object * @since 1.5 */ protected $_editor = null; /** * Editor Plugin name * * @var string * @since 1.5 */ protected $_name = null; /** * Object asset * * @var string * @since 1.6 */ protected $asset = null; /** * Object author * * @var string * @since 1.6 */ protected $author = null; /** * Editor instances container. * * @var Editor[] * @since 2.5 */ protected static $instances = []; /** * Constructor * * @param string $editor The editor name * @param DispatcherInterface $dispatcher The event dispatcher we're going to use */ public function __construct($editor = 'none', DispatcherInterface $dispatcher = null) { $this->_name = $editor; // Set the dispatcher if (!\is_object($dispatcher)) { $dispatcher = Factory::getContainer()->get('dispatcher'); } $this->setDispatcher($dispatcher); // Register the getButtons event $this->getDispatcher()->addListener( 'getButtons', function (AbstractEvent $event) { $event['result'] = (array) $this->getButtons( $event->getArgument('editor', null), $event->getArgument('buttons', null) ); } ); } /** * Returns the global Editor object, only creating it * if it doesn't already exist. * * @param string $editor The editor to use. * * @return Editor The Editor object. * * @since 1.5 */ public static function getInstance($editor = 'none') { $signature = serialize($editor); if (empty(self::$instances[$signature])) { self::$instances[$signature] = new static($editor); } return self::$instances[$signature]; } /** * Initialise the editor * * @return void * * @since 1.5 */ public function initialise() { // Check if editor is already loaded if ($this->_editor === null) { return; } if (method_exists($this->_editor, 'onInit')) { \call_user_func([$this->_editor, 'onInit']); } } /** * Display the editor area. * * @param string $name The control name. * @param string $html The contents of the text area. * @param string $width The width of the text area (px or %). * @param string $height The height of the text area (px or %). * @param integer $col The number of columns for the textarea. * @param integer $row The number of rows for the textarea. * @param boolean $buttons True and the editor buttons will be displayed. * @param string $id An optional ID for the textarea (note: since 1.6). If not supplied the name is used. * @param string $asset The object asset * @param object $author The author. * @param array $params Associative array of editor parameters. * * @return string * * @since 1.5 */ public function display($name, $html, $width, $height, $col, $row, $buttons = true, $id = null, $asset = null, $author = null, $params = []) { $this->asset = $asset; $this->author = $author; $this->_loadEditor($params); // Check whether editor is already loaded if ($this->_editor === null) { Factory::getApplication()->enqueueMessage(Text::_('JLIB_NO_EDITOR_PLUGIN_PUBLISHED'), 'danger'); return; } // Backwards compatibility. Width and height should be passed without a semicolon from now on. // If editor plugins need a unit like "px" for CSS styling, they need to take care of that $width = str_replace(';', '', $width); $height = str_replace(';', '', $height); $args = [ 'name' => $name, 'content' => $html, 'width' => $width, 'height' => $height, 'col' => $col, 'row' => $row, 'buttons' => $buttons, 'id' => ($id ?: $name), 'asset' => $asset, 'author' => $author, 'params' => $params, ]; return \call_user_func_array([$this->_editor, 'onDisplay'], $args); } /** * Get the editor extended buttons (usually from plugins) * * @param string $editor The name of the editor. * @param mixed $buttons Can be boolean or array, if boolean defines if the buttons are * displayed, if array defines a list of buttons not to show. * * @return array * * @since 1.5 */ public function getButtons($editor, $buttons = true) { $result = []; if (\is_bool($buttons) && !$buttons) { return $result; } // Get plugins $plugins = PluginHelper::getPlugin('editors-xtd'); foreach ($plugins as $plugin) { if (\is_array($buttons) && \in_array($plugin->name, $buttons)) { continue; } $plugin = Factory::getApplication()->bootPlugin($plugin->name, 'editors-xtd'); if (!$plugin) { return $result; } // Try to authenticate if (!method_exists($plugin, 'onDisplay')) { continue; } $button = $plugin->onDisplay($editor, $this->asset, $this->author); if (empty($button)) { continue; } if (\is_array($button)) { $result = array_merge($result, $button); continue; } $button->editor = $editor; $result[] = $button; } return $result; } /** * Load the editor * * @param array $config Associative array of editor config parameters * * @return mixed * * @since 1.5 */ protected function _loadEditor($config = []) { // Check whether editor is already loaded if ($this->_editor !== null) { return false; } // Build the path to the needed editor plugin $name = InputFilter::getInstance()->clean($this->_name, 'cmd'); // Boot the editor plugin $this->_editor = Factory::getApplication()->bootPlugin($name, 'editors'); // Check if the editor can be loaded if (!$this->_editor) { Log::add(Text::_('JLIB_HTML_EDITOR_CANNOT_LOAD'), Log::WARNING, 'jerror'); return false; } $this->_editor->params->loadArray($config); $this->initialise(); PluginHelper::importPlugin('editors-xtd'); return true; } } PK1A#]K��\(\(src/Table/Usergroup.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Usergroup table class. * * @since 1.7.0 */ class Usergroup extends Table { /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__usergroups', 'id', $db); } /** * Method to check the current record to save * * @return boolean True on success * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Validate the title. if ((trim($this->title)) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_TITLE')); return false; } // The parent_id can not be equal to the current id if ($this->id === (int) $this->parent_id) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } // Check for a duplicate parent_id, title. // There is a unique index on the (parent_id, title) field in the table. $db = $this->_db; $parentId = (int) $this->parent_id; $title = trim($this->title); $id = (int) $this->id; $query = $db->getQuery(true) ->select('COUNT(title)') ->from($this->_tbl) ->where($db->quoteName('title') . ' = :title') ->where($db->quoteName('parent_id') . ' = :parentid') ->where($db->quoteName('id') . ' <> :id') ->bind(':title', $title) ->bind(':parentid', $parentId, ParameterType::INTEGER) ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); if ($db->loadResult() > 0) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_TITLE_EXISTS')); return false; } // We do not allow to move non public to root and public to non-root if (!empty($this->id)) { $table = self::getInstance('Usergroup', 'JTable', ['dbo' => $this->getDbo()]); $table->load($this->id); if ((!$table->parent_id && $this->parent_id) || ($table->parent_id && !$this->parent_id)) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } } elseif (!$this->parent_id) { // New entry should always be greater 0 $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } // The new parent_id has to be a valid group if ($this->parent_id) { $table = self::getInstance('Usergroup', 'JTable', ['dbo' => $this->getDbo()]); $table->load($this->parent_id); if ($table->id != $this->parent_id) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } } return true; } /** * Method to recursively rebuild the nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $left The left id to start with in building the tree. * * @return boolean True on success * * @since 1.7.0 */ public function rebuild($parentId = 0, $left = 0) { // Get the database object $db = $this->_db; $query = $db->getQuery(true); $parentId = (int) $parentId; // Get all children of this node $query->clear() ->select($db->quoteName('id')) ->from($db->quoteName($this->_tbl)) ->where($db->quoteName('parent_id') . ' = :parentid') ->bind(':parentid', $parentId, ParameterType::INTEGER) ->order([$db->quoteName('parent_id'), $db->quoteName('title')]); $db->setQuery($query); $children = $db->loadColumn(); // The right value of this node is the left value + 1 $right = $left + 1; // Execute this function recursively over all children for ($i = 0, $n = \count($children); $i < $n; $i++) { // $right is the current right value, which is incremented on recursion return $right = $this->rebuild($children[$i], $right); // If there is an update failure, return false to break out of the recursion if ($right === false) { return false; } } $left = (int) $left; $right = (int) $right; // We've got the left value, and now that we've processed // the children of this node we also know the right value $query->clear() ->update($db->quoteName($this->_tbl)) ->set($db->quoteName('lft') . ' = :lft') ->set($db->quoteName('rgt') . ' = :rgt') ->where($db->quoteName('id') . ' = :id') ->bind(':lft', $left, ParameterType::INTEGER) ->bind(':rgt', $right, ParameterType::INTEGER) ->bind(':id', $parentId, ParameterType::INTEGER); $db->setQuery($query); // If there is an update failure, return false to break out of the recursion try { $db->execute(); } catch (ExecutionFailureException $e) { return false; } // Return the right value of this node + 1 return $right + 1; } /** * Inserts a new row if id is zero or updates an existing row in the database table * * @param boolean $updateNulls If false, null object variables are not updated * * @return boolean True if successful, false otherwise and an internal error message is set * * @since 1.7.0 */ public function store($updateNulls = false) { if ($result = parent::store($updateNulls)) { // Rebuild the nested set tree. $this->rebuild(); } return $result; } /** * Delete this object and its dependencies * * @param integer $oid The primary key of the user group to delete. * * @return mixed Boolean or Exception. * * @since 1.7.0 * @throws \RuntimeException on database error. * @throws \UnexpectedValueException on data error. */ public function delete($oid = null) { if ($oid) { $this->load($oid); } if ($this->id == 0) { throw new \UnexpectedValueException('Usergroup not found'); } if ($this->parent_id == 0) { throw new \UnexpectedValueException('Root usergroup cannot be deleted.'); } if ($this->lft == 0 || $this->rgt == 0) { throw new \UnexpectedValueException('Left-Right data inconsistency. Cannot delete usergroup.'); } $db = $this->_db; $lft = (int) $this->lft; $rgt = (int) $this->rgt; // Select the usergroup ID and its children $query = $db->getQuery(true) ->select($db->quoteName('c.id')) ->from($db->quoteName($this->_tbl, 'c')) ->where($db->quoteName('c.lft') . ' >= :lft') ->where($db->quoteName('c.rgt') . ' <= :rgt') ->bind(':lft', $lft, ParameterType::INTEGER) ->bind(':rgt', $rgt, ParameterType::INTEGER); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { throw new \UnexpectedValueException('Left-Right data inconsistency. Cannot delete usergroup.'); } // Delete the usergroup and its children $query->clear() ->delete($db->quoteName($this->_tbl)) ->whereIn($db->quoteName('id'), $ids); $db->setQuery($query); $db->execute(); // Rebuild the nested set tree. $this->rebuild(); // Delete the usergroup in view levels $replace = []; foreach ($ids as $id) { $replace[] = ',' . $db->quote("[$id,") . ',' . $db->quote('['); $replace[] = ',' . $db->quote(",$id,") . ',' . $db->quote(','); $replace[] = ',' . $db->quote(",$id]") . ',' . $db->quote(']'); $replace[] = ',' . $db->quote("[$id]") . ',' . $db->quote('[]'); } $query->clear() ->select( [ $db->quoteName('id'), $db->quoteName('rules'), ] ) ->from($db->quoteName('#__viewlevels')); $db->setQuery($query); $rules = $db->loadObjectList(); $matchIds = []; foreach ($rules as $rule) { foreach ($ids as $id) { if (strstr($rule->rules, '[' . $id) || strstr($rule->rules, ',' . $id) || strstr($rule->rules, $id . ']')) { $matchIds[] = $rule->id; } } } if (!empty($matchIds)) { $query->clear() ->update($db->quoteName('#__viewlevels')) ->set($db->quoteName('rules') . ' = ' . str_repeat('REPLACE(', 4 * \count($ids)) . $db->quoteName('rules') . implode(')', $replace) . ')') ->whereIn($db->quoteName('id'), $matchIds); $db->setQuery($query); $db->execute(); } // Delete the user to usergroup mappings for the group(s) from the database. $query->clear() ->delete($db->quoteName('#__user_usergroup_map')) ->whereIn($db->quoteName('group_id'), $ids); $db->setQuery($query); $db->execute(); return true; } } PK1A#]�(w��src/Table/Asset.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Table class supporting modified pre-order tree traversal behavior. * * @since 1.7.0 */ class Asset extends Nested { /** * The primary key of the asset. * * @var integer * @since 1.7.0 */ public $id = null; /** * The unique name of the asset. * * @var string * @since 1.7.0 */ public $name = null; /** * The human readable title of the asset. * * @var string * @since 1.7.0 */ public $title = null; /** * The rules for the asset stored in a JSON string * * @var string * @since 1.7.0 */ public $rules = null; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__assets', 'id', $db); } /** * Method to load an asset by its name. * * @param string $name The name of the asset. * * @return integer * * @since 1.7.0 */ public function loadByName($name) { return $this->load(['name' => $name]); } /** * Assert that the nested set data is valid. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } $this->parent_id = (int) $this->parent_id; if (empty($this->rules)) { $this->rules = '{}'; } // Nested does not allow parent_id = 0, override this. if ($this->parent_id > 0) { // Get the DatabaseQuery object $query = $this->_db->getQuery(true) ->select('1') ->from($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName('id') . ' = ' . $this->parent_id); $query->setLimit(1); if ($this->_db->setQuery($query)->loadResult()) { return true; } $this->setError(Text::_('JLIB_DATABASE_ERROR_INVALID_PARENT_ID')); return false; } return true; } /** * Method to recursively rebuild the whole nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $leftId The left id to start with in building the tree. * @param integer $level The level to assign to the current nodes. * @param string $path The path to the current nodes. * * @return integer 1 + value of root rgt on success, false on failure * * @since 3.5 * @throws \RuntimeException on database error. */ public function rebuild($parentId = null, $leftId = 0, $level = 0, $path = null) { // If no parent is provided, try to find it. if ($parentId === null) { // Get the root item. $parentId = $this->getRootId(); if ($parentId === false) { return false; } } $query = $this->_db->getQuery(true); // Build the structure of the recursive query. if (!isset($this->_cache['rebuild.sql'])) { $query->clear() ->select($this->_tbl_key) ->from($this->_tbl) ->where('parent_id = %d'); // If the table has an ordering field, use that for ordering. if ($this->hasField('ordering')) { $query->order('parent_id, ordering, lft'); } else { $query->order('parent_id, lft'); } $this->_cache['rebuild.sql'] = (string) $query; } // Make a shortcut to database object. // Assemble the query to find all children of this node. $this->_db->setQuery(sprintf($this->_cache['rebuild.sql'], (int) $parentId)); $children = $this->_db->loadObjectList(); // The right value of this node is the left value + 1 $rightId = $leftId + 1; // Execute this function recursively over all children foreach ($children as $node) { /* * $rightId is the current right value, which is incremented on recursion return. * Increment the level for the children. * Add this item's alias to the path (but avoid a leading /) */ $rightId = $this->rebuild($node->{$this->_tbl_key}, $rightId, $level + 1); // If there is an update failure, return false to break out of the recursion. if ($rightId === false) { return false; } } // We've got the left value, and now that we've processed // the children of this node we also know the right value. $query->clear() ->update($this->_tbl) ->set('lft = ' . (int) $leftId) ->set('rgt = ' . (int) $rightId) ->set('level = ' . (int) $level) ->where($this->_tbl_key . ' = ' . (int) $parentId); $this->_db->setQuery($query)->execute(); // Return the right value of this node + 1. return $rightId + 1; } } PK1A#].D'.EEsrc/Table/User.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Mail\MailHelper; use Joomla\CMS\String\PunycodeHelper; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Users table * * @since 1.7.0 */ class User extends Table { /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Associative array of group ids => group ids for the user * * @var array * @since 1.7.0 */ public $groups; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__users', 'id', $db); // Initialise. $this->id = 0; $this->sendEmail = 0; } /** * Method to load a user, user groups, and any other necessary data * from the database so that it can be bound to the user object. * * @param integer $userId An optional user id. * @param boolean $reset False if row not found or on error * (internal error state set in that case). * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function load($userId = null, $reset = true) { // Get the id to load. if ($userId !== null) { $this->id = $userId; } else { $userId = $this->id; } // Check for a valid id to load. if ($userId === null) { return false; } // Reset the table. $this->reset(); $userId = (int) $userId; // Load the user data. $query = $this->_db->getQuery(true) ->select('*') ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('id') . ' = :userid') ->bind(':userid', $userId, ParameterType::INTEGER); $this->_db->setQuery($query); $data = (array) $this->_db->loadAssoc(); if (!\count($data)) { return false; } // Convert email from punycode $data['email'] = PunycodeHelper::emailToUTF8($data['email']); // Bind the data to the table. $return = $this->bind($data); if ($return !== false) { // Load the user groups. $query->clear() ->select($this->_db->quoteName('g.id')) ->select($this->_db->quoteName('g.title')) ->from($this->_db->quoteName('#__usergroups', 'g')) ->join( 'INNER', $this->_db->quoteName('#__user_usergroup_map', 'm'), $this->_db->quoteName('m.group_id') . ' = ' . $this->_db->quoteName('g.id') ) ->where($this->_db->quoteName('m.user_id') . ' = :muserid') ->bind(':muserid', $userId, ParameterType::INTEGER); $this->_db->setQuery($query); // Add the groups to the user data. $this->groups = $this->_db->loadAssocList('id', 'id'); } return $return; } /** * Method to bind the user, user groups, and any other necessary data. * * @param array $array The data to bind. * @param mixed $ignore An array or space separated list of fields to ignore. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function bind($array, $ignore = '') { if (\array_key_exists('params', $array) && \is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } // Attempt to bind the data. $return = parent::bind($array, $ignore); // Load the real group data based on the bound ids. if ($return && !empty($this->groups)) { // Set the group ids. $this->groups = ArrayHelper::toInteger($this->groups); // Get the titles for the user groups. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->select($this->_db->quoteName('title')) ->from($this->_db->quoteName('#__usergroups')) ->whereIn($this->_db->quoteName('id'), array_values($this->groups)); $this->_db->setQuery($query); // Set the titles for the user groups. $this->groups = $this->_db->loadAssocList('id', 'id'); } return $return; } /** * Validation and filtering * * @return boolean True if satisfactory * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Set user id to null instead of 0, if needed if ($this->id === 0) { $this->id = null; } $filterInput = InputFilter::getInstance(); // Validate user information if ($filterInput->clean($this->name, 'TRIM') == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_PLEASE_ENTER_YOUR_NAME')); return false; } if ($filterInput->clean($this->username, 'TRIM') == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_PLEASE_ENTER_A_USER_NAME')); return false; } if ( preg_match('#[<>"\'%;()&\\\\]|\\.\\./#', $this->username) || StringHelper::strlen($this->username) < 2 || $filterInput->clean($this->username, 'TRIM') !== $this->username || StringHelper::strlen($this->username) > 150 ) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_VALID_AZ09', 2)); return false; } if ( ($filterInput->clean($this->email, 'TRIM') == '') || !MailHelper::isEmailAddress($this->email) || StringHelper::strlen($this->email) > 100 ) { $this->setError(Text::_('JLIB_DATABASE_ERROR_VALID_MAIL')); return false; } // Convert email to punycode for storage $this->email = PunycodeHelper::emailToPunycode($this->email); // Set the registration timestamp if (empty($this->registerDate)) { $this->registerDate = Factory::getDate()->toSql(); } // Set the lastvisitDate timestamp if (empty($this->lastvisitDate)) { $this->lastvisitDate = null; } // Set the lastResetTime timestamp if (empty($this->lastResetTime)) { $this->lastResetTime = null; } $uid = (int) $this->id; // Check for existing username $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('username') . ' = :username') ->where($this->_db->quoteName('id') . ' != :userid') ->bind(':username', $this->username) ->bind(':userid', $uid, ParameterType::INTEGER); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ($xid && $xid != (int) $this->id) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERNAME_INUSE')); return false; } // Check for existing email $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where('LOWER(' . $this->_db->quoteName('email') . ') = LOWER(:mail)') ->where($this->_db->quoteName('id') . ' != :muserid') ->bind(':mail', $this->email) ->bind(':muserid', $uid, ParameterType::INTEGER); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ($xid && $xid != (int) $this->id) { $this->setError(Text::_('JLIB_DATABASE_ERROR_EMAIL_INUSE')); return false; } // Check for root_user != username $rootUser = Factory::getApplication()->get('root_user'); if (!is_numeric($rootUser)) { $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('username') . ' = :username') ->bind(':username', $rootUser); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ( $rootUser == $this->username && (!$xid || $xid && $xid != (int) $this->id) || $xid && $xid == (int) $this->id && $rootUser != $this->username ) { $this->setError(Text::_('JLIB_DATABASE_ERROR_USERNAME_CANNOT_CHANGE')); return false; } } // Set an empty string value to the legacy otpKey and otep columns if empty $this->otpKey = $this->otpKey ?: ''; $this->otep = $this->otep ?: ''; return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.7.0 */ public function store($updateNulls = true) { // Get the table key and key value. $k = $this->_tbl_key; $key = $this->$k; // @todo: This is a dumb way to handle the groups. // Store groups locally so as to not update directly. $groups = $this->groups; unset($this->groups); // Insert or update the object based on presence of a key value. if ($key) { // Already have a table key, update the row. $this->_db->updateObject($this->_tbl, $this, $this->_tbl_key, $updateNulls); } else { // Don't have a table key, insert the row. $this->_db->insertObject($this->_tbl, $this, $this->_tbl_key); } // Reset groups to the local object. $this->groups = $groups; $query = $this->_db->getQuery(true); // Store the group data if the user data was saved. if (\is_array($this->groups) && \count($this->groups)) { $uid = (int) $this->id; // Grab all usergroup entries for the user $query->clear() ->select($this->_db->quoteName('group_id')) ->from($this->_db->quoteName('#__user_usergroup_map')) ->where($this->_db->quoteName('user_id') . ' = :userid') ->bind(':userid', $uid, ParameterType::INTEGER); $this->_db->setQuery($query); $result = $this->_db->loadObjectList(); // Loop through them and check if database contains something $this->groups does not if (\count($result)) { $mapGroupId = []; foreach ($result as $map) { if (\array_key_exists($map->group_id, $this->groups)) { // It already exists, no action required unset($groups[$map->group_id]); } else { $mapGroupId[] = (int) $map->group_id; } } if (\count($mapGroupId)) { $query->clear() ->delete($this->_db->quoteName('#__user_usergroup_map')) ->where($this->_db->quoteName('user_id') . ' = :uid') ->whereIn($this->_db->quoteName('group_id'), $mapGroupId) ->bind(':uid', $uid, ParameterType::INTEGER); $this->_db->setQuery($query); $this->_db->execute(); } } // If there is anything left in this->groups it needs to be inserted if (\count($groups)) { // Set the new user group maps. $query->clear() ->insert($this->_db->quoteName('#__user_usergroup_map')) ->columns([$this->_db->quoteName('user_id'), $this->_db->quoteName('group_id')]); foreach ($groups as $group) { $query->values( implode( ',', $query->bindArray( [$this->id , $group], [ParameterType::INTEGER, ParameterType::INTEGER] ) ) ); } $this->_db->setQuery($query); $this->_db->execute(); } unset($groups); } // If a user is blocked, delete the cookie login rows if ($this->block == 1) { $query->clear() ->delete($this->_db->quoteName('#__user_keys')) ->where($this->_db->quoteName('user_id') . ' = :user_id') ->bind(':user_id', $this->username); $this->_db->setQuery($query); $this->_db->execute(); } return true; } /** * Method to delete a user, user groups, and any other necessary data from the database. * * @param integer $userId An optional user id. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function delete($userId = null) { // Set the primary key to delete. $k = $this->_tbl_key; if ($userId) { $this->$k = (int) $userId; } $key = (int) $this->$k; // Delete the user. $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName($this->_tbl_key) . ' = :key') ->bind(':key', $key, ParameterType::INTEGER); $this->_db->setQuery($query); $this->_db->execute(); // Delete the user group maps. $query->clear() ->delete($this->_db->quoteName('#__user_usergroup_map')) ->where($this->_db->quoteName('user_id') . ' = :key') ->bind(':key', $key, ParameterType::INTEGER); $this->_db->setQuery($query); $this->_db->execute(); /* * Clean Up Related Data. */ $query->clear() ->delete($this->_db->quoteName('#__messages_cfg')) ->where($this->_db->quoteName('user_id') . ' = :key') ->bind(':key', $key, ParameterType::INTEGER); $this->_db->setQuery($query); $this->_db->execute(); $query->clear() ->delete($this->_db->quoteName('#__messages')) ->where($this->_db->quoteName('user_id_to') . ' = :key') ->bind(':key', $key, ParameterType::INTEGER); $this->_db->setQuery($query); $this->_db->execute(); $query->clear() ->delete($this->_db->quoteName('#__user_keys')) ->where($this->_db->quoteName('user_id') . ' = :username') ->bind(':username', $this->username); $this->_db->setQuery($query); $this->_db->execute(); return true; } /** * Updates last visit time of user * * @param integer $timeStamp The timestamp, defaults to 'now'. * @param integer $userId The user id (optional). * * @return boolean False if an error occurs * * @since 1.7.0 */ public function setLastVisit($timeStamp = null, $userId = null) { // Check for User ID if (\is_null($userId)) { if (isset($this)) { $userId = $this->id; } else { jexit('No userid in setLastVisit'); } } // If no timestamp value is passed to function, then current time is used. if ($timeStamp === null) { $timeStamp = 'now'; } $date = Factory::getDate($timeStamp); $userId = (int) $userId; $lastVisit = $date->toSql(); // Update the database row for the user. $db = $this->_db; $query = $db->getQuery(true) ->update($db->quoteName($this->_tbl)) ->set($db->quoteName('lastvisitDate') . ' = :lastvisitDate') ->where($db->quoteName('id') . ' = :id') ->bind(':lastvisitDate', $lastVisit) ->bind(':id', $userId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); return true; } } PK1A#]�Z�h�*�*src/Table/Content.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Tag\TaggableTableInterface; use Joomla\CMS\Tag\TaggableTableTrait; use Joomla\CMS\Versioning\VersionableTableInterface; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Content table * * @since 1.5 */ class Content extends Table implements VersionableTableInterface, TaggableTableInterface { use TaggableTableTrait; /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Constructor * * @param DatabaseDriver $db A database connector object * * @since 1.5 */ public function __construct(DatabaseDriver $db) { $this->typeAlias = 'com_content.article'; parent::__construct('#__content', 'id', $db); // Set the alias since the column is called state $this->setColumnAlias('published', 'state'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.6 */ protected function _getAssetName() { $k = $this->_tbl_key; return 'com_content.article.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 1.6 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset id for the record * * @param Table $table A Table object (optional) for the asset parent * @param integer $id The id (optional) of the content. * * @return integer * * @since 1.6 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is an article under a category. if ($this->catid) { $catId = (int) $this->catid; // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('asset_id')) ->from($this->_db->quoteName('#__categories')) ->where($this->_db->quoteName('id') . ' = :catid') ->bind(':catid', $catId, ParameterType::INTEGER); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error string * * @see Table::bind() * @since 1.6 */ public function bind($array, $ignore = '') { // Search for the {readmore} tag and split the text up accordingly. if (isset($array['articletext'])) { $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i'; $tagPos = preg_match($pattern, $array['articletext']); if ($tagPos == 0) { $this->introtext = $array['articletext']; $this->fulltext = ''; } else { list($this->introtext, $this->fulltext) = preg_split($pattern, $array['articletext'], 2); } } if (isset($array['attribs']) && \is_array($array['attribs'])) { $registry = new Registry($array['attribs']); $array['attribs'] = (string) $registry; } if (isset($array['metadata']) && \is_array($array['metadata'])) { $registry = new Registry($array['metadata']); $array['metadata'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && \is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 1.5 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } if (trim($this->title) == '') { $this->setError(Text::_('COM_CONTENT_WARNING_PROVIDE_VALID_NAME')); return false; } if (trim($this->alias) == '') { $this->alias = $this->title; } $this->alias = ApplicationHelper::stringURLSafe($this->alias, $this->language); if (trim(str_replace('-', '', $this->alias)) == '') { $this->alias = Factory::getDate()->format('Y-m-d-H-i-s'); } // Check for a valid category. if (!$this->catid = (int) $this->catid) { $this->setError(Text::_('JLIB_DATABASE_ERROR_CATEGORY_REQUIRED')); return false; } if (trim(str_replace(' ', '', $this->fulltext)) == '') { $this->fulltext = ''; } /** * Ensure any new items have compulsory fields set. This is needed for things like * frontend editing where we don't show all the fields or using some kind of API */ if (!$this->id) { // Images can be an empty json string if (!isset($this->images)) { $this->images = '{}'; } // URLs can be an empty json string if (!isset($this->urls)) { $this->urls = '{}'; } // Attributes (article params) can be an empty json string if (!isset($this->attribs)) { $this->attribs = '{}'; } // Metadata can be an empty json string if (!isset($this->metadata)) { $this->metadata = '{}'; } // Hits must be zero on a new item $this->hits = 0; } // Set publish_up to null if not set if (!$this->publish_up) { $this->publish_up = null; } // Set publish_down to null if not set if (!$this->publish_down) { $this->publish_down = null; } // Check the publish down date is not earlier than publish up. if (!is_null($this->publish_up) && !is_null($this->publish_down) && $this->publish_down < $this->publish_up) { // Swap the dates. $temp = $this->publish_up; $this->publish_up = $this->publish_down; $this->publish_down = $temp; } // Clean up keywords -- eliminate extra spaces between phrases // and cr (\r) and lf (\n) characters from string if (!empty($this->metakey)) { // Only process if not empty // Array of characters to remove $badCharacters = ["\n", "\r", "\"", '<', '>']; // Remove bad characters $afterClean = StringHelper::str_ireplace($badCharacters, '', $this->metakey); // Create array using commas as delimiter $keys = explode(',', $afterClean); $cleanKeys = []; foreach ($keys as $key) { if (trim($key)) { // Ignore blank keywords $cleanKeys[] = trim($key); } } // Put array back together delimited by ", " $this->metakey = implode(', ', $cleanKeys); } else { $this->metakey = ''; } if ($this->metadesc === null) { $this->metadesc = ''; } return true; } /** * Overrides Table::store to set modified data and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 */ public function store($updateNulls = true) { $date = Factory::getDate()->toSql(); $user = Factory::getUser(); // Set created date if not set. if (!(int) $this->created) { $this->created = $date; } if ($this->id) { // Existing item $this->modified_by = $user->get('id'); $this->modified = $date; } else { // Field created_by can be set by the user, so we don't touch it if it's set. if (empty($this->created_by)) { $this->created_by = $user->get('id'); } // Set modified to created date if not set if (!(int) $this->modified) { $this->modified = $this->created; } // Set modified_by to created_by user if not set if (empty($this->modified_by)) { $this->modified_by = $this->created_by; } } // Verify that the alias is unique $table = Table::getInstance('Content', 'JTable', ['dbo' => $this->getDbo()]); if ($table->load(['alias' => $this->alias, 'catid' => $this->catid]) && ($table->id != $this->id || $this->id == 0)) { // Is the existing article trashed? $this->setError(Text::_('COM_CONTENT_ERROR_UNIQUE_ALIAS')); if ($table->state === -2) { $this->setError(Text::_('COM_CONTENT_ERROR_UNIQUE_ALIAS_TRASHED')); } return false; } return parent::store($updateNulls); } /** * Get the type alias for UCM features * * @return string The alias as described above * * @since 4.0.0 */ public function getTypeAlias() { return $this->typeAlias; } } PK1A#])>W��src/Table/Ucm.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * UCM map table * * @since 3.1 */ class Ucm extends Table { /** * Constructor * * @param \Joomla\Database\DatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct($db) { parent::__construct('#__ucm_base', 'ucm_id', $db); } } PK1A#]yP�� � src/Table/Category.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Tag\TaggableTableInterface; use Joomla\CMS\Tag\TaggableTableTrait; use Joomla\CMS\Versioning\VersionableTableInterface; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Category table * * @since 1.5 */ class Category extends Nested implements VersionableTableInterface, TaggableTableInterface { use TaggableTableTrait; /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(DatabaseDriver $db) { /** * @deprecated 4.0 will be removed in 6.0 * This format was used by tags and versioning before 4.0 before * the introduction of the getTypeAlias function. */ $this->typeAlias = '{extension}.category'; parent::__construct('#__categories', 'id', $db); $this->access = (int) Factory::getApplication()->get('access'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.6 */ protected function _getAssetName() { $k = $this->_tbl_key; return $this->extension . '.category.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 1.6 */ protected function _getAssetTitle() { return $this->title; } /** * Get the parent asset id for the record * * @param Table $table A Table object for the asset parent. * @param integer $id The id for the asset * * @return integer The id of the asset's parent * * @since 1.6 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is a category under a category. if ($this->parent_id > 1) { // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('asset_id')) ->from($this->_db->quoteName('#__categories')) ->where($this->_db->quoteName('id') . ' = :parentId') ->bind(':parentId', $this->parent_id, ParameterType::INTEGER); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } elseif ($assetId === null) { // This is a category that needs to parent with the extension. // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__assets')) ->where($this->_db->quoteName('name') . ' = :extension') ->bind(':extension', $this->extension); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Override check function * * @return boolean * * @see Table::check() * @since 1.5 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for a title. if (trim($this->title) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_CATEGORY')); return false; } $this->alias = trim($this->alias); if (empty($this->alias)) { $this->alias = $this->title; } $this->alias = ApplicationHelper::stringURLSafe($this->alias, $this->language); if (trim(str_replace('-', '', $this->alias)) == '') { $this->alias = Factory::getDate()->format('Y-m-d-H-i-s'); } return true; } /** * Overloaded bind function. * * @param array $array named array * @param string $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.6 */ public function bind($array, $ignore = '') { if (isset($array['params']) && \is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } if (isset($array['metadata']) && \is_array($array['metadata'])) { $registry = new Registry($array['metadata']); $array['metadata'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && \is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Overridden Table::store to set created/modified and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 */ public function store($updateNulls = true) { $date = Factory::getDate()->toSql(); $user = Factory::getUser(); // Set created date if not set. if (!(int) $this->created_time) { $this->created_time = $date; } if ($this->id) { // Existing category $this->modified_user_id = $user->get('id'); $this->modified_time = $date; } else { if (!(int) ($this->modified_time)) { $this->modified_time = $this->created_time; } // Field created_user_id can be set by the user, so we don't touch it if it's set. if (empty($this->created_user_id)) { $this->created_user_id = $user->get('id'); } if (empty($this->modified_user_id)) { $this->modified_user_id = $this->created_user_id; } } // Verify that the alias is unique $table = Table::getInstance('Category', 'JTable', ['dbo' => $this->getDbo()]); if ( $table->load(['alias' => $this->alias, 'parent_id' => (int) $this->parent_id, 'extension' => $this->extension]) && ($table->id != $this->id || $this->id == 0) ) { // Is the existing category trashed? $this->setError(Text::_('JLIB_DATABASE_ERROR_CATEGORY_UNIQUE_ALIAS')); if ($table->published === -2) { $this->setError(Text::_('JLIB_DATABASE_ERROR_CATEGORY_UNIQUE_ALIAS_TRASHED')); } return false; } return parent::store($updateNulls); } /** * Get the type alias for the history table * * @return string The alias as described above * * @since 4.0.0 */ public function getTypeAlias() { return $this->extension . '.category'; } } PK1A#]/��X��src/Table/Language.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Languages table. * * @since 1.7.0 */ class Language extends Table { /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__languages', 'lang_id', $db); } /** * Overloaded check method to ensure data integrity * * @return boolean True on success * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } if (trim($this->title) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_LANGUAGE_NO_TITLE')); return false; } return true; } /** * Overrides Table::store to check unique fields. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 2.5.0 */ public function store($updateNulls = false) { $table = Table::getInstance('Language', 'JTable', ['dbo' => $this->getDbo()]); // Verify that the language code is unique if ($table->load(['lang_code' => $this->lang_code]) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(Text::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_LANG_CODE')); return false; } // Verify that the sef field is unique if ($table->load(['sef' => $this->sef]) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(Text::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_IMAGE')); return false; } // Verify that the image field is unique if ($this->image && $table->load(['image' => $this->image]) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(Text::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_IMAGE')); return false; } return parent::store($updateNulls); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.8.0 */ protected function _getAssetName() { return 'com_languages.language.' . $this->lang_id; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.8.0 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 3.8.0 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; $asset = Table::getInstance('asset'); if ($asset->loadByName('com_languages')) { $assetId = $asset->id; } return $assetId ?? parent::_getAssetParentId($table, $id); } } PK1A#]��mw| | src/Table/Extension.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extension table * * @since 1.7.0 */ class Extension extends Table { /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Ensure the params in json encoded in the bind method * * @var array * @since 4.0.0 */ protected $_jsonEncode = ['params']; /** * Custom data can be used by extension developers * * @var string * @since 4.0.0 */ public $custom_data = ''; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__extensions', 'extension_id', $db); // Set the alias since the column is called enabled $this->setColumnAlias('published', 'enabled'); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for valid name if (trim($this->name) == '' || trim($this->element) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } return true; } /** * Method to create and execute a SELECT WHERE query. * * @param array $options Array of options * * @return string The database query result * * @since 1.7.0 */ public function find($options = []) { // Get the DatabaseQuery object $query = $this->_db->getQuery(true); foreach ($options as $col => $val) { $query->where($col . ' = ' . $this->_db->quote($val)); } $query->select($this->_db->quoteName('extension_id')) ->from($this->_db->quoteName('#__extensions')); $this->_db->setQuery($query); return $this->_db->loadResult(); } } PK1A#]v>���src/Table/TableInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Table class interface. * * @since 3.2 */ interface TableInterface { /** * Method to bind an associative array or object to the TableInterface instance. * * This method only binds properties that are publicly accessible and optionally takes an array of properties to ignore when binding. * * @param mixed $src An associative array or object to bind to the TableInterface instance. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 3.2 * @throws \UnexpectedValueException */ public function bind($src, $ignore = []); /** * Method to perform sanity checks on the TableInterface instance properties to ensure they are safe to store in the database. * * Implementations of this interface should use this method to make sure the data they are storing in the database is safe and * as expected before storage. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 3.2 */ public function check(); /** * Method to delete a record. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 3.2 * @throws \UnexpectedValueException */ public function delete($pk = null); /** * Method to get the DatabaseDriver object. * * @return DatabaseDriver The internal database driver object. * * @since 3.2 */ public function getDbo(); /** * Method to get the primary key field name for the table. * * @return string The name of the primary key for the table. * * @since 3.2 */ public function getKeyName(); /** * Method to load a row from the database by primary key and bind the fields to the TableInterface instance properties. * * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not * set the instance property value is used. * @param boolean $reset True to reset the default values before loading the new row. * * @return boolean True if successful. False if row not found. * * @since 3.2 * @throws \RuntimeException * @throws \UnexpectedValueException */ public function load($keys = null, $reset = true); /** * Method to reset class properties to the defaults set in the class definition. * * It will ignore the primary key as well as any private class properties. * * @return void * * @since 3.2 */ public function reset(); /** * Method to store a row in the database from the TableInterface instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the TableInterface instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.2 */ public function store($updateNulls = false); /** * Returns the identity (primary key) value of this record * * @return mixed * * @since 4.0.0 */ public function getId(); /** * Check if the record has a property (applying a column alias if it exists) * * @param string $key key to be checked * * @return boolean * * @since 4.0.0 */ public function hasField($key); } PK1A#]R�r� � src/Table/Update.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Update table * Stores updates temporarily * * @since 1.7.0 */ class Update extends Table { /** * Ensure the params in json encoded in the bind method * * @var array * @since 4.0.0 */ protected $_jsonEncode = ['params']; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__updates', 'update_id', $db); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for valid name if (trim($this->name) == '' || trim($this->element) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } if (!$this->update_id && !$this->data) { $this->data = ''; } // While column is not nullable, make sure we have a value. if ($this->description === null) { $this->description = ''; } return true; } /** * Method to create and execute a SELECT WHERE query. * * @param array $options Array of options * * @return string Results of query * * @since 1.7.0 */ public function find($options = []) { $where = []; foreach ($options as $col => $val) { $where[] = $col . ' = ' . $this->_db->quote($val); } $query = $this->_db->getQuery(true) ->select($this->_db->quoteName($this->_tbl_key)) ->from($this->_db->quoteName($this->_tbl)) ->where(implode(' AND ', $where)); $this->_db->setQuery($query); return $this->_db->loadResult(); } } PK1A#],�'G��src/Table/UpdateSite.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Update site table * Stores the update sites for extensions * * @since 3.4 */ class UpdateSite extends Table { /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 3.4 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__update_sites', 'update_site_id', $db); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 3.4 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for valid name if (trim($this->name) == '' || trim($this->location) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } return true; } } PK1A#]�����(�(src/Table/CoreContent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Helper\ContentHelper; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Core content table * * @since 3.1 */ class CoreContent extends Table { /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Encode necessary fields to JSON in the bind method * * @var array * @since 4.0.0 */ protected $_jsonEncode = ['core_params', 'core_metadata', 'core_images', 'core_urls', 'core_body']; /** * Constructor * * @param DatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__ucm_content', 'core_content_id', $db); $this->setColumnAlias('published', 'core_state'); $this->setColumnAlias('checked_out', 'core_checked_out_user_id'); $this->setColumnAlias('checked_out_time', 'core_checked_out_time'); $this->_trackAssets = false; } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 3.1 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } if (trim($this->core_title) === '') { $this->setError(Text::_('JLIB_CMS_WARNING_PROVIDE_VALID_NAME')); return false; } if (trim($this->core_alias) === '') { $this->core_alias = $this->core_title; } $this->core_alias = ApplicationHelper::stringURLSafe($this->core_alias); if (trim(str_replace('-', '', $this->core_alias)) === '') { $this->core_alias = Factory::getDate()->format('Y-m-d-H-i-s'); } // Not Null sanity check if (empty($this->core_images)) { $this->core_images = '{}'; } if (empty($this->core_urls)) { $this->core_urls = '{}'; } // Check the publish down date is not earlier than publish up. if ( $this->core_publish_up !== null && $this->core_publish_down !== null && $this->core_publish_down < $this->core_publish_up && $this->core_publish_down > $this->_db->getNullDate() ) { // Swap the dates. $temp = $this->core_publish_up; $this->core_publish_up = $this->core_publish_down; $this->core_publish_down = $temp; } // Clean up keywords -- eliminate extra spaces between phrases // and cr (\r) and lf (\n) characters from string if (!empty($this->core_metakey)) { // Only process if not empty // Array of characters to remove $bad_characters = ["\n", "\r", "\"", '<', '>']; // Remove bad characters $after_clean = StringHelper::str_ireplace($bad_characters, '', $this->core_metakey); // Create array using commas as delimiter $keys = explode(',', $after_clean); $clean_keys = []; foreach ($keys as $key) { if (trim($key)) { // Ignore blank keywords $clean_keys[] = trim($key); } } // Put array back together delimited by ", " $this->core_metakey = implode(', ', $clean_keys); } return true; } /** * Override JTable delete method to include deleting corresponding row from #__ucm_base. * * @param integer $pk primary key value to delete. Must be set or throws an exception. * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function delete($pk = null) { $baseTable = Table::getInstance('Ucm', 'JTable', ['dbo' => $this->getDbo()]); return parent::delete($pk) && $baseTable->delete($pk); } /** * Method to delete a row from the #__ucm_content table by content_item_id. * * @param integer $contentItemId value of the core_content_item_id to delete. Corresponds to the primary key of the content table. * @param string $typeAlias Alias for the content type * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function deleteByContentId($contentItemId = null, $typeAlias = null) { $contentItemId = (int) $contentItemId; if ($contentItemId === 0) { throw new \UnexpectedValueException('Null content item key not allowed.'); } if ($typeAlias === null) { throw new \UnexpectedValueException('Null type alias not allowed.'); } $db = $this->getDbo(); $query = $db->getQuery(true); $query->select($db->quoteName('core_content_id')) ->from($db->quoteName('#__ucm_content')) ->where( [ $db->quoteName('core_content_item_id') . ' = :contentItemId', $db->quoteName('core_type_alias') . ' = :typeAlias', ] ) ->bind(':contentItemId', $contentItemId, ParameterType::INTEGER) ->bind(':typeAlias', $typeAlias); $db->setQuery($query); if ($ucmId = $db->loadResult()) { return $this->delete($ucmId); } else { return true; } } /** * Overrides Table::store to set modified data and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.1 */ public function store($updateNulls = true) { $date = Factory::getDate(); $user = Factory::getUser(); if ($this->core_content_id) { // Existing item $this->core_modified_time = $date->toSql(); $this->core_modified_user_id = $user->get('id'); $isNew = false; } else { // New content item. A content item core_created_time and core_created_user_id field can be set by the user, // so we don't touch either of these if they are set. if (!(int) $this->core_created_time) { $this->core_created_time = $date->toSql(); } if (empty($this->core_created_user_id)) { $this->core_created_user_id = $user->get('id'); } if (!(int) $this->core_modified_time) { $this->core_modified_time = $this->core_created_time; } if (empty($this->core_modified_user_id)) { $this->core_modified_user_id = $this->core_created_user_id; } $isNew = true; } $oldRules = $this->getRules(); if (empty($oldRules)) { $this->setRules('{}'); } $result = parent::store($updateNulls); return $result && $this->storeUcmBase($updateNulls, $isNew); } /** * Insert or update row in ucm_base table * * @param boolean $updateNulls True to update fields even if they are null. * @param boolean $isNew if true, need to insert. Otherwise update. * * @return boolean True on success. * * @since 3.1 */ protected function storeUcmBase($updateNulls = true, $isNew = false) { // Store the ucm_base row $db = $this->getDbo(); $query = $db->getQuery(true); $languageId = ContentHelper::getLanguageId($this->core_language); // Selecting "all languages" doesn't give a language id - we can't store a blank string in non mysql databases, so save 0 (the default value) if (!$languageId) { $languageId = 0; } if ($isNew) { $query->insert($db->quoteName('#__ucm_base')) ->columns( [ $db->quoteName('ucm_id'), $db->quoteName('ucm_item_id'), $db->quoteName('ucm_type_id'), $db->quoteName('ucm_language_id'), ] ) ->values( implode( ',', $query->bindArray( [ $this->core_content_id, $this->core_content_item_id, $this->core_type_id, $languageId, ] ) ) ); } else { $query->update($db->quoteName('#__ucm_base')) ->set( [ $db->quoteName('ucm_item_id') . ' = :coreContentItemId', $db->quoteName('ucm_type_id') . ' = :typeId', $db->quoteName('ucm_language_id') . ' = :languageId', ] ) ->where($db->quoteName('ucm_id') . ' = :coreContentId') ->bind(':coreContentItemId', $this->core_content_item_id, ParameterType::INTEGER) ->bind(':typeId', $this->core_type_id, ParameterType::INTEGER) ->bind(':languageId', $languageId, ParameterType::INTEGER) ->bind(':coreContentId', $this->core_content_id, ParameterType::INTEGER); } $db->setQuery($query); return $db->execute(); } } PK1A#]�Mދ2+2+src/Table/MenuType.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Menu Types table * * @since 1.6 */ class MenuType extends Table { /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.6 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__menu_types', 'id', $db); } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 1.6 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } $this->menutype = ApplicationHelper::stringURLSafe($this->menutype); if (empty($this->menutype)) { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENUTYPE_EMPTY')); return false; } // Sanitise data. if (trim($this->title) === '') { $this->title = $this->menutype; } $id = (int) $this->id; // Check for unique menutype. $query = $this->_db->getQuery(true) ->select('COUNT(id)') ->from($this->_db->quoteName('#__menu_types')) ->where($this->_db->quoteName('menutype') . ' = :menutype') ->where($this->_db->quoteName('id') . ' <> :id') ->bind(':menutype', $this->menutype) ->bind(':id', $id, ParameterType::INTEGER); $this->_db->setQuery($query); if ($this->_db->loadResult()) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_MENUTYPE_EXISTS', $this->menutype)); return false; } return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 */ public function store($updateNulls = false) { if ($this->id) { // Get the user id $userId = (int) Factory::getUser()->id; $notIn = [0, $userId]; // Get the old value of the table $table = Table::getInstance('Menutype', 'JTable', ['dbo' => $this->getDbo()]); $table->load($this->id); // Verify that no items are checked out $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__menu')) ->where($this->_db->quoteName('menutype') . ' = :menutype') ->whereNotIn($this->_db->quoteName('checked_out'), $notIn) ->bind(':menutype', $table->menutype); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError( Text::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', \get_class($this), Text::_('JLIB_DATABASE_ERROR_MENUTYPE_CHECKOUT')) ); return false; } // Verify that no module for this menu are checked out $searchParams = '%"menutype":' . json_encode($table->menutype) . '%'; $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__modules')) ->where($this->_db->quoteName('module') . ' = ' . $this->_db->quote('mod_menu')) ->where($this->_db->quoteName('params') . ' LIKE :params') ->whereNotIn($this->_db->quoteName('checked_out'), $notIn) ->bind(':params', $searchParams); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError( Text::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', \get_class($this), Text::_('JLIB_DATABASE_ERROR_MENUTYPE_CHECKOUT')) ); return false; } // Update the menu items $query->clear() ->update($this->_db->quoteName('#__menu')) ->set($this->_db->quoteName('menutype') . ' = :setmenutype') ->where($this->_db->quoteName('menutype') . ' = :menutype') ->bind(':setmenutype', $this->menutype) ->bind(':menutype', $table->menutype); $this->_db->setQuery($query); $this->_db->execute(); // Update the module items $whereParams = '%"menutype":' . json_encode($table->menutype) . '%'; $searchParams = '"menutype":' . json_encode($table->menutype); $replaceParams = '"menutype":' . json_encode($this->menutype); $query->clear() ->update($this->_db->quoteName('#__modules')) ->set( $this->_db->quoteName('params') . ' = REPLACE(' . $this->_db->quoteName('params') . ', :search, :value)' ); $query->where($this->_db->quoteName('module') . ' = ' . $this->_db->quote('mod_menu')) ->where($this->_db->quoteName('params') . ' LIKE :whereparams') ->bind(':search', $searchParams) ->bind(':value', $replaceParams) ->bind(':whereparams', $whereParams); $this->_db->setQuery($query); $this->_db->execute(); } return parent::store($updateNulls); } /** * Method to delete a row from the database table by primary key value. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.6 */ public function delete($pk = null) { $k = $this->_tbl_key; $pk = $pk === null ? $this->$k : $pk; // If no primary key is given, return false. if ($pk !== null) { // Get the user id $userId = (int) Factory::getUser()->id; $notIn = [0, $userId]; $star = '*'; // Get the old value of the table $table = Table::getInstance('Menutype', 'JTable', ['dbo' => $this->getDbo()]); $table->load($pk); // Verify that no items are checked out $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__menu')) ->where($this->_db->quoteName('menutype') . ' = :menutype') ->where('(' . $this->_db->quoteName('checked_out') . ' NOT IN (NULL, :id)' . ' OR ' . $this->_db->quoteName('home') . ' = 1' . ' AND ' . $this->_db->quoteName('language') . ' = :star' . ')') ->bind(':menutype', $table->menutype) ->bind(':id', $userId, ParameterType::INTEGER) ->bind(':star', $star); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_DELETE_FAILED', \get_class($this), Text::_('JLIB_DATABASE_ERROR_MENUTYPE'))); return false; } // Verify that no module for this menu are checked out $searchParams = '%"menutype":' . json_encode($table->menutype) . '%'; $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__modules')) ->where($this->_db->quoteName('module') . ' = ' . $this->_db->quote('mod_menu')) ->where($this->_db->quoteName('params') . ' LIKE :menutype') ->whereNotIn($this->_db->quoteName('checked_out'), $notIn) ->bind(':menutype', $searchParams); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_DELETE_FAILED', \get_class($this), Text::_('JLIB_DATABASE_ERROR_MENUTYPE'))); return false; } // Delete the menu items $query->clear() ->delete('#__menu') ->where('menutype=' . $this->_db->quote($table->menutype)); $this->_db->setQuery($query); $this->_db->execute(); // Update the module items $query->clear() ->delete('#__modules') ->where('module=' . $this->_db->quote('mod_menu')) ->where('params LIKE ' . $this->_db->quote('%"menutype":' . json_encode($table->menutype) . '%')); $this->_db->setQuery($query); $this->_db->execute(); } return parent::delete($pk); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.6 */ protected function _getAssetName() { return 'com_menus.menu.' . $this->id; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.6 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 3.6 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; $asset = Table::getInstance('asset'); if ($asset->loadByName('com_menus')) { $assetId = $asset->id; } return $assetId === null ? parent::_getAssetParentId($table, $id) : $assetId; } } PK1A#]@���E E src/Table/ViewLevel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Viewlevels table class. * * @since 1.7.0 */ class ViewLevel extends Table { /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__viewlevels', 'id', $db); } /** * Method to bind the data. * * @param array $array The data to bind. * @param mixed $ignore An array or space separated list of fields to ignore. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function bind($array, $ignore = '') { // Bind the rules as appropriate. if (isset($array['rules'])) { if (\is_array($array['rules'])) { $array['rules'] = json_encode($array['rules']); } } return parent::bind($array, $ignore); } /** * Method to check the current record to save * * @return boolean True on success * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Validate the title. if ((trim($this->title)) == '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_VIEWLEVEL')); return false; } $id = (int) $this->id; // Check for a duplicate title. $db = $this->_db; $query = $db->getQuery(true) ->select('COUNT(' . $db->quoteName('title') . ')') ->from($db->quoteName('#__viewlevels')) ->where($db->quoteName('title') . ' = :title') ->where($db->quoteName('id') . ' != :id') ->bind(':title', $this->title) ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); if ($db->loadResult() > 0) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_USERLEVEL_NAME_EXISTS', $this->title)); return false; } return true; } } PK1A#]� h�src/Table/ContentType.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Tags table * * @since 3.1 */ class ContentType extends Table { /** * Constructor * * @param DatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__content_types', 'type_id', $db); } /** * Overloaded check method to ensure data integrity. * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for valid name. if (trim($this->type_title) === '') { throw new \UnexpectedValueException(sprintf('The title is empty')); } $this->type_title = ucfirst($this->type_title); if (empty($this->type_alias)) { throw new \UnexpectedValueException(sprintf('The type_alias is empty')); } return true; } /** * Overridden Table::store. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.1 */ public function store($updateNulls = false) { // Verify that the alias is unique $table = Table::getInstance('Contenttype', 'JTable', ['dbo' => $this->getDbo()]); if ($table->load(['type_alias' => $this->type_alias]) && ($table->type_id != $this->type_id || $this->type_id == 0)) { $this->setError(Text::_('COM_TAGS_ERROR_UNIQUE_ALIAS')); return false; } return parent::store($updateNulls); } /** * Method to expand the field mapping * * @param boolean $assoc True to return an associative array. * * @return mixed Array or object with field mappings. Defaults to object. * * @since 3.1 */ public function fieldmapExpand($assoc = true) { return $this->fieldmap = json_decode($this->fieldmappings, $assoc); } /** * Method to get the id given the type alias * * @param string $typeAlias Content type alias (for example, 'com_content.article'). * * @return mixed type_id for this alias if successful, otherwise null. * * @since 3.2 */ public function getTypeId($typeAlias) { $db = $this->_db; $query = $db->getQuery(true); $query->select($db->quoteName('type_id')) ->from($db->quoteName($this->_tbl)) ->where($db->quoteName('type_alias') . ' = :type_alias') ->bind(':type_alias', $typeAlias); $db->setQuery($query); return $db->loadResult(); } /** * Method to get the Table object for the content type from the table object. * * @return mixed Table object on success, otherwise false. * * @since 3.2 * * @throws \RuntimeException */ public function getContentTable() { $result = false; $tableInfo = json_decode($this->table); if (\is_object($tableInfo) && isset($tableInfo->special)) { if (\is_object($tableInfo->special) && isset($tableInfo->special->type) && isset($tableInfo->special->prefix)) { $class = $tableInfo->special->class ?? 'Joomla\\CMS\\Table\\Table'; if (!class_implements($class, 'Joomla\\CMS\\Table\\TableInterface')) { // This isn't an instance of TableInterface. Stop. throw new \RuntimeException('Class must be an instance of Joomla\\CMS\\Table\\TableInterface'); } $result = $class::getInstance($tableInfo->special->type, $tableInfo->special->prefix); } } return $result; } } PK1A#]Y��<����src/Table/Nested.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Event\AbstractEvent; use Joomla\Event\Dispatcher; use Joomla\Event\Event; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Table class supporting modified pre-order tree traversal behavior. * * @since 1.7.0 */ class Nested extends Table { /** * Object property holding the primary key of the parent node. Provides adjacency list data for nodes. * * @var integer * @since 1.7.0 */ public $parent_id; /** * Object property holding the depth level of the node in the tree. * * @var integer * @since 1.7.0 */ public $level; /** * Object property holding the left value of the node for managing its placement in the nested sets tree. * * @var integer * @since 1.7.0 */ public $lft; /** * Object property holding the right value of the node for managing its placement in the nested sets tree. * * @var integer * @since 1.7.0 */ public $rgt; /** * Object property holding the alias of this node used to construct the full text path, forward-slash delimited. * * @var string * @since 1.7.0 */ public $alias; /** * Object property to hold the location type to use when storing the row. * * @var string * @since 1.7.0 * @see Nested::$_validLocations */ protected $_location; /** * Object property to hold the primary key of the location reference node to use when storing the row. * * A combination of location type and reference node describes where to store the current node in the tree. * * @var integer * @since 1.7.0 */ protected $_location_id; /** * An array to cache values in recursive processes. * * @var array * @since 1.7.0 */ protected $_cache = []; /** * Debug level * * @var integer * @since 1.7.0 */ protected $_debug = 0; /** * Cache for the root ID * * @var integer * @since 3.3 */ protected static $root_id = 0; /** * Array declaring the valid location values for moving a node * * @var array * @since 3.7.0 */ private $_validLocations = ['before', 'after', 'first-child', 'last-child']; /** * Sets the debug level on or off * * @param integer $level 0 = off, 1 = on * * @return void * * @since 1.7.0 */ public function debug($level) { $this->_debug = (int) $level; } /** * Method to get an array of nodes from a given node to its root. * * @param integer $pk Primary key of the node for which to get the path. * @param boolean $diagnostic Only select diagnostic data for the nested sets. * * @return mixed An array of node objects including the start node. * * @since 1.7.0 * @throws \RuntimeException on database error */ public function getPath($pk = null, $diagnostic = false) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Get the path from the node to the root. $select = ($diagnostic) ? 'p.' . $k . ', p.parent_id, p.level, p.lft, p.rgt' : 'p.*'; $query = $this->_db->getQuery(true) ->select($select) ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('n.' . $k . ' = ' . (int) $pk) ->order('p.lft'); $this->_db->setQuery($query); return $this->_db->loadObjectList(); } /** * Method to get a node and all its child nodes. * * @param integer $pk Primary key of the node for which to get the tree. * @param boolean $diagnostic Only select diagnostic data for the nested sets. * * @return mixed Boolean false on failure or array of node objects on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function getTree($pk = null, $diagnostic = false) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Get the node and children as a tree. $select = ($diagnostic) ? 'n.' . $k . ', n.parent_id, n.level, n.lft, n.rgt' : 'n.*'; $query = $this->_db->getQuery(true) ->select($select) ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('p.' . $k . ' = ' . (int) $pk) ->order('n.lft'); return $this->_db->setQuery($query)->loadObjectList(); } /** * Method to determine if a node is a leaf node in the tree (has no children). * * @param integer $pk Primary key of the node to check. * * @return boolean True if a leaf node, false if not or null if the node does not exist. * * @note Since 3.0.0 this method returns null if the node does not exist. * @since 1.7.0 * @throws \RuntimeException on database error. */ public function isLeaf($pk = null) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; $node = $this->_getNode($pk); // Get the node by primary key. if (empty($node)) { // Error message set in getNode method. return; } // The node is a leaf node. return ($node->rgt - $node->lft) == 1; } /** * Method to set the location of a node in the tree object. This method does not * save the new location to the database, but will set it in the object so * that when the node is stored it will be stored in the new location. * * @param integer $referenceId The primary key of the node to reference new location by. * @param string $position Location type string. * * @return void * * @note Since 3.0.0 this method returns void and throws an \InvalidArgumentException when an invalid position is passed. * @see Nested::$_validLocations * @since 1.7.0 * @throws \InvalidArgumentException */ public function setLocation($referenceId, $position = 'after') { // Make sure the location is valid. if (!\in_array($position, $this->_validLocations)) { throw new \InvalidArgumentException( sprintf('Invalid location "%1$s" given, valid values are %2$s', $position, implode(', ', $this->_validLocations)) ); } // Set the location properties. $this->_location = $position; $this->_location_id = $referenceId; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string $where WHERE clause to use for limiting the selection of rows to compact the * ordering values. * * @return mixed Boolean true on success. * * @since 1.7.0 */ public function move($delta, $where = '') { $k = $this->_tbl_key; $pk = $this->$k; $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('parent_id = ' . $this->parent_id); if ($where) { $query->where($where); } if ($delta > 0) { $query->where('rgt > ' . $this->rgt) ->order('rgt ASC'); $position = 'after'; } else { $query->where('lft < ' . $this->lft) ->order('lft DESC'); $position = 'before'; } $this->_db->setQuery($query); $referenceId = $this->_db->loadResult(); if ($referenceId) { return $this->moveByReference($referenceId, $position, $pk); } else { return false; } } /** * Method to move a node and its children to a new location in the tree. * * @param integer $referenceId The primary key of the node to reference new location by. * @param string $position Location type string. ['before', 'after', 'first-child', 'last-child'] * @param integer $pk The primary key of the node to move. * @param boolean $recursiveUpdate Flag indicate that method recursiveUpdatePublishedColumn should be call. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function moveByReference($referenceId, $position = 'after', $pk = null, $recursiveUpdate = true) { if ($this->_debug) { echo "\nMoving ReferenceId:$referenceId, Position:$position, PK:$pk"; } $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Get the node by id. if (!$node = $this->_getNode($pk)) { // Error message set in getNode method. return false; } // Get the ids of child nodes. $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $children = $this->_db->setQuery($query)->loadColumn(); if ($this->_debug) { $this->_logtable(false); } // Cannot move the node to be a child of itself. if (\in_array($referenceId, $children)) { $this->setError( new \UnexpectedValueException( sprintf('%1$s::moveByReference() is trying to make record ID %2$d a child of itself.', \get_class($this), $pk) ) ); return false; } // Lock the table for writing. if (!$this->_lock()) { return false; } /* * Move the sub-tree out of the nested sets by negating its left and right values. */ $query->clear() ->update($this->_tbl) ->set('lft = lft * (-1), rgt = rgt * (-1)') ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); /* * Close the hole in the tree that was opened by removing the sub-tree from the nested sets. */ // Compress the left values. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->where('lft > ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Compress the right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - ' . (int) $node->width) ->where('rgt > ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // We are moving the tree relative to a reference node. if ($referenceId) { // Get the reference node by primary key. if (!$reference = $this->_getNode($referenceId)) { // Error message set in getNode method. $this->_unlock(); return false; } // Get the reposition data for shifting the tree and re-inserting the node. if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, $position)) { // Error message set in getNode method. $this->_unlock(); return false; } } else { // We are moving the tree to be the last child of the root node // Get the last root node as the reference node. $query->clear() ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where('parent_id = 0') ->order('lft DESC'); $query->setLimit(1); $this->_db->setQuery($query); $reference = $this->_db->loadObject(); if ($this->_debug) { $this->_logtable(false); } // Get the reposition data for re-inserting the node after the found root. if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, 'last-child')) { // Error message set in getNode method. $this->_unlock(); return false; } } /* * Create space in the nested sets at the new location for the moved sub-tree. */ // Shift left values. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $node->width) ->where($repositionData->left_where); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Shift right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt + ' . (int) $node->width) ->where($repositionData->right_where); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); /* * Calculate the offset between where the node used to be in the tree and * where it needs to be in the tree for left ids (also works for right ids). */ $offset = $repositionData->new_lft - $node->lft; $levelOffset = $repositionData->new_level - $node->level; // Move the nodes back into position in the tree using the calculated offsets. $query->clear() ->update($this->_tbl) ->set('rgt = ' . (int) $offset . ' - rgt') ->set('lft = ' . (int) $offset . ' - lft') ->set('level = level + ' . (int) $levelOffset) ->where('lft < 0'); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Set the correct parent id for the moved node if required. if ($node->parent_id != $repositionData->new_parent_id) { $query = $this->_db->getQuery(true) ->update($this->_tbl); // Update the title and alias fields if they exist for the table. $fields = $this->getFields(); if ($this->hasField('title') && $this->title !== null) { $query->set('title = ' . $this->_db->quote($this->title)); } if (\array_key_exists('alias', $fields) && $this->alias !== null) { $query->set('alias = ' . $this->_db->quote($this->alias)); } $query->set('parent_id = ' . (int) $repositionData->new_parent_id) ->where($this->_tbl_key . ' = ' . (int) $node->$k); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); } // Unlock the table for writing. $this->_unlock(); if ($this->hasField('published') && $recursiveUpdate) { $this->recursiveUpdatePublishedColumn($node->$k); } // Set the object values. $this->parent_id = $repositionData->new_parent_id; $this->level = $repositionData->new_level; $this->lft = $repositionData->new_lft; $this->rgt = $repositionData->new_rgt; return true; } /** * Method to delete a node and, optionally, its child nodes from the table. * * @param integer $pk The primary key of the node to delete. * @param boolean $children True to delete child nodes, false to move them up a level. * * @return boolean True on success. * * @since 1.7.0 */ public function delete($pk = null, $children = true) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Pre-processing by observers $event = new Event( 'onBeforeDelete', [ 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onBeforeDelete', $event); // If tracking assets, remove the asset first. if ($this->_trackAssets) { $name = $this->_getAssetName(); /** @var Asset $asset */ $asset = Table::getInstance('Asset', 'JTable', ['dbo' => $this->getDbo()]); if ($asset->loadByName($name)) { // Delete the node in assets table. if (!$asset->delete(null, $children)) { $this->setError($asset->getError()); return false; } } else { $this->setError($asset->getError()); return false; } } // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // Get the node by id. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } $query = $this->_db->getQuery(true); // Should we delete all children along with the node? if ($children) { // Delete the node and all of its children. $query->clear() ->delete($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Compress the left values. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->where('lft > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Compress the right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - ' . (int) $node->width) ->where('rgt > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); } else { // Leave the children and move them up a level. // Delete the node. $query->clear() ->delete($this->_tbl) ->where('lft = ' . (int) $node->lft); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all node's children up a level. $query->clear() ->update($this->_tbl) ->set('lft = lft - 1') ->set('rgt = rgt - 1') ->set('level = level - 1') ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Adjust all the parent values for direct children of the deleted node. $query->clear() ->update($this->_tbl) ->set('parent_id = ' . (int) $node->parent_id) ->where('parent_id = ' . (int) $node->$k); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all of the left values that are right of the node. $query->clear() ->update($this->_tbl) ->set('lft = lft - 2') ->where('lft > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all of the right values that are right of the node. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - 2') ->where('rgt > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); } // Unlock the table for writing. $this->_unlock(); // Post-processing by observers $event = new Event( 'onAfterDelete', [ 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onAfterDelete', $event); return true; } /** * Checks that the object is valid and able to be stored. * * This method checks that the parent_id is non-zero and exists in the database. * Note that the root node (parent_id = 0) cannot be manipulated with this class. * * @return boolean True if all checks pass. * * @since 1.7.0 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } $this->parent_id = (int) $this->parent_id; // Set up a mini exception handler. try { // Check that the parent_id field is valid. if ($this->parent_id == 0) { throw new \UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, \get_class($this))); } $query = $this->_db->getQuery(true) ->select('1') ->from($this->_tbl) ->where($this->_tbl_key . ' = ' . $this->parent_id); if (!$this->_db->setQuery($query)->loadResult()) { throw new \UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, \get_class($this))); } } catch (\UnexpectedValueException $e) { // Validation error - record it and return false. $this->setError($e); return false; } return true; } /** * Method to store a node in the database table. * * @param boolean $updateNulls True to update null values as well. * * @return boolean True on success. * * @since 1.7.0 */ public function store($updateNulls = false) { $k = $this->_tbl_key; // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeStore', [ 'subject' => $this, 'updateNulls' => $updateNulls, 'k' => $k, ] ); $this->getDispatcher()->dispatch('onTableBeforeStore', $event); if ($this->_debug) { echo "\n" . \get_class($this) . "::store\n"; $this->_logtable(true, false); } /* * If the primary key is empty, then we assume we are inserting a new node into the * tree. From this point we would need to determine where in the tree to insert it. */ if (empty($this->$k)) { /* * We are inserting a node somewhere in the tree with a known reference * node. We have to make room for the new node and set the left and right * values before we insert the row. */ if ($this->_location_id >= 0) { // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // We are inserting a node relative to the last root node. if ($this->_location_id == 0) { // Get the last root node as the reference node. $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where('parent_id = 0') ->order('lft DESC'); $query->setLimit(1); $this->_db->setQuery($query); $reference = $this->_db->loadObject(); if ($this->_debug) { $this->_logtable(false); } } else { // We have a real node set as a location reference. // Get the reference node by primary key. if (!$reference = $this->_getNode($this->_location_id)) { // Error message set in getNode method. $this->_unlock(); return false; } } // Get the reposition data for shifting the tree and re-inserting the node. if (!($repositionData = $this->_getTreeRepositionData($reference, 2, $this->_location))) { // Error message set in getNode method. $this->_unlock(); return false; } // Create space in the tree at the new location for the new node in left ids. $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set('lft = lft + 2') ->where($repositionData->left_where); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); // Create space in the tree at the new location for the new node in right ids. $query->clear() ->update($this->_tbl) ->set('rgt = rgt + 2') ->where($repositionData->right_where); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); // Set the object values. $this->parent_id = $repositionData->new_parent_id; $this->level = $repositionData->new_level; $this->lft = $repositionData->new_lft; $this->rgt = $repositionData->new_rgt; } else { // Negative parent ids are invalid $e = new \UnexpectedValueException(sprintf('%s::store() used a negative _location_id', \get_class($this))); $this->setError($e); return false; } } else { /** * If we have a given primary key then we assume we are simply updating this * node in the tree. We should assess whether or not we are moving the node * or just updating its data fields. */ // If the location has been set, move the node to its new location. if ($this->_location_id > 0) { // Skip recursiveUpdatePublishedColumn method, it will be called later. if (!$this->moveByReference($this->_location_id, $this->_location, $this->$k, false)) { // Error message set in move method. return false; } } // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } } // We do not want parent::store to update observers since tables are locked and we are updating it from this // level of store(): $oldDispatcher = clone $this->getDispatcher(); $blankDispatcher = new Dispatcher(); $this->setDispatcher($blankDispatcher); $result = parent::store($updateNulls); // Restore previous callable dispatcher state: $this->setDispatcher($oldDispatcher); if ($result) { if ($this->_debug) { $this->_logtable(); } } // Unlock the table for writing. $this->_unlock(); if ($result && $this->hasField('published')) { $this->recursiveUpdatePublishedColumn($this->$k); } // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterStore', [ 'subject' => $this, 'result' => &$result, ] ); $this->getDispatcher()->dispatch('onTableAfterStore', $event); return $result; } /** * Method to set the publishing state for a node or list of nodes in the database * table. The method respects rows checked out by other users and will attempt * to checkin rows that it can after adjustments are made. The method will not * allow you to set a publishing state higher than any ancestor node and will * not allow you to set a publishing state on a node with a checked out child. * * @param mixed $pks An optional array of primary key values to update. If not * set the instance property value is used. * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published] * @param integer $userId The user id of the user performing the operation. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function publish($pks = null, $state = 1, $userId = 0) { $k = $this->_tbl_key; $query = $this->_db->getQuery(true); $table = $this->_db->quoteName($this->_tbl); $published = $this->_db->quoteName($this->getColumnAlias('published')); $checkedOut = $this->_db->quoteName($this->getColumnAlias('checked_out')); $key = $this->_db->quoteName($k); // Sanitize input. $pks = ArrayHelper::toInteger($pks); $userId = (int) $userId; $state = (int) $state; // If $state > 1, then we allow state changes even if an ancestor has lower state // (for example, can change a child state to Archived (2) if an ancestor is Published (1) $compareState = ($state > 1) ? 1 : $state; // If there are no primary keys set check to see if the instance key is set. if (empty($pks)) { if ($this->$k) { $pks = explode(',', $this->$k); } else { // Nothing to set publishing state on, return false. $e = new \UnexpectedValueException(sprintf('%s::publish(%s, %d, %d) empty.', \get_class($this), implode(',', $pks), $state, $userId)); $this->setError($e); return false; } } // Determine if there is checkout support for the table. $checkoutSupport = ($this->hasField('checked_out') || $this->hasField('checked_out_time')); // Iterate over the primary keys to execute the publish action if possible. foreach ($pks as $pk) { // Get the node by primary key. if (!$node = $this->_getNode($pk)) { // Error message set in getNode method. return false; } // If the table has checkout support, verify no children are checked out. if ($checkoutSupport) { // Ensure that children are not checked out. $query->clear() ->select('COUNT(' . $k . ')') ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt) ->where('(' . $checkedOut . ' <> 0 AND ' . $checkedOut . ' <> ' . (int) $userId . ')'); $this->_db->setQuery($query); // Check for checked out children. if ($this->_db->loadResult()) { // @todo Convert to a conflict exception when available. $e = new \RuntimeException(sprintf('%s::publish(%s, %d, %d) checked-out conflict.', \get_class($this), $pks[0], $state, $userId)); $this->setError($e); return false; } } // If any parent nodes have lower published state values, we cannot continue. if ($node->parent_id) { // Get any ancestor nodes that have a lower publishing state. $query->clear() ->select('1') ->from($table) ->where('lft < ' . (int) $node->lft) ->where('rgt > ' . (int) $node->rgt) ->where('parent_id > 0') ->where($published . ' < ' . (int) $compareState); // Just fetch one row (one is one too many). $query->setLimit(1); $this->_db->setQuery($query); if ($this->_db->loadResult()) { $e = new \UnexpectedValueException( sprintf('%s::publish(%s, %d, %d) ancestors have lower state.', \get_class($this), $pks[0], $state, $userId) ); $this->setError($e); return false; } } $this->recursiveUpdatePublishedColumn($pk, $state); // If checkout support exists for the object, check the row in. if ($checkoutSupport) { $this->checkIn($pk); } } // If the Table instance value is in the list of primary keys that were set, set the instance. if (\in_array($this->$k, $pks)) { $this->published = $state; } $this->setError(''); return true; } /** * Method to move a node one position to the left in the same level. * * @param integer $pk Primary key of the node to move. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function orderUp($pk) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // Get the node by primary key. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } // Get the left sibling node. $sibling = $this->_getNode($node->lft - 1, 'right'); if (empty($sibling)) { // Error message set in getNode method. $this->_unlock(); return false; } try { // Get the primary keys of child nodes. $query = $this->_db->getQuery(true) ->select($this->_tbl_key) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $children = $this->_db->setQuery($query)->loadColumn(); // Shift left and right values for the node and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $sibling->width) ->set('rgt = rgt - ' . (int) $sibling->width) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query)->execute(); // Shift left and right values for the sibling and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $node->width) ->set('rgt = rgt + ' . (int) $node->width) ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt) ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')'); $this->_db->setQuery($query)->execute(); } catch (\RuntimeException $e) { $this->_unlock(); throw $e; } // Unlock the table for writing. $this->_unlock(); return true; } /** * Method to move a node one position to the right in the same level. * * @param integer $pk Primary key of the node to move. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function orderDown($pk) { $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // Get the node by primary key. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } $query = $this->_db->getQuery(true); // Get the right sibling node. $sibling = $this->_getNode($node->rgt + 1, 'left'); if (empty($sibling)) { // Error message set in getNode method. $this->_unlock(); return false; } try { // Get the primary keys of child nodes. $query->clear() ->select($this->_tbl_key) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query); $children = $this->_db->loadColumn(); // Shift left and right values for the node and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $sibling->width) ->set('rgt = rgt + ' . (int) $sibling->width) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query)->execute(); // Shift left and right values for the sibling and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->set('rgt = rgt - ' . (int) $node->width) ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt) ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')'); $this->_db->setQuery($query)->execute(); } catch (\RuntimeException $e) { $this->_unlock(); throw $e; } // Unlock the table for writing. $this->_unlock(); return true; } /** * Gets the ID of the root item in the tree * * @return mixed The primary id of the root row, or false if not found and the internal error is set. * * @since 1.7.0 */ public function getRootId() { if ((int) self::$root_id > 0) { return self::$root_id; } // Get the root item. $k = $this->_tbl_key; // Test for a unique record with parent_id = 0 $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('parent_id = 0'); $result = $this->_db->setQuery($query)->loadColumn(); if (\count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } // Test for a unique record with lft = 0 $query->clear() ->select($k) ->from($this->_tbl) ->where('lft = 0'); $result = $this->_db->setQuery($query)->loadColumn(); if (\count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } $fields = $this->getFields(); if (\array_key_exists('alias', $fields)) { // Test for a unique record alias = root $query->clear() ->select($k) ->from($this->_tbl) ->where('alias = ' . $this->_db->quote('root')); $result = $this->_db->setQuery($query)->loadColumn(); if (\count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } } $e = new \UnexpectedValueException(sprintf('%s::getRootId', \get_class($this))); $this->setError($e); self::$root_id = false; return false; } /** * Method to recursively rebuild the whole nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $leftId The left id to start with in building the tree. * @param integer $level The level to assign to the current nodes. * @param string $path The path to the current nodes. * * @return integer 1 + value of root rgt on success, false on failure * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function rebuild($parentId = null, $leftId = 0, $level = 0, $path = '') { // If no parent is provided, try to find it. if ($parentId === null) { // Get the root item. $parentId = $this->getRootId(); if ($parentId === false) { return false; } } $query = $this->_db->getQuery(true); // Build the structure of the recursive query. if (!isset($this->_cache['rebuild.sql'])) { $query->clear() ->select($this->_tbl_key . ', alias') ->from($this->_tbl) ->where('parent_id = %d'); // If the table has an ordering field, use that for ordering. if ($this->hasField('ordering')) { $query->order('parent_id, ' . $this->_db->quoteName($this->getColumnAlias('ordering')) . ', lft'); } else { $query->order('parent_id, lft'); } $this->_cache['rebuild.sql'] = (string) $query; } // Make a shortcut to database object. // Assemble the query to find all children of this node. $this->_db->setQuery(sprintf($this->_cache['rebuild.sql'], (int) $parentId)); $children = $this->_db->loadObjectList(); // The right value of this node is the left value + 1 $rightId = $leftId + 1; // Execute this function recursively over all children foreach ($children as $node) { /* * $rightId is the current right value, which is incremented on recursion return. * Increment the level for the children. * Add this item's alias to the path (but avoid a leading /) */ $rightId = $this->rebuild($node->{$this->_tbl_key}, $rightId, $level + 1, $path . (empty($path) ? '' : '/') . $node->alias); // If there is an update failure, return false to break out of the recursion. if ($rightId === false) { return false; } } // We've got the left value, and now that we've processed // the children of this node we also know the right value. $query->clear() ->update($this->_tbl) ->set('lft = ' . (int) $leftId) ->set('rgt = ' . (int) $rightId) ->set('level = ' . (int) $level) ->set('path = ' . $this->_db->quote($path)) ->where($this->_tbl_key . ' = ' . (int) $parentId); $this->_db->setQuery($query)->execute(); // Return the right value of this node + 1. return $rightId + 1; } /** * Method to rebuild the node's path field from the alias values of the nodes from the current node to the root node of the tree. * * @param integer $pk Primary key of the node for which to get the path. * * @return boolean True on success. * * @since 1.7.0 */ public function rebuildPath($pk = null) { $fields = $this->getFields(); // If there is no alias or path field, just return true. if (!\array_key_exists('alias', $fields) || !\array_key_exists('path', $fields)) { return true; } $k = $this->_tbl_key; $pk = (\is_null($pk)) ? $this->$k : $pk; // Get the aliases for the path from the node to the root node. $query = $this->_db->getQuery(true) ->select('p.alias') ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('n.' . $this->_tbl_key . ' = ' . (int) $pk) ->order('p.lft'); $this->_db->setQuery($query); $segments = $this->_db->loadColumn(); // Make sure to remove the root path if it exists in the list. if ($segments[0] === 'root') { array_shift($segments); } // Build the path. $path = trim(implode('/', $segments), ' /\\'); // Update the path field for the node. $query->clear() ->update($this->_tbl) ->set('path = ' . $this->_db->quote($path)) ->where($this->_tbl_key . ' = ' . (int) $pk); $this->_db->setQuery($query)->execute(); // Update the current record's path to the new one: $this->path = $path; return true; } /** * Method to reset class properties to the defaults set in the class * definition. It will ignore the primary key as well as any private class * properties (except $_errors). * * @return void * * @since 3.2.1 */ public function reset() { parent::reset(); // Reset the location properties. $this->setLocation(0); } /** * Method to update order of table rows * * @param array $idArray id numbers of rows to be reordered. * @param array $lftArray lft values of rows to be reordered. * * @return integer|boolean 1 + value of root rgt on success, false on failure. * * @since 1.7.0 * @throws \Exception on database error. */ public function saveorder($idArray = null, $lftArray = null) { try { $query = $this->_db->getQuery(true); // Validate arguments if (\is_array($idArray) && \is_array($lftArray) && \count($idArray) == \count($lftArray)) { for ($i = 0, $count = \count($idArray); $i < $count; $i++) { // Do an update to change the lft values in the table for each id $query->clear() ->update($this->_tbl) ->where($this->_tbl_key . ' = ' . (int) $idArray[$i]) ->set('lft = ' . (int) $lftArray[$i]); $this->_db->setQuery($query)->execute(); if ($this->_debug) { $this->_logtable(); } } return $this->rebuild(); } else { return false; } } catch (\Exception $e) { $this->_unlock(); throw $e; } } /** * Method to recursive update published column for children rows. * * @param integer $pk Id number of row which published column was changed. * @param integer $newState An optional value for published column of row identified by $pk. * * @return boolean True on success. * * @since 3.7.0 * @throws \RuntimeException on database error. */ protected function recursiveUpdatePublishedColumn($pk, $newState = null) { $query = $this->_db->getQuery(true); $table = $this->_db->quoteName($this->_tbl); $key = $this->_db->quoteName($this->_tbl_key); $published = $this->_db->quoteName($this->getColumnAlias('published')); if ($newState !== null) { // Use a new published state in changed row. $newState = "(CASE WHEN p2.$key = " . (int) $pk . " THEN " . (int) $newState . " ELSE p2.$published END)"; } else { $newState = "p2.$published"; } /** * We have to calculate the correct value for c2.published * based on p2.published and own c2.published column, * where (p2) is parent category is and (c2) current category * * p2.published <= c2.published AND p2.published > 0 THEN c2.published * 2 <= 2 THEN 2 (If archived in archived then archived) * 1 <= 2 THEN 2 (If archived in published then archived) * 1 <= 1 THEN 1 (If published in published then published) * * p2.published > c2.published AND c2.published > 0 THEN p2.published * 2 > 1 THEN 2 (If published in archived then archived) * * p2.published > c2.published THEN c2.published ELSE p2.published * 2 > -2 THEN -2 (If trashed in archived then trashed) * 2 > 0 THEN 0 (If unpublished in archived then unpublished) * 1 > 0 THEN 0 (If unpublished in published then unpublished) * 0 > -2 THEN -2 (If trashed in unpublished then trashed) * ELSE * 0 <= 2 THEN 0 (If archived in unpublished then unpublished) * 0 <= 1 THEN 0 (If published in unpublished then unpublished) * 0 <= 0 THEN 0 (If unpublished in unpublished then unpublished) * -2 <= -2 THEN -2 (If trashed in trashed then trashed) * -2 <= 0 THEN -2 (If unpublished in trashed then trashed) * -2 <= 1 THEN -2 (If published in trashed then trashed) * -2 <= 2 THEN -2 (If archived in trashed then trashed) */ // Find node and all children keys $query->select("c.$key") ->from("$table AS node") ->leftJoin("$table AS c ON node.lft <= c.lft AND c.rgt <= node.rgt") ->where("node.$key = " . (int) $pk); $pks = $this->_db->setQuery($query)->loadColumn(); // Prepare a list of correct published states. $subquery = (string) $query->clear() ->select("c2.$key AS newId") ->select("CASE WHEN MIN($newState) > 0 THEN MAX($newState) ELSE MIN($newState) END AS newPublished") ->from("$table AS c2") ->innerJoin("$table AS p2 ON p2.lft <= c2.lft AND c2.rgt <= p2.rgt") ->where("c2.$key IN (" . implode(',', $pks) . ")") ->group("c2.$key"); // Update and cascade the publishing state. $query->clear() ->update($table) ->innerJoin("($subquery) AS c2") ->set("$published = " . $this->_db->quoteName("c2.newpublished")) ->where("$key = c2.newId") ->where("$key IN (" . implode(',', $pks) . ")"); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); return true; } /** * Method to get nested set properties for a node in the tree. * * @param integer $id Value to look up the node by. * @param string $key An optional key to look up the node by (parent | left | right). * If omitted, the primary key of the table is used. * * @return mixed Boolean false on failure or node object on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ protected function _getNode($id, $key = null) { // Determine which key to get the node base on. switch ($key) { case 'parent': $k = 'parent_id'; break; case 'left': $k = 'lft'; break; case 'right': $k = 'rgt'; break; default: $k = $this->_tbl_key; break; } // Get the node data. $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where($k . ' = ' . (int) $id); $query->setLimit(1); $row = $this->_db->setQuery($query)->loadObject(); // Check for no $row returned if (empty($row)) { $e = new \UnexpectedValueException(sprintf('%s::_getNode(%d, %s) failed.', \get_class($this), $id, $k)); $this->setError($e); return false; } // Do some simple calculations. $row->numChildren = (int) ($row->rgt - $row->lft - 1) / 2; $row->width = (int) $row->rgt - $row->lft + 1; return $row; } /** * Method to get various data necessary to make room in the tree at a location * for a node and its children. The returned data object includes conditions * for SQL WHERE clauses for updating left and right id values to make room for * the node as well as the new left and right ids for the node. * * @param object $referenceNode A node object with at least a 'lft' and 'rgt' with * which to make room in the tree around for a new node. * @param integer $nodeWidth The width of the node for which to make room in the tree. * @param string $position The position relative to the reference node where the room * should be made. * * @return mixed Boolean false on failure or data object on success. * * @since 1.7.0 */ protected function _getTreeRepositionData($referenceNode, $nodeWidth, $position = 'before') { // Make sure the reference an object with a left and right id. if (!\is_object($referenceNode) || !(isset($referenceNode->lft) && isset($referenceNode->rgt))) { return false; } // A valid node cannot have a width less than 2. if ($nodeWidth < 2) { return false; } $k = $this->_tbl_key; $data = new \stdClass(); // Run the calculations and build the data object by reference position. switch ($position) { case 'first-child': $data->left_where = 'lft > ' . $referenceNode->lft; $data->right_where = 'rgt >= ' . $referenceNode->lft; $data->new_lft = $referenceNode->lft + 1; $data->new_rgt = $referenceNode->lft + $nodeWidth; $data->new_parent_id = $referenceNode->$k; $data->new_level = $referenceNode->level + 1; break; case 'last-child': $data->left_where = 'lft > ' . ($referenceNode->rgt); $data->right_where = 'rgt >= ' . ($referenceNode->rgt); $data->new_lft = $referenceNode->rgt; $data->new_rgt = $referenceNode->rgt + $nodeWidth - 1; $data->new_parent_id = $referenceNode->$k; $data->new_level = $referenceNode->level + 1; break; case 'before': $data->left_where = 'lft >= ' . $referenceNode->lft; $data->right_where = 'rgt >= ' . $referenceNode->lft; $data->new_lft = $referenceNode->lft; $data->new_rgt = $referenceNode->lft + $nodeWidth - 1; $data->new_parent_id = $referenceNode->parent_id; $data->new_level = $referenceNode->level; break; default: case 'after': $data->left_where = 'lft > ' . $referenceNode->rgt; $data->right_where = 'rgt > ' . $referenceNode->rgt; $data->new_lft = $referenceNode->rgt + 1; $data->new_rgt = $referenceNode->rgt + $nodeWidth; $data->new_parent_id = $referenceNode->parent_id; $data->new_level = $referenceNode->level; break; } if ($this->_debug) { echo "\nRepositioning Data for $position\n-----------------------------------\nLeft Where: $data->left_where" . "\nRight Where: $data->right_where\nNew Lft: $data->new_lft\nNew Rgt: $data->new_rgt" . "\nNew Parent ID: $data->new_parent_id\nNew Level: $data->new_level\n"; } return $data; } /** * Method to create a log table in the buffer optionally showing the query and/or data. * * @param boolean $showData True to show data * @param boolean $showQuery True to show query * * @return void * * @codeCoverageIgnore * @since 1.7.0 */ protected function _logtable($showData = true, $showQuery = true) { $sep = "\n" . str_pad('', 40, '-'); $buffer = ''; if ($showQuery) { $buffer .= "\n" . htmlspecialchars($this->_db->getQuery(), ENT_QUOTES, 'UTF-8') . $sep; } if ($showData) { $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, lft, rgt, level') ->from($this->_tbl) ->order($this->_tbl_key); $this->_db->setQuery($query); $rows = $this->_db->loadRowList(); $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $this->_tbl_key, 'par', 'lft', 'rgt'); $buffer .= $sep; foreach ($rows as $row) { $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $row[0], $row[1], $row[2], $row[3]); } $buffer .= $sep; } echo $buffer; } /** * Runs a query and unlocks the database on an error. * * @param mixed $query A string or DatabaseQuery object. * @param string $errorMessage Unused. * * @return void * * @note Since 3.0.0 this method returns void and will rethrow the database exception. * @since 1.7.0 * @throws \Exception on database error. */ protected function _runQuery($query, $errorMessage) { // Prepare to catch an exception. try { $this->_db->setQuery($query)->execute(); if ($this->_debug) { $this->_logtable(); } } catch (\Exception $e) { // Unlock the tables and rethrow. $this->_unlock(); throw $e; } } } PK1A#]��?0��src/Table/ContentHistory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Factory; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Content History table. * * @since 3.2 */ class ContentHistory extends Table { /** * Array of object fields to unset from the data object before calculating SHA1 hash. This allows us to detect a meaningful change * in the database row using the hash. This can be read from the #__content_types content_history_options column. * * @var array * @since 3.2 */ public $ignoreChanges = []; /** * Array of object fields to convert to integers before calculating SHA1 hash. Some values are stored differently * when an item is created than when the item is changed and saved. This works around that issue. * This can be read from the #__content_types content_history_options column. * * @var array * @since 3.2 */ public $convertToInt = []; /** * Constructor * * @param DatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__history', 'version_id', $db); $this->ignoreChanges = [ 'modified_by', 'modified_user_id', 'modified', 'modified_time', 'checked_out', 'checked_out_time', 'version', 'hits', 'path', ]; $this->convertToInt = ['publish_up', 'publish_down', 'ordering', 'featured']; } /** * Overrides Table::store to set modified hash, user id, and save date. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.2 */ public function store($updateNulls = false) { $this->set('character_count', \strlen($this->get('version_data'))); $typeTable = Table::getInstance('ContentType', 'JTable', ['dbo' => $this->getDbo()]); $typeAlias = explode('.', $this->item_id); array_pop($typeAlias); $typeTable->load(['type_alias' => implode('.', $typeAlias)]); if (!isset($this->sha1_hash)) { $this->set('sha1_hash', $this->getSha1($this->get('version_data'), $typeTable)); } // Modify author and date only when not toggling Keep Forever if ($this->get('keep_forever') === null) { $this->set('editor_user_id', Factory::getUser()->id); $this->set('save_date', Factory::getDate()->toSql()); } return parent::store($updateNulls); } /** * Utility method to get the hash after removing selected values. This lets us detect changes other than * modified date (which will change on every save). * * @param mixed $jsonData Either an object or a string with json-encoded data * @param ContentType $typeTable Table object with data for this content type * * @return string SHA1 hash on success. Empty string on failure. * * @since 3.2 */ public function getSha1($jsonData, ContentType $typeTable) { $object = \is_object($jsonData) ? $jsonData : json_decode($jsonData); if (isset($typeTable->content_history_options) && \is_object(json_decode($typeTable->content_history_options))) { $options = json_decode($typeTable->content_history_options); $this->ignoreChanges = $options->ignoreChanges ?? $this->ignoreChanges; $this->convertToInt = $options->convertToInt ?? $this->convertToInt; } foreach ($this->ignoreChanges as $remove) { if (property_exists($object, $remove)) { unset($object->$remove); } } // Convert integers, booleans, and nulls to strings to get a consistent hash value foreach ($object as $name => $value) { if (\is_object($value)) { // Go one level down for JSON column values foreach ($value as $subName => $subValue) { $object->$subName = \is_int($subValue) || \is_bool($subValue) || $subValue === null ? (string) $subValue : $subValue; } } else { $object->$name = \is_int($value) || \is_bool($value) || $value === null ? (string) $value : $value; } } // Work around empty values foreach ($this->convertToInt as $convert) { if (isset($object->$convert)) { $object->$convert = (int) $object->$convert; } } if (isset($object->review_time)) { $object->review_time = (int) $object->review_time; } return sha1(json_encode($object)); } /** * Utility method to get a matching row based on the hash value and id columns. * This lets us check to make sure we don't save duplicate versions. * * @return string SHA1 hash on success. Empty string on failure. * * @since 3.2 */ public function getHashMatch() { $db = $this->_db; $itemId = $this->get('item_id'); $sha1Hash = $this->get('sha1_hash'); $query = $db->getQuery(true); $query->select('*') ->from($db->quoteName('#__history')) ->where($db->quoteName('item_id') . ' = :item_id') ->where($db->quoteName('sha1_hash') . ' = :sha1_hash') ->bind(':item_id', $itemId, ParameterType::STRING) ->bind(':sha1_hash', $sha1Hash); $query->setLimit(1); $db->setQuery($query); return $db->loadObject(); } /** * Utility method to remove the oldest versions of an item, saving only the most recent versions. * * @param integer $maxVersions The maximum number of versions to save. All others will be deleted. * * @return boolean true on success, false on failure. * * @since 3.2 */ public function deleteOldVersions($maxVersions) { $result = true; // Get the list of version_id values we want to save $db = $this->_db; $itemId = $this->get('item_id'); $query = $db->getQuery(true); $query->select($db->quoteName('version_id')) ->from($db->quoteName('#__history')) ->where($db->quoteName('item_id') . ' = :item_id') ->where($db->quoteName('keep_forever') . ' != 1') ->bind(':item_id', $itemId, ParameterType::STRING) ->order($db->quoteName('save_date') . ' DESC '); $query->setLimit((int) $maxVersions); $db->setQuery($query); $idsToSave = $db->loadColumn(0); // Don't process delete query unless we have at least the maximum allowed versions if (\count($idsToSave) === (int) $maxVersions) { // Delete any rows not in our list and and not flagged to keep forever. $query = $db->getQuery(true); $query->delete($db->quoteName('#__history')) ->where($db->quoteName('item_id') . ' = :item_id') ->whereNotIn($db->quoteName('version_id'), $idsToSave) ->where($db->quoteName('keep_forever') . ' != 1') ->bind(':item_id', $itemId, ParameterType::STRING); $db->setQuery($query); $result = (bool) $db->execute(); } return $result; } } PK1A#],3U�aasrc/Table/Module.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Access\Rules; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module table * * @since 1.5 */ class Module extends Table { /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Constructor. * * @param DatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__modules', 'id', $db); $this->access = (int) Factory::getApplication()->get('access'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.2 */ protected function _getAssetName() { $k = $this->_tbl_key; return 'com_modules.module.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.2 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset id for the record * * @param Table $table A Table object (optional) for the asset parent * @param integer $id The id (optional) of the content. * * @return integer * * @since 3.2 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is a module that needs to parent with the extension. if ($assetId === null) { // Build the query to get the asset id of the parent component. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__assets')) ->where($this->_db->quoteName('name') . ' = ' . $this->_db->quote('com_modules')); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Overloaded check function. * * @return boolean True if the instance is sane and able to be stored in the database. * * @see Table::check() * @since 1.5 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for valid name if (trim($this->title) === '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_MODULE')); return false; } // Set publish_up, publish_down to null if not set if (!$this->publish_up) { $this->publish_up = null; } if (!$this->publish_down) { $this->publish_down = null; } // Prevent to save too large content > 65535 if ((\strlen($this->content) > 65535) || (\strlen($this->params) > 65535)) { $this->setError(Text::_('COM_MODULES_FIELD_CONTENT_TOO_LARGE')); return false; } // Check the publish down date is not earlier than publish up. if ((int) $this->publish_down > 0 && $this->publish_down < $this->publish_up) { // Swap the dates. $temp = $this->publish_up; $this->publish_up = $this->publish_down; $this->publish_down = $temp; } return true; } /** * Overloaded bind function. * * @param array $array Named array. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.5 */ public function bind($array, $ignore = '') { if (isset($array['params']) && \is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && \is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Stores a module. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success, false on failure. * * @since 3.7.0 */ public function store($updateNulls = true) { if (!$this->ordering) { $this->ordering = $this->getNextOrder($this->_db->quoteName('position') . ' = ' . $this->_db->quote($this->position)); } return parent::store($updateNulls); } } PK1A#]��̣T�T�src/Table/Table.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Access\Rules; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Object\CMSObject; use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseQuery; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract Table class * * Parent class to all tables. * * @since 1.7.0 */ #[\AllowDynamicProperties] abstract class Table extends CMSObject implements TableInterface, DispatcherAwareInterface { use DispatcherAwareTrait; /** * Include paths for searching for Table classes. * * @var array * @since 3.0.0 */ private static $_includePaths = []; /** * Table fields cache * * @var array * @since 3.10.4 */ private static $tableFields; /** * Name of the database table to model. * * @var string * @since 1.7.0 */ protected $_tbl = ''; /** * Name of the primary key field in the table. * * @var string * @since 1.7.0 */ protected $_tbl_key = ''; /** * Name of the primary key fields in the table. * * @var array * @since 3.0.1 */ protected $_tbl_keys = []; /** * DatabaseDriver object. * * @var DatabaseDriver * @since 1.7.0 */ protected $_db; /** * Should rows be tracked as ACL assets? * * @var boolean * @since 1.7.0 */ protected $_trackAssets = false; /** * The rules associated with this record. * * @var Rules A Rules object. * @since 1.7.0 */ protected $_rules; /** * Indicator that the tables have been locked. * * @var boolean * @since 1.7.0 */ protected $_locked = false; /** * Indicates that the primary keys autoincrement. * * @var boolean * @since 3.1.4 */ protected $_autoincrement = true; /** * Array with alias for "special" columns such as ordering, hits etc etc * * @var array * @since 3.4.0 */ protected $_columnAlias = []; /** * An array of key names to be json encoded in the bind function * * @var array * @since 3.3 */ protected $_jsonEncode = []; /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 3.10.0 */ protected $_supportNullValue = false; /** * The UCM type alias. Used for tags, content versioning etc. Leave blank to effectively disable these features. * * @var string * @since 4.0.0 */ public $typeAlias = null; /** * Object constructor to set table and key fields. In most cases this will * be overridden by child classes to explicitly set the table and key fields * for a particular database table. * * @param string $table Name of the table to model. * @param mixed $key Name of the primary key field in the table or array of field names that compose the primary key. * @param DatabaseDriver $db DatabaseDriver object. * @param DispatcherInterface $dispatcher Event dispatcher for this table * * @since 1.7.0 */ public function __construct($table, $key, DatabaseDriver $db, DispatcherInterface $dispatcher = null) { parent::__construct(); // Set internal variables. $this->_tbl = $table; // Set the key to be an array. if (\is_string($key)) { $key = [$key]; } elseif (\is_object($key)) { $key = (array) $key; } $this->_tbl_keys = $key; if (\count($key) == 1) { $this->_autoincrement = true; } else { $this->_autoincrement = false; } // Set the singular table key for backwards compatibility. $this->_tbl_key = $this->getKeyName(); $this->_db = $db; // Initialise the table properties. $fields = $this->getFields(); if ($fields) { foreach ($fields as $name => $v) { // Add the field if it is not already present. if (!$this->hasField($name)) { $this->$name = null; } } } // If we are tracking assets, make sure an access field exists and initially set the default. if ($this->hasField('asset_id')) { $this->_trackAssets = true; } // If the access property exists, set the default. if ($this->hasField('access')) { $this->access = (int) Factory::getApplication()->get('access'); } // Create or set a Dispatcher if (!\is_object($dispatcher) || !($dispatcher instanceof DispatcherInterface)) { // @todo Maybe we should use a dedicated "behaviour" dispatcher for performance reasons and to prevent system plugins from butting in? $dispatcher = Factory::getApplication()->getDispatcher(); } $this->setDispatcher($dispatcher); $event = AbstractEvent::create( 'onTableObjectCreate', [ 'subject' => $this, ] ); $this->getDispatcher()->dispatch('onTableObjectCreate', $event); } /** * Get the columns from database table. * * @param bool $reload flag to reload cache * * @return mixed An array of the field names, or false if an error occurs. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function getFields($reload = false) { $key = $this->_db->getServerType() . ':' . $this->_db->getName() . ':' . $this->_tbl; if (!isset(self::$tableFields[$key]) || $reload) { // Lookup the fields for this table only once. $name = $this->_tbl; $fields = $this->_db->getTableColumns($name, false); if (empty($fields)) { throw new \UnexpectedValueException(sprintf('No columns found for %s table', $name)); } self::$tableFields[$key] = $fields; } return self::$tableFields[$key]; } /** * Static method to get an instance of a Table class if it can be found in the table include paths. * * To add include paths for searching for Table classes see Table::addIncludePath(). * * @param string $type The type (name) of the Table class to get an instance of. * @param string $prefix An optional prefix for the table class name. * @param array $config An optional array of configuration values for the Table object. * * @return Table|boolean A Table object if found or boolean false on failure. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the MvcFactory instead * Example: Factory::getApplication()->bootComponent('...')->getMVCFactory()->createTable($name, $prefix, $config); */ public static function getInstance($type, $prefix = 'JTable', $config = []) { // Sanitize and prepare the table class name. $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); $tableClass = $prefix . ucfirst($type); // Only try to load the class if it doesn't already exist. if (!class_exists($tableClass)) { // Search for the class file in the JTable include paths. $paths = self::addIncludePath(); $pathIndex = 0; while (!class_exists($tableClass) && $pathIndex < \count($paths)) { if ($tryThis = Path::find($paths[$pathIndex++], strtolower($type) . '.php')) { // Import the class file. include_once $tryThis; } } if (!class_exists($tableClass)) { /* * If unable to find the class file in the Table include paths. Return false. * The warning JLIB_DATABASE_ERROR_NOT_SUPPORTED_FILE_NOT_FOUND has been removed in 3.6.3. * In 4.0 an Exception (type to be determined) will be thrown. * For more info see https://github.com/joomla/joomla-cms/issues/11570 */ return false; } } // If a database object was passed in the configuration array use it, otherwise get the global one from Factory. $db = $config['dbo'] ?? Factory::getDbo(); // Check for a possible service from the container otherwise manually instantiate the class if (Factory::getContainer()->has($tableClass)) { return Factory::getContainer()->get($tableClass); } // Instantiate a new table class and return it. return new $tableClass($db); } /** * Add a filesystem path where Table should search for table class files. * * @param array|string $path A filesystem path or array of filesystem paths to add. * * @return array An array of filesystem paths to find Table classes in. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Should not be used anymore as tables are loaded through the MvcFactory */ public static function addIncludePath($path = null) { // If the internal paths have not been initialised, do so with the base table path. if (empty(self::$_includePaths)) { self::$_includePaths = [__DIR__]; } // Convert the passed path(s) to add to an array. settype($path, 'array'); // If we have new paths to add, do so. if (!empty($path)) { // Check and add each individual new path. foreach ($path as $dir) { // Sanitize path. $dir = trim($dir); // Add to the front of the list so that custom paths are searched first. if (!\in_array($dir, self::$_includePaths)) { array_unshift(self::$_includePaths, $dir); } } } return self::$_includePaths; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.7.0 */ protected function _getAssetName() { $keys = []; foreach ($this->_tbl_keys as $k) { $keys[] = (int) $this->$k; } return $this->_tbl . '.' . implode('.', $keys); } /** * Method to return the title to use for the asset table. * * In tracking the assets a title is kept for each asset so that there is some context available in a unified access manager. * Usually this would just return $this->title or $this->name or whatever is being used for the primary name of the row. * If this method is not overridden, the asset name is used. * * @return string The string to use as the title in the asset table. * * @since 1.7.0 */ protected function _getAssetTitle() { return $this->_getAssetName(); } /** * Method to get the parent asset under which to register this one. * * By default, all assets are registered to the ROOT node with ID, which will default to 1 if none exists. * An extended class can define a table and ID to lookup. If the asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 1.7.0 */ protected function _getAssetParentId(Table $table = null, $id = null) { // For simple cases, parent to the asset root. /** @var Asset $assets */ $assets = self::getInstance('Asset', 'JTable', ['dbo' => $this->getDbo()]); $rootId = $assets->getRootId(); if (!empty($rootId)) { return $rootId; } return 1; } /** * Method to append the primary keys for this table to a query. * * @param DatabaseQuery $query A query object to append. * @param mixed $pk Optional primary key parameter. * * @return void * * @since 3.1.4 */ public function appendPrimaryKeys($query, $pk = null) { if (\is_null($pk)) { foreach ($this->_tbl_keys as $k) { $query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($this->$k)); } } else { if (\is_string($pk)) { $pk = [$this->_tbl_key => $pk]; } $pk = (object) $pk; foreach ($this->_tbl_keys as $k) { $query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($pk->$k)); } } } /** * Method to get the database table name for the class. * * @return string The name of the database table being modeled. * * @since 1.7.0 */ public function getTableName() { return $this->_tbl; } /** * Method to get the primary key field name for the table. * * @param boolean $multiple True to return all primary keys (as an array) or false to return just the first one (as a string). * * @return mixed Array of primary key field names or string containing the first primary key field. * * @since 1.7.0 */ public function getKeyName($multiple = false) { // Count the number of keys if (\count($this->_tbl_keys)) { if ($multiple) { // If we want multiple keys, return the raw array. return $this->_tbl_keys; } else { // If we want the standard method, just return the first key. return $this->_tbl_keys[0]; } } return ''; } /** * Returns the identity (primary key) value of this record * * @return mixed * * @since 4.0.0 */ public function getId() { $key = $this->getKeyName(); return $this->$key; } /** * Method to get the DatabaseDriver object. * * @return DatabaseDriver The internal database driver object. * * @since 1.7.0 */ public function getDbo() { return $this->_db; } /** * Method to set the DatabaseDriver object. * * @param DatabaseDriver $db A DatabaseDriver object to be used by the table object. * * @return boolean True on success. * * @since 1.7.0 */ public function setDbo(DatabaseDriver $db) { $this->_db = $db; return true; } /** * Method to set rules for the record. * * @param mixed $input A Rules object, JSON string, or array. * * @return void * * @since 1.7.0 */ public function setRules($input) { if ($input instanceof Rules) { $this->_rules = $input; } else { $this->_rules = new Rules($input); } } /** * Method to get the rules for the record. * * @return Rules object * * @since 1.7.0 */ public function getRules() { return $this->_rules; } /** * Method to reset class properties to the defaults set in the class * definition. It will ignore the primary key as well as any private class * properties (except $_errors). * * @return void * * @since 1.7.0 */ public function reset() { $event = AbstractEvent::create( 'onTableBeforeReset', [ 'subject' => $this, ] ); $this->getDispatcher()->dispatch('onTableBeforeReset', $event); // Get the default values for the class from the table. foreach ($this->getFields() as $k => $v) { // If the property is not the primary key or private, reset it. if (!\in_array($k, $this->_tbl_keys) && (strpos($k, '_') !== 0)) { $this->$k = $v->Default; } } // Reset table errors $this->_errors = []; $event = AbstractEvent::create( 'onTableAfterReset', [ 'subject' => $this, ] ); $this->getDispatcher()->dispatch('onTableAfterReset', $event); } /** * Method to bind an associative array or object to the Table instance.This * method only binds properties that are publicly accessible and optionally * takes an array of properties to ignore when binding. * * @param array|object $src An associative array or object to bind to the Table instance. * @param array|string $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 1.7.0 * @throws \InvalidArgumentException */ public function bind($src, $ignore = []) { // Check if the source value is an array or object if (!\is_object($src) && !\is_array($src)) { throw new \InvalidArgumentException( sprintf( 'Could not bind the data source in %1$s::bind(), the source must be an array or object but a "%2$s" was given.', \get_class($this), \gettype($src) ) ); } // If the ignore value is a string, explode it over spaces. if (!\is_array($ignore)) { $ignore = explode(' ', $ignore); } $event = AbstractEvent::create( 'onTableBeforeBind', [ 'subject' => $this, 'src' => $src, 'ignore' => $ignore, ] ); $this->getDispatcher()->dispatch('onTableBeforeBind', $event); // If the source value is an object, get its accessible properties. if (\is_object($src)) { $src = get_object_vars($src); } // JSON encode any fields required if (!empty($this->_jsonEncode)) { foreach ($this->_jsonEncode as $field) { if (isset($src[$field]) && \is_array($src[$field])) { $src[$field] = json_encode($src[$field]); } } } // Bind the source value, excluding the ignored fields. foreach ($this->getProperties() as $k => $v) { // Only process fields not in the ignore array. if (!\in_array($k, $ignore)) { if (isset($src[$k])) { $this->$k = $src[$k]; } } } $event = AbstractEvent::create( 'onTableAfterBind', [ 'subject' => $this, 'src' => $src, 'ignore' => $ignore, ] ); $this->getDispatcher()->dispatch('onTableAfterBind', $event); return true; } /** * Method to load a row from the database by primary key and bind the fields to the Table instance properties. * * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. * If not set the instance property value is used. * @param boolean $reset True to reset the default values before loading the new row. * * @return boolean True if successful. False if row not found. * * @since 1.7.0 * @throws \InvalidArgumentException * @throws \RuntimeException * @throws \UnexpectedValueException */ public function load($keys = null, $reset = true) { // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeLoad', [ 'subject' => $this, 'keys' => $keys, 'reset' => $reset, ] ); $this->getDispatcher()->dispatch('onTableBeforeLoad', $event); if (empty($keys)) { $empty = true; $keys = []; // If empty, use the value of the current key foreach ($this->_tbl_keys as $key) { $empty = $empty && empty($this->$key); $keys[$key] = $this->$key; } // If empty primary key there's is no need to load anything if ($empty) { return true; } } elseif (!\is_array($keys)) { // Load by primary key. $keyCount = \count($this->_tbl_keys); if ($keyCount) { if ($keyCount > 1) { throw new \InvalidArgumentException('Table has multiple primary keys specified, only one primary key value provided.'); } $keys = [$this->getKeyName() => $keys]; } else { throw new \RuntimeException('No table keys defined.'); } } if ($reset) { $this->reset(); } // Initialise the query. $query = $this->_db->getQuery(true) ->select('*') ->from($this->_db->quoteName($this->_tbl)); $fields = array_keys($this->getProperties()); foreach ($keys as $field => $value) { // Check that $field is in the table. if (!\in_array($field, $fields)) { throw new \UnexpectedValueException(sprintf('Missing field in database: %s   %s.', \get_class($this), $field)); } // Add the search tuple to the query. $query->where($this->_db->quoteName($field) . ' = ' . $this->_db->quote($value)); } $this->_db->setQuery($query); $row = $this->_db->loadAssoc(); // Check that we have a result. if (empty($row)) { $result = false; } else { // Bind the object with the row and return. $result = $this->bind($row); } // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterLoad', [ 'subject' => $this, 'result' => &$result, 'row' => $row, ] ); $this->getDispatcher()->dispatch('onTableAfterLoad', $event); return $result; } /** * Method to perform sanity checks on the Table instance properties to ensure they are safe to store in the database. * * Child classes should override this method to make sure the data they are storing in the database is safe and as expected before storage. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 1.7.0 */ public function check() { // Post-processing by observers $event = AbstractEvent::create( 'onTableCheck', [ 'subject' => $this, ] ); $this->getDispatcher()->dispatch('onTableCheck', $event); return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.7.0 */ public function store($updateNulls = false) { $result = true; $k = $this->_tbl_keys; // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeStore', [ 'subject' => $this, 'updateNulls' => $updateNulls, 'k' => $k, ] ); $this->getDispatcher()->dispatch('onTableBeforeStore', $event); $currentAssetId = 0; if (!empty($this->asset_id)) { $currentAssetId = $this->asset_id; } // The asset id field is managed privately by this class. if ($this->_trackAssets) { unset($this->asset_id); } // We have to unset typeAlias since updateObject / insertObject will try to insert / update all public variables... $typeAlias = $this->typeAlias; unset($this->typeAlias); try { // If a primary key exists update the object, otherwise insert it. if ($this->hasPrimaryKey()) { $this->_db->updateObject($this->_tbl, $this, $this->_tbl_keys, $updateNulls); } else { $this->_db->insertObject($this->_tbl, $this, $this->_tbl_keys[0]); } } catch (\Exception $e) { $this->setError($e->getMessage()); $result = false; } $this->typeAlias = $typeAlias; // If the table is not set to track assets return true. if ($this->_trackAssets) { if ($this->_locked) { $this->_unlock(); } /* * Asset Tracking */ $parentId = $this->_getAssetParentId(); $name = $this->_getAssetName(); $title = $this->_getAssetTitle(); /** @var Asset $asset */ $asset = self::getInstance('Asset', 'JTable', ['dbo' => $this->getDbo()]); $asset->loadByName($name); // Re-inject the asset id. $this->asset_id = $asset->id; // Check for an error. $error = $asset->getError(); if ($error) { $this->setError($error); return false; } else { // Specify how a new or moved node asset is inserted into the tree. if (empty($this->asset_id) || $asset->parent_id != $parentId) { $asset->setLocation($parentId, 'last-child'); } // Prepare the asset to be stored. $asset->parent_id = $parentId; $asset->name = $name; // Respect the table field limits $asset->title = StringHelper::substr($title, 0, 100); if ($this->_rules instanceof Rules) { $asset->rules = (string) $this->_rules; } if (!$asset->check() || !$asset->store()) { $this->setError($asset->getError()); return false; } else { // Create an asset_id or heal one that is corrupted. if (empty($this->asset_id) || ($currentAssetId != $this->asset_id && !empty($this->asset_id))) { // Update the asset_id field in this table. $this->asset_id = (int) $asset->id; $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set('asset_id = ' . (int) $this->asset_id); $this->appendPrimaryKeys($query); $this->_db->setQuery($query)->execute(); } } } } // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterStore', [ 'subject' => $this, 'result' => &$result, ] ); $this->getDispatcher()->dispatch('onTableAfterStore', $event); return $result; } /** * Method to provide a shortcut to binding, checking and storing a Table instance to the database table. * * The method will check a row in once the data has been stored and if an ordering filter is present will attempt to reorder * the table rows based on the filter. The ordering filter is an instance property name. The rows that will be reordered * are those whose value matches the Table instance for the property specified. * * @param array|object $src An associative array or object to bind to the Table instance. * @param string $orderingFilter Filter for the order updating * @param array|string $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 1.7.0 */ public function save($src, $orderingFilter = '', $ignore = '') { // Attempt to bind the source to the instance. if (!$this->bind($src, $ignore)) { return false; } // Run any sanity checks on the instance and verify that it is ready for storage. if (!$this->check()) { return false; } // Attempt to store the properties to the database table. if (!$this->store()) { return false; } // Attempt to check the row in, just in case it was checked out. if (!$this->checkIn()) { return false; } // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value. if ($orderingFilter) { $filterValue = $this->$orderingFilter; $this->reorder($orderingFilter ? $this->_db->quoteName($orderingFilter) . ' = ' . $this->_db->quote($filterValue) : ''); } // Set the error to empty and return true. $this->setError(''); return true; } /** * Method to delete a row from the database table by primary key value. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function delete($pk = null) { if (\is_null($pk)) { $pk = []; foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!\is_array($pk)) { $pk = [$this->_tbl_key => $pk]; } foreach ($this->_tbl_keys as $key) { $pk[$key] = \is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } $this->$key = $pk[$key]; } // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeDelete', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableBeforeDelete', $event); // If tracking assets, remove the asset first. if ($this->_trackAssets) { // Get the asset name $name = $this->_getAssetName(); /** @var Asset $asset */ $asset = self::getInstance('Asset'); if ($asset->loadByName($name)) { if (!$asset->delete()) { $this->setError($asset->getError()); return false; } } } // Delete the row by primary key. $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); // Check for a database error. $this->_db->execute(); // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterDelete', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableAfterDelete', $event); return true; } /** * Method to check a row out if the necessary properties/fields exist. * * To prevent race conditions while editing rows in a database, a row can be checked out if the fields 'checked_out' and 'checked_out_time' * are available. While a row is checked out, any attempt to store the row by a user other than the one who checked the row out should be * held until the row is checked in again. * * @param integer $userId The Id of the user checking out the row. * @param mixed $pk An optional primary key value to check out. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function checkOut($userId, $pk = null) { // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeCheckout', [ 'subject' => $this, 'userId' => $userId, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableBeforeCheckout', $event); // If there is no checked_out or checked_out_time field, just return true. if (!$this->hasField('checked_out') || !$this->hasField('checked_out_time')) { return true; } if (\is_null($pk)) { $pk = []; foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!\is_array($pk)) { $pk = [$this->_tbl_key => $pk]; } foreach ($this->_tbl_keys as $key) { $pk[$key] = \is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Get column names. $checkedOutField = $this->getColumnAlias('checked_out'); $checkedOutTimeField = $this->getColumnAlias('checked_out_time'); // Get the current time in the database format. $time = Factory::getDate()->toSql(); // Check the row out by primary key. $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName($checkedOutField) . ' = ' . (int) $userId) ->set($this->_db->quoteName($checkedOutTimeField) . ' = ' . $this->_db->quote($time)); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); $this->_db->execute(); // Set table values in the object. $this->$checkedOutField = (int) $userId; $this->$checkedOutTimeField = $time; // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterCheckout', [ 'subject' => $this, 'userId' => $userId, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableAfterCheckout', $event); return true; } /** * Method to check a row in if the necessary properties/fields exist. * * Checking a row in will allow other users the ability to edit the row. * * @param mixed $pk An optional primary key value to check out. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function checkIn($pk = null) { // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeCheckin', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableBeforeCheckin', $event); // If there is no checked_out or checked_out_time field, just return true. if (!$this->hasField('checked_out') || !$this->hasField('checked_out_time')) { return true; } if (\is_null($pk)) { $pk = []; foreach ($this->_tbl_keys as $key) { $pk[$this->$key] = $this->$key; } } elseif (!\is_array($pk)) { $pk = [$this->_tbl_key => $pk]; } foreach ($this->_tbl_keys as $key) { $pk[$key] = empty($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Get column names. $checkedOutField = $this->getColumnAlias('checked_out'); $checkedOutTimeField = $this->getColumnAlias('checked_out_time'); $nullDate = $this->_supportNullValue ? 'NULL' : $this->_db->quote($this->_db->getNullDate()); $nullID = $this->_supportNullValue ? 'NULL' : '0'; // Check the row in by primary key. $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName($checkedOutField) . ' = ' . $nullID) ->set($this->_db->quoteName($checkedOutTimeField) . ' = ' . $nullDate); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); // Check for a database error. $this->_db->execute(); // Set table values in the object. $this->$checkedOutField = $this->_supportNullValue ? null : 0; $this->$checkedOutTimeField = $this->_supportNullValue ? null : ''; // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterCheckin', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableAfterCheckin', $event); Factory::getApplication()->triggerEvent('onAfterCheckin', [$this->_tbl]); return true; } /** * Validate that the primary key has been set. * * @return boolean True if the primary key(s) have been set. * * @since 3.1.4 */ public function hasPrimaryKey() { if ($this->_autoincrement) { $empty = true; foreach ($this->_tbl_keys as $key) { $empty = $empty && empty($this->$key); } } else { $query = $this->_db->getQuery(true) ->select('COUNT(*)') ->from($this->_db->quoteName($this->_tbl)); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $count = $this->_db->loadResult(); if ($count == 1) { $empty = false; } else { $empty = true; } } return !$empty; } /** * Method to increment the hits for a row if the necessary property/field exists. * * @param mixed $pk An optional primary key value to increment. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function hit($pk = null) { // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeHit', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableBeforeHit', $event); // If there is no hits field, just return true. if (!$this->hasField('hits')) { return true; } if (\is_null($pk)) { $pk = []; foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!\is_array($pk)) { $pk = [$this->_tbl_key => $pk]; } foreach ($this->_tbl_keys as $key) { $pk[$key] = \is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Get column name. $hitsField = $this->getColumnAlias('hits'); // Check the row in by primary key. $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName($hitsField) . ' = (' . $this->_db->quoteName($hitsField) . ' + 1)'); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); $this->_db->execute(); // Set table values in the object. $this->hits++; // Pre-processing by observers $event = AbstractEvent::create( 'onTableAfterHit', [ 'subject' => $this, 'pk' => $pk, ] ); $this->getDispatcher()->dispatch('onTableAfterHit', $event); return true; } /** * Method to determine if a row is checked out and therefore uneditable by a user. * * If the row is checked out by the same user, then it is considered not checked out -- as the user can still edit it. * * @param integer $with The user ID to perform the match with, if an item is checked out by this user the function will return false. * @param integer $against The user ID to perform the match against when the function is used as a static function. * * @return boolean True if checked out. * * @since 1.7.0 */ public function isCheckedOut($with = 0, $against = null) { // Handle the non-static case. if (isset($this) && ($this instanceof Table) && \is_null($against)) { $checkedOutField = $this->getColumnAlias('checked_out'); $against = $this->get($checkedOutField); } // The item is not checked out or is checked out by the same user. if (!$against || ($against == $with)) { return false; } // This last check can only be relied on if tracking session metadata if (Factory::getApplication()->get('session_metadata', true)) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('COUNT(userid)') ->from($db->quoteName('#__session')) ->where($db->quoteName('userid') . ' = ' . (int) $against); $db->setQuery($query); $checkedOut = (bool) $db->loadResult(); // If a session exists for the user then it is checked out. return $checkedOut; } // Assume if we got here that there is a value in the checked out column but it doesn't match the given user return true; } /** * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause. * * This is useful for placing a new item last in a group of items in the table. * * @param string $where WHERE clause to use for selecting the MAX(ordering) for the table. * * @return integer The next ordering value. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function getNextOrder($where = '') { // Check if there is an ordering field set if (!$this->hasField('ordering')) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', \get_class($this))); } // Get the largest ordering value for a given where clause. $query = $this->_db->getQuery(true) ->select('MAX(' . $this->_db->quoteName($this->getColumnAlias('ordering')) . ')') ->from($this->_db->quoteName($this->_tbl)); if ($where) { $query->where($where); } $this->_db->setQuery($query); $max = (int) $this->_db->loadResult(); // Return the largest ordering value + 1. return $max + 1; } /** * Get the primary key values for this table using passed in values as a default. * * @param array $keys Optional primary key values to use. * * @return array An array of primary key names and values. * * @since 3.1.4 */ public function getPrimaryKey(array $keys = []) { foreach ($this->_tbl_keys as $key) { if (!isset($keys[$key])) { if (!empty($this->$key)) { $keys[$key] = $this->$key; } } } return $keys; } /** * Method to compact the ordering values of rows in a group of rows defined by an SQL WHERE clause. * * @param string|string[] $where WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @return mixed Boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function reorder($where = '') { // Check if there is an ordering field set if (!$this->hasField('ordering')) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', \get_class($this))); } $quotedOrderingField = $this->_db->quoteName($this->getColumnAlias('ordering')); $subquery = $this->_db->getQuery(true) ->from($this->_db->quoteName($this->_tbl)) ->selectRowNumber($quotedOrderingField, 'new_ordering'); $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($quotedOrderingField . ' = sq.new_ordering'); $innerOn = []; // Get the primary keys for the selection. foreach ($this->_tbl_keys as $i => $k) { $subquery->select($this->_db->quoteName($k, 'pk__' . $i)); $innerOn[] = $this->_db->quoteName($k) . ' = sq.' . $this->_db->quoteName('pk__' . $i); } // Setup the extra where and ordering clause data. if ($where) { $subquery->where($where); $query->where($where); } $subquery->where($quotedOrderingField . ' >= 0'); $query->where($quotedOrderingField . ' >= 0'); $query->innerJoin('(' . (string) $subquery . ') AS sq '); foreach ($innerOn as $key) { $query->where($key); } // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeReorder', [ 'subject' => $this, 'query' => $query, 'where' => $where, ] ); $this->getDispatcher()->dispatch('onTableBeforeReorder', $event); $this->_db->setQuery($query); $this->_db->execute(); // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterReorder', [ 'subject' => $this, 'where' => $where, ] ); $this->getDispatcher()->dispatch('onTableAfterReorder', $event); return true; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string|string[] $where WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function move($delta, $where = '') { // Check if there is an ordering field set if (!$this->hasField('ordering')) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', \get_class($this))); } $orderingField = $this->getColumnAlias('ordering'); $quotedOrderingField = $this->_db->quoteName($orderingField); // If the change is none, do nothing. if (empty($delta)) { return true; } $row = null; $query = $this->_db->getQuery(true); // Select the primary key and ordering values from the table. $query->select(implode(',', $this->_tbl_keys) . ', ' . $quotedOrderingField) ->from($this->_db->quoteName($this->_tbl)); // If the movement delta is negative move the row up. if ($delta < 0) { $query->where($quotedOrderingField . ' < ' . (int) $this->$orderingField) ->order($quotedOrderingField . ' DESC'); } elseif ($delta > 0) { // If the movement delta is positive move the row down. $query->where($quotedOrderingField . ' > ' . (int) $this->$orderingField) ->order($quotedOrderingField . ' ASC'); } // Add the custom WHERE clause if set. if ($where) { $query->where($where); } // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforeMove', [ 'subject' => $this, 'query' => $query, 'delta' => $delta, 'where' => $where, ] ); $this->getDispatcher()->dispatch('onTableBeforeMove', $event); // Select the first row with the criteria. $query->setLimit(1); $this->_db->setQuery($query); $row = $this->_db->loadObject(); // If a row is found, move the item. if (!empty($row)) { // Update the ordering field for this instance to the row's ordering value. $query->clear() ->update($this->_db->quoteName($this->_tbl)) ->set($quotedOrderingField . ' = ' . (int) $row->$orderingField); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $this->_db->execute(); // Update the ordering field for the row to this instance's ordering value. $query->clear() ->update($this->_db->quoteName($this->_tbl)) ->set($quotedOrderingField . ' = ' . (int) $this->$orderingField); $this->appendPrimaryKeys($query, $row); $this->_db->setQuery($query); $this->_db->execute(); // Update the instance value. $this->$orderingField = $row->$orderingField; } else { // Update the ordering field for this instance. $query->clear() ->update($this->_db->quoteName($this->_tbl)) ->set($quotedOrderingField . ' = ' . (int) $this->$orderingField); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $this->_db->execute(); } // Post-processing by observers $event = AbstractEvent::create( 'onTableAfterMove', [ 'subject' => $this, 'row' => $row, 'delta' => $delta, 'where' => $where, ] ); $this->getDispatcher()->dispatch('onTableAfterMove', $event); return true; } /** * Method to set the publishing state for a row or list of rows in the database table. * * The method respects checked out rows by other users and will attempt to checkin rows that it can after adjustments are made. * * @param mixed $pks An optional array of primary key values to update. If not set the instance property value is used. * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published] * @param integer $userId The user ID of the user performing the operation. * * @return boolean True on success; false if $pks is empty. * * @since 1.7.0 */ public function publish($pks = null, $state = 1, $userId = 0) { // Sanitize input $userId = (int) $userId; $state = (int) $state; // Pre-processing by observers $event = AbstractEvent::create( 'onTableBeforePublish', [ 'subject' => $this, 'pks' => $pks, 'state' => $state, 'userId' => $userId, ] ); $this->getDispatcher()->dispatch('onTableBeforePublish', $event); if (!\is_null($pks)) { if (!\is_array($pks)) { $pks = [$pks]; } foreach ($pks as $key => $pk) { if (!\is_array($pk)) { $pks[$key] = [$this->_tbl_key => $pk]; } } } // If there are no primary keys set check to see if the instance key is set. if (empty($pks)) { $pk = []; foreach ($this->_tbl_keys as $key) { if ($this->$key) { $pk[$key] = $this->$key; } else { // We don't have a full primary key - return false $this->setError(Text::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED')); return false; } } $pks = [$pk]; } $publishedField = $this->getColumnAlias('published'); $checkedOutField = $this->getColumnAlias('checked_out'); foreach ($pks as $pk) { // Update the publishing state for rows with the given primary keys. $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName($publishedField) . ' = ' . (int) $state); // If publishing, set published date/time if not previously set if ($state && $this->hasField('publish_up') && (int) $this->publish_up == 0) { $nowDate = $this->_db->quote(Factory::getDate()->toSql()); $query->set($this->_db->quoteName($this->getColumnAlias('publish_up')) . ' = ' . $nowDate); } // Determine if there is checkin support for the table. if ($this->hasField('checked_out') || $this->hasField('checked_out_time')) { $query->where( '(' . $this->_db->quoteName($checkedOutField) . ' = 0' . ' OR ' . $this->_db->quoteName($checkedOutField) . ' = ' . (int) $userId . ' OR ' . $this->_db->quoteName($checkedOutField) . ' IS NULL' . ')' ); $checkin = true; } else { $checkin = false; } // Build the WHERE clause for the primary keys. $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); try { $this->_db->execute(); } catch (\RuntimeException $e) { $this->setError($e->getMessage()); return false; } // If checkin is supported and all rows were adjusted, check them in. if ($checkin && (\count($pks) == $this->_db->getAffectedRows())) { $this->checkIn($pk); } // If the Table instance value is in the list of primary keys that were set, set the instance. $ours = true; foreach ($this->_tbl_keys as $key) { if ($this->$key != $pk[$key]) { $ours = false; } } if ($ours) { $this->$publishedField = $state; } } $this->setError(''); // Pre-processing by observers $event = AbstractEvent::create( 'onTableAfterPublish', [ 'subject' => $this, 'pks' => $pks, 'state' => $state, 'userId' => $userId, ] ); $this->getDispatcher()->dispatch('onTableAfterPublish', $event); return true; } /** * Method to lock the database table for writing. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException */ protected function _lock() { $this->_db->lockTable($this->_tbl); $this->_locked = true; return true; } /** * Method to return the real name of a "special" column such as ordering, hits, published * etc etc. In this way you are free to follow your db naming convention and use the * built in \Joomla functions. * * @param string $column Name of the "special" column (ie ordering, hits) * * @return string The string that identify the special * * @since 3.4 */ public function getColumnAlias($column) { // Get the column data if set if (isset($this->_columnAlias[$column])) { $return = $this->_columnAlias[$column]; } else { $return = $column; } // Sanitize the name $return = preg_replace('#[^A-Z0-9_]#i', '', $return); return $return; } /** * Method to register a column alias for a "special" column. * * @param string $column The "special" column (ie ordering) * @param string $columnAlias The real column name (ie foo_ordering) * * @return void * * @since 3.4 */ public function setColumnAlias($column, $columnAlias) { // Sanitize the column name alias $column = strtolower($column); $column = preg_replace('#[^A-Z0-9_]#i', '', $column); // Set the column alias internally $this->_columnAlias[$column] = $columnAlias; } /** * Method to unlock the database table for writing. * * @return boolean True on success. * * @since 1.7.0 */ protected function _unlock() { if ($this->_locked) { $this->_db->unlockTables(); $this->_locked = false; } return true; } /** * Check if the record has a property (applying a column alias if it exists) * * @param string $key key to be checked * * @return boolean * * @since 3.9.11 */ public function hasField($key) { $key = $this->getColumnAlias($key); return property_exists($this, $key); } } PK1A#]�4�++src/Table/Menu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Menu table * * @since 1.5 */ class Menu extends Nested { /** * Indicates that columns fully support the NULL value in the database * * @var boolean * @since 4.0.0 */ protected $_supportNullValue = true; /** * Constructor * * @param DatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(DatabaseDriver $db) { parent::__construct('#__menu', 'id', $db); // Set the default access level. $this->access = (int) Factory::getApplication()->get('access'); } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.5 */ public function bind($array, $ignore = '') { // Verify that the default home menu is not unset if ($this->home == '1' && $this->language === '*' && $array['home'] == '0') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_CANNOT_UNSET_DEFAULT_DEFAULT')); return false; } // Verify that the default home menu set to "all" languages" is not unset if ($this->home == '1' && $this->language === '*' && $array['language'] !== '*') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_CANNOT_UNSET_DEFAULT')); return false; } // Verify that the default home menu is not unpublished if ($this->home == '1' && $this->language === '*' && $array['published'] != '1') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_UNPUBLISH_DEFAULT_HOME')); return false; } if (isset($array['params']) && \is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } return parent::bind($array, $ignore); } /** * Overloaded check function * * @return boolean True on success * * @see Table::check() * @since 1.5 */ public function check() { try { parent::check(); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } // Check for a title. if ($this->title === null || trim($this->title) === '') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_MENUITEM')); return false; } // Check for a path. if ($this->path === null || trim($this->path) === '') { $this->path = $this->alias; } // Check for params. if ($this->params === null || trim($this->params) === '') { $this->params = '{}'; } // Check for img. if ($this->img === null || trim($this->img) === '') { $this->img = ' '; } // Cast the home property to an int for checking. $this->home = (int) $this->home; // Verify that the home item is a component. if ($this->home && $this->type !== 'component') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_HOME_NOT_COMPONENT')); return false; } // Set publish_up, publish_down to null if not set if (!$this->publish_up) { $this->publish_up = null; } if (!$this->publish_down) { $this->publish_down = null; } return true; } /** * Overloaded store function * * @param boolean $updateNulls True to update fields even if they are null. * * @return mixed False on failure, positive integer on success. * * @see Table::store() * @since 1.6 */ public function store($updateNulls = true) { $db = $this->getDbo(); // Verify that the alias is unique $table = Table::getInstance('Menu', 'JTable', ['dbo' => $db]); $originalAlias = trim($this->alias); $this->alias = !$originalAlias ? $this->title : $originalAlias; $this->alias = ApplicationHelper::stringURLSafe(trim($this->alias), $this->language); if ($this->parent_id == 1 && $this->client_id == 0) { // Verify that a first level menu item alias is not 'component'. if ($this->alias === 'component') { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_ROOT_ALIAS_COMPONENT')); return false; } // Verify that a first level menu item alias is not the name of a folder. if (\in_array($this->alias, Folder::folders(JPATH_ROOT))) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_MENU_ROOT_ALIAS_FOLDER', $this->alias, $this->alias)); return false; } } // If alias still empty (for instance, new menu item with chinese characters with no unicode alias setting). if (empty($this->alias)) { $this->alias = Factory::getDate()->format('Y-m-d-H-i-s'); } else { $itemSearch = ['alias' => $this->alias, 'parent_id' => $this->parent_id, 'client_id' => (int) $this->client_id]; $error = false; // Check if the alias already exists. For multilingual site. if (Multilanguage::isEnabled() && (int) $this->client_id == 0) { // If there is a menu item at the same level with the same alias (in the All or the same language). if ( ($table->load(array_replace($itemSearch, ['language' => '*'])) && ($table->id != $this->id || $this->id == 0)) || ($table->load(array_replace($itemSearch, ['language' => $this->language])) && ($table->id != $this->id || $this->id == 0)) || ($this->language === '*' && $this->id == 0 && $table->load($itemSearch)) ) { $error = true; } elseif ($this->language === '*' && $this->id != 0) { // When editing an item with All language check if there are more menu items with the same alias in any language. $id = (int) $this->id; $query = $db->getQuery(true) ->select('id') ->from($db->quoteName('#__menu')) ->where($db->quoteName('parent_id') . ' = 1') ->where($db->quoteName('client_id') . ' = 0') ->where($db->quoteName('id') . ' != :id') ->where($db->quoteName('alias') . ' = :alias') ->bind(':id', $id, ParameterType::INTEGER) ->bind(':alias', $this->alias); $otherMenuItemId = (int) $db->setQuery($query)->loadResult(); if ($otherMenuItemId) { $table->load(['id' => $otherMenuItemId]); $error = true; } } } else { // Check if the alias already exists. For monolingual site. // If there is a menu item at the same level with the same alias (in any language). if ($table->load($itemSearch) && ($table->id != $this->id || $this->id == 0)) { $error = true; } } // The alias already exists. Enqueue an error message. if ($error) { $menuTypeTable = Table::getInstance('MenuType', 'JTable', ['dbo' => $db]); $menuTypeTable->load(['menutype' => $table->menutype]); $url = Route::_('index.php?option=com_menus&task=item.edit&id=' . (int) $table->id); // Is the existing menu item trashed? $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_MENU_UNIQUE_ALIAS', $this->alias, $table->title, $menuTypeTable->title, $url)); if ($table->published === -2) { $this->setError(Text::sprintf('JLIB_DATABASE_ERROR_MENU_UNIQUE_ALIAS_TRASHED', $this->alias, $table->title, $menuTypeTable->title, $url)); } return false; } } if ($this->home == '1') { // Verify that the home page for this menu is unique. if ( $table->load( [ 'menutype' => $this->menutype, 'client_id' => (int) $this->client_id, 'home' => '1', ] ) && ($table->language != $this->language) ) { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_HOME_NOT_UNIQUE_IN_MENU')); return false; } // Verify that the home page for this language is unique per client id if ($table->load(['home' => '1', 'language' => $this->language, 'client_id' => (int) $this->client_id])) { if ($table->checked_out && $table->checked_out != $this->checked_out) { $this->setError(Text::_('JLIB_DATABASE_ERROR_MENU_DEFAULT_CHECKIN_USER_MISMATCH')); return false; } $table->home = 0; $table->checked_out = null; $table->checked_out_time = null; $table->store(); } } if (!parent::store($updateNulls)) { return false; } // Get the new path in case the node was moved $pathNodes = $this->getPath(); $segments = []; foreach ($pathNodes as $node) { // Don't include root in path if ($node->alias !== 'root') { $segments[] = $node->alias; } } $newPath = trim(implode('/', $segments), ' /\\'); // Use new path for partial rebuild of table // Rebuild will return positive integer on success, false on failure return $this->rebuild($this->{$this->_tbl_key}, $this->lft, $this->level, $newPath) > 0; } } PK1A#]V#Q��src/Crypt/Crypt.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Crypt; use Joomla\Crypt\Crypt as JCrypt; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Crypt is a Joomla Platform class for handling basic encryption/decryption of data. * * @since 3.0.0 */ class Crypt extends JCrypt { /** * A timing safe comparison method. * * This defeats hacking attempts that use timing based attack vectors. * * NOTE: Length will leak. * * @param string $known A known string to check against. * @param string $unknown An unknown string to check. * * @return boolean True if the two strings are exactly the same. * * @since 3.2 */ public static function timingSafeCompare($known, $unknown) { /** * Explanation about the function_exists * * Yes, hash_equals has existed since PHP 5.6.0 and Joomla's minimum requirements are higher * than that. However, this does not prevent a misguided server administrator from disabling * hash_equals in php.ini. Hence the need for checking whether the function exists or not. */ if (function_exists('hash_equals')) { return hash_equals($known, $unknown); } /** * If hash_equals is not available we use a pure PHP implementation by Anthony Ferrara. * * @see https://blog.ircmaxell.com/2014/11/its-all-about-time.html */ $safeLen = strlen($known); $userLen = strlen($unknown); if ($userLen != $safeLen) { return false; } $result = 0; for ($i = 0; $i < $userLen; $i++) { $result |= (ord($known[$i]) ^ ord($unknown[$i])); } // They are only identical strings if $result is exactly 0... return $result === 0; } /** * Safely detect a string's length * * This method is derived from \ParagonIE\Halite\Util::safeStrlen() * * @param string $str String to check the length of * * @return integer * * @since 3.5 * @ref mbstring.func_overload * @throws \RuntimeException */ public static function safeStrlen($str) { static $exists = null; if ($exists === null) { $exists = \function_exists('mb_strlen'); } if ($exists) { $length = mb_strlen($str, '8bit'); if ($length === false) { throw new \RuntimeException('mb_strlen() failed unexpectedly'); } return $length; } // If we reached here, we can rely on strlen to count bytes: return \strlen($str); } /** * Safely extract a substring * * This method is derived from \ParagonIE\Halite\Util::safeSubstr() * * @param string $str The string to extract the substring from * @param integer $start The starting position to extract from * @param integer $length The length of the string to return * * @return string * * @since 3.5 */ public static function safeSubstr($str, $start, $length = null) { static $exists = null; if ($exists === null) { $exists = \function_exists('mb_substr'); } if ($exists) { // In PHP 5.3 mb_substr($str, 0, NULL, '8bit') returns an empty string, so we have to find the length ourselves. if ($length === null) { if ($start >= 0) { $length = static::safeStrlen($str) - $start; } else { $length = -$start; } } return mb_substr($str, $start, $length, '8bit'); } // Unlike mb_substr(), substr() doesn't accept NULL for length if ($length !== null) { return substr($str, $start, $length); } return substr($str, $start); } } PK1A#]��A���!src/Crypt/Cipher/CryptoCipher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Crypt\Cipher; use Joomla\Crypt\CipherInterface; use Joomla\Crypt\Key; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Crypt cipher for encryption, decryption and key generation via the php-encryption library. * * @since 3.5 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement use SodiumCipher instead */ class CryptoCipher implements CipherInterface { /** * Method to decrypt a data string. * * @param string $data The encrypted string to decrypt. * @param Key $key The key object to use for decryption. * * @return string The decrypted data string. * * @since 3.5 * @throws \RuntimeException */ public function decrypt($data, Key $key) { // Validate key. if ($key->getType() !== 'crypto') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected crypto.'); } // Decrypt the data. try { return \Crypto::Decrypt($data, $key->getPublic()); } catch (\InvalidCiphertextException $ex) { throw new \RuntimeException('DANGER! DANGER! The ciphertext has been tampered with!', $ex->getCode(), $ex); } catch (\CryptoTestFailedException $ex) { throw new \RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); } catch (\CannotPerformOperationException $ex) { throw new \RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); } } /** * Method to encrypt a data string. * * @param string $data The data string to encrypt. * @param Key $key The key object to use for encryption. * * @return string The encrypted data string. * * @since 3.5 * @throws \RuntimeException */ public function encrypt($data, Key $key) { // Validate key. if ($key->getType() !== 'crypto') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected crypto.'); } // Encrypt the data. try { return \Crypto::Encrypt($data, $key->getPublic()); } catch (\CryptoTestFailedException $ex) { throw new \RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); } catch (\CannotPerformOperationException $ex) { throw new \RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); } } /** * Method to generate a new encryption key object. * * @param array $options Key generation options. * * @return Key * * @since 3.5 * @throws \RuntimeException */ public function generateKey(array $options = []) { // Generate the encryption key. try { $public = \Crypto::CreateNewRandomKey(); } catch (\CryptoTestFailedException $ex) { throw new \RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); } catch (\CannotPerformOperationException $ex) { throw new \RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); } // Explicitly flag the private as unused in this cipher. $private = 'unused'; return new Key('crypto', $private, $public); } /** * Check if the cipher is supported in this environment. * * @return boolean * * @since 4.0.0 */ public static function isSupported(): bool { try { \Crypto::RuntimeTest(); return true; } catch (\CryptoTestFailedException $e) { return false; } } } PK1A#]���__!src/Crypt/Cipher/SodiumCipher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Crypt\Cipher; use Joomla\Crypt\CipherInterface; use Joomla\Crypt\Key; use ParagonIE\Sodium\Compat; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JCrypt cipher for sodium algorithm encryption, decryption and key generation. * * @since 3.8.0 */ class SodiumCipher implements CipherInterface { /** * The message nonce to be used with encryption/decryption * * @var string * @since 3.8.0 */ private $nonce; /** * Method to decrypt a data string. * * @param string $data The encrypted string to decrypt. * @param Key $key The key object to use for decryption. * * @return string The decrypted data string. * * @since 3.8.0 * @throws \RuntimeException */ public function decrypt($data, Key $key) { // Validate key. if ($key->getType() !== 'sodium') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected sodium.'); } if (!$this->nonce) { throw new \RuntimeException('Missing nonce to decrypt data'); } $decrypted = Compat::crypto_box_open( $data, $this->nonce, Compat::crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic()) ); if ($decrypted === false) { throw new \RuntimeException('Malformed message or invalid MAC'); } return $decrypted; } /** * Method to encrypt a data string. * * @param string $data The data string to encrypt. * @param Key $key The key object to use for encryption. * * @return string The encrypted data string. * * @since 3.8.0 * @throws \RuntimeException */ public function encrypt($data, Key $key) { // Validate key. if ($key->getType() !== 'sodium') { throw new \InvalidArgumentException('Invalid key of type: ' . $key->getType() . '. Expected sodium.'); } if (!$this->nonce) { throw new \RuntimeException('Missing nonce to decrypt data'); } return Compat::crypto_box( $data, $this->nonce, Compat::crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic()) ); } /** * Method to generate a new encryption key object. * * @param array $options Key generation options. * * @return Key * * @since 3.8.0 * @throws \RuntimeException */ public function generateKey(array $options = []) { // Generate the encryption key. $pair = Compat::crypto_box_keypair(); return new Key('sodium', Compat::crypto_box_secretkey($pair), Compat::crypto_box_publickey($pair)); } /** * Check if the cipher is supported in this environment. * * @return boolean * * @since 4.0.0 */ public static function isSupported(): bool { return class_exists(Compat::class); } /** * Set the nonce to use for encrypting/decrypting messages * * @param string $nonce The message nonce * * @return void * * @since 3.8.0 */ public function setNonce($nonce) { $this->nonce = $nonce; } } PK1A#]���00src/WebAsset/WebAssetAttachBehaviorInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\Document\Document; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Behavior interface * * @since 4.0.0 */ interface WebAssetAttachBehaviorInterface { /** * Method called when asset attached to the Document. * Useful for Asset to add a Script options. * * @param Document $doc Active document * * @return void * * @since 4.0.0 */ public function onAttachCallback(Document $doc); } PK1A#]��`�� � &src/WebAsset/WebAssetItemInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item interface * * Asset Item are "read only" object, all properties must be set through class constructor. * Only properties allowed to be edited is an attributes and an options. * Changing an uri or a dependencies are not allowed, prefer to create a new asset instance. * * @since 4.0.0 */ interface WebAssetItemInterface { /** * Return Asset name * * @return string * * @since 4.0.0 */ public function getName(): string; /** * Return Asset version * * @return string * * @since 4.0.0 */ public function getVersion(): string; /** * Return dependencies list * * @return array * * @since 4.0.0 */ public function getDependencies(): array; /** * Get the URI of the asset * * @param boolean $resolvePath Whether need to search for a real paths * * @return string * * @since 4.0.0 */ public function getUri($resolvePath = true): string; /** * Get the option * * @param string $key An option key * @param string $default A default value * * @return mixed * * @since 4.0.0 */ public function getOption(string $key, $default = null); /** * Set the option * * @param string $key An option key * @param string $value An option value * * @return self * * @since 4.0.0 */ public function setOption(string $key, $value = null): WebAssetItemInterface; /** * Get all options of the asset * * @return array * * @since 4.0.0 */ public function getOptions(): array; /** * Get the attribute * * @param string $key An attributes key * @param string $default A default value * * @return mixed * * @since 4.0.0 */ public function getAttribute(string $key, $default = null); /** * Set the attribute * * @param string $key An attribute key * @param string $value An attribute value * * @return self * * @since 4.0.0 */ public function setAttribute(string $key, $value = null): WebAssetItemInterface; /** * Get all attributes * * @return array * * @since 4.0.0 */ public function getAttributes(): array; } PK1A#]�'����5src/WebAsset/Exception/WebAssetExceptionInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception interface defining a WebAsset error * * @since 4.0.0 */ interface WebAssetExceptionInterface { } PK1A#]tk?1src/WebAsset/Exception/InvalidActionException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an Invalid Action error * * @since 4.0.0 */ class InvalidActionException extends \RuntimeException implements WebAssetExceptionInterface { } PK1A#])8�~))9src/WebAsset/Exception/UnsatisfiedDependencyException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an Unsatisfied Dependency * * @since 4.0.0 */ class UnsatisfiedDependencyException extends \RuntimeException implements WebAssetExceptionInterface { } PK1A#]�H0src/WebAsset/Exception/UnknownAssetException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an Unknown Asset * * @since 4.0.0 */ class UnknownAssetException extends \RuntimeException implements WebAssetExceptionInterface { } PK1A#]v��{� � )src/WebAsset/WebAssetManagerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\WebAsset\Exception\InvalidActionException; use Joomla\CMS\WebAsset\Exception\UnknownAssetException; use Joomla\CMS\WebAsset\Exception\UnsatisfiedDependencyException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Manager Interface * * @since 4.0.0 */ interface WebAssetManagerInterface { /** * Enable an asset item to be attached to a Document * * @param string $type Asset type, script or style etc * @param string $name The asset name * * @return self * * @throws UnknownAssetException When Asset cannot be found * @throws InvalidActionException When the Manager already attached to a Document * * @since 4.0.0 */ public function useAsset(string $type, string $name): self; /** * Deactivate an asset item, so it will not be attached to a Document * * @param string $type Asset type, script or style etc * @param string $name The asset name * * @return self * * @throws UnknownAssetException When Asset cannot be found * @throws InvalidActionException When the Manager already attached to a Document * * @since 4.0.0 */ public function disableAsset(string $type, string $name): self; /** * Check whether the asset are enabled * * @param string $type Asset type, script or style etc * @param string $name The asset name * * @return boolean * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function isAssetActive(string $type, string $name): bool; /** * Get all assets that was enabled for given type * * @param string $type Asset type, script or style etc * @param bool $sort Whether need to sort the assets to follow the dependency Graph * * @return WebAssetItemInterface[] * * @throws UnknownAssetException When Asset cannot be found * @throws UnsatisfiedDependencyException When Dependency cannot be found * * @since 4.0.0 */ public function getAssets(string $type, bool $sort = false): array; } PK1A#]�%�5GG*src/WebAsset/WebAssetRegistryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\WebAsset\Exception\UnknownAssetException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Registry interface * * @since 4.0.0 */ interface WebAssetRegistryInterface { /** * Get an existing Asset from a registry, by asset name and asset type. * * @param string $type Asset type, script or style etc * @param string $name Asset name * * @return WebAssetItem * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function get(string $type, string $name): WebAssetItemInterface; /** * Add Asset to registry of known assets * * @param string $type Asset type, script or style etc * @param WebAssetItemInterface $asset Asset instance * * @return self * * @since 4.0.0 */ public function add(string $type, WebAssetItemInterface $asset): self; /** * Remove Asset from registry. * * @param string $type Asset type, script or style etc * @param string $name Asset name * * @return self * * @since 4.0.0 */ public function remove(string $type, string $name): self; /** * Check whether the asset exists in the registry. * * @param string $type Asset type, script or style etc * @param string $name Asset name * * @return boolean * * @since 4.0.0 */ public function exists(string $type, string $name): bool; } PK1A#]�Ά�;;src/WebAsset/WebAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class * * Asset Item are "read only" object, all properties must be set through class constructor. * Only properties allowed to be edited is an attributes and an options. * Changing an uri or a dependencies are not allowed, prefer to create a new asset instance. * * @since 4.0.0 */ class WebAssetItem implements WebAssetItemInterface { /** * Asset name * * @var string $name * @since 4.0.0 */ protected $name = ''; /** * The URI for the asset * * @var string * @since 4.0.0 */ protected $uri = ''; /** * Additional options for the asset * * @var array * @since 4.0.0 */ protected $options = []; /** * Attributes for the asset, to be rendered in the asset's HTML tag * * @var array * @since 4.0.0 */ protected $attributes = []; /** * Asset dependencies * * @var string[] * @since 4.0.0 */ protected $dependencies = []; /** * Asset version * * @var string * @since 4.0.0 */ protected $version = 'auto'; /** * Class constructor * * @param string $name The asset name * @param string $uri The URI for the asset * @param array $options Additional options for the asset * @param array $attributes Attributes for the asset * @param array $dependencies Asset dependencies * * @since 4.0.0 */ public function __construct( string $name, string $uri = null, array $options = [], array $attributes = [], array $dependencies = [] ) { $this->name = $name; $this->uri = $uri; if (array_key_exists('version', $options)) { $this->version = $options['version']; unset($options['version']); } if (array_key_exists('attributes', $options)) { $this->attributes = (array) $options['attributes']; unset($options['attributes']); } else { $this->attributes = $attributes; } if (array_key_exists('dependencies', $options)) { $this->dependencies = (array) $options['dependencies']; unset($options['dependencies']); } else { $this->dependencies = $dependencies; } $this->options = $options; } /** * Return Asset name * * @return string * * @since 4.0.0 */ public function getName(): string { return $this->name; } /** * Return Asset version * * @return string * * @since 4.0.0 */ public function getVersion(): string { return (string) $this->version; } /** * Return dependencies list * * @return array * * @since 4.0.0 */ public function getDependencies(): array { return $this->dependencies; } /** * Get the file path * * @param boolean $resolvePath Whether need to search for a real paths * * @return string The resolved path if resolved, else an empty string. * * @since 4.0.0 */ public function getUri($resolvePath = true): string { $path = $this->uri; if ($resolvePath && $path) { switch (pathinfo($path, PATHINFO_EXTENSION)) { case 'js': $path = $this->resolvePath($path, 'script'); break; case 'css': $path = $this->resolvePath($path, 'stylesheet'); break; default: break; } } return $path ?? ''; } /** * Get the option * * @param string $key An option key * @param string $default A default value * * @return mixed * * @since 4.0.0 */ public function getOption(string $key, $default = null) { if (array_key_exists($key, $this->options)) { return $this->options[$key]; } return $default; } /** * Set the option * * @param string $key An option key * @param string $value An option value * * @return self * * @since 4.0.0 */ public function setOption(string $key, $value = null): WebAssetItemInterface { $this->options[$key] = $value; return $this; } /** * Get all options * * @return array * * @since 4.0.0 */ public function getOptions(): array { return $this->options; } /** * Get the attribute * * @param string $key An attributes key * @param string $default A default value * * @return mixed * * @since 4.0.0 */ public function getAttribute(string $key, $default = null) { if (array_key_exists($key, $this->attributes)) { return $this->attributes[$key]; } return $default; } /** * Set the attribute * * @param string $key An attribute key * @param string $value An attribute value * * @return self * * @since 4.0.0 */ public function setAttribute(string $key, $value = null): WebAssetItemInterface { $this->attributes[$key] = $value; return $this; } /** * Get all attributes * * @return array * * @since 4.0.0 */ public function getAttributes(): array { return $this->attributes; } /** * Resolve path * * @param string $path The path to resolve * @param string $type The resolver method * * @return string * * @since 4.0.0 */ protected function resolvePath(string $path, string $type): string { if ($type !== 'script' && $type !== 'stylesheet') { throw new \UnexpectedValueException('Unexpected [type], expected "script" or "stylesheet"'); } $file = $path; $external = $this->isPathExternal($path); if (!$external) { // Get the file path $file = HTMLHelper::_( $type, $path, [ 'pathOnly' => true, 'relative' => !$this->isPathAbsolute($path), ] ); } return $file ?? ''; } /** * Check if the Path is External * * @param string $path Path to test * * @return boolean * * @since 4.0.0 */ protected function isPathExternal(string $path): bool { return strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0 || strpos($path, '//') === 0; } /** * Check if the Path is relative to /media folder or absolute * * @param string $path Path to test * * @return boolean * * @since 4.0.0 */ protected function isPathAbsolute(string $path): bool { // We have a full path or not return strpos($path, '/') !== false && is_file(JPATH_ROOT . '/' . $path); } } PK1A#]��,��-src/WebAsset/AssetItem/KeepaliveAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\AssetItem; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory; use Joomla\CMS\Router\Route; use Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface; use Joomla\CMS\WebAsset\WebAssetItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class for Keepalive asset * * @since 4.0.0 */ class KeepaliveAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface { /** * Method called when asset attached to the Document. * Useful for Asset to add a Script options. * * @param Document $doc Active document * * @return void * * @since 4.0.0 */ public function onAttachCallback(Document $doc) { $app = Factory::getApplication(); $sessionHandler = $app->get('session_handler', 'database'); // If the handler is not 'Database', we set a fixed, small refresh value (here: 5 min) $refreshTime = 300; if ($sessionHandler === 'database') { $lifeTime = $app->getSession()->getExpire(); $refreshTime = $lifeTime <= 60 ? 45 : $lifeTime - 60; // The longest refresh period is one hour to prevent integer overflow. if ($refreshTime > 3600 || $refreshTime <= 0) { $refreshTime = 3600; } } // If we are in the frontend or logged in as a user, we can use the ajax component to reduce the load $uri = 'index.php' . ($app->isClient('site') || !Factory::getUser()->guest ? '?option=com_ajax&format=json' : ''); // Add keepalive script options. $doc->addScriptOptions('system.keepalive', ['interval' => $refreshTime * 1000, 'uri' => Route::_($uri)]); } } PK1A#]D�8�tt(src/WebAsset/AssetItem/CoreAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\AssetItem; use Joomla\CMS\Document\Document; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Uri\Uri; use Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface; use Joomla\CMS\WebAsset\WebAssetItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class for Core asset * * @since 4.0.0 */ class CoreAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface { /** * Method called when asset attached to the Document. * Useful for Asset to add a Script options. * * @param Document $doc Active document * * @return void * * @since 4.0.0 */ public function onAttachCallback(Document $doc) { // Add core and base uri paths so javascript scripts can use them. $doc->addScriptOptions( 'system.paths', [ 'root' => Uri::root(true), 'rootFull' => Uri::root(), 'base' => Uri::base(true), 'baseFull' => Uri::base(), ] ); HTMLHelper::_('form.csrf'); } } PK1A#],9 �NN0src/WebAsset/AssetItem/TableColumnsAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\AssetItem; use Joomla\CMS\Document\Document; use Joomla\CMS\Language\Text; use Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface; use Joomla\CMS\WebAsset\WebAssetItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class for tables.column asset * * @since 4.2.0 */ class TableColumnsAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface { /** * Method called when asset attached to the Document. * Used to add the language strings required by the script. * * @param Document $doc Active document * * @return void * * @since 4.2.0 */ public function onAttachCallback(Document $doc) { // Add table-columns.js language strings Text::script('JGLOBAL_COLUMNS'); } } PK1A#]�F���0src/WebAsset/AssetItem/FormValidateAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\AssetItem; use Joomla\CMS\Document\Document; use Joomla\CMS\Language\Text; use Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface; use Joomla\CMS\WebAsset\WebAssetItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class for form.validate asset * * @since 4.0.0 */ class FormValidateAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface { /** * Method called when asset attached to the Document. * Useful for Asset to add a Script options. * * @param Document $doc Active document * * @return void * * @since 4.0.0 */ public function onAttachCallback(Document $doc) { // Add validate.js language strings Text::script('JLIB_FORM_CONTAINS_INVALID_FIELDS'); Text::script('JLIB_FORM_FIELD_REQUIRED_VALUE'); Text::script('JLIB_FORM_FIELD_REQUIRED_CHECK'); Text::script('JLIB_FORM_FIELD_INVALID_VALUE'); } } PK1A#]��Ig��.src/WebAsset/AssetItem/LangActiveAssetItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset\AssetItem; use Joomla\CMS\Factory; use Joomla\CMS\WebAsset\WebAssetItem; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Item class for load asset file for active language. * Used in core templates. * * @since 4.0.0 */ class LangActiveAssetItem extends WebAssetItem { /** * Class constructor * * @param string $name The asset name * @param string $uri The URI for the asset * @param array $options Additional options for the asset * @param array $attributes Attributes for the asset * @param array $dependencies Asset dependencies * * @since 4.0.0 */ public function __construct( string $name, string $uri = null, array $options = [], array $attributes = [], array $dependencies = [] ) { parent::__construct($name, $uri, $options, $attributes, $dependencies); // Prepare Uri depend from the active language $langTag = Factory::getApplication()->getLanguage()->getTag(); $client = $this->getOption('client'); // Create Uri <client>/language/<langTag>/<langTag>.css if ($client) { $this->uri = $client . '/language/' . $langTag . '/' . $langTag . '.css'; } else { $this->uri = 'language/' . $langTag . '/' . $langTag . '.css'; } } } PK1A#]���D�2�2!src/WebAsset/WebAssetRegistry.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\WebAsset\Exception\UnknownAssetException; use Joomla\Event\Dispatcher as EventDispatcher; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Registry class * * @since 4.0.0 */ class WebAssetRegistry implements WebAssetRegistryInterface, DispatcherAwareInterface { use DispatcherAwareTrait; /** * Files with Asset info. File path should be relative. * * @var array * @example of registry file: * * { * "title" : "Example", * "name" : "com_example", * "author": "Joomla! CMS", * "assets": [ * { * "name": "library1", * "version": "3.5.0", * "type": "script", * "uri": "com_example/library1.min.js" * }, * { * "name": "library2", * "version": "3.5.0", * "type": "script", * "uri": "com_example/library2.min.js", * "dependencies": [ * "core", * "library1" * ], * "attribute": { * "attr-name": "attr value" * "defer": true * } * }, * { * "name": "library1", * "version": "3.5.0", * "type": "style", * "uri": "com_example/library1.min.css" * "attribute": { * "media": "all" * } * }, * { * "name": "library1", * "type": "preset", * "dependencies": { * "library1#style", * "library1#script" * } * }, * ] * } * * @since 4.0.0 */ protected $dataFilesNew = []; /** * List of parsed files * * @var array * * @since 4.0.0 */ protected $dataFilesParsed = []; /** * Registry of available Assets * * @var array * * @since 4.0.0 */ protected $assets = []; /** * Registry constructor * * @since 4.0.0 */ public function __construct() { // Use a dedicated dispatcher $this->setDispatcher(new EventDispatcher()); } /** * Get an existing Asset from a registry, by asset name. * * @param string $type Asset type, script or style * @param string $name Asset name * * @return WebAssetItem * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function get(string $type, string $name): WebAssetItemInterface { // Check if any new file was added $this->parseRegistryFiles(); if (empty($this->assets[$type][$name])) { throw new UnknownAssetException(sprintf('There is no "%s" asset of a "%s" type in the registry.', $name, $type)); } return $this->assets[$type][$name]; } /** * Add Asset to registry of known assets * * @param string $type Asset type, script or style * @param WebAssetItemInterface $asset Asset instance * * @return self * * @since 4.0.0 */ public function add(string $type, WebAssetItemInterface $asset): WebAssetRegistryInterface { $type = strtolower($type); if (!array_key_exists($type, $this->assets)) { $this->assets[$type] = []; } // Check if any new file was added $this->parseRegistryFiles(); $eventChange = 'new'; $eventAsset = $asset; // Use "old" asset for "Changed" event, a "new" asset can be loaded by a name from the registry if (!empty($this->assets[$type][$asset->getName()])) { $eventChange = 'override'; $eventAsset = $this->assets[$type][$asset->getName()]; } $this->assets[$type][$asset->getName()] = $asset; $this->dispatchAssetChanged($type, $eventAsset, $eventChange); return $this; } /** * Remove Asset from registry. * * @param string $type Asset type, script or style * @param string $name Asset name * * @return self * * @since 4.0.0 */ public function remove(string $type, string $name): WebAssetRegistryInterface { // Check if any new file was added $this->parseRegistryFiles(); if (!empty($this->assets[$type][$name])) { $asset = $this->assets[$type][$name]; unset($this->assets[$type][$name]); $this->dispatchAssetChanged($type, $asset, 'remove'); } return $this; } /** * Check whether the asset exists in the registry. * * @param string $type Asset type, script or style * @param string $name Asset name * * @return boolean * * @since 4.0.0 */ public function exists(string $type, string $name): bool { // Check if any new file was added $this->parseRegistryFiles(); return !empty($this->assets[$type][$name]); } /** * Prepare new Asset instance. * * @param string $name The asset name * @param string $uri The URI for the asset * @param array $options Additional options for the asset * @param array $attributes Attributes for the asset * @param array $dependencies Asset dependencies * * @return WebAssetItem * * @since 4.0.0 */ public function createAsset( string $name, string $uri = null, array $options = [], array $attributes = [], array $dependencies = [] ): WebAssetItem { $nameSpace = \array_key_exists('namespace', $options) ? $options['namespace'] : __NAMESPACE__ . '\\AssetItem'; $className = \array_key_exists('class', $options) ? $options['class'] : null; if ($className && class_exists($nameSpace . '\\' . $className)) { $className = $nameSpace . '\\' . $className; return new $className($name, $uri, $options, $attributes, $dependencies); } return new WebAssetItem($name, $uri, $options, $attributes, $dependencies); } /** * Register new file with Asset(s) info * * @param string $path Relative path * * @return self * * @since 4.0.0 */ public function addRegistryFile(string $path): self { $path = Path::clean($path); if (isset($this->dataFilesNew[$path]) || isset($this->dataFilesParsed[$path])) { return $this; } if (is_file(JPATH_ROOT . '/' . $path)) { $this->dataFilesNew[$path] = $path; } return $this; } /** * Get a list of the registry files * * @return array * * @since 4.0.0 */ public function getRegistryFiles(): array { return array_values($this->dataFilesParsed + $this->dataFilesNew); } /** * Helper method to register new file with Template Asset(s) info * * @param string $template The template name * @param integer $client The application client id * * @return self * * @since 4.0.0 */ public function addTemplateRegistryFile(string $template, int $client): self { switch ($client) { case 0: $this->addRegistryFile('templates/' . $template . '/joomla.asset.json'); break; case 1: $this->addRegistryFile('administrator/templates/' . $template . '/joomla.asset.json'); break; default: break; } return $this; } /** * Helper method to register new file with Extension Asset(s) info * * @param string $name A full extension name, actually a name in the /media folder, eg: com_example, plg_system_example etc. * * @return self * * @since 4.0.0 */ public function addExtensionRegistryFile(string $name): self { $this->addRegistryFile('media/' . $name . '/joomla.asset.json'); return $this; } /** * Parse registered files * * @return void * * @since 4.0.0 */ protected function parseRegistryFiles() { if (!$this->dataFilesNew) { return; } $paths = $this->dataFilesNew; $this->dataFilesNew = []; foreach ($paths as $path) { // Parse only if the file was not parsed already if (empty($this->dataFilesParsed[$path])) { $this->parseRegistryFile($path); // Mark the file as parsed $this->dataFilesParsed[$path] = $path; } } } /** * Parse registry file * * @param string $path Relative path to the data file * * @return void * * @throws \RuntimeException If file is empty or invalid * * @since 4.0.0 */ protected function parseRegistryFile($path) { $data = file_get_contents(JPATH_ROOT . '/' . $path); $data = $data ? json_decode($data, true) : null; if ($data === null) { throw new \RuntimeException(sprintf('Asset registry file "%s" contains invalid JSON', $path)); } // Check if asset field exists and contains data. If it doesn't - we can just bail here. if (empty($data['assets'])) { return; } // Keep source info $assetSource = [ 'registryFile' => $path, ]; $namespace = \array_key_exists('namespace', $data) ? $data['namespace'] : null; // Prepare WebAssetItem instances foreach ($data['assets'] as $i => $item) { if (empty($item['name'])) { throw new \RuntimeException( sprintf('Failed parsing asset registry file "%s". Property "name" is required for asset index "%s"', $path, $i) ); } if (empty($item['type'])) { throw new \RuntimeException( sprintf('Failed parsing asset registry file "%s". Property "type" is required for asset "%s"', $path, $item['name']) ); } $item['type'] = strtolower($item['type']); $name = $item['name']; $uri = $item['uri'] ?? ''; $options = $item; $options['assetSource'] = $assetSource; unset($options['uri'], $options['name']); // Inheriting the Namespace if ($namespace && !\array_key_exists('namespace', $options)) { $options['namespace'] = $namespace; } $assetItem = $this->createAsset($name, $uri, $options); $this->add($item['type'], $assetItem); } } /** * Dispatch an event to notify listeners about asset changes: new, remove, override * Events: * - onWebAssetRegistryChangedAssetNew When new asset added to the registry * - onWebAssetRegistryChangedAssetOverride When the asset overridden * - onWebAssetRegistryChangedAssetRemove When new asset was removed from the registry * * @param string $type Asset type, script or style * @param WebAssetItemInterface $asset Asset instance * @param string $change A type of change: new, remove, override * * @return void * * @since 4.0.0 */ protected function dispatchAssetChanged(string $type, WebAssetItemInterface $asset, string $change) { // Trigger the event $event = AbstractEvent::create( 'onWebAssetRegistryChangedAsset' . ucfirst($change), [ 'eventClass' => 'Joomla\\CMS\\Event\\WebAsset\\WebAssetRegistryAssetChanged', 'subject' => $this, 'assetType' => $type, 'asset' => $asset, 'change' => $change, ] ); $this->getDispatcher()->dispatch($event->getName(), $event); } } PK1A#]�Y���� src/WebAsset/WebAssetManager.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\WebAsset; use Joomla\CMS\Event\WebAsset\WebAssetRegistryAssetChanged; use Joomla\CMS\WebAsset\Exception\InvalidActionException; use Joomla\CMS\WebAsset\Exception\UnknownAssetException; use Joomla\CMS\WebAsset\Exception\UnsatisfiedDependencyException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Asset Manager class * * @method WebAssetManager registerStyle(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager registerAndUseStyle(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager useStyle($name) * @method WebAssetManager disableStyle($name) * @method WebAssetManager addInlineStyle(WebAssetItem|string $content, $options = [], $attributes = [], $dependencies = []) * * @method WebAssetManager registerScript(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager registerAndUseScript(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager useScript($name) * @method WebAssetManager disableScript($name) * @method WebAssetManager addInlineScript(WebAssetItem|string $content, $options = [], $attributes = [], $dependencies = []) * * @method WebAssetManager registerPreset(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager registerAndUsePreset(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = []) * @method WebAssetManager usePreset($name) * @method WebAssetManager disablePreset($name) * * @since 4.0.0 */ class WebAssetManager implements WebAssetManagerInterface { /** * Mark inactive asset * * @var integer * * @since 4.0.0 */ public const ASSET_STATE_INACTIVE = 0; /** * Mark active asset. Just enabled, but WITHOUT dependency resolved * * @var integer * * @since 4.0.0 */ public const ASSET_STATE_ACTIVE = 1; /** * Mark active asset that is enabled as dependency to another asset * * @var integer * * @since 4.0.0 */ public const ASSET_STATE_DEPENDENCY = 2; /** * The WebAsset Registry instance * * @var WebAssetRegistry * * @since 4.0.0 */ protected $registry; /** * A list of active assets (including their dependencies). * Array of Name => State * * @var array * * @since 4.0.0 */ protected $activeAssets = []; /** * Internal marker to check the manager state, * to prevent use of the manager after an assets are rendered * * @var boolean * * @since 4.0.0 */ protected $locked = false; /** * Internal marker to keep track when need to recheck dependencies * * @var boolean * * @since 4.0.0 */ protected $dependenciesIsActual = false; /** * Class constructor * * @param WebAssetRegistry $registry The WebAsset Registry instance * * @since 4.0.0 */ public function __construct(WebAssetRegistry $registry) { $this->registry = $registry; // Listen to changes in the registry $this->registry->getDispatcher()->addListener( 'onWebAssetRegistryChangedAssetOverride', function (WebAssetRegistryAssetChanged $event) { // If the changed asset are used if (!empty($this->activeAssets[$event->getAssetType()][$event->getAsset()->getName()])) { $this->dependenciesIsActual = false; } } ); $this->registry->getDispatcher()->addListener( 'onWebAssetRegistryChangedAssetRemove', function (WebAssetRegistryAssetChanged $event) { // If the changed asset are used if (!empty($this->activeAssets[$event->getAssetType()][$event->getAsset()->getName()])) { $this->dependenciesIsActual = false; } } ); } /** * Get associated registry instance * * @return WebAssetRegistry * * @since 4.0.0 */ public function getRegistry(): WebAssetRegistry { return $this->registry; } /** * Clears all collected items. * * @return self * * @since 4.1.1 */ public function reset(): WebAssetManagerInterface { if ($this->locked) { throw new InvalidActionException('WebAssetManager is locked'); } $this->activeAssets = []; $this->dependenciesIsActual = false; return $this; } /** * Adds support for magic method calls * * @param string $method A method name * @param array $arguments Arguments for a method * * @return mixed * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __call($method, $arguments) { $method = strtolower($method); if (0 === strpos($method, 'use')) { $type = substr($method, 3); if (empty($arguments[0])) { throw new \BadMethodCallException('An asset name is required'); } return $this->useAsset($type, $arguments[0]); } if (0 === strpos($method, 'addinline')) { $type = substr($method, 9); if (empty($arguments[0])) { throw new \BadMethodCallException('Content is required'); } return $this->addInline($type, ...$arguments); } if (0 === strpos($method, 'disable')) { $type = substr($method, 7); if (empty($arguments[0])) { throw new \BadMethodCallException('An asset name is required'); } return $this->disableAsset($type, $arguments[0]); } if (0 === strpos($method, 'register')) { // Check for registerAndUse<Type> $andUse = substr($method, 8, 6) === 'anduse'; // Extract the type $type = $andUse ? substr($method, 14) : substr($method, 8); if (empty($arguments[0])) { throw new \BadMethodCallException('An asset instance or an asset name is required'); } if ($andUse) { $name = $arguments[0] instanceof WebAssetItemInterface ? $arguments[0]->getName() : $arguments[0]; return $this->registerAsset($type, ...$arguments)->useAsset($type, $name); } else { return $this->registerAsset($type, ...$arguments); } } throw new \BadMethodCallException(sprintf('Undefined method %s in class %s', $method, get_class($this))); } /** * Enable an asset item to be attached to a Document * * @param string $type The asset type, script or style * @param string $name The asset name * * @return self * * @throws UnknownAssetException When Asset cannot be found * @throws InvalidActionException When the Manager already attached to a Document * * @since 4.0.0 */ public function useAsset(string $type, string $name): WebAssetManagerInterface { if ($this->locked) { throw new InvalidActionException('WebAssetManager is locked, you came late'); } // Check whether asset exists $asset = $this->registry->get($type, $name); if (empty($this->activeAssets[$type])) { $this->activeAssets[$type] = []; } // For "preset" need to check the dependencies first if ($type === 'preset') { $this->usePresetItems($name); } // Asset already enabled if (!empty($this->activeAssets[$type][$name])) { // Set state to active, in case it was ASSET_STATE_DEPENDENCY $this->activeAssets[$type][$name] = static::ASSET_STATE_ACTIVE; return $this; } $this->activeAssets[$type][$name] = static::ASSET_STATE_ACTIVE; // To re-check dependencies if ($asset->getDependencies()) { $this->dependenciesIsActual = false; } return $this; } /** * Deactivate an asset item, so it will not be attached to a Document * * @param string $type The asset type, script or style * @param string $name The asset name * * @return self * * @throws UnknownAssetException When Asset cannot be found * @throws InvalidActionException When the Manager already attached to a Document * * @since 4.0.0 */ public function disableAsset(string $type, string $name): WebAssetManagerInterface { if ($this->locked) { throw new InvalidActionException('WebAssetManager is locked, you came late'); } // Check whether asset exists $this->registry->get($type, $name); unset($this->activeAssets[$type][$name]); // To re-check dependencies $this->dependenciesIsActual = false; // For Preset case if ($type === 'preset') { $this->disablePresetItems($name); } return $this; } /** * Enable list of assets provided by Preset item. * * "Preset" a special kind of asset that hold a list of assets that has to be enabled, * same as direct call of useAsset() to each of item in list. * Can hold mixed types of assets (script, style, another preset, etc), the type provided after # symbol, after * the asset name, example: foo#style, bar#script. * * The method call useAsset() internally for each of its dependency, this is important for keeping FIFO order * of enabled items. * The Preset not a strict asset, and each of its dependency can be safely disabled by use of disableAsset() later. * * @param string $name The asset name * * @return self * * @throws UnsatisfiedDependencyException When Asset dependency cannot be found * * @since 4.0.0 */ protected function usePresetItems($name): WebAssetManagerInterface { // Get the asset object $asset = $this->registry->get('preset', $name); // Call useAsset() to each of its dependency foreach ($asset->getDependencies() as $dependency) { $depType = ''; $depName = $dependency; $pos = strrpos($dependency, '#'); // Check for cross-dependency "dependency-name#type" case if ($pos) { $depType = substr($dependency, $pos + 1); $depName = substr($dependency, 0, $pos); } $depType = $depType ?: 'preset'; // Make sure dependency exists if (!$this->registry->exists($depType, $depName)) { throw new UnsatisfiedDependencyException( sprintf('Unsatisfied dependency "%s" for an asset "%s" of type "%s"', $dependency, $name, 'preset') ); } $this->useAsset($depType, $depName); } return $this; } /** * Deactivate list of assets provided by Preset item. * * @param string $name The asset name * * @return self * * @throws UnsatisfiedDependencyException When Asset dependency cannot be found * * @since 4.0.0 */ protected function disablePresetItems($name): WebAssetManagerInterface { // Get the asset object $asset = $this->registry->get('preset', $name); // Call disableAsset() to each of its dependency foreach ($asset->getDependencies() as $dependency) { $depType = ''; $depName = $dependency; $pos = strrpos($dependency, '#'); // Check for cross-dependency "dependency-name#type" case if ($pos) { $depType = substr($dependency, $pos + 1); $depName = substr($dependency, 0, $pos); } $depType = $depType ?: 'preset'; // Make sure dependency exists if (!$this->registry->exists($depType, $depName)) { throw new UnsatisfiedDependencyException( sprintf('Unsatisfied dependency "%s" for an asset "%s" of type "%s"', $dependency, $name, 'preset') ); } $this->disableAsset($depType, $depName); } return $this; } /** * Get a state for the Asset * * @param string $type The asset type, script or style * @param string $name The asset name * * @return integer * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function getAssetState(string $type, string $name): int { // Check whether asset exists first $this->registry->get($type, $name); // Make sure that all dependencies are active if (!$this->dependenciesIsActual) { $this->enableDependencies(); } if (!empty($this->activeAssets[$type][$name])) { return $this->activeAssets[$type][$name]; } return static::ASSET_STATE_INACTIVE; } /** * Check whether the asset are enabled * * @param string $type The asset type, script or style * @param string $name The asset name * * @return boolean * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function isAssetActive(string $type, string $name): bool { return $this->getAssetState($type, $name) !== static::ASSET_STATE_INACTIVE; } /** * Helper method to check whether the asset exists in the registry. * * @param string $type Asset type, script or style * @param string $name Asset name * * @return boolean * * @since 4.0.0 */ public function assetExists(string $type, string $name): bool { return $this->registry->exists($type, $name); } /** * Register a new asset. * Allow to register WebAssetItem instance in the registry, by call registerAsset($type, $assetInstance) * Or create an asset on fly (from name and Uri) and register in the registry, by call registerAsset($type, $assetName, $uri, $options ....) * * @param string $type The asset type, script or style * @param WebAssetItem|string $asset The asset name or instance to register * @param string $uri The URI for the asset * @param array $options Additional options for the asset * @param array $attributes Attributes for the asset * @param array $dependencies Asset dependencies * * @return self * * @since 4.0.0 * * @throws \InvalidArgumentException */ public function registerAsset(string $type, $asset, string $uri = '', array $options = [], array $attributes = [], array $dependencies = []) { if ($asset instanceof WebAssetItemInterface) { $this->registry->add($type, $asset); } elseif (is_string($asset)) { $options['type'] = $type; $assetInstance = $this->registry->createAsset($asset, $uri, $options, $attributes, $dependencies); $this->registry->add($type, $assetInstance); } else { throw new \InvalidArgumentException( sprintf( '%s(): Argument #2 ($asset) must be a string or an instance of %s, %s given.', __METHOD__, WebAssetItemInterface::class, \is_object($asset) ? \get_class($asset) : \gettype($asset) ) ); } return $this; } /** * Helper method to get the asset from the registry. * * @param string $type Asset type, script or style * @param string $name Asset name * * @return WebAssetItemInterface * * @throws UnknownAssetException When Asset cannot be found * * @since 4.0.0 */ public function getAsset(string $type, string $name): WebAssetItemInterface { return $this->registry->get($type, $name); } /** * Get all active assets, optionally sort them to follow the dependency Graph * * @param string $type The asset type, script or style * @param bool $sort Whether need to sort the assets to follow the dependency Graph * * @return WebAssetItem[] * * @throws UnknownAssetException When Asset cannot be found * @throws UnsatisfiedDependencyException When Dependency cannot be found * * @since 4.0.0 */ public function getAssets(string $type, bool $sort = false): array { // Make sure that all dependencies are active if (!$this->dependenciesIsActual) { $this->enableDependencies(); } if (empty($this->activeAssets[$type])) { return []; } // Apply Tree sorting for regular asset items, but return FIFO order for "preset" if ($sort && $type !== 'preset') { $assets = $this->calculateOrderOfActiveAssets($type); } else { $assets = []; foreach (array_keys($this->activeAssets[$type]) as $name) { $assets[$name] = $this->registry->get($type, $name); } } return $assets; } /** * Helper method to calculate inline to non inline relation (before/after positions). * Return associated array, which contain dependency (handle) name as key, and list of inline items for each position. * Example: ['handle.name' => ['before' => ['inline1', 'inline2'], 'after' => ['inline3', 'inline4']]] * * Note: If inline asset have a multiple dependencies, then will be used last one from the list for positioning * * @param WebAssetItem[] $assets The assets list * * @return array * * @since 4.0.0 */ public function getInlineRelation(array $assets): array { $inlineRelation = []; // Find an inline assets and their relations to non inline foreach ($assets as $k => $asset) { if (!$asset->getOption('inline')) { continue; } // Check whether position are requested with dependencies $position = $asset->getOption('position'); $position = $position === 'before' || $position === 'after' ? $position : null; $deps = $asset->getDependencies(); if ($position && $deps) { // If inline asset have a multiple dependencies, then use last one from the list for positioning $handle = end($deps); $inlineRelation[$handle][$position][$asset->getName()] = $asset; } } return $inlineRelation; } /** * Helper method to filter an inline assets * * @param WebAssetItem[] $assets Reference to a full list of active assets * * @return WebAssetItem[] Array of inline assets * * @since 4.0.0 */ public function filterOutInlineAssets(array &$assets): array { $inlineAssets = []; foreach ($assets as $k => $asset) { if (!$asset->getOption('inline')) { continue; } // Remove inline assets from assets list, and add to list of inline unset($assets[$k]); $inlineAssets[$asset->getName()] = $asset; } return $inlineAssets; } /** * Add a new inline content asset. * Allow to register WebAssetItem instance in the registry, by call addInline($type, $assetInstance) * Or create an asset on fly (from name and Uri) and register in the registry, by call addInline($type, $content, $options ....) * * @param string $type The asset type, script or style * @param WebAssetItem|string $content The content to of inline asset * @param array $options Additional options for the asset * @param array $attributes Attributes for the asset * @param array $dependencies Asset dependencies * * @return self * * @since 4.0.0 * * @throws \InvalidArgumentException */ public function addInline(string $type, $content, array $options = [], array $attributes = [], array $dependencies = []): self { if ($content instanceof WebAssetItemInterface) { $assetInstance = $content; } elseif (is_string($content)) { $name = $options['name'] ?? ('inline.' . md5($content)); $assetInstance = $this->registry->createAsset($name, '', $options, $attributes, $dependencies); $assetInstance->setOption('content', $content); } else { throw new \InvalidArgumentException( sprintf( '%s(): Argument #2 ($content) must be a string or an instance of %s, %s given.', __METHOD__, WebAssetItemInterface::class, \is_object($content) ? \get_class($content) : \gettype($content) ) ); } // Get the name $asset = $assetInstance->getName(); // Set required options $assetInstance->setOption('type', $type); $assetInstance->setOption('inline', true); // Add to registry $this->registry->add($type, $assetInstance); // And make active $this->useAsset($type, $asset); return $this; } /** * Lock the manager to prevent further modifications * * @return self * * @since 4.0.0 */ public function lock(): self { $this->locked = true; return $this; } /** * Get the manager state. A collection of registry files and active asset names (per type). * * @return array * * @since 4.0.0 */ public function getManagerState(): array { return [ 'registryFiles' => $this->getRegistry()->getRegistryFiles(), 'activeAssets' => $this->activeAssets, ]; } /** * Update Dependencies state for all active Assets or only for given * * @param string $type The asset type, script or style * @param WebAssetItem $asset The asset instance to which need to enable dependencies * * @return self * * @since 4.0.0 */ protected function enableDependencies(string $type = null, WebAssetItem $asset = null): self { if ($type === 'preset') { // Preset items already was enabled by usePresetItems() return $this; } if ($asset) { // Get all dependencies of given asset recursively $allDependencies = $this->getDependenciesForAsset($type, $asset, true); foreach ($allDependencies as $depType => $depItems) { foreach ($depItems as $depItem) { // Set dependency state only when it is inactive, to keep a manually activated Asset in their original state if (empty($this->activeAssets[$depType][$depItem->getName()])) { // Add the dependency at the top of the list of active assets $this->activeAssets[$depType] = [$depItem->getName() => static::ASSET_STATE_DEPENDENCY] + $this->activeAssets[$depType]; } } } } else { // Re-Check for dependencies for all active assets // Firstly, filter out only active assets foreach ($this->activeAssets as $type => $activeAsset) { $this->activeAssets[$type] = array_filter( $activeAsset, function ($state) { return $state === WebAssetManager::ASSET_STATE_ACTIVE; } ); } // Secondary, check for dependencies of each active asset // This need to be separated from previous step because we may have "cross type" dependency foreach ($this->activeAssets as $type => $activeAsset) { foreach (array_keys($activeAsset) as $name) { $asset = $this->registry->get($type, $name); $this->enableDependencies($type, $asset); } } $this->dependenciesIsActual = true; } return $this; } /** * Calculate weight of active Assets, by its Dependencies * * @param string $type The asset type, script or style * * @return WebAssetItem[] * * @since 4.0.0 */ protected function calculateOrderOfActiveAssets($type): array { // See https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm $graphOrder = []; $activeAssets = $this->getAssets($type, false); // Get Graph of Outgoing and Incoming connections $connectionsGraph = $this->getConnectionsGraph($activeAssets); $graphOutgoing = $connectionsGraph['outgoing']; $graphIncoming = $connectionsGraph['incoming']; // Make a copy to be used during weight processing $graphIncomingCopy = $graphIncoming; // Find items without incoming connections $emptyIncoming = array_keys( array_filter( $graphIncoming, function ($el) { return !$el; } ) ); // Reverse, to start from a last enabled and move up to a first enabled, this helps to maintain an original sorting $emptyIncoming = array_reverse($emptyIncoming); // Loop through, and sort the graph while ($emptyIncoming) { // Add the node without incoming connection to the result $item = array_shift($emptyIncoming); $graphOrder[] = $item; // Check of each neighbor of the node foreach (array_reverse($graphOutgoing[$item]) as $neighbor) { // Remove incoming connection of already visited node unset($graphIncoming[$neighbor][$item]); // If there no more incoming connections add the node to queue if (empty($graphIncoming[$neighbor])) { $emptyIncoming[] = $neighbor; } } } // Sync Graph order with FIFO order $fifoWeights = []; $graphWeights = []; $requestedWeights = []; foreach (array_keys($this->activeAssets[$type]) as $index => $name) { $fifoWeights[$name] = $index * 10 + 10; } foreach (array_reverse($graphOrder) as $index => $name) { $graphWeights[$name] = $index * 10 + 10; $requestedWeights[$name] = $activeAssets[$name]->getOption('weight') ?: $fifoWeights[$name]; } // Try to set a requested weight, or make it close as possible to requested, but keep the Graph order while ($requestedWeights) { $item = key($requestedWeights); $weight = array_shift($requestedWeights); // Skip empty items if ($weight === null) { continue; } // Check the predecessors (Outgoing vertexes), the weight cannot be lighter than the predecessor have $topBorder = $weight - 1; if (!empty($graphOutgoing[$item])) { $prevWeights = []; foreach ($graphOutgoing[$item] as $pItem) { $prevWeights[] = $graphWeights[$pItem]; } $topBorder = max($prevWeights); } // Calculate a new weight $newWeight = $weight > $topBorder ? $weight : $topBorder + 1; // If a new weight heavier than existing, then we need to update all incoming connections (children) if ($newWeight > $graphWeights[$item] && !empty($graphIncomingCopy[$item])) { // Sort Graph of incoming by actual position foreach ($graphIncomingCopy[$item] as $incomingItem) { // Set a weight heavier than current, then this node to be processed in next iteration if (empty($requestedWeights[$incomingItem])) { $requestedWeights[$incomingItem] = $graphWeights[$incomingItem] + $newWeight; } } } // Set a new weight $graphWeights[$item] = $newWeight; } asort($graphWeights); // Get Assets in calculated order $resultAssets = []; foreach (array_keys($graphWeights) as $name) { $resultAssets[$name] = $activeAssets[$name]; } return $resultAssets; } /** * Build Graph of Outgoing and Incoming connections for given assets. * * @param WebAssetItem[] $assets Asset instances * * @return array * * @since 4.0.0 */ protected function getConnectionsGraph(array $assets): array { $graphOutgoing = []; $graphIncoming = []; foreach ($assets as $asset) { $name = $asset->getName(); // Initialise an array for outgoing nodes of the asset $graphOutgoing[$name] = []; // Initialise an array for incoming nodes of the asset if (!\array_key_exists($name, $graphIncoming)) { $graphIncoming[$name] = []; } // Collect an outgoing/incoming nodes foreach ($asset->getDependencies() as $depName) { $graphOutgoing[$name][$depName] = $depName; $graphIncoming[$depName][$name] = $name; } } return [ 'outgoing' => $graphOutgoing, 'incoming' => $graphIncoming, ]; } /** * Return dependencies for Asset as array of WebAssetItem objects * * @param string $type The asset type, script or style * @param WebAssetItem $asset Asset instance * @param boolean $recursively Whether to search for dependency recursively * @param string $recursionType The type of initial item to prevent loop * @param WebAssetItem $recursionRoot Initial item to prevent loop * * @return array * * @throws UnsatisfiedDependencyException When Dependency cannot be found * * @since 4.0.0 */ protected function getDependenciesForAsset( string $type, WebAssetItem $asset, $recursively = false, string $recursionType = null, WebAssetItem $recursionRoot = null ): array { $assets = []; $recursionRoot = $recursionRoot ?? $asset; $recursionType = $recursionType ?? $type; foreach ($asset->getDependencies() as $depName) { $depType = $type; // Skip already loaded in recursion if ($recursionRoot->getName() === $depName && $recursionType === $depType) { continue; } if (!$this->registry->exists($depType, $depName)) { throw new UnsatisfiedDependencyException( sprintf('Unsatisfied dependency "%s" for an asset "%s" of type "%s"', $depName, $asset->getName(), $depType) ); } $dep = $this->registry->get($depType, $depName); $assets[$depType][$depName] = $dep; if (!$recursively) { continue; } $parentDeps = $this->getDependenciesForAsset($depType, $dep, true, $recursionType, $recursionRoot); $assets = array_replace_recursive($assets, $parentDeps); } return $assets; } } PK1A#]qLV� � src/Tree/NodeTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tree; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a Node Interface Trait Class. * * @since 4.0.0 */ trait NodeTrait { use ImmutableNodeTrait; /** * Set the parent of this node * * If the node already has a parent, the link is unset * * @param NodeInterface $parent NodeInterface for the parent to be set * * @return void * * @since 4.0.0 */ public function setParent(NodeInterface $parent) { if (!\is_null($this->_parent)) { $key = array_search($this, $this->_parent->_children); unset($this->_parent->_children[$key]); } $this->_parent = $parent; $this->_parent->_children[] = &$this; if (\count($this->_parent->_children) > 1) { end($this->_parent->_children); $this->_leftSibling = prev($this->_parent->_children); $this->_leftSibling->_rightSibling = $this; } } /** * Add child to this node * * If the child already has a parent, the link is unset * * @param NodeInterface $child The child to be added. * * @return void * * @since 4.0.0 */ public function addChild(NodeInterface $child) { $child->setParent($this); } /** * Remove a specific child * * @param NodeInterface $child Child to remove * * @return void * * @since 4.0.0 */ public function removeChild(NodeInterface $child) { $key = array_search($child, $this->_children); unset($this->_children[$key]); } /** * Function to set the left or right sibling of a node * * @param NodeInterface $sibling NodeInterface object for the sibling * @param boolean $right If set to false, the sibling is the left one * * @return void * * @since 4.0.0 */ public function setSibling(NodeInterface $sibling, $right = true) { if ($right) { $this->_rightSibling = $sibling; } else { $this->_leftSibling = $sibling; } } } PK1A#]T�����src/Tree/ImmutableNodeTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tree; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for an Immutable Node Class. * * @since 4.0.0 */ trait ImmutableNodeTrait { /** * Parent node object * * @var NodeInterface * @since 1.6 */ protected $_parent = null; /** * Array of Children * * @var NodeInterface[] * @since 1.6 */ protected $_children = []; /** * Node left of this one * * @var NodeInterface * @since 1.6 */ protected $_leftSibling = null; /** * Node right of this one * * @var NodeInterface * @since 1.6 */ protected $_rightSibling = null; /** * Get the children of this node * * @param boolean $recursive False by default * * @return NodeInterface[] The children * * @since 4.0.0 */ public function &getChildren($recursive = false) { if ($recursive) { $items = []; foreach ($this->_children as $child) { $items[] = $child; $items = array_merge($items, $child->getChildren(true)); } return $items; } return $this->_children; } /** * Get the parent of this node * * @return NodeInterface|null * * @since 4.0.0 */ public function getParent() { return $this->_parent; } /** * Get the root of the tree * * @return ImmutableNodeInterface * * @since 4.0.0 */ public function getRoot() { $root = $this->getParent(); if (!$root) { return $this; } while ($root->hasParent()) { $root = $root->getParent(); } return $root; } /** * Test if this node has children * * @return boolean True if there is a child * * @since 4.0.0 */ public function hasChildren() { return (bool) \count($this->_children); } /** * Test if this node has a parent * * @return boolean True if there is a parent * * @since 4.0.0 */ public function hasParent() { return $this->getParent() != null; } /** * Returns the right or left sibling of a node * * @param boolean $right If set to false, returns the left sibling * * @return NodeInterface|null NodeInterface object of the sibling. * * @since 4.0.0 */ public function getSibling($right = true) { if ($right) { return $this->_rightSibling; } else { return $this->_leftSibling; } } } PK1A#]�7����src/Tree/NodeInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tree; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a node class * * @since 4.0.0 */ interface NodeInterface extends ImmutableNodeInterface { /** * Set the parent of this node * * If the node already has a parent, the link is unset * * @param NodeInterface $parent NodeInterface for the parent to be set * * @return void * * @since 4.0.0 */ public function setParent(NodeInterface $parent); /** * Add child to this node * * If the child already has a parent, the link is unset * * @param NodeInterface $child The child to be added. * * @return void * * @since 4.0.0 */ public function addChild(NodeInterface $child); /** * Remove a specific child * * @param NodeInterface $child Child to remove * * @return void * * @since 4.0.0 */ public function removeChild(NodeInterface $child); /** * Function to set the left or right sibling of a node * * @param NodeInterface $sibling NodeInterface object for the sibling * @param boolean $right If set to false, the sibling is the left one * * @return void * * @since 4.0.0 */ public function setSibling(NodeInterface $sibling, $right = true); } PK1A#]�y�`��#src/Tree/ImmutableNodeInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tree; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for an immutable node class * * @since 4.0.0 */ interface ImmutableNodeInterface { /** * Get the children of this node * * @param boolean $recursive False by default * * @return NodeInterface[] The children * * @since 4.0.0 */ public function &getChildren($recursive = false); /** * Get the parent of this node * * @return NodeInterface|null * * @since 4.0.0 */ public function getParent(); /** * Get the root of the tree * * @return ImmutableNodeInterface * * @since 4.0.0 */ public function getRoot(); /** * Test if this node has children * * @return boolean True if there is a child * * @since 4.0.0 */ public function hasChildren(); /** * Test if this node has a parent * * @return boolean True if there is a parent * * @since 4.0.0 */ public function hasParent(); /** * Returns the right or left sibling of a node * * @param boolean $right If set to false, returns the left sibling * * @return NodeInterface|null NodeInterface object of the sibling. * * @since 4.0.0 */ public function getSibling($right = true); } PK1A#]N�NN!src/Extension/LegacyComponent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Categories\CategoryInterface; use Joomla\CMS\Categories\CategoryServiceInterface; use Joomla\CMS\Categories\CategoryServiceTrait; use Joomla\CMS\Categories\SectionNotFoundException; use Joomla\CMS\Component\Router\RouterInterface; use Joomla\CMS\Component\Router\RouterLegacy; use Joomla\CMS\Component\Router\RouterServiceInterface; use Joomla\CMS\Dispatcher\DispatcherInterface; use Joomla\CMS\Dispatcher\LegacyComponentDispatcher; use Joomla\CMS\Fields\FieldsServiceInterface; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Menu\AbstractMenu; use Joomla\CMS\MVC\Factory\LegacyFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Factory\MVCFactoryServiceInterface; use Joomla\CMS\Tag\TagServiceInterface; use Joomla\CMS\Tag\TagServiceTrait; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to component specific services. * * @since 4.0.0 */ class LegacyComponent implements ComponentInterface, MVCFactoryServiceInterface, CategoryServiceInterface, FieldsServiceInterface, RouterServiceInterface, TagServiceInterface { use CategoryServiceTrait, TagServiceTrait { CategoryServiceTrait::getTableNameForSection insteadof TagServiceTrait; CategoryServiceTrait::getStateColumnForSection insteadof TagServiceTrait; } /** * @var string * * @since 4.0.0 */ private $component; /** * LegacyComponentContainer constructor. * * @param string $component The component * * @since 4.0.0 */ public function __construct(string $component) { $this->component = str_replace('com_', '', $component); } /** * Returns the dispatcher for the given application. * * @param CMSApplicationInterface $application The application * * @return DispatcherInterface * * @since 4.0.0 */ public function getDispatcher(CMSApplicationInterface $application): DispatcherInterface { return new LegacyComponentDispatcher($application); } /** * Get the factory. * * @return MVCFactoryInterface * * @since 4.0.0 * @throws \UnexpectedValueException May be thrown if the factory has not been set. */ public function getMVCFactory(): MVCFactoryInterface { return new LegacyFactory(); } /** * Returns the category service. * * @param array $options The options * @param string $section The section * * @return CategoryInterface * * @since 4.0.0 * @throws SectionNotFoundException */ public function getCategory(array $options = [], $section = ''): CategoryInterface { $classname = ucfirst($this->component) . ucfirst($section) . 'Categories'; if (!class_exists($classname)) { $path = JPATH_SITE . '/components/com_' . $this->component . '/helpers/category.php'; if (!is_file($path)) { throw new SectionNotFoundException(); } include_once $path; } if (!class_exists($classname)) { throw new SectionNotFoundException(); } return new $classname($options); } /** * Adds Count Items for Category Manager. * * @param \stdClass[] $items The category objects * @param string $section The section * * @return void * * @since 4.0.0 * @throws \Exception */ public function countItems(array $items, string $section) { $helper = $this->loadHelper(); if (!$helper || !\is_callable([$helper, 'countItems'])) { return; } $helper::countItems($items, $section); } /** * Adds Count Items for Tag Manager. * * @param \stdClass[] $items The content objects * @param string $extension The name of the active view. * * @return void * * @since 4.0.0 * @throws \Exception */ public function countTagItems(array $items, string $extension) { $helper = $this->loadHelper(); if (!$helper || !\is_callable([$helper, 'countTagItems'])) { return; } $helper::countTagItems($items, $extension); } /** * Returns a valid section for articles. If it is not valid then null * is returned. * * @param string $section The section to get the mapping for * @param object $item The item * * @return string|null The new section * * @since 4.0.0 */ public function validateSection($section, $item = null) { $helper = $this->loadHelper(); if (!$helper || !\is_callable([$helper, 'validateSection'])) { return $section; } return $helper::validateSection($section, $item); } /** * Returns valid contexts. * * @return array * * @since 4.0.0 */ public function getContexts(): array { $helper = $this->loadHelper(); if (!$helper || !\is_callable([$helper, 'getContexts'])) { return []; } return $helper::getContexts(); } /** * Returns the router. * * @param CMSApplicationInterface $application The application object * @param AbstractMenu $menu The menu object to work with * * @return RouterInterface * * @since 4.0.0 */ public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface { $compname = ucfirst($this->component); $class = $compname . 'Router'; if (!class_exists($class)) { // Use the component routing handler if it exists $path = JPATH_SITE . '/components/com_' . $this->component . '/router.php'; // Use the custom routing handler if it exists if (is_file($path)) { require_once $path; } } if (class_exists($class)) { $reflection = new \ReflectionClass($class); if (\in_array('Joomla\\CMS\\Component\\Router\\RouterInterface', $reflection->getInterfaceNames())) { return new $class($application, $menu); } } return new RouterLegacy($compname); } /** * Returns the classname of the legacy helper class. If none is found it returns false. * * @return boolean|string * * @since 4.0.0 */ private function loadHelper() { $className = ucfirst($this->component) . 'Helper'; if (class_exists($className)) { return $className; } $file = Path::clean(JPATH_ADMINISTRATOR . '/components/com_' . $this->component . '/helpers/' . $this->component . '.php'); if (!is_file($file)) { return false; } \JLoader::register($className, $file); if (!class_exists($className)) { return false; } return $className; } } PK1A#]� ���src/Extension/DummyPlugin.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Plugin\CMSPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Placeholder plugin. * * @since 4.0.0 */ class DummyPlugin extends CMSPlugin { } PK1A#]{@mm$src/Extension/ComponentInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Dispatcher\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to component specific services. * * @since 4.0.0 */ interface ComponentInterface { /** * Returns the dispatcher for the given application. * * @param CMSApplicationInterface $application The application * * @return DispatcherInterface * * @since 4.0.0 */ public function getDispatcher(CMSApplicationInterface $application): DispatcherInterface; } PK1A#]� �<kk!src/Extension/ModuleInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Dispatcher\DispatcherInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to module specific services. * * @since 4.0.0 */ interface ModuleInterface { /** * Returns the dispatcher for the given application, module and input. * * @param \stdClass $module The module * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function getDispatcher(\stdClass $module, CMSApplicationInterface $application, Input $input = null): DispatcherInterface; } PK1A#]�C���!src/Extension/PluginInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\Event\DispatcherAwareInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to plugin specific services. * * @since 4.0.0 */ interface PluginInterface extends DispatcherAwareInterface { /** * Registers its listeners. * * @return void * * @since 4.0.0 */ public function registerListeners(); } PK1A#]���sssrc/Extension/MVCComponent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\MVC\Factory\MVCFactoryServiceInterface; use Joomla\CMS\MVC\Factory\MVCFactoryServiceTrait; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * MVC Component class. * * @since 4.0.0 */ class MVCComponent extends Component implements MVCFactoryServiceInterface { use MVCFactoryServiceTrait; } PK1A#]WW���F�F!src/Extension/ExtensionHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Factory; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extension Helper class. * * @since 3.7.4 */ class ExtensionHelper { /** * The loaded extensions. * * @var array * @since 4.0.0 */ public static $extensions = [ModuleInterface::class => [], ComponentInterface::class => [], PluginInterface::class => []]; /** * The loaded extensions. * * @var array * @since 4.0.0 */ private static $loadedExtensions = []; /** * Array of core extensions * Each element is an array with elements "type", "element", "folder" and * "client_id". * * @var array * @since 3.7.4 */ protected static $coreExtensions = [ // Format: `type`, `element`, `folder`, `client_id` // Core component extensions ['component', 'com_actionlogs', '', 1], ['component', 'com_admin', '', 1], ['component', 'com_ajax', '', 1], ['component', 'com_associations', '', 1], ['component', 'com_banners', '', 1], ['component', 'com_cache', '', 1], ['component', 'com_categories', '', 1], ['component', 'com_checkin', '', 1], ['component', 'com_config', '', 1], ['component', 'com_contact', '', 1], ['component', 'com_content', '', 1], ['component', 'com_contenthistory', '', 1], ['component', 'com_cpanel', '', 1], ['component', 'com_fields', '', 1], ['component', 'com_finder', '', 1], ['component', 'com_guidedtours', '', 1], ['component', 'com_installer', '', 1], ['component', 'com_joomlaupdate', '', 1], ['component', 'com_languages', '', 1], ['component', 'com_login', '', 1], ['component', 'com_mails', '', 1], ['component', 'com_media', '', 1], ['component', 'com_menus', '', 1], ['component', 'com_messages', '', 1], ['component', 'com_modules', '', 1], ['component', 'com_newsfeeds', '', 1], ['component', 'com_plugins', '', 1], ['component', 'com_postinstall', '', 1], ['component', 'com_privacy', '', 1], ['component', 'com_redirect', '', 1], ['component', 'com_scheduler', '', 1], ['component', 'com_tags', '', 1], ['component', 'com_templates', '', 1], ['component', 'com_users', '', 1], ['component', 'com_workflow', '', 1], ['component', 'com_wrapper', '', 1], // Core file extensions ['file', 'joomla', '', 0], // Core language extensions - administrator ['language', 'en-GB', '', 1], // Core language extensions - site ['language', 'en-GB', '', 0], // Core language extensions - API ['language', 'en-GB', '', 3], // Core library extensions ['library', 'joomla', '', 0], ['library', 'phpass', '', 0], // Core module extensions - administrator ['module', 'mod_custom', '', 1], ['module', 'mod_feed', '', 1], ['module', 'mod_frontend', '', 1], ['module', 'mod_guidedtours', '', 1], ['module', 'mod_latest', '', 1], ['module', 'mod_latestactions', '', 1], ['module', 'mod_logged', '', 1], ['module', 'mod_login', '', 1], ['module', 'mod_loginsupport', '', 1], ['module', 'mod_menu', '', 1], ['module', 'mod_messages', '', 1], ['module', 'mod_multilangstatus', '', 1], ['module', 'mod_popular', '', 1], ['module', 'mod_post_installation_messages', '', 1], ['module', 'mod_privacy_dashboard', '', 1], ['module', 'mod_privacy_status', '', 1], ['module', 'mod_quickicon', '', 1], ['module', 'mod_sampledata', '', 1], ['module', 'mod_stats_admin', '', 1], ['module', 'mod_submenu', '', 1], ['module', 'mod_title', '', 1], ['module', 'mod_toolbar', '', 1], ['module', 'mod_user', '', 1], ['module', 'mod_version', '', 1], // Core module extensions - site ['module', 'mod_articles_archive', '', 0], ['module', 'mod_articles_categories', '', 0], ['module', 'mod_articles_category', '', 0], ['module', 'mod_articles_latest', '', 0], ['module', 'mod_articles_news', '', 0], ['module', 'mod_articles_popular', '', 0], ['module', 'mod_banners', '', 0], ['module', 'mod_breadcrumbs', '', 0], ['module', 'mod_custom', '', 0], ['module', 'mod_feed', '', 0], ['module', 'mod_finder', '', 0], ['module', 'mod_footer', '', 0], ['module', 'mod_languages', '', 0], ['module', 'mod_login', '', 0], ['module', 'mod_menu', '', 0], ['module', 'mod_random_image', '', 0], ['module', 'mod_related_items', '', 0], ['module', 'mod_stats', '', 0], ['module', 'mod_syndicate', '', 0], ['module', 'mod_tags_popular', '', 0], ['module', 'mod_tags_similar', '', 0], ['module', 'mod_users_latest', '', 0], ['module', 'mod_whosonline', '', 0], ['module', 'mod_wrapper', '', 0], // Core package extensions ['package', 'pkg_en-GB', '', 0], // Core plugin extensions - actionlog ['plugin', 'joomla', 'actionlog', 0], // Core plugin extensions - API Authentication ['plugin', 'basic', 'api-authentication', 0], ['plugin', 'token', 'api-authentication', 0], // Core plugin extensions - authentication ['plugin', 'cookie', 'authentication', 0], ['plugin', 'joomla', 'authentication', 0], ['plugin', 'ldap', 'authentication', 0], // Core plugin extensions - behaviour ['plugin', 'compat', 'behaviour', 0], ['plugin', 'taggable', 'behaviour', 0], ['plugin', 'versionable', 'behaviour', 0], // Core plugin extensions - captcha ['plugin', 'recaptcha', 'captcha', 0], ['plugin', 'recaptcha_invisible', 'captcha', 0], // Core plugin extensions - content ['plugin', 'confirmconsent', 'content', 0], ['plugin', 'contact', 'content', 0], ['plugin', 'emailcloak', 'content', 0], ['plugin', 'fields', 'content', 0], ['plugin', 'finder', 'content', 0], ['plugin', 'joomla', 'content', 0], ['plugin', 'loadmodule', 'content', 0], ['plugin', 'pagebreak', 'content', 0], ['plugin', 'pagenavigation', 'content', 0], ['plugin', 'vote', 'content', 0], // Core plugin extensions - editors ['plugin', 'codemirror', 'editors', 0], ['plugin', 'none', 'editors', 0], ['plugin', 'tinymce', 'editors', 0], // Core plugin extensions - editors xtd ['plugin', 'article', 'editors-xtd', 0], ['plugin', 'contact', 'editors-xtd', 0], ['plugin', 'fields', 'editors-xtd', 0], ['plugin', 'image', 'editors-xtd', 0], ['plugin', 'menu', 'editors-xtd', 0], ['plugin', 'module', 'editors-xtd', 0], ['plugin', 'pagebreak', 'editors-xtd', 0], ['plugin', 'readmore', 'editors-xtd', 0], // Core plugin extensions - extension ['plugin', 'joomla', 'extension', 0], ['plugin', 'namespacemap', 'extension', 0], ['plugin', 'finder', 'extension', 0], // Core plugin extensions - fields ['plugin', 'calendar', 'fields', 0], ['plugin', 'checkboxes', 'fields', 0], ['plugin', 'color', 'fields', 0], ['plugin', 'editor', 'fields', 0], ['plugin', 'imagelist', 'fields', 0], ['plugin', 'integer', 'fields', 0], ['plugin', 'list', 'fields', 0], ['plugin', 'media', 'fields', 0], ['plugin', 'radio', 'fields', 0], ['plugin', 'sql', 'fields', 0], ['plugin', 'subform', 'fields', 0], ['plugin', 'text', 'fields', 0], ['plugin', 'textarea', 'fields', 0], ['plugin', 'url', 'fields', 0], ['plugin', 'user', 'fields', 0], ['plugin', 'usergrouplist', 'fields', 0], // Core plugin extensions - filesystem ['plugin', 'local', 'filesystem', 0], // Core plugin extensions - finder ['plugin', 'categories', 'finder', 0], ['plugin', 'contacts', 'finder', 0], ['plugin', 'content', 'finder', 0], ['plugin', 'newsfeeds', 'finder', 0], ['plugin', 'tags', 'finder', 0], // Core plugin extensions - installer ['plugin', 'folderinstaller', 'installer', 0], ['plugin', 'override', 'installer', 0], ['plugin', 'packageinstaller', 'installer', 0], ['plugin', 'urlinstaller', 'installer', 0], ['plugin', 'webinstaller', 'installer', 0], // Core plugin extensions - media-action ['plugin', 'crop', 'media-action', 0], ['plugin', 'resize', 'media-action', 0], ['plugin', 'rotate', 'media-action', 0], // Core plugin extensions - Multi-factor Authentication ['plugin', 'email', 'multifactorauth', 0], ['plugin', 'fixed', 'multifactorauth', 0], ['plugin', 'totp', 'multifactorauth', 0], ['plugin', 'webauthn', 'multifactorauth', 0], ['plugin', 'yubikey', 'multifactorauth', 0], // Core plugin extensions - privacy ['plugin', 'actionlogs', 'privacy', 0], ['plugin', 'consents', 'privacy', 0], ['plugin', 'contact', 'privacy', 0], ['plugin', 'content', 'privacy', 0], ['plugin', 'message', 'privacy', 0], ['plugin', 'user', 'privacy', 0], // Core plugin extensions - quick icon ['plugin', 'downloadkey', 'quickicon', 0], ['plugin', 'extensionupdate', 'quickicon', 0], ['plugin', 'joomlaupdate', 'quickicon', 0], ['plugin', 'overridecheck', 'quickicon', 0], ['plugin', 'phpversioncheck', 'quickicon', 0], ['plugin', 'privacycheck', 'quickicon', 0], ['plugin', 'eos', 'quickicon', 0], // Core plugin extensions - sample data ['plugin', 'blog', 'sampledata', 0], ['plugin', 'multilang', 'sampledata', 0], // Core plugin extensions - system ['plugin', 'accessibility', 'system', 0], ['plugin', 'actionlogs', 'system', 0], ['plugin', 'cache', 'system', 0], ['plugin', 'debug', 'system', 0], ['plugin', 'fields', 'system', 0], ['plugin', 'guidedtours', 'system', 0], ['plugin', 'highlight', 'system', 0], ['plugin', 'httpheaders', 'system', 0], ['plugin', 'jooa11y', 'system', 0], ['plugin', 'languagecode', 'system', 0], ['plugin', 'languagefilter', 'system', 0], ['plugin', 'log', 'system', 0], ['plugin', 'logout', 'system', 0], ['plugin', 'logrotation', 'system', 0], ['plugin', 'privacyconsent', 'system', 0], ['plugin', 'redirect', 'system', 0], ['plugin', 'remember', 'system', 0], ['plugin', 'schedulerunner', 'system', 0], ['plugin', 'sef', 'system', 0], ['plugin', 'sessiongc', 'system', 0], ['plugin', 'shortcut', 'system', 0], ['plugin', 'skipto', 'system', 0], ['plugin', 'stats', 'system', 0], ['plugin', 'tasknotification', 'system', 0], ['plugin', 'updatenotification', 'system', 0], ['plugin', 'webauthn', 'system', 0], // Core plugin extensions - task scheduler ['plugin', 'checkfiles', 'task', 0], ['plugin', 'demotasks', 'task', 0], ['plugin', 'requests', 'task', 0], ['plugin', 'sitestatus', 'task', 0], // Core plugin extensions - user ['plugin', 'contactcreator', 'user', 0], ['plugin', 'joomla', 'user', 0], ['plugin', 'profile', 'user', 0], ['plugin', 'terms', 'user', 0], ['plugin', 'token', 'user', 0], // Core plugin extensions - webservices ['plugin', 'banners', 'webservices', 0], ['plugin', 'config', 'webservices', 0], ['plugin', 'contact', 'webservices', 0], ['plugin', 'content', 'webservices', 0], ['plugin', 'installer', 'webservices', 0], ['plugin', 'languages', 'webservices', 0], ['plugin', 'media', 'webservices', 0], ['plugin', 'menus', 'webservices', 0], ['plugin', 'messages', 'webservices', 0], ['plugin', 'modules', 'webservices', 0], ['plugin', 'newsfeeds', 'webservices', 0], ['plugin', 'plugins', 'webservices', 0], ['plugin', 'privacy', 'webservices', 0], ['plugin', 'redirect', 'webservices', 0], ['plugin', 'tags', 'webservices', 0], ['plugin', 'templates', 'webservices', 0], ['plugin', 'users', 'webservices', 0], // Core plugin extensions - workflow ['plugin', 'featuring', 'workflow', 0], ['plugin', 'notification', 'workflow', 0], ['plugin', 'publishing', 'workflow', 0], // Core template extensions - administrator ['template', 'atum', '', 1], // Core template extensions - site ['template', 'cassiopeia', '', 0], ]; /** * Array of core extension IDs. * * @var array * @since 4.0.0 */ protected static $coreExtensionIds; /** * Gets the core extensions. * * @return array Array with core extensions. * Each extension is an array with following format: * `type`, `element`, `folder`, `client_id`. * * @since 3.7.4 */ public static function getCoreExtensions() { return self::$coreExtensions; } /** * Returns an array of core extension IDs. * * @return array * * @since 4.0.0 * @throws \RuntimeException */ public static function getCoreExtensionIds() { if (self::$coreExtensionIds !== null) { return self::$coreExtensionIds; } $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')); $values = []; foreach (self::$coreExtensions as $extension) { $values[] = $extension[0] . '|' . $extension[1] . '|' . $extension[2] . '|' . $extension[3]; } $query->whereIn( $query->concatenate( [ $db->quoteName('type'), $db->quoteName('element'), $db->quoteName('folder'), $db->quoteName('client_id'), ], '|' ), $values, ParameterType::STRING ); $db->setQuery($query); self::$coreExtensionIds = $db->loadColumn(); return self::$coreExtensionIds; } /** * Check if an extension is core or not * * @param string $type The extension's type. * @param string $element The extension's element name. * @param integer $clientId The extension's client ID. Default 0. * @param string $folder The extension's folder. Default ''. * * @return boolean True if core, false if not. * * @since 3.7.4 */ public static function checkIfCoreExtension($type, $element, $clientId = 0, $folder = '') { return \in_array([$type, $element, $folder, $clientId], self::$coreExtensions); } /** * Returns an extension record for the given name. * * @param string $element The extension element * @param string $type The extension type * @param integer|null $clientId The client ID * @param string|null $folder Plugin folder * * @return \stdClass|null The object or null if not found. * * @since 4.0.0 * @throws \InvalidArgumentException */ public static function getExtensionRecord(string $element, string $type, ?int $clientId = null, ?string $folder = null): ?\stdClass { if ($type === 'plugin' && $folder === null) { throw new \InvalidArgumentException(sprintf('`$folder` is required when `$type` is `plugin` in %s()', __METHOD__)); } if (\in_array($type, ['module', 'language', 'template'], true) && $clientId === null) { throw new \InvalidArgumentException( sprintf('`$clientId` is required when `$type` is `module`, `language` or `template` in %s()', __METHOD__) ); } $key = $element . '.' . $type . '.' . $clientId . '.' . $folder; if (!\array_key_exists($key, self::$loadedExtensions)) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('element') . ' = :element', $db->quoteName('type') . ' = :type', ] ) ->bind(':element', $element) ->bind(':type', $type); if ($clientId !== null) { $query->where($db->quoteName('client_id') . ' = :clientId') ->bind(':clientId', $clientId, ParameterType::INTEGER); } if ($folder !== null) { $query->where($db->quoteName('folder') . ' = :folder') ->bind(':folder', $folder); } $query->setLimit(1); $db->setQuery($query); self::$loadedExtensions[$key] = $db->loadObject(); } return self::$loadedExtensions[$key]; } } PK1A#]gKc���,src/Extension/BootableExtensionInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Psr\Container\ContainerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface which defines that an extension can be booted. * * @since 4.0.0 */ interface BootableExtensionInterface { /** * Booting the extension. This is the function to set up the environment of the extension like * registering new class loaders, etc. * * If required, some initial set up can be done from services of the container, eg. * registering HTML services. * * @param ContainerInterface $container The container * * @return void * * @since 4.0.0 */ public function boot(ContainerInterface $container); } PK1A#]N�|� � src/Extension/Module.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Dispatcher\DispatcherInterface; use Joomla\CMS\Dispatcher\ModuleDispatcherFactoryInterface; use Joomla\CMS\Helper\HelperFactoryAwareInterface; use Joomla\CMS\Helper\HelperFactoryInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to module specific services. * * @since 4.0.0 */ class Module implements ModuleInterface, HelperFactoryInterface { /** * The dispatcher factory. * * @var ModuleDispatcherFactoryInterface * * @since 4.0.0 */ private $dispatcherFactory; /** * The helper factory. * * @var HelperFactoryInterface * * @since 4.0.0 */ private $helperFactory; /** * Module constructor. * * @param ModuleDispatcherFactoryInterface $dispatcherFactory The dispatcher factory * @param HelperFactoryInterface $helperFactory The helper factory * * @since 4.0.0 */ public function __construct(ModuleDispatcherFactoryInterface $dispatcherFactory, ?HelperFactoryInterface $helperFactory) { $this->dispatcherFactory = $dispatcherFactory; $this->helperFactory = $helperFactory; } /** * Returns the dispatcher for the given application, module and input. * * @param \stdClass $module The module * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function getDispatcher(\stdClass $module, CMSApplicationInterface $application, Input $input = null): DispatcherInterface { $dispatcher = $this->dispatcherFactory->createDispatcher($module, $application, $input); if ($dispatcher instanceof HelperFactoryAwareInterface) { $dispatcher->setHelperFactory($this->helperFactory); } return $dispatcher; } /** * Returns a helper instance for the given name. * * @param string $name The name * @param array $config The config * * @return \stdClass * * @since 4.0.0 */ public function getHelper(string $name, array $config = []) { return $this->helperFactory->getHelper($name, $config); } } PK1A#]���src/Extension/Component.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface; use Joomla\CMS\Dispatcher\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to component specific services. * * @since 4.0.0 */ class Component implements ComponentInterface { /** * The dispatcher factory. * * @var ComponentDispatcherFactoryInterface * * @since 4.0.0 */ private $dispatcherFactory; /** * Component constructor. * * @param ComponentDispatcherFactoryInterface $dispatcherFactory The dispatcher factory * * @since 4.0.0 */ public function __construct(ComponentDispatcherFactoryInterface $dispatcherFactory) { $this->dispatcherFactory = $dispatcherFactory; } /** * Returns the dispatcher for the given application. * * @param CMSApplicationInterface $application The application * * @return DispatcherInterface * * @since 4.0.0 */ public function getDispatcher(CMSApplicationInterface $application): DispatcherInterface { return $this->dispatcherFactory->createDispatcher($application); } } PK1A#]<��'src/Extension/ExtensionManagerTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; use Joomla\CMS\Dispatcher\ModuleDispatcherFactory; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Helper\HelperFactory; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\Exception\ContainerNotFoundException; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for classes which can load extensions * * @since 4.0.0 */ trait ExtensionManagerTrait { /** * Boots the component with the given name. * * @param string $component The component to boot. * * @return ComponentInterface * * @since 4.0.0 */ public function bootComponent($component): ComponentInterface { // Normalize the component name $component = strtolower($component); $component = str_starts_with($component, 'com_') ? substr($component, 4) : $component; // Path to look for services $path = JPATH_ADMINISTRATOR . '/components/com_' . $component; return $this->loadExtension(ComponentInterface::class, $component, $path); } /** * Boots the module with the given name. * * @param string $module The module to boot * @param string $applicationName The application name * * @return ModuleInterface * * @since 4.0.0 */ public function bootModule($module, $applicationName): ModuleInterface { // Normalize the module name $module = strtolower($module); $module = str_starts_with($module, 'mod_') ? substr($module, 4) : $module; // Path to look for services $path = JPATH_SITE . '/modules/mod_' . $module; if ($applicationName === 'administrator') { $path = JPATH_ADMINISTRATOR . '/modules/mod_' . $module; } return $this->loadExtension(ModuleInterface::class, $module, $path); } /** * Boots the plugin with the given name and type. * * @param string $plugin The plugin name * @param string $type The type of the plugin * * @return PluginInterface * * @since 4.0.0 */ public function bootPlugin($plugin, $type): PluginInterface { // Normalize the plugin name $plugin = strtolower($plugin); $plugin = str_starts_with($plugin, 'plg_') ? substr($plugin, 4) : $plugin; // Path to look for services $path = JPATH_SITE . '/plugins/' . $type . '/' . $plugin; return $this->loadExtension(PluginInterface::class, $plugin . ':' . $type, $path); } /** * Loads the extension. * * @param string $type The extension type * @param string $extensionName The extension name * @param string $extensionPath The path of the extension * * @return ComponentInterface|ModuleInterface|PluginInterface * * @since 4.0.0 */ private function loadExtension($type, $extensionName, $extensionPath) { // Check if the extension is already loaded if (!empty(ExtensionHelper::$extensions[$type][$extensionName])) { return ExtensionHelper::$extensions[$type][$extensionName]; } // The container to get the services from $container = $this->getContainer()->createChild(); $container->get(DispatcherInterface::class)->dispatch( 'onBeforeExtensionBoot', AbstractEvent::create( 'onBeforeExtensionBoot', [ 'subject' => $this, 'type' => $type, 'extensionName' => $extensionName, 'container' => $container, ] ) ); // The path of the loader file $path = $extensionPath . '/services/provider.php'; if (is_file($path)) { // Load the file $provider = require_once $path; // Check if the extension supports the service provider interface if ($provider instanceof ServiceProviderInterface) { $provider->register($container); } } // Fallback to legacy if (!$container->has($type)) { switch ($type) { case ComponentInterface::class: $container->set($type, new LegacyComponent('com_' . $extensionName)); break; case ModuleInterface::class: $container->set($type, new Module(new ModuleDispatcherFactory(''), new HelperFactory(''))); break; case PluginInterface::class: list($pluginName, $pluginType) = explode(':', $extensionName); $container->set($type, $this->loadPluginFromFilesystem($pluginName, $pluginType)); } } $container->get(DispatcherInterface::class)->dispatch( 'onAfterExtensionBoot', AbstractEvent::create( 'onAfterExtensionBoot', [ 'subject' => $this, 'type' => $type, 'extensionName' => $extensionName, 'container' => $container, ] ) ); $extension = $container->get($type); if ($extension instanceof BootableExtensionInterface) { $extension->boot($container); } // Cache the extension ExtensionHelper::$extensions[$type][$extensionName] = $extension; return $extension; } /** * Creates a CMS plugin from the filesystem. * * @param string $plugin The plugin * @param string $type The type * * @return CMSPlugin * * @since 4.0.0 */ private function loadPluginFromFilesystem(string $plugin, string $type) { // The dispatcher $dispatcher = $this->getContainer()->get(DispatcherInterface::class); // Clear the names $plugin = preg_replace('/[^A-Z0-9_\.-]/i', '', $plugin); $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); // The path of the plugin $path = JPATH_PLUGINS . '/' . $type . '/' . $plugin . '/' . $plugin . '.php'; // Return an empty class when the file doesn't exist if (!is_file($path)) { return new DummyPlugin($dispatcher); } // Include the file of the plugin require_once $path; // Compile the classname $className = 'Plg' . str_replace('-', '', $type) . $plugin; // Editors don't follow the convention if ($type === 'editors') { $className = 'PlgEditor' . ucfirst($plugin); } // Editor buttons don't follow the convention if ($type === 'editors-xtd') { $className = 'PlgEditorsXtd' . $plugin; if (!class_exists($className)) { $className = 'PlgButton' . $plugin; } } // Return an empty class when the class doesn't exist if (!class_exists($className)) { return new DummyPlugin($dispatcher); } // Instantiate the plugin return new $className($dispatcher, (array) PluginHelper::getPlugin($type, $plugin)); } /** * Get the DI container. * * @return Container * * @since 4.0.0 * @throws ContainerNotFoundException May be thrown if the container has not been set. */ abstract protected function getContainer(); } PK1A#]�]d��2src/Extension/Service/Provider/CategoryFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Categories\CategoryFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service categories. * * @since 4.0.0 */ class CategoryFactory implements ServiceProviderInterface { /** * The namespace to create the categories from. * * @var string * @since 4.0.0 */ private $namespace; /** * The namespace must be like: * Joomla\Component\Content * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct($namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( CategoryFactoryInterface::class, function (Container $container) { $factory = new \Joomla\CMS\Categories\CategoryFactory($this->namespace); $factory->setDatabase($container->get(DatabaseInterface::class)); return $factory; } ); } } PK1A#]�5@ll)src/Extension/Service/Provider/Module.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Dispatcher\ModuleDispatcherFactoryInterface; use Joomla\CMS\Extension\ModuleInterface; use Joomla\CMS\Helper\HelperFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service based modules. * * @since 4.0.0 */ class Module implements ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( ModuleInterface::class, function (Container $container) { return new \Joomla\CMS\Extension\Module( $container->get(ModuleDispatcherFactoryInterface::class), $container->has(HelperFactoryInterface::class) ? $container->get(HelperFactoryInterface::class) : null ); } ); } } PK1A#]6�h/��0src/Extension/Service/Provider/RouterFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Categories\CategoryFactoryInterface; use Joomla\CMS\Component\Router\RouterFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service router factory. * * @since 4.0.0 */ class RouterFactory implements ServiceProviderInterface { /** * The module namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * DispatcherFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( RouterFactoryInterface::class, function (Container $container) { $categoryFactory = null; if ($container->has(CategoryFactoryInterface::class)) { $categoryFactory = $container->get(CategoryFactoryInterface::class); } return new \Joomla\CMS\Component\Router\RouterFactory( $this->namespace, $categoryFactory, $container->get(DatabaseInterface::class) ); } ); } } PK1A#]�����:src/Extension/Service/Provider/ModuleDispatcherFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Dispatcher\ModuleDispatcherFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service dispatcher factory. * * @since 4.0.0 */ class ModuleDispatcherFactory implements ServiceProviderInterface { /** * The module namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * ComponentDispatcherFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( ModuleDispatcherFactoryInterface::class, function (Container $container) { return new \Joomla\CMS\Dispatcher\ModuleDispatcherFactory($this->namespace); } ); } } PK1A#]�Q�hh=src/Extension/Service/Provider/ComponentDispatcherFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service dispatcher factory. * * @since 4.0.0 */ class ComponentDispatcherFactory implements ServiceProviderInterface { /** * The component namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * ComponentDispatcherFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( ComponentDispatcherFactoryInterface::class, function (Container $container) { return new \Joomla\CMS\Dispatcher\ComponentDispatcherFactory($this->namespace, $container->get(MVCFactoryInterface::class)); } ); } } PK1A#]�U�\ \ -src/Extension/Service/Provider/MVCFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Form\FormFactoryInterface; use Joomla\CMS\Mail\MailerFactoryInterface; use Joomla\CMS\MVC\Factory\ApiMVCFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Router\SiteRouter; use Joomla\CMS\User\UserFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service MVC factory. * * @since 4.0.0 */ class MVCFactory implements ServiceProviderInterface { /** * The extension namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * MVCFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( MVCFactoryInterface::class, function (Container $container) { if (\Joomla\CMS\Factory::getApplication()->isClient('api')) { $factory = new ApiMVCFactory($this->namespace); } else { $factory = new \Joomla\CMS\MVC\Factory\MVCFactory($this->namespace); } $factory->setFormFactory($container->get(FormFactoryInterface::class)); $factory->setDispatcher($container->get(DispatcherInterface::class)); $factory->setDatabase($container->get(DatabaseInterface::class)); $factory->setSiteRouter($container->get(SiteRouter::class)); $factory->setCacheControllerFactory($container->get(CacheControllerFactoryInterface::class)); $factory->setUserFactory($container->get(UserFactoryInterface::class)); $factory->setMailerFactory($container->get(MailerFactoryInterface::class)); return $factory; } ); } } PK1A#]|�bSS0src/Extension/Service/Provider/HelperFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension\Service\Provider; use Joomla\CMS\Helper\HelperFactoryInterface; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Service provider for the service helper factory. * * @since 4.0.0 */ class HelperFactory implements ServiceProviderInterface { /** * The namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * HelperFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 4.0.0 */ public function register(Container $container) { $container->set( HelperFactoryInterface::class, function (Container $container) { $factory = new \Joomla\CMS\Helper\HelperFactory($this->namespace); $factory->setDatabase($container->get(DatabaseInterface::class)); return $factory; } ); } } PK1A#]�U5�WW+src/Extension/ExtensionManagerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Extension; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Loads extensions. * * @since 4.0.0 */ interface ExtensionManagerInterface { /** * Boots the component with the given name. * * @param string $component The component to boot. * * @return ComponentInterface * * @since 4.0.0 */ public function bootComponent($component): ComponentInterface; /** * Boots the module with the given name. * * @param string $module The module to boot * @param string $applicationName The application name * * @return ModuleInterface * * @since 4.0.0 */ public function bootModule($module, $applicationName): ModuleInterface; /** * Boots the plugin with the given name and type. * * @param string $plugin The plugin name * @param string $type The type of the plugin * * @return PluginInterface * * @since 4.0.0 */ public function bootPlugin($plugin, $type): PluginInterface; } PK1A#]�p�j��-src/Authentication/AuthenticationResponse.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Authentication response class, provides an object for storing user and error details * * @since 1.7.0 */ class AuthenticationResponse { /** * Response status (see status codes) * * @var string * @since 1.7.0 */ public $status = Authentication::STATUS_FAILURE; /** * The type of authentication that was successful * * @var string * @since 1.7.0 */ public $type = ''; /** * The error message * * @var string * @since 1.7.0 */ public $error_message = ''; /** * Any UTF-8 string that the End User wants to use as a username. * * @var string * @since 1.7.0 */ public $username = ''; /** * Any UTF-8 string that the End User wants to use as a password. * * @var string * @since 1.7.0 */ public $password = ''; /** * The email address of the End User as specified in section 3.4.1 of [RFC2822] * * @var string * @since 1.7.0 */ public $email = ''; /** * UTF-8 string free text representation of the End User's full name. * * @var string * @since 1.7.0 */ public $fullname = ''; /** * The End User's date of birth as YYYY-MM-DD. Any values whose representation uses * fewer than the specified number of digits should be zero-padded. The length of this * value MUST always be 10. If the End User user does not want to reveal any particular * component of this value, it MUST be set to zero. * * For instance, if an End User wants to specify that their date of birth is in 1980, but * not the month or day, the value returned SHALL be "1980-00-00". * * @var string * @since 1.7.0 */ public $birthdate = ''; /** * The End User's gender, "M" for male, "F" for female. * * @var string * @since 1.7.0 */ public $gender = ''; /** * UTF-8 string free text that SHOULD conform to the End User's country's postal system. * * @var string * @since 1.7.0 */ public $postcode = ''; /** * The End User's country of residence as specified by ISO3166. * * @var string * @since 1.7.0 */ public $country = ''; /** * End User's preferred language as specified by ISO639. * * @var string * @since 1.7.0 */ public $language = ''; /** * ASCII string from TimeZone database * * @var string * @since 1.7.0 */ public $timezone = ''; } PK1A#]�Xb���Asrc/Authentication/ProviderAwareAuthenticationPluginInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface class defining the necessary methods for an authentication plugin to be provider aware * Please note: might be deprecated with Joomla 4.2 * * @since 3.10.7 */ interface ProviderAwareAuthenticationPluginInterface { /** * Return if plugin acts as primary provider * * @return true * * @since 3.10.7 */ public static function isPrimaryProvider(); /** * Return provider name * * @return string * * @since 3.10.7 */ public static function getProviderName(); } PK1A#]<�٢��.src/Authentication/Password/Argon2iHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\Argon2iHandler as BaseArgon2iHandler; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler for Argon2i hashed passwords * * @since 4.0.0 */ class Argon2iHandler extends BaseArgon2iHandler implements CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { return password_needs_rehash($hash, PASSWORD_ARGON2I); } } PK1A#]0��kCsrc/Authentication/Password/CheckIfRehashNeededHandlerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a password handler which supports checking if the password requires rehashing * * @since 4.0.0 */ interface CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool; } PK1A#]��r -src/Authentication/Password/PHPassHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\HandlerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler for PHPass hashed passwords * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Support for PHPass hashed passwords will be removed without replacement */ class PHPassHandler implements HandlerInterface, CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { return true; } /** * Generate a hash for a plaintext password * * @param string $plaintext The plaintext password to validate * @param array $options Options for the hashing operation * * @return string * * @since 4.0.0 */ public function hashPassword($plaintext, array $options = []) { return $this->getPasswordHash()->HashPassword($plaintext); } /** * Check that the password handler is supported in this environment * * @return boolean * * @since 4.0.0 */ public static function isSupported() { return class_exists(\PasswordHash::class); } /** * Validate a password * * @param string $plaintext The plain text password to validate * @param string $hashed The password hash to validate against * * @return boolean * * @since 4.0.0 */ public function validatePassword($plaintext, $hashed) { return $this->getPasswordHash()->CheckPassword($plaintext, $hashed); } /** * Get an instance of the PasswordHash class * * @return \PasswordHash * * @since 4.0.0 */ private function getPasswordHash(): \PasswordHash { return new \PasswordHash(10, true); } } PK1A#]��1�.src/Authentication/Password/ChainedHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\HandlerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler supporting testing against a chain of handlers * * @since 4.0.0 */ class ChainedHandler implements HandlerInterface, CheckIfRehashNeededHandlerInterface { /** * The password handlers in use by this chain. * * @var HandlerInterface[] * @since 4.0.0 */ private $handlers = []; /** * Add a handler to the chain * * @param HandlerInterface $handler The password handler to add * * @return void * * @since 4.0.0 */ public function addHandler(HandlerInterface $handler) { $this->handlers[] = $handler; } /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { foreach ($this->handlers as $handler) { if ($handler instanceof CheckIfRehashNeededHandlerInterface && $handler->isSupported() && $handler->checkIfRehashNeeded($hash)) { return true; } } return false; } /** * Generate a hash for a plaintext password * * @param string $plaintext The plaintext password to validate * @param array $options Options for the hashing operation * * @return void * * @since 4.0.0 * @throws \RuntimeException */ public function hashPassword($plaintext, array $options = []) { throw new \RuntimeException('The chained password handler cannot be used to hash a password'); } /** * Check that the password handler is supported in this environment * * @return boolean * * @since 4.0.0 */ public static function isSupported() { return true; } /** * Validate a password * * @param string $plaintext The plain text password to validate * @param string $hashed The password hash to validate against * * @return boolean * * @since 4.0.0 */ public function validatePassword($plaintext, $hashed) { foreach ($this->handlers as $handler) { if ($handler->isSupported() && $handler->validatePassword($plaintext, $hashed)) { return true; } } return false; } } PK1A#]�[!��-src/Authentication/Password/BCryptHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\BCryptHandler as BaseBCryptHandler; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler for BCrypt hashed passwords * * @since 4.0.0 */ class BCryptHandler extends BaseBCryptHandler implements CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { return password_needs_rehash($hash, PASSWORD_BCRYPT); } } PK1A#]�����/src/Authentication/Password/Argon2idHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\Argon2idHandler as BaseArgon2idHandler; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler for Argon2id hashed passwords * * @since 4.0.0 */ class Argon2idHandler extends BaseArgon2idHandler implements CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { return password_needs_rehash($hash, PASSWORD_ARGON2ID); } } PK1A#]EzP�� � *src/Authentication/Password/MD5Handler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication\Password; use Joomla\Authentication\Password\HandlerInterface; use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\User\UserHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Password handler for MD5 hashed passwords * * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Support for MD5 hashed passwords will be removed without replacement */ class MD5Handler implements HandlerInterface, CheckIfRehashNeededHandlerInterface { /** * Check if the password requires rehashing * * @param string $hash The password hash to check * * @return boolean * * @since 4.0.0 */ public function checkIfRehashNeeded(string $hash): bool { return true; } /** * Generate a hash for a plaintext password * * @param string $plaintext The plaintext password to validate * @param array $options Options for the hashing operation * * @return string * * @since 4.0.0 */ public function hashPassword($plaintext, array $options = []) { $salt = UserHelper::genRandomPassword(32); $crypted = md5($plaintext . $salt); return $crypted . ':' . $salt; } /** * Check that the password handler is supported in this environment * * @return boolean * * @since 4.0.0 */ public static function isSupported() { return true; } /** * Validate a password * * @param string $plaintext The plain text password to validate * @param string $hashed The password hash to validate against * * @return boolean * * @since 4.0.0 */ public function validatePassword($plaintext, $hashed) { // Check the password $parts = explode(':', $hashed); $salt = @$parts[1]; // Compile the hash to compare // If the salt is empty AND there is a ':' in the original hash, we must append ':' at the end $testcrypt = md5($plaintext . $salt) . ($salt ? ':' . $salt : (strpos($hashed, ':') !== false ? ':' : '')); return Crypt::timingSafeCompare($hashed, $testcrypt); } } PK1A#] 11��%src/Authentication/Authentication.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Authentication; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Authentication class, provides an interface for the Joomla authentication system * * @since 1.7.0 */ class Authentication { use DispatcherAwareTrait; /** * This is the status code returned when the authentication is success (permit login) * * @var integer * @since 1.7.0 */ public const STATUS_SUCCESS = 1; /** * Status to indicate cancellation of authentication (unused) * * @var integer * @since 1.7.0 */ public const STATUS_CANCEL = 2; /** * This is the status code returned when the authentication failed (prevent login if no success) * * @var integer * @since 1.7.0 */ public const STATUS_FAILURE = 4; /** * This is the status code returned when the account has expired (prevent login) * * @var integer * @since 1.7.0 */ public const STATUS_EXPIRED = 8; /** * This is the status code returned when the account has been denied (prevent login) * * @var integer * @since 1.7.0 */ public const STATUS_DENIED = 16; /** * This is the status code returned when the account doesn't exist (not an error) * * @var integer * @since 1.7.0 */ public const STATUS_UNKNOWN = 32; /** * @var Authentication[] JAuthentication instances container. * @since 1.7.3 */ protected static $instance = []; /** * Plugin Type to run * * @var string * @since 4.0.0 */ protected $pluginType; /** * Constructor * * @param string $pluginType The plugin type to run authorisation and authentication on * @param DispatcherInterface $dispatcher The event dispatcher we're going to use * * @since 1.7.0 */ public function __construct(string $pluginType = 'authentication', DispatcherInterface $dispatcher = null) { // Set the dispatcher if (!\is_object($dispatcher)) { $dispatcher = Factory::getContainer()->get('dispatcher'); } $this->setDispatcher($dispatcher); $this->pluginType = $pluginType; $isLoaded = PluginHelper::importPlugin($this->pluginType); if (!$isLoaded) { Log::add(Text::_('JLIB_USER_ERROR_AUTHENTICATION_LIBRARIES'), Log::WARNING, 'jerror'); } } /** * Returns the global authentication object, only creating it * if it doesn't already exist. * * @param string $pluginType The plugin type to run authorisation and authentication on * * @return Authentication The global Authentication object * * @since 1.7.0 */ public static function getInstance(string $pluginType = 'authentication') { if (empty(self::$instance[$pluginType])) { self::$instance[$pluginType] = new static($pluginType); } return self::$instance[$pluginType]; } /** * Finds out if a set of login credentials are valid by asking all observing * objects to run their respective authentication routines. * * @param array $credentials Array holding the user credentials. * @param array $options Array holding user options. * * @return AuthenticationResponse Response object with status variable filled in for last plugin or first successful plugin. * * @see AuthenticationResponse * @since 1.7.0 */ public function authenticate($credentials, $options = []) { // Get plugins $plugins = PluginHelper::getPlugin($this->pluginType); // Create authentication response $response = new AuthenticationResponse(); /* * Loop through the plugins and check if the credentials can be used to authenticate * the user * * Any errors raised in the plugin should be returned via the AuthenticationResponse * and handled appropriately. */ foreach ($plugins as $plugin) { $plugin = Factory::getApplication()->bootPlugin($plugin->name, $plugin->type); if (!method_exists($plugin, 'onUserAuthenticate')) { // Bail here if the plugin can't be created Log::add(Text::sprintf('JLIB_USER_ERROR_AUTHENTICATION_FAILED_LOAD_PLUGIN', $plugin->name), Log::WARNING, 'jerror'); continue; } // Try to authenticate $plugin->onUserAuthenticate($credentials, $options, $response); // If authentication is successful break out of the loop if ($response->status === self::STATUS_SUCCESS) { if (empty($response->type)) { $response->type = $plugin->_name ?? $plugin->name; } break; } } if (empty($response->username)) { $response->username = $credentials['username']; } if (empty($response->fullname)) { $response->fullname = $credentials['username']; } if (empty($response->password) && isset($credentials['password'])) { $response->password = $credentials['password']; } return $response; } /** * Authorises that a particular user should be able to login * * @param AuthenticationResponse $response response including username of the user to authorise * @param array $options list of options * * @return AuthenticationResponse[] Array of authentication response objects * * @since 1.7.0 * @throws \Exception */ public function authorise($response, $options = []) { // Get plugins in case they haven't been imported already PluginHelper::importPlugin('user'); $results = Factory::getApplication()->triggerEvent('onUserAuthorisation', [$response, $options]); return $results; } } PK1A#]/^.]#]#src/Event/AbstractEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; use Joomla\Event\Event; use Joomla\Event\Event as BaseEvent; use Joomla\String\Normalise; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This class implements the base Event object used system-wide to offer orthogonality. Core objects such as Models, * Controllers, etc create such events on-the-fly and dispatch them through the application's Dispatcher (colloquially * known as the "Joomla! plugin system"). This way a suitable plugin, typically a "system" plugin, can modify the * behaviour of any internal class, providing system-wide services such as tags, content versioning, comments or even * low-level services such as the implementation of created/modified/locked behaviours, record hit counter etc. * * You can create a new Event with something like this: * * $event = AbstractEvent::create('onModelBeforeSomething', $myModel, $arguments); * * You can access the subject object from your event Listener using $event['subject']. It is up to your listener to * determine whether it should apply its functionality against the subject. * * This AbstractEvent class implements a mutable event which is allowed to change its arguments at runtime. This is * generally unadvisable. It's best to use AbstractImmutableEvent instead and constrict all your interaction to the * subject class. * * @since 4.0.0 */ abstract class AbstractEvent extends BaseEvent { use CoreEventAware; /** * Creates a new CMS event object for a given event name and subject. The following arguments must be given: * subject object The subject of the event. This is the core object you are going to manipulate. * eventClass string The Event class name. If you do not provide it Joomla\CMS\Events\<eventNameWithoutOnPrefix> * will be used. * * @param string $eventName The name of the event, e.g. onTableBeforeLoad * @param array $arguments Additional arguments to pass to the event * * @return static * * @since 4.0.0 * @throws \BadMethodCallException If you do not provide a subject argument */ public static function create(string $eventName, array $arguments = []) { // Get the class name from the arguments, if specified $eventClassName = ''; if (isset($arguments['eventClass'])) { $eventClassName = $arguments['eventClass']; unset($arguments['eventClass']); } /** * If the class name isn't set/found determine it from the event name, e.g. TableBeforeLoadEvent from * the onTableBeforeLoad event name. */ if (empty($eventClassName) || !class_exists($eventClassName, true)) { $bareName = strpos($eventName, 'on') === 0 ? substr($eventName, 2) : $eventName; $parts = Normalise::fromCamelCase($bareName, true); $eventClassName = __NAMESPACE__ . '\\' . ucfirst(array_shift($parts)) . '\\'; $eventClassName .= implode('', $parts); $eventClassName .= 'Event'; } // Make sure a non-empty subject argument exists and that it is an object if (!isset($arguments['subject']) || empty($arguments['subject']) || !\is_object($arguments['subject'])) { throw new \BadMethodCallException("No subject given for the $eventName event"); } // Create and return the event object if (class_exists($eventClassName, true)) { return new $eventClassName($eventName, $arguments); } /** * The detection code above failed. This is to be expected, it was written back when we only * had the Table events. It does not address most other core events. So, let's use our * fancier detection instead. */ $eventClassName = self::getEventClassByEventName($eventName); if (!empty($eventClassName) && ($eventClassName !== Event::class)) { return new $eventClassName($eventName, $arguments); } return new GenericEvent($eventName, $arguments); } /** * Constructor. Overridden to go through the argument setters. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 4.0.0 */ public function __construct(string $name, array $arguments = []) { parent::__construct($name, $arguments); $this->arguments = []; foreach ($arguments as $argumentName => $value) { $this->setArgument($argumentName, $value); } } /** * Get an event argument value. * It will use a pre-processing method if one exists. The method has the signature: * * onGet<ArgumentName>($value): mixed * * where: * * $value is the value currently stored in the $arguments array of the event * It returns the value to return to the caller. * * @param string $name The argument name. * @param mixed $default The default value if not found. * * @return mixed The argument value or the default value. * * @since 4.0.0 */ public function getArgument($name, $default = null) { // B/C check for numeric access to named argument, eg $event->getArgument('0'). if (is_numeric($name)) { if (key($this->arguments) != 0) { $argNames = \array_keys($this->arguments); $name = $argNames[$name] ?? ''; } @trigger_error( sprintf( 'Numeric access to named event arguments is deprecated, and will not work in Joomla 6. Event %s argument %s', \get_class($this), $name ), E_USER_DEPRECATED ); } // Look for the method for the value pre-processing/validation $ucfirst = ucfirst($name); $methodName1 = 'onGet' . $ucfirst; $methodName2 = 'get' . $ucfirst; $value = parent::getArgument($name, $default); if (method_exists($this, $methodName1)) { return $this->{$methodName1}($value); } elseif (method_exists($this, $methodName2)) { @trigger_error( sprintf( 'Use method "%s" for value pre-processing is deprecated, and will not work in Joomla 6. Use "%s" instead. Event %s', $methodName2, $methodName1, \get_class($this) ), E_USER_DEPRECATED ); return $this->{$methodName2}($value); } return $value; } /** * Add argument to event. * It will use a pre-processing method if one exists. The method has the signature: * * onSet<ArgumentName>($value): mixed * * where: * * $value is the value being set by the user * It returns the value to return to set in the $arguments array of the event. * * @param string $name Argument name. * @param mixed $value Value. * * @return $this * * @since 4.0.0 */ public function setArgument($name, $value) { // B/C check for numeric access to named argument, eg $event->setArgument('0', $value). if (is_numeric($name)) { if (key($this->arguments) != 0) { $argNames = \array_keys($this->arguments); $name = $argNames[$name] ?? ''; } @trigger_error( sprintf( 'Numeric access to named event arguments is deprecated, and will not work in Joomla 6. Event %s argument %s', \get_class($this), $name ), E_USER_DEPRECATED ); } // Look for the method for the value pre-processing/validation $ucfirst = ucfirst($name); $methodName1 = 'onSet' . $ucfirst; $methodName2 = 'set' . $ucfirst; if (method_exists($this, $methodName1)) { $value = $this->{$methodName1}($value); } elseif (method_exists($this, $methodName2)) { @trigger_error( sprintf( 'Use method "%s" for value pre-processing is deprecated, and will not work in Joomla 6. Use "%s" instead. Event %s', $methodName2, $methodName1, \get_class($this) ), E_USER_DEPRECATED ); $value = $this->{$methodName2}($value); } return parent::setArgument($name, $value); } } PK1A#]U���src/Event/GenericEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This class gives a concrete implementation of the AbstractEvent class. * * @see \Joomla\CMS\Event\AbstractEvent * @since 4.0.0 */ class GenericEvent extends AbstractEvent { } PK1A#]�.�{;;$src/Event/WebAsset/AbstractEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\WebAsset; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for WebAsset events * * @since 4.0.0 */ abstract class AbstractEvent extends AbstractImmutableEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('subject', $arguments)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is required but has not been provided"); } parent::__construct($name, $arguments); } } PK1A#]�M��c c 3src/Event/WebAsset/WebAssetRegistryAssetChanged.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\WebAsset; use Joomla\CMS\WebAsset\WebAssetItemInterface; use Joomla\CMS\WebAsset\WebAssetRegistryInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for WebAssetRegistry "asset changed" events * * @since 4.0.0 */ class WebAssetRegistryAssetChanged extends AbstractEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { parent::__construct($name, $arguments); // Check for required arguments if (!\array_key_exists('asset', $arguments) || !($arguments['asset'] instanceof WebAssetItemInterface)) { throw new \BadMethodCallException("Argument 'asset' of event $name is not of the expected type"); } if (!\array_key_exists('assetType', $arguments) || !is_string($arguments['assetType'])) { throw new \BadMethodCallException("Argument 'assetType' of event $name is not of the expected type"); } if (!\array_key_exists('change', $arguments) || !is_string($arguments['change'])) { throw new \BadMethodCallException("Argument 'change' of event $name is not of the expected type"); } } /** * Setter for the subject argument * * @param WebAssetRegistryInterface $value The value to set * * @return WebAssetRegistryInterface * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.0.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setSubject($value) { if (!$value || !($value instanceof WebAssetRegistryInterface)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is not of the expected type"); } return $value; } /** * Setter for the subject argument * * @param WebAssetRegistryInterface $value The value to set * * @return WebAssetRegistryInterface * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetSubject($value) { return $this->setSubject($value); } /** * Return modified asset * * @return WebAssetItemInterface * * @since 4.0.0 */ public function getAsset(): WebAssetItemInterface { return $this->arguments['asset']; } /** * Return a type of modified asset * * @return string * * @since 4.0.0 */ public function getAssetType(): string { return $this->arguments['assetType']; } /** * Return a type of changes: new, remove, override * * @return string * * @since 4.0.0 */ public function getChange(): string { return $this->arguments['change']; } } PK1A#]z%n�#src/Event/ReshapeArgumentsAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A Trait to reshape arguments maintaining b/c with legacy plugin events. * * Old plugin event handlers expect positional arguments, not named arguments, since they are simple * PHP methods, e.g. * public onExample($foo, $bar, $baz). * Concrete Event classes, however, use named arguments which can be passed in any order. The * following two examples are equivalent: * $event1 = new ConcreteEventClass('onExample', ['foo' => 1, 'bar' => 2, 'baz' => 3]; * $event2 = new ConcreteEventClass('onExample', ['bar' => 2, 'baz' => 3, 'foo' => 1,]; * However, this means that the internal $arguments property of the event object holds the named * arguments in a **different** order in each case. * * When the event handler is aware of the ConcreteEventClass it can retrieve named arguments and * all is good in the world. However, when you have a legacy plugin listener registered through * CMSPlugin::registerLegacyListener you have a major problem! The legacy listener is passing the * arguments **positionally**, in the order they were added to the Event object. * * In the previous example, $event1 would work as expected because the foo, bar, and baz arguments * were given in the same order legacy listeners expected them. On the other hand, $event2 would * fail miserably because the call order would be $bar, $baz, $foo which is NOT what the legacy * listener expected. * * The only way to fix that is to *reshape the argument* in the concrete event's constructor so that * the order of arguments is guaranteed to be the same as expected by legacy listeners. Moreover, * since Joomla is passing all arguments (except the 'result' argument) blindly to the legacy * listener we must ensure that a. all necessary arguments are set and b. any other named arguments * do NOT exist. Otherwise our legacy listeners would receive the wrong number of positional * arguments and break. * * All this is achieved by the reshapeArguments() method in this trait which has to be called in the * constructor of the concrete event class. * * This trait is marked as deprecated with a removal target of 5.0 because in Joomla 5 we will only * be using concrete event classes with named arguments, removing legacy listeners and their * positional arguments headaches. * * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ trait ReshapeArgumentsAware { /** * Reshape the arguments array to preserve b/c with legacy listeners * * @param array $arguments The named arguments array passed to the constructor. * @param array $argumentNames The allowed argument names (mandatory AND optional). * @param array $defaults Default values for optional arguments. * * @return array The reshaped arguments. * * @since 4.2.0 */ protected function reshapeArguments(array $arguments, array $argumentNames, array $defaults = []) { $mandatoryKeys = array_diff($argumentNames, array_keys($defaults)); $currentKeys = array_keys($arguments); $missingKeys = array_diff($mandatoryKeys, $currentKeys); $extraKeys = array_diff($currentKeys, $argumentNames); // Am I missing any mandatory arguments? if ($missingKeys) { throw new \DomainException(sprintf('Missing arguments for ‘%s’ event: %s', $this->getName(), implode(', ', $missingKeys))); } // Do I have unknown arguments? if ($extraKeys) { throw new \DomainException(sprintf('Unknown arguments for ‘%s’ event: %s', $this->getName(), implode(', ', $missingKeys))); } // Reconstruct the arguments in the order specified in $argumentTypes $reconstructed = []; foreach ($argumentNames as $key) { $reconstructed[$key] = $arguments[$key] ?? $defaults[$key]; } // Return the reconstructed arguments array return $reconstructed; } } PK1A#]���ff$src/Event/QuickIcon/GetIconEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\QuickIcon; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\ReshapeArgumentsAware; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeArrayAware; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for the onGetIcon event. * * @since 4.2.0 */ class GetIconEvent extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeArrayAware; use ReshapeArgumentsAware; /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 4.2.0 * @throws \BadMethodCallException */ public function __construct(string $name, array $arguments = []) { $arguments = $this->reshapeArguments($arguments, ['context']); parent::__construct($name, $arguments); } /** * A method to validate the 'context' named parameter. * * @param string $value The calling context for retrieving icons. * * @return string * * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setContext(string $value) { if (empty($value)) { throw new \DomainException(sprintf("Argument 'context' of event %s must be a non-empty string.", $this->name)); } return $value; } /** * A method to validate the 'context' named parameter. * * @param string $value The calling context for retrieving icons. * * @return string * * @since 4.4.0 */ protected function onSetContext(string $value) { return $this->setContext($value); } } PK1A#]����%src/Event/Table/AfterCheckinEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterCheckin event * * @since 4.0.0 */ class AfterCheckinEvent extends BeforeCheckinEvent { } PK1A#]x l�..$src/Event/Table/BeforeStoreEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeStore event * * @since 4.0.0 */ class BeforeStoreEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * updateNulls boolean True to update fields even if they are null. * k mixed Name of the primary key fields in the table (string or array of strings). * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('updateNulls', $arguments)) { throw new \BadMethodCallException("Argument 'updateNulls' is required for event $name"); } if (!\array_key_exists('k', $arguments)) { throw new \BadMethodCallException("Argument 'k' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the updateNulls attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setUpdateNulls($value) { return $value ? true : false; } /** * Setter for the updateNulls attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @since 4.4.0 */ protected function onSetUpdateNulls($value) { return $this->setUpdateNulls($value); } } PK1A#] ��(��%src/Event/Table/ObjectCreateEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onObjectCreate event * * @since 4.0.0 */ class ObjectCreateEvent extends AbstractEvent { } PK1A#]C�p~� � &src/Event/Table/BeforeReorderEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; use Joomla\Database\DatabaseQuery; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeReorder event * * @since 4.0.0 */ class BeforeReorderEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * query DatabaseQuery The query to get the primary keys and ordering values for the selection. * where string WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('query', $arguments)) { throw new \BadMethodCallException("Argument 'query' is required for event $name"); } if (!\array_key_exists('where', $arguments)) { throw new \BadMethodCallException("Argument 'where' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the query argument * * @param DatabaseQuery $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setQuery($value) { if (!($value instanceof DatabaseQuery)) { throw new \BadMethodCallException("Argument 'query' of event {$this->name} must be of DatabaseQuery type"); } return $value; } /** * Setter for the where argument * * @param array|string|null $value A string or array of where conditions. * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setWhere($value) { if (!empty($value) && !\is_string($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'where' of event {$this->name} must be empty or string or array of strings"); } return $value; } /** * Setter for the query argument * * @param DatabaseQuery $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetQuery($value) { return $this->setQuery($value); } /** * Setter for the where argument * * @param array|string|null $value A string or array of where conditions. * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetWhere($value) { return $this->setWhere($value); } } PK1A#]ʉm�zz&src/Event/Table/BeforePublishEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforePublish event * * @since 4.0.0 */ class BeforePublishEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * pks mixed An optional array of primary key values to update. * state int The publishing state. eg. [0 = unpublished, 1 = published] * userId int The user id of the user performing the operation. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('pks', $arguments)) { throw new \BadMethodCallException("Argument 'pks' is required for event $name"); } if (!\array_key_exists('state', $arguments)) { throw new \BadMethodCallException("Argument 'state' is required for event $name"); } if (!\array_key_exists('userId', $arguments)) { throw new \BadMethodCallException("Argument 'userId' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the pks argument * * @param array|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setQuery($value) { if (!empty($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'pks' of event {$this->name} must be empty or an array"); } return $value; } /** * Setter for the state argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setState($value) { if (!is_numeric($value)) { throw new \BadMethodCallException("Argument 'state' of event {$this->name} must be an integer"); } return (int) $value; } /** * Setter for the userId argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setUserId($value) { if (!is_numeric($value)) { throw new \BadMethodCallException("Argument 'userId' of event {$this->name} must be an integer"); } return (int) $value; } /** * Setter for the pks argument * * @param array|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetQuery($value) { return $this->setQuery($value); } /** * Setter for the state argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetState($value) { return $this->setState($value); } /** * Setter for the userId argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetUserId($value) { return $this->setUserId($value); } } PK1A#]�X88"src/Event/Table/AfterLoadEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterLoad event * * @since 4.0.0 */ class AfterLoadEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * result boolean Did the table record load succeed? * row null|array The values loaded from the database, null if it failed * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('result', $arguments)) { throw new \BadMethodCallException("Argument 'result' is required for event $name"); } if (!\array_key_exists('row', $arguments)) { throw new \BadMethodCallException("Argument 'row' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the result argument * * @param boolean $value The value to set * * @return boolean * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setResult($value) { return $value ? true : false; } /** * Setter for the row argument * * @param array|null $value The value to set * * @return array|null * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setRow($value) { if (!\is_null($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'row' of event {$this->name} is not of the expected type"); } return $value; } /** * Setter for the result argument * * @param boolean $value The value to set * * @return boolean * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetResult($value) { return $this->setResult($value); } /** * Setter for the row argument * * @param array|null $value The value to set * * @return array|null * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetRow($value) { return $this->setRow($value); } } PK1A#]�ۡH# # 'src/Event/Table/BeforeCheckoutEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeCheckout event * * @since 4.0.0 */ class BeforeCheckoutEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * userId integer The Id of the user checking out the row. * pk mixed An optional primary key value to check out. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('userId', $arguments)) { throw new \BadMethodCallException("Argument 'userId' is required for event $name"); } if (!\array_key_exists('pk', $arguments)) { throw new \BadMethodCallException("Argument 'pk' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the userId argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setUserId($value) { if (!is_numeric($value) || empty($value)) { throw new \BadMethodCallException("Argument 'userId' of event {$this->name} must be an integer"); } return (int) $value; } /** * Setter for the userId argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetUserId($value) { return $this->setUserId($value); } } PK1A#]8ż��"src/Event/Table/BeforeHitEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeHit event * * @since 4.0.0 */ class BeforeHitEvent extends BeforeCheckinEvent { } PK1A#]&*Ӕ��#src/Event/Table/AfterResetEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterReset event * * @since 4.0.0 */ class AfterResetEvent extends AbstractEvent { } PK1A#]�W��"src/Event/Table/AfterMoveEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; use stdClass; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterMove event * * @since 4.0.0 */ class AfterMoveEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * row stdClass|null The primary keys and ordering value for the selection. * delta int The direction and magnitude to move the row in the ordering sequence. * where string WHERE clause which was used for limiting the selection of rows to compact the ordering values. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('row', $arguments)) { throw new \BadMethodCallException("Argument 'row' is required for event $name"); } if (!\array_key_exists('delta', $arguments)) { throw new \BadMethodCallException("Argument 'delta' is required for event $name"); } if (!\array_key_exists('where', $arguments)) { throw new \BadMethodCallException("Argument 'ignore' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the rows argument * * @param \stdClass|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setRow($value) { if (!($value instanceof \stdClass) && !empty($value)) { throw new \BadMethodCallException("Argument 'row' of event {$this->name} must be an stdClass object or null"); } return $value; } /** * Setter for the delta argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setDelta($value) { if (!is_numeric($value)) { throw new \BadMethodCallException("Argument 'delta' of event {$this->name} must be an integer"); } return (int) $value; } /** * Setter for the where argument * * @param string|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setWhere($value) { if (!empty($value) && !\is_string($value)) { throw new \BadMethodCallException("Argument 'where' of event {$this->name} must be empty or string"); } return $value; } /** * Setter for the rows argument * * @param \stdClass|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetRow($value) { return $this->setRow($value); } /** * Setter for the delta argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetDelta($value) { return $this->setDelta($value); } /** * Setter for the where argument * * @param string|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetWhere($value) { return $this->setWhere($value); } } PK1A#]�l.� #src/Event/Table/BeforeBindEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeBind event * * @since 4.0.0 */ class BeforeBindEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * src mixed An associative array or object to bind to the JTable instance. * ignore mixed An optional array or space separated list of properties to ignore while binding. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('src', $arguments)) { throw new \BadMethodCallException("Argument 'src' is required for event $name"); } if (!\array_key_exists('ignore', $arguments)) { throw new \BadMethodCallException("Argument 'ignore' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the src argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setSrc($value) { if (!empty($value) && !\is_object($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'src' of event {$this->name} must be empty, object or array"); } return $value; } /** * Setter for the ignore argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setIgnore($value) { if (!empty($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'ignore' of event {$this->name} must be empty or array"); } return $value; } /** * Setter for the src argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetSrc($value) { return $this->setSrc($value); } /** * Setter for the ignore argument * * @param mixed $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetIgnore($value) { return $this->setIgnore($value); } } PK1A#]��#src/Event/Table/BeforeLoadEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeLoad event * * @since 4.0.0 */ class BeforeLoadEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * keys mixed The optional primary key value to load the row by, or an array of fields to match. * reset boolean True to reset the default values before loading the new row. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('keys', $arguments)) { throw new \BadMethodCallException("Argument 'keys' is required for event $name"); } if (!\array_key_exists('reset', $arguments)) { throw new \BadMethodCallException("Argument 'reset' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the reset attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setReset($value) { return $value ? true : false; } /** * Setter for the reset attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @since 4.4.0 */ protected function onSetReset($value) { return $this->setReset($value); } } PK1A#]����#src/Event/Table/AfterStoreEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterStore event * * @since 4.0.0 */ class AfterStoreEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * result boolean Did the save succeed? * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('result', $arguments)) { throw new \BadMethodCallException("Argument 'result' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the result argument * * @param boolean $value The value to set * * @return boolean * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setResult($value) { return $value ? true : false; } /** * Setter for the result argument * * @param boolean $value The value to set * * @return boolean * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetResult($value) { return $this->setResult($value); } } PK1A#]Qa��dd$src/Event/Table/AfterDeleteEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterDelete event * * @since 4.0.0 */ class AfterDeleteEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * pk The optional primary key value we deleted. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('pk', $arguments)) { throw new \BadMethodCallException("Argument 'pk' is required for event $name"); } parent::__construct($name, $arguments); } } PK1A#]'�*�#src/Event/Table/BeforeMoveEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; use Joomla\Database\DatabaseQuery; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeMove event * * @since 4.0.0 */ class BeforeMoveEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * query DatabaseQuery The query to get the primary keys and ordering values for the selection. * delta int The direction and magnitude to move the row in the ordering sequence. * where string WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('query', $arguments)) { throw new \BadMethodCallException("Argument 'query' is required for event $name"); } if (!\array_key_exists('delta', $arguments)) { throw new \BadMethodCallException("Argument 'delta' is required for event $name"); } if (!\array_key_exists('where', $arguments)) { throw new \BadMethodCallException("Argument 'where' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the query argument * * @param DatabaseQuery $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setQuery($value) { if (!($value instanceof DatabaseQuery)) { throw new \BadMethodCallException("Argument 'query' of event {$this->name} must be of DatabaseQuery type"); } return $value; } /** * Setter for the delta argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setDelta($value) { if (!is_numeric($value)) { throw new \BadMethodCallException("Argument 'delta' of event {$this->name} must be an integer"); } return (int) $value; } /** * Setter for the where argument * * @param string|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setWhere($value) { if (!empty($value) && !\is_string($value)) { throw new \BadMethodCallException("Argument 'where' of event {$this->name} must be empty or string"); } return $value; } /** * Setter for the query argument * * @param DatabaseQuery $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetQuery($value) { return $this->setQuery($value); } /** * Setter for the delta argument * * @param int $value The value to set * * @return integer * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetDelta($value) { return $this->setDelta($value); } /** * Setter for the where argument * * @param string|null $value The value to set * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetWhere($value) { return $this->setWhere($value); } } PK1A#]XlY���"src/Event/Table/AfterBindEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterBind event * * @since 4.0.0 */ class AfterBindEvent extends BeforeBindEvent { } PK1A#]�m^��$src/Event/Table/BeforeResetEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeReset event * * @since 4.0.0 */ class BeforeResetEvent extends AbstractEvent { } PK1A#]hX�dd%src/Event/Table/BeforeDeleteEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeDelete event * * @since 4.0.0 */ class BeforeDeleteEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * pk An optional primary key value to delete. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('pk', $arguments)) { throw new \BadMethodCallException("Argument 'pk' is required for event $name"); } parent::__construct($name, $arguments); } } PK1A#]��.��src/Event/Table/CheckEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onCheck event * * @since 4.0.0 */ class CheckEvent extends AbstractEvent { } PK1A#]Q@�k��!src/Event/Table/AfterHitEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterHit event * * @since 4.0.0 */ class AfterHitEvent extends BeforeCheckinEvent { } PK1A#]���yy&src/Event/Table/BeforeCheckinEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onBeforeCheckin event * * @since 4.0.0 */ class BeforeCheckinEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * pk mixed An optional primary key value to check out. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('pk', $arguments)) { throw new \BadMethodCallException("Argument 'pk' is required for event $name"); } parent::__construct($name, $arguments); } } PK1A#]L�ʞ��%src/Event/Table/AfterPublishEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterPublish event * * @since 4.0.0 */ class AfterPublishEvent extends BeforePublishEvent { } PK1A#]��- - %src/Event/Table/AfterReorderEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterReorder event * * @since 4.0.0 */ class AfterReorderEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * rows stdClass[]|null The primary keys and ordering values for the selection. * where string WHERE clause which was used for limiting the selection of rows to compact the ordering values. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('where', $arguments)) { throw new \BadMethodCallException("Argument 'ignore' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the where argument * * @param array|string|null $value A string or array of where conditions. * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setWhere($value) { if (!empty($value) && !\is_string($value) && !\is_array($value)) { throw new \BadMethodCallException("Argument 'where' of event {$this->name} must be empty or string or array of strings"); } return $value; } /** * Setter for the where argument * * @param array|string|null $value A string or array of where conditions. * * @return mixed * * @throws \BadMethodCallException if the argument is not of the expected type * * @since 4.4.0 */ protected function onSetWhere($value) { return $this->setWhere($value); } } PK1A#]���PP!src/Event/Table/AbstractEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Table\TableInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for the Table's events * * @since 4.0.0 */ abstract class AbstractEvent extends AbstractImmutableEvent { /** * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 1.0 */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('subject', $arguments)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is required but has not been provided"); } parent::__construct($name, $arguments); } /** * Setter for the subject argument * * @param TableInterface $value The value to set * * @return TableInterface * * @throws \BadMethodCallException If the argument is not of the expected type. * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setSubject($value) { if (!\is_object($value) || !($value instanceof TableInterface)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is not of the expected type"); } return $value; } /** * Setter for the subject argument * * @param TableInterface $value The value to set * * @return TableInterface * * @throws \BadMethodCallException If the argument is not of the expected type. * * @since 4.4.0 */ protected function onSetSubject($value): TableInterface { return $this->setSubject($value); } } PK1A#]��Y���&src/Event/Table/AfterCheckoutEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onAfterCheckout event * * @since 4.0.0 */ class AfterCheckoutEvent extends BeforeCheckoutEvent { } PK1A#]��Ro��#src/Event/Table/SetNewTagsEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Table; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for JTable's onSetNewTags event * * @since 4.0.0 * @todo Only used in JModelAdmin::batchTag since we can't use JTable::save as we don't want the data to be saved. Maybe trigger onBeforeStore? */ class SetNewTagsEvent extends AbstractEvent { /** * Constructor. * * Mandatory arguments: * subject JTableInterface The table we are operating on * newTags int[] New tags to be added to or replace current tags for an item * replaceTags bool Replace tags (true) or add them (false) * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('newTags', $arguments)) { throw new \BadMethodCallException("Argument 'newTags' is required for event $name"); } if (!\array_key_exists('replaceTags', $arguments)) { throw new \BadMethodCallException("Argument 'replaceTags' is required for event $name"); } parent::__construct($name, $arguments); } /** * Setter for the replaceTags attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setReplaceTags($value) { return $value ? true : false; } /** * Setter for the replaceTags attribute * * @param mixed $value The value to set * * @return boolean Normalised value * * @since 4.4.0 */ protected function onSetReplaceTags($value) { return $this->setReplaceTags($value); } } PK1A#]1.��src/Event/ErrorEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; use Joomla\Application\AbstractApplication; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for representing the application's `onError` event * * @since 4.0.0 */ class ErrorEvent extends AbstractEvent { /** * Get the event's application object * * @return AbstractApplication * * @since 4.0.0 */ public function getApplication(): AbstractApplication { return $this->arguments['application']; } /** * Get the event's error object * * @return \Throwable * * @since 4.0.0 */ public function getError(): \Throwable { return $this->getArgument('subject'); } /** * Set the event's error object * * @param \Throwable $error The new error to process * * @return void * * @since 4.0.0 */ public function setError(\Throwable $error) { $this->setArgument('subject', $error); } } PK1A#]�jȡ��.src/Event/Workflow/WorkflowTransitionEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Workflow; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for Workflow Functionality Used events * * @since 4.0.0 */ class WorkflowTransitionEvent extends AbstractEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { $arguments['stopTransition'] = false; parent::__construct($name, $arguments); } /** * Set used parameter to true * * @param bool $value The value to set * * @return void * * @since 4.0.0 */ public function setStopTransition($value = true) { $this->arguments['stopTransition'] = $value; if ($value === true) { $this->stopPropagation(); } } } PK1A#]ثǿ��5src/Event/Workflow/WorkflowFunctionalityUsedEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Workflow; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for Workflow Functionality Used events * * @since 4.0.0 */ class WorkflowFunctionalityUsedEvent extends AbstractEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { $arguments['used'] = false; parent::__construct($name, $arguments); } /** * Set used parameter to true * * @param bool $value The value to set * * @return void * * @since 4.0.0 */ public function setUsed($value = true) { $this->arguments['used'] = $value; if ($value === true) { $this->stopPropagation(); } } } PK1A#]�qE77$src/Event/Workflow/AbstractEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Workflow; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for WebAsset events * * @since 4.0.0 */ abstract class AbstractEvent extends AbstractImmutableEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('subject', $arguments)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is required but has not been provided"); } if (!\array_key_exists('extension', $arguments)) { throw new \BadMethodCallException("Argument 'extension' of event {$this->name} is required but has not been provided"); } if (strpos($arguments['extension'], '.') === false) { throw new \BadMethodCallException("Argument 'extension' of event {$this->name} has wrong format. Valid format: 'component.section'"); } if (!\array_key_exists('extensionName', $arguments) || !\array_key_exists('section', $arguments)) { $parts = \explode('.', $arguments['extension']); $arguments['extensionName'] = $arguments['extensionName'] ?? $parts[0]; $arguments['section'] = $arguments['section'] ?? $parts[1]; } parent::__construct($name, $arguments); } } PK1A#]�Dsrc/Event/CoreEventAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; use Joomla\CMS\Event\Model\BeforeBatchEvent; use Joomla\CMS\Event\Plugin\System\Webauthn\Ajax as PlgSystemWebauthnAjax; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxChallenge as PlgSystemWebauthnAjaxChallenge; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxCreate as PlgSystemWebauthnAjaxCreate; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxDelete as PlgSystemWebauthnAjaxDelete; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxInitCreate as PlgSystemWebauthnAjaxInitCreate; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxLogin as PlgSystemWebauthnAjaxLogin; use Joomla\CMS\Event\Plugin\System\Webauthn\AjaxSaveLabel as PlgSystemWebauthnAjaxSaveLabel; use Joomla\CMS\Event\QuickIcon\GetIconEvent; use Joomla\CMS\Event\Table\AfterBindEvent; use Joomla\CMS\Event\Table\AfterCheckinEvent; use Joomla\CMS\Event\Table\AfterCheckoutEvent; use Joomla\CMS\Event\Table\AfterDeleteEvent; use Joomla\CMS\Event\Table\AfterHitEvent; use Joomla\CMS\Event\Table\AfterLoadEvent; use Joomla\CMS\Event\Table\AfterMoveEvent; use Joomla\CMS\Event\Table\AfterPublishEvent; use Joomla\CMS\Event\Table\AfterReorderEvent; use Joomla\CMS\Event\Table\AfterResetEvent; use Joomla\CMS\Event\Table\AfterStoreEvent; use Joomla\CMS\Event\Table\BeforeBindEvent; use Joomla\CMS\Event\Table\BeforeCheckinEvent; use Joomla\CMS\Event\Table\BeforeCheckoutEvent; use Joomla\CMS\Event\Table\BeforeDeleteEvent; use Joomla\CMS\Event\Table\BeforeHitEvent; use Joomla\CMS\Event\Table\BeforeLoadEvent; use Joomla\CMS\Event\Table\BeforeMoveEvent; use Joomla\CMS\Event\Table\BeforePublishEvent; use Joomla\CMS\Event\Table\BeforeReorderEvent; use Joomla\CMS\Event\Table\BeforeResetEvent; use Joomla\CMS\Event\Table\BeforeStoreEvent; use Joomla\CMS\Event\Table\CheckEvent; use Joomla\CMS\Event\Table\ObjectCreateEvent; use Joomla\CMS\Event\Table\SetNewTagsEvent; use Joomla\CMS\Event\View\DisplayEvent; use Joomla\CMS\Event\Workflow\WorkflowFunctionalityUsedEvent; use Joomla\CMS\Event\Workflow\WorkflowTransitionEvent; use Joomla\Event\Event; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Returns the most suitable event class for a Joomla core event name * * @since 4.2.0 */ trait CoreEventAware { /** * Maps event names to concrete Event classes. * * This is only for events with invariable names. Events with variable names are handled with * PHP logic in the getEventClassByEventName class. * * @var array * @since 4.2.0 */ private static $eventNameToConcreteClass = [ // Model 'onBeforeBatch' => BeforeBatchEvent::class, // Quickicon 'onGetIcon' => GetIconEvent::class, // Table 'onTableAfterBind' => AfterBindEvent::class, 'onTableAfterCheckin' => AfterCheckinEvent::class, 'onTableAfterCheckout' => AfterCheckoutEvent::class, 'onTableAfterDelete' => AfterDeleteEvent::class, 'onTableAfterHit' => AfterHitEvent::class, 'onTableAfterLoad' => AfterLoadEvent::class, 'onTableAfterMove' => AfterMoveEvent::class, 'onTableAfterPublish' => AfterPublishEvent::class, 'onTableAfterReorder' => AfterReorderEvent::class, 'onTableAfterReset' => AfterResetEvent::class, 'onTableAfterStore' => AfterStoreEvent::class, 'onTableBeforeBind' => BeforeBindEvent::class, 'onTableBeforeCheckin' => BeforeCheckinEvent::class, 'onTableBeforeCheckout' => BeforeCheckoutEvent::class, 'onTableBeforeDelete' => BeforeDeleteEvent::class, 'onTableBeforeHit' => BeforeHitEvent::class, 'onTableBeforeLoad' => BeforeLoadEvent::class, 'onTableBeforeMove' => BeforeMoveEvent::class, 'onTableBeforePublish' => BeforePublishEvent::class, 'onTableBeforeReorder' => BeforeReorderEvent::class, 'onTableBeforeReset' => BeforeResetEvent::class, 'onTableBeforeStore' => BeforeStoreEvent::class, 'onTableCheck' => CheckEvent::class, 'onTableObjectCreate' => ObjectCreateEvent::class, 'onTableSetNewTags' => SetNewTagsEvent::class, // View 'onBeforeDisplay' => DisplayEvent::class, 'onAfterDisplay' => DisplayEvent::class, // Workflow 'onWorkflowFunctionalityUsed' => WorkflowFunctionalityUsedEvent::class, 'onWorkflowAfterTransition' => WorkflowTransitionEvent::class, 'onWorkflowBeforeTransition' => WorkflowTransitionEvent::class, // Plugin: System, WebAuthn 'onAjaxWebauthn' => PlgSystemWebauthnAjax::class, 'onAjaxWebauthnChallenge' => PlgSystemWebauthnAjaxChallenge::class, 'onAjaxWebauthnCreate' => PlgSystemWebauthnAjaxCreate::class, 'onAjaxWebauthnDelete' => PlgSystemWebauthnAjaxDelete::class, 'onAjaxWebauthnInitcreate' => PlgSystemWebauthnAjaxInitCreate::class, 'onAjaxWebauthnLogin' => PlgSystemWebauthnAjaxLogin::class, 'onAjaxWebauthnSavelabel' => PlgSystemWebauthnAjaxSaveLabel::class, ]; /** * Get the concrete event class name for the given event name. * * This method falls back to the generic Joomla\Event\Event class if the event name is unknown * to this trait. * * @param string $eventName The event name * * @return string The event class name * @since 4.2.0 */ protected static function getEventClassByEventName(string $eventName): string { return self::$eventNameToConcreteClass[$eventName] ?? Event::class; } } PK1A#]w��/src/Event/Plugin/System/Webauthn/AjaxCreate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeStringAware; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnCreate event * * @since 4.2.0 */ class AjaxCreate extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeStringAware; } PK1A#]S梅 /src/Event/Plugin/System/Webauthn/AjaxDelete.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeBooleanAware; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnDelete event * * @since 4.2.0 */ class AjaxDelete extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeBooleanAware; } PK1A#]�N�A��2src/Event/Plugin/System/Webauthn/AjaxChallenge.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnChallenge event * * @since 4.2.0 */ class AjaxChallenge extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; /** * Make sure the result is valid JSON or boolean false * * @param mixed $data The data to check * * @return void * @since 4.2.0 */ public function typeCheckResult($data): void { if ($data === false) { return; } if (!is_string($data) || @json_decode($data) === null) { throw new \InvalidArgumentException(sprintf('Event %s only accepts JSON results.', $this->getName())); } } } PK1A#]!\�-''.src/Event/Plugin/System/Webauthn/AjaxLogin.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnLogin event * * @since 4.2.0 */ class AjaxLogin extends AbstractImmutableEvent { } PK1A#]��R�)src/Event/Plugin/System/Webauthn/Ajax.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthn event * * @since 4.2.0 */ class Ajax extends AbstractImmutableEvent { } PK1A#]��6�2src/Event/Plugin/System/Webauthn/AjaxSaveLabel.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeBooleanAware; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnSavelabel event * * @since 4.2.0 */ class AjaxSaveLabel extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeBooleanAware; } PK1A#]�˧���3src/Event/Plugin/System/Webauthn/AjaxInitCreate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Plugin\System\Webauthn; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeObjectAware; use Webauthn\PublicKeyCredentialCreationOptions; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the onAjaxWebauthnInitcreate event * * @since 4.2.0 */ class AjaxInitCreate extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeObjectAware; /** * Constructor * * @param string $name Event name * @param array $arguments Event arguments * * @since 4.2.0 */ public function __construct(string $name, array $arguments = []) { parent::__construct($name, $arguments); $this->resultAcceptableClasses = [ \stdClass::class, PublicKeyCredentialCreationOptions::class, ]; } } PK1A#]�{��%src/Event/AfterExtensionBootEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; use Joomla\DI\Container; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for representing the extensions's `onBeforeExtensionBoot` event * * @since 4.0.0 */ class AfterExtensionBootEvent extends AbstractImmutableEvent { /** * Get the event's extension type. Can be: * - component * * @return string * * @since 4.0.0 */ public function getExtensionType(): string { return $this->getArgument('type'); } /** * Get the event's extension name. * * @return string * * @since 4.0.0 */ public function getExtensionName(): string { return $this->arguments['extensionName']; } /** * Get the event's container object * * @return Container * * @since 4.0.0 */ public function getContainer(): Container { return $this->arguments['container']; } } PK1A#]�����+src/Event/Result/ResultTypeIntegerAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of an Integer type. * * @since 4.2.0 */ trait ResultTypeIntegerAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_int($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts Integer results.', $this->getName())); } } } PK1A#]1R�r *src/Event/Result/ResultTypeObjectAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of an object type. * * If you do not set a list of acceptable result classes any PHP object will satisfy this type check. * * @since 4.2.0 */ trait ResultTypeObjectAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Acceptable class names for result values. * * @var array * @since 4.2.0 */ protected $resultAcceptableClasses = []; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_object($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts object results.', $this->getName())); } if (empty($this->resultAcceptableClasses)) { return; } foreach ($this->resultAcceptableClasses as $className) { if (is_a($data, $className)) { return; } } $acceptableTypes = implode(', ', $this->resultAcceptableClasses); $messageTemplate = 'Event %s only accepts object results which are instances of one of %s.'; throw new \InvalidArgumentException(sprintf($messageTemplate, $this->getName(), $acceptableTypes)); } } PK1A#]�%�n�� src/Event/Result/ResultAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; use BadMethodCallException; use Joomla\Event\Event as BaseEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for mutable and immutable events. * * You must additionally implement the typeCheckResult method or use one of the ResultType*Aware * traits in your Event handler. * * @since 4.2.0 */ trait ResultAware { /** * Disallow setting the result argument directly with setArgument() instead of going through addResult(). * * You should set this to true ONLY for event names which did NOT exist before Joomla 4.2.0 * or if you are a third party developer introducing new event names for use only in your software. * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Using setResult() for the result argument will always be disallowed. */ protected $preventSetArgumentResult = false; /** * Appends data to the result array of the event. * * @param mixed $data What to add to the result array. * * @return void * @since 4.2.0 */ public function addResult($data): void { // Ensure this trait is applied to an Event object. if (!($this instanceof BaseEvent)) { throw new \LogicException(sprintf('Event class ‘%s‘ must implement %s.', get_class($this), BaseEvent::class)); } // Ensure the Event object fully implements the ResultAwareInterface. if (!($this instanceof ResultAwareInterface)) { throw new \LogicException(sprintf('Event class ‘%s‘ must implement %s.', get_class($this), ResultAwareInterface::class)); } // Make sure the data type is correct $this->typeCheckResult($data); // Append the result. We use the arguments property directly to allow this to work on immutable events. $this->arguments['result'] = $this->arguments['result'] ?? []; $this->arguments['result'][] = $data; } /** * Handle setting the result argument directly. * * This method serves a dual purpose: backwards compatibility and enforcing the use of addResult. * * When $this->preventSetArgumentResult is false it acts as a backwards compatibility shim for * event handlers expecting generic event classes instead of the concrete Events implemented in * this package. This allows the migration to concrete event classes throughout the lifetime of * Joomla 4.x. * * When $this->preventSetArgumentResult is false (which will always be the case on Joomla 5.0) * it will throw a BadMethodCallException if the developer tries to call setArgument('result', ...) * instead of going through the addResult() method. * * @param array $value The new result array. * * @return array * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ protected function setResult(array $value) { if ($this->preventSetArgumentResult) { throw new \BadMethodCallException('You are not allowed to set the result argument directly. Use addResult() instead.'); } // Always assume that the last element of the array is the result the handler is trying to append. $latestValue = array_pop($value); $this->addResult($latestValue); return $this->arguments['result']; } /** * Handle setting the result argument directly. * * This method serves a dual purpose: backwards compatibility and enforcing the use of addResult. * * When $this->preventSetArgumentResult is false it acts as a backwards compatibility shim for * event handlers expecting generic event classes instead of the concrete Events implemented in * this package. This allows the migration to concrete event classes throughout the lifetime of * Joomla 4.x. * * When $this->preventSetArgumentResult is false (which will always be the case on Joomla 5.0) * it will throw a BadMethodCallException if the developer tries to call setArgument('result', ...) * instead of going through the addResult() method. * * @param array $value The new result array. * * @return array * @since 4.4.0 */ protected function onSetResult(array $value) { return $this->setResult($value); } } PK1A#]J�;��)src/Event/Result/ResultTypeMixedAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of a any type. THIS IS A COP OUT! If you expect a nullable or union type it's best to implement * the typeCheckResult method yourself to check for the exact types you expect. * * @since 4.2.0 */ trait ResultTypeMixedAware { /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { // Intentionally left blank; no type check is performed. } } PK1A#]G����+src/Event/Result/ResultTypeNumericAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of a Numeric type. * * @since 4.2.0 */ trait ResultTypeNumericAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_numeric($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts Numeric results.', $this->getName())); } } } PK1A#]�3�m��)src/Event/Result/ResultTypeFloatAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of a Float type. * * @since 4.2.0 */ trait ResultTypeFloatAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_float($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts Float results.', $this->getName())); } } } PK1A#]o82��*src/Event/Result/ResultTypeStringAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of a String type. * * @since 4.2.0 */ trait ResultTypeStringAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_string($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts String results.', $this->getName())); } } } PK1A#]^U�RR+src/Event/Result/ResultTypeBooleanAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of a Boolean type. * * @since 4.2.0 */ trait ResultTypeBooleanAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if (!is_bool($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts Boolean results.', $this->getName())); } } } PK1A#]�I(���)src/Event/Result/ResultTypeArrayAware.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This Trait partially implements the ResultAwareInterface for type checking. * * Events using this Trait (and the ResultAware trait) will expect event handlers to set results * of an Array type. * * @since 4.2.0 */ trait ResultTypeArrayAware { /** * Can the result attribute values also be NULL? * * @var boolean * @since 4.2.0 */ protected $resultIsNullable = false; /** * Can the result attribute values also be boolean FALSE? * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * You should use nullable values or exceptions instead of returning boolean false results. */ protected $resultIsFalseable = false; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void { if ($this->resultIsNullable && $data === null) { return; } if ($this->resultIsFalseable && $data === false) { return; } if (!is_array($data)) { throw new \InvalidArgumentException(sprintf('Event %s only accepts Array results.', $this->getName())); } } } PK1A#]tX�RAA)src/Event/Result/ResultAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Result; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines an Event which has an append-only array argument named 'result'. * * This is used for Events whose handlers are expected to return something when called, similar to * how many plugin events worked in earlier versions of Joomla. * * This interface is partially implemented by the ResultAware trait. The typeCheckResult method is * implemented by the various ResultType*Aware traits. Your event needs to use both the ResultAware * trait and one of the ResultType*Aware traits. For example, if your event returns boolean results * you need to use the ResultAware and ResultTypeBooleanAware traits in your event. * * @since 4.2.0 */ interface ResultAwareInterface { /** * Appends data to the result array of the event. * * @param mixed $data What to add to the result array. * * @return void * @since 4.2.0 */ public function addResult($data): void; /** * Checks the type of the data being appended to the result argument. * * @param mixed $data The data to type check * * @return void * @throws \InvalidArgumentException * * @internal * @since 4.2.0 */ public function typeCheckResult($data): void; } PK1A#]�lu���$src/Event/Model/BeforeBatchEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\Model; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for modifying a table object before a batch event is applied * * @since 4.0.0 */ class BeforeBatchEvent extends AbstractImmutableEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('src', $arguments)) { throw new \BadMethodCallException("Argument 'src' is required for event $name"); } if (!\array_key_exists('type', $arguments)) { throw new \BadMethodCallException("Argument 'type' is required for event $name"); } parent::__construct($name, $arguments); } } PK1A#]�SU� � $src/Event/AbstractImmutableEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This class implements the immutable base Event object used system-wide to offer orthogonality. * * @see \Joomla\CMS\Event\AbstractEvent * @since 4.0.0 */ class AbstractImmutableEvent extends AbstractEvent { /** * A flag to see if the constructor has been already called. * * @var boolean * @since 4.0.0 */ private $constructed = false; /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 4.0.0 * @throws \BadMethodCallException */ public function __construct(string $name, array $arguments = []) { if ($this->constructed) { throw new \BadMethodCallException( sprintf('Cannot reconstruct the AbstractImmutableEvent %s.', $this->name) ); } $this->constructed = true; parent::__construct($name, $arguments); } /** * Set the value of an event argument. * * @param string $name The argument name. * @param mixed $value The argument value. * * @return void * * @since 4.0.0 * @throws \BadMethodCallException */ public function offsetSet($name, $value) { // B/C check for plugins which use $event['result'] = $result; if ($name === 'result') { parent::offsetSet($name, $value); @trigger_error( 'Setting a result in an immutable event is deprecated, and will not work in Joomla 6. Event ' . $this->getName(), E_USER_DEPRECATED ); return; } throw new \BadMethodCallException( sprintf( 'Cannot set the argument %s of the immutable event %s.', $name, $this->name ) ); } /** * Remove an event argument. * * @param string $name The argument name. * * @return void * * @since 4.0.0 * @throws \BadMethodCallException */ public function offsetUnset($name) { throw new \BadMethodCallException( sprintf( 'Cannot remove the argument %s of the immutable event %s.', $name, $this->name ) ); } } PK1A#]�����src/Event/View/DisplayEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\View; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\MVC\View\ViewInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for WebAsset events * * @since 4.0.0 */ class DisplayEvent extends AbstractImmutableEvent { /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws \BadMethodCallException * * @since 4.0.0 */ public function __construct($name, array $arguments = []) { if (!isset($arguments['subject'])) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is required but has not been provided"); } if (!($arguments['subject'] instanceof ViewInterface)) { throw new \BadMethodCallException("Argument 'subject' of event {$this->name} is not of type 'ViewInterface'"); } if (!isset($arguments['extension'])) { throw new \BadMethodCallException("Argument 'extension' of event {$this->name} is required but has not been provided"); } if (!isset($arguments['extension']) || !is_string($arguments['extension'])) { throw new \BadMethodCallException("Argument 'extension' of event {$this->name} is not of type 'string'"); } if (strpos($arguments['extension'], '.') === false) { throw new \BadMethodCallException("Argument 'extension' of event {$this->name} has wrong format. Valid format: 'component.section'"); } if (!\array_key_exists('extensionName', $arguments) || !\array_key_exists('section', $arguments)) { $parts = explode('.', $arguments['extension']); $arguments['extensionName'] = $arguments['extensionName'] ?? $parts[0]; $arguments['section'] = $arguments['section'] ?? $parts[1]; } parent::__construct($name, $arguments); } } PK1A#] �!ǣ�"src/Event/MultiFactor/GetSetup.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeObjectAware; use Joomla\Component\Users\Administrator\DataShape\SetupRenderOptions; use Joomla\Component\Users\Administrator\Table\MfaTable; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorGetSetup event * * @since 4.2.0 */ class GetSetup extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeObjectAware; /** * Public constructor * * @param MfaTable $record The record to display the setup page for * * @since 4.2.0 */ public function __construct(MfaTable $record) { parent::__construct('onUserMultifactorGetSetup', ['record' => $record]); $this->resultIsNullable = true; $this->resultAcceptableClasses = [ SetupRenderOptions::class, ]; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setRecord(MfaTable $value): MfaTable { if (empty($value)) { throw new \DomainException(sprintf('Argument \'record\' of event %s must be a MfaTable object.', $this->name)); } return $value; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.4.0 */ protected function onSetRecord(MfaTable $value): MfaTable { return $this->setRecord($value); } } PK1A#]�����"src/Event/MultiFactor/Validate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeBooleanAware; use Joomla\CMS\User\User; use Joomla\Component\Users\Administrator\Table\MfaTable; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorValidate event * * @since 4.2.0 */ class Validate extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeBooleanAware; /** * Public constructor * * @param MfaTable $record The MFA record to validate against * @param User $user The user currently logged into the site * @param string $code The MFA code we are validating * * @since 4.2.0 */ public function __construct(MfaTable $record, User $user, string $code) { parent::__construct( 'onUserMultifactorValidate', [ 'record' => $record, 'user' => $user, 'code' => $code, ] ); } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setRecord(MfaTable $value): MfaTable { if (empty($value)) { throw new \DomainException(sprintf('Argument \'record\' of event %s must be a MfaTable object.', $this->name)); } return $value; } /** * Validate the value of the 'user' named parameter * * @param User $value The value to validate * * @return User * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setUser(User $value): User { if (empty($value) || ($value->id <= 0) || ($value->guest == 1)) { throw new \DomainException(sprintf('Argument \'user\' of event %s must be a non-guest User object.', $this->name)); } return $value; } /** * Validate the value of the 'code' named parameter * * @param string|null $value The value to validate * * @return string|null * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setCode(?string $value): ?string { // No validation necessary, the type check in the method options is enough return $value; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.4.0 */ protected function onSetRecord(MfaTable $value): MfaTable { return $this->setRecord($value); } /** * Validate the value of the 'user' named parameter * * @param User $value The value to validate * * @return User * @since 4.4.0 */ protected function onSetUser(User $value): User { return $this->setUser($value); } /** * Validate the value of the 'code' named parameter * * @param string|null $value The value to validate * * @return string|null * @since 4.4.0 */ protected function onSetCode(?string $value): ?string { return $this->setCode($value); } } PK1A#]v-�C��#src/Event/MultiFactor/GetMethod.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeObjectAware; use Joomla\Component\Users\Administrator\DataShape\MethodDescriptor; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorGetMethod event * * @since 4.2.0 */ class GetMethod extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeObjectAware; /** * Public constructor * * @since 4.2.0 */ public function __construct() { parent::__construct('onUserMultifactorGetMethod', []); $this->resultIsNullable = true; $this->resultAcceptableClasses = [ MethodDescriptor::class, ]; } } PK1A#]5N���)src/Event/MultiFactor/NotifyActionLog.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete event class for the custom events used to notify the User Action Log plugin about Two * Factor Authentication actions. * * @since 4.2.0 */ class NotifyActionLog extends AbstractImmutableEvent { private const ACCEPTABLE_EVENTS = [ 'onComUsersCaptiveValidateSuccess', 'onComUsersViewMethodsAfterDisplay', 'onComUsersCaptiveShowCaptive', 'onComUsersCaptiveShowSelect', 'onComUsersCaptiveValidateFailed', 'onComUsersCaptiveValidateInvalidMethod', 'onComUsersCaptiveValidateTryLimitReached', 'onComUsersCaptiveValidateSuccess', 'onComUsersControllerMethodAfterRegenerateBackupCodes', 'onComUsersControllerMethodBeforeAdd', 'onComUsersControllerMethodBeforeDelete', 'onComUsersControllerMethodBeforeEdit', 'onComUsersControllerMethodBeforeSave', 'onComUsersControllerMethodsBeforeDisable', 'onComUsersControllerMethodsBeforeDoNotShowThisAgain', ]; /** * Public constructor * * @param string $name Event name. Must belong in self::ACCEPTABLE_EVENTS * @param array $arguments Event arguments (different for each event). * * @since 4.2.0 */ public function __construct(string $name, array $arguments = []) { if (!in_array($name, self::ACCEPTABLE_EVENTS)) { throw new \InvalidArgumentException(sprintf('The %s event class does not support the %s event name.', __CLASS__, $name)); } parent::__construct($name, $arguments); } } PK1A#])�"��!src/Event/MultiFactor/Captive.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeObjectAware; use Joomla\Component\Users\Administrator\DataShape\CaptiveRenderOptions; use Joomla\Component\Users\Administrator\Table\MfaTable; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorCaptive event * * @since 4.2.0 */ class Captive extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeObjectAware; /** * Public constructor * * @param MfaTable $record The MFA record to use in the captive login page * * @since 4.2.0 */ public function __construct(MfaTable $record) { parent::__construct('onUserMultifactorCaptive', ['record' => $record]); $this->resultIsNullable = true; $this->resultAcceptableClasses = [ CaptiveRenderOptions::class, ]; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setRecord(MfaTable $value): MfaTable { if (empty($value)) { throw new \DomainException(sprintf('Argument \'record\' of event %s must be a MfaTable object.', $this->name)); } return $value; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.4.0 */ protected function onSetRecord(MfaTable $value): MfaTable { return $this->setRecord($value); } } PK1A#]gI�Z��#src/Event/MultiFactor/SaveSetup.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\Event\Result\ResultAwareInterface; use Joomla\CMS\Event\Result\ResultTypeArrayAware; use Joomla\Component\Users\Administrator\Table\MfaTable; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorSaveSetup event * * @since 4.2.0 */ class SaveSetup extends AbstractImmutableEvent implements ResultAwareInterface { use ResultAware; use ResultTypeArrayAware; /** * Public constructor * * @param MfaTable $record The record to save into * @param Input $input The application input object * * @since 4.2.0 */ public function __construct(MfaTable $record, Input $input) { parent::__construct( 'onUserMultifactorSaveSetup', [ 'record' => $record, 'input' => $input, ] ); $this->resultIsNullable = true; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setRecord(MfaTable $value): MfaTable { if (empty($value)) { throw new \DomainException(sprintf('Argument \'record\' of event %s must be a MfaTable object.', $this->name)); } return $value; } /** * Validate the value of the 'record' named parameter * * @param Input $value The value to validate * * @return Input * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setInput(Input $value): Input { if (empty($value)) { throw new \DomainException(sprintf('Argument \'input\' of event %s must be an Input object.', $this->name)); } return $value; } /** * Validate the value of the 'record' named parameter * * @param MfaTable $value The value to validate * * @return MfaTable * @since 4.4.0 */ protected function onSetRecord(MfaTable $value): MfaTable { return $this->setRecord($value); } /** * Validate the value of the 'record' named parameter * * @param Input $value The value to validate * * @return Input * @since 4.4.0 */ protected function onSetInput(Input $value): Input { return $this->setInput($value); } } PK1A#]�~Ls��"src/Event/MultiFactor/Callback.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorCallback event * * @since 4.2.0 */ class Callback extends AbstractImmutableEvent { /** * Public constructor * * @param string $method The MFA method name * * @since 4.2.0 */ public function __construct(string $method) { parent::__construct('onUserMultifactorCallback', ['method' => $method]); } /** * Validate the value of the 'method' named parameter * * @param string|null $value The value to validate * * @return string * @throws \DomainException * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setMethod(string $value): string { if (empty($value)) { throw new \DomainException(sprintf("Argument 'method' of event %s must be a non-empty string.", $this->name)); } return $value; } /** * Validate the value of the 'method' named parameter * * @param string|null $value The value to validate * * @return string * @throws \DomainException * @since 4.4.0 */ protected function onSetMethod(string $value): string { return $this->setMethod($value); } } PK1A#]���((.src/Event/MultiFactor/BeforeDisplayMethods.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event\MultiFactor; use Joomla\CMS\Event\AbstractImmutableEvent; use Joomla\CMS\Event\Result\ResultAware; use Joomla\CMS\User\User; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Concrete Event class for the onUserMultifactorBeforeDisplayMethods event * * @since 4.2.0 */ class BeforeDisplayMethods extends AbstractImmutableEvent { use ResultAware; /** * Public constructor * * @param User $user The user the MFA methods are displayed for * * @since 4.2.0 */ public function __construct(User $user) { parent::__construct('onUserMultifactorBeforeDisplayMethods', ['user' => $user]); } /** * Validate the value of the 'user' named parameter * * @param User $value The value to validate * * @return User * @since 4.2.0 * * @deprecated 4.4.0 will be removed in 6.0 * Use counterpart with onSet prefix */ public function setUser(User $value): User { if (empty($value) || ($value->id <= 0) || ($value->guest == 1)) { throw new \DomainException(sprintf('Argument \'user\' of event %s must be a non-guest User object.', $this->name)); } return $value; } /** * Validate the value of the 'user' named parameter * * @param User $value The value to validate * * @return User * @since 4.4.0 */ protected function onSetUser(User $value): User { return $this->setUser($value); } } PK1A#]�(��&src/Event/BeforeExtensionBootEvent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Event; use Joomla\DI\Container; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event class for representing the extensions's `onBeforeExtensionBoot` event * * @since 4.0.0 */ class BeforeExtensionBootEvent extends AbstractImmutableEvent { /** * Get the event's extension type. Can be: * - component * * @return string * * @since 4.0.0 */ public function getExtensionType(): string { return $this->getArgument('type'); } /** * Get the event's extension name. * * @return string * * @since 4.0.0 */ public function getExtensionName(): string { return $this->arguments['extensionName']; } /** * Get the event's container object * * @return Container * * @since 4.0.0 */ public function getContainer(): Container { return $this->arguments['container']; } } PK1A#]�",�UUsrc/Object/CMSObject.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Object; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform Object Class * * This class allows for simple but smart objects with get and set methods * and an internal error handler. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use \stdClass or \Joomla\Registry\Registry instead. * Example: new \Joomla\Registry\Registry(); */ class CMSObject extends \stdClass { use LegacyErrorHandlingTrait; use LegacyPropertyManagementTrait; /** * Class constructor, overridden in descendant classes. * * @param mixed $properties Either and associative array or another * object to set the initial properties of the object. * * @since 1.7.0 */ public function __construct($properties = null) { if ($properties !== null) { $this->setProperties($properties); } } /** * Magic method to convert the object to a string gracefully. * * @return string The classname. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Classes should provide their own __toString() implementation. */ public function __toString() { return \get_class($this); } } PK1A#],�˧ss'src/Object/LegacyErrorHandlingTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Object; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which contains the legacy methods that formerly were inherited from \Joomla\CMS\Object\CMSObject to set and * get errors in a class. * * @since 4.3.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement * Throw an Exception instead of setError */ trait LegacyErrorHandlingTrait { /** * An array of error messages or Exception objects. * * @var array * @since 1.7.0 * @deprecated 3.1.4 JError has been deprecated */ // phpcs:disable PSR2.Classes.PropertyDeclaration protected $_errors = []; // phpcs:enable PSR2.Classes.PropertyDeclaration /** * Get the most recent error message. * * @param integer $i Option error index. * @param boolean $toString Indicates if Exception objects should return their error message. * * @return string Error message * * @since 1.7.0 * * @deprecated 3.1.4 will be removed in 6.0 * Will be removed without replacement * Catch thrown Exceptions instead of getError */ public function getError($i = null, $toString = true) { // Find the error if ($i === null) { // Default, return the last message $error = end($this->_errors); } elseif (!\array_key_exists($i, $this->_errors)) { // If $i has been specified but does not exist, return false return false; } else { $error = $this->_errors[$i]; } // Check if only the string is requested if ($error instanceof \Exception && $toString) { return $error->getMessage(); } return $error; } /** * Return all errors, if any. * * @return array Array of error messages. * * @since 1.7.0 * * @deprecated 3.1.4 will be removed in 6.0 * Will be removed without replacement * Catch thrown Exceptions instead of getErrors */ public function getErrors() { return $this->_errors; } /** * Add an error message. * * @param string $error Error message. * * @return void * * @since 1.7.0 * * @deprecated 3.1.4 will be removed in 6.0 * Will be removed without replacement * Throw an Exception instead of using setError */ public function setError($error) { $this->_errors[] = $error; } } PK1A#]���,src/Object/LegacyPropertyManagementTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Object; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait which contains the legacy methods that formerly were inherited from \Joomla\CMS\Object\CMSObject to set and * get properties of the current class. * * @since 4.3.0 * * @deprecated 4.3.0 will be removed in 6.0 * Will be removed without replacement * Create proper setter functions for the individual properties or use a \Joomla\Registry\Registry */ trait LegacyPropertyManagementTrait { /** * Sets a default value if not already assigned * * @param string $property The name of the property. * @param mixed $default The default value. * * @return mixed * * @since 1.7.0 * * @deprecated 4.3.0 will be removed in 6.0 * Defining dynamic properties should not be used anymore */ public function def($property, $default = null) { $value = $this->get($property, $default); return $this->set($property, $value); } /** * Returns a property of the object or the default value if the property is not set. * * @param string $property The name of the property. * @param mixed $default The default value. * * @return mixed The value of the property. * * @since 1.7.0 * * @see CMSObject::getProperties() * * @deprecated 4.3.0 will be removed in 6.0 * Create a proper getter function for the property */ public function get($property, $default = null) { if (isset($this->$property)) { return $this->$property; } return $default; } /** * Returns an associative array of object properties. * * @param boolean $public If true, returns only the public properties. * * @return array * * @since 1.7.0 * * @see CMSObject::get() * * @deprecated 4.3.0 will be removed in 6.0 * Create a proper getter function for the property */ public function getProperties($public = true) { $vars = get_object_vars($this); if ($public) { foreach ($vars as $key => $value) { if ('_' == substr($key, 0, 1)) { unset($vars[$key]); } } } return $vars; } /** * Modifies a property of the object, creating it if it does not already exist. * * @param string $property The name of the property. * @param mixed $value The value of the property to set. * * @return mixed Previous value of the property. * * @since 1.7.0 * * @deprecated 4.3.0 will be removed in 6.0 * Create a proper setter function for the property */ public function set($property, $value = null) { $previous = $this->$property ?? null; $this->$property = $value; return $previous; } /** * Set the object properties based on a named array/hash. * * @param mixed $properties Either an associative array or another object. * * @return boolean * * @since 1.7.0 * * @see CMSObject::set() * * @deprecated 4.3.0 will be removed in 6.0 * Create a proper setter function for the property */ public function setProperties($properties) { if (\is_array($properties) || \is_object($properties)) { foreach ((array) $properties as $k => $v) { // Use the set function which might be overridden. $this->set($k, $v); } return true; } return false; } } PK1A#]�N>>src/Helper/LibraryHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\Database\DatabaseInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Library helper class * * @since 3.2 */ class LibraryHelper { /** * The component list cache * * @var array * @since 3.2 */ protected static $libraries = []; /** * Get the library information. * * @param string $element Element of the library in the extensions table. * @param boolean $strict If set and the library does not exist, the enabled attribute will be set to false. * * @return \stdClass An object with the library's information. * * @since 3.2 */ public static function getLibrary($element, $strict = false) { // Is already cached? if (isset(static::$libraries[$element]) || static::loadLibrary($element)) { $result = static::$libraries[$element]; // Convert the params to an object. if (\is_string($result->params)) { $result->params = new Registry($result->params); } } else { $result = new \stdClass(); $result->enabled = $strict ? false : true; $result->params = new Registry(); } return $result; } /** * Checks if a library is enabled * * @param string $element Element of the library in the extensions table. * * @return boolean * * @since 3.2 */ public static function isEnabled($element) { return static::getLibrary($element, true)->enabled; } /** * Gets the parameter object for the library * * @param string $element Element of the library in the extensions table. * @param boolean $strict If set and the library does not exist, false will be returned * * @return Registry A Registry object. * * @see Registry * @since 3.2 */ public static function getParams($element, $strict = false) { return static::getLibrary($element, $strict)->params; } /** * Save the parameters object for the library * * @param string $element Element of the library in the extensions table. * @param Registry $params Params to save * * @return Registry|boolean A Registry object. * * @see Registry * @since 3.2 */ public static function saveParams($element, $params) { if (static::isEnabled($element)) { // Save params in DB $db = Factory::getContainer()->get(DatabaseInterface::class); $paramsString = $params->toString(); $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('params') . ' = :params') ->where($db->quoteName('type') . ' = ' . $db->quote('library')) ->where($db->quoteName('element') . ' = :element') ->bind(':params', $paramsString) ->bind(':element', $element); $db->setQuery($query); $result = $db->execute(); // Update params in libraries cache if ($result && isset(static::$libraries[$element])) { static::$libraries[$element]->params = $params; } return $result; } return false; } /** * Load the installed library into the libraries property. * * @param string $element The element value for the extension * * @return boolean True on success * * @since 3.7.0 */ protected static function loadLibrary($element) { $loader = function ($element) { $db = Factory::getContainer()->get(DatabaseInterface::class); $query = $db->getQuery(true) ->select($db->quoteName(['extension_id', 'element', 'params', 'enabled'], ['id', 'option', null, null])) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('type') . ' = ' . $db->quote('library')) ->where($db->quoteName('element') . ' = :element') ->bind(':element', $element); $db->setQuery($query); return $db->loadObject(); }; /** @var CallbackController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('callback', ['defaultgroup' => '_system']); try { static::$libraries[$element] = $cache->get($loader, [$element], __METHOD__ . $element); } catch (CacheExceptionInterface $e) { static::$libraries[$element] = $loader($element); } if (empty(static::$libraries[$element])) { // Fatal error. $error = Text::_('JLIB_APPLICATION_ERROR_LIBRARY_NOT_FOUND'); Log::add(Text::sprintf('JLIB_APPLICATION_ERROR_LIBRARY_NOT_LOADING', $element, $error), Log::WARNING, 'jerror'); return false; } return true; } } PK1A#]���S��*src/Helper/HelperFactoryAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a helper factory. * * @since 4.2.0 */ interface HelperFactoryAwareInterface { /** * Sets the helper factory to use. * * @param HelperFactory $helper The helper factory to use. * * @return void * * @since 4.2.0 */ public function setHelperFactory(HelperFactory $helper); } PK1A#]�2 {����src/Helper/TagsHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Table\CoreContent; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\UCM\UCMContent; use Joomla\CMS\UCM\UCMType; use Joomla\Database\DatabaseInterface; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Tags helper class, provides methods to perform various tasks relevant * tagging of content. * * @since 3.1 */ class TagsHelper extends CMSHelper { /** * Helper object for storing and deleting tag information. * * @var boolean * @since 3.1 */ protected $tagsChanged = false; /** * Whether up replace all tags or just add tags * * @var boolean * @since 3.1 */ protected $replaceTags = false; /** * Alias for querying mapping and content type table. * * @var string * @since 3.1 */ public $typeAlias; /** * Array of item tags. * * @var array * @since 3.1 */ public $itemTags; /** * The tags as comma separated string or array. * * @var mixed * @since 4.3.0 */ public $tags; /** * The new tags as comma separated string or array. * * @var mixed * @since 4.3.0 */ public $newTags; /** * The old tags as comma separated string or array. * * @var mixed * @since 4.3.0 */ public $oldTags; /** * Method to add tag rows to mapping table. * * @param integer $ucmId ID of the #__ucm_content item being tagged * @param TableInterface $table Table object being tagged * @param array $tags Array of tags to be applied. * * @return boolean true on success, otherwise false. * * @since 3.1 */ public function addTagMapping($ucmId, TableInterface $table, $tags = []) { $db = $table->getDbo(); $key = $table->getKeyName(); $item = $table->$key; $ucm = new UCMType($this->typeAlias, $db); $typeId = $ucm->getTypeId(); // Insert the new tag maps if (strpos(implode(',', $tags), '#') !== false) { $tags = self::createTagsFromField($tags); } // Prevent saving duplicate tags $tags = array_values(array_unique($tags)); if (!$tags) { return true; } $query = $db->getQuery(true); $query->insert('#__contentitem_tag_map'); $query->columns( [ $db->quoteName('core_content_id'), $db->quoteName('content_item_id'), $db->quoteName('tag_id'), $db->quoteName('type_id'), $db->quoteName('type_alias'), $db->quoteName('tag_date'), ] ); foreach ($tags as $tag) { $query->values( implode( ',', array_merge( $query->bindArray([(int) $ucmId, (int) $item, (int) $tag, (int) $typeId]), $query->bindArray([$this->typeAlias], ParameterType::STRING), [$query->currentTimestamp()] ) ) ); } $db->setQuery($query); return (bool) $db->execute(); } /** * Function that converts tags paths into paths of names * * @param array $tags Array of tags * * @return array * * @since 3.1 */ public static function convertPathsToNames($tags) { // We will replace path aliases with tag names if ($tags) { // Create an array with all the aliases of the results $aliases = []; foreach ($tags as $tag) { if (!empty($tag->path)) { if ($pathParts = explode('/', $tag->path)) { $aliases = array_merge($aliases, $pathParts); } } } // Get the aliases titles in one single query and map the results if ($aliases) { // Remove duplicates $aliases = array_values(array_unique($aliases)); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('alias'), $db->quoteName('title'), ] ) ->from($db->quoteName('#__tags')) ->whereIn($db->quoteName('alias'), $aliases, ParameterType::STRING); $db->setQuery($query); try { $aliasesMapper = $db->loadAssocList('alias'); } catch (\RuntimeException $e) { return false; } // Rebuild the items path if ($aliasesMapper) { foreach ($tags as $tag) { $namesPath = []; if (!empty($tag->path)) { if ($pathParts = explode('/', $tag->path)) { foreach ($pathParts as $alias) { if (isset($aliasesMapper[$alias])) { $namesPath[] = $aliasesMapper[$alias]['title']; } else { $namesPath[] = $alias; } } $tag->text = implode('/', $namesPath); } } } } } } return $tags; } /** * Create any new tags by looking for #new# in the strings * * @param array $tags Tags text array from the field * * @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false. * * @since 3.1 */ public function createTagsFromField($tags) { if (empty($tags) || $tags[0] == '') { return; } else { // We will use the tags table to store them $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); $newTags = []; $canCreate = Factory::getUser()->authorise('core.create', 'com_tags'); foreach ($tags as $key => $tag) { // User is not allowed to create tags, so don't create. if (!$canCreate && strpos($tag, '#new#') !== false) { continue; } // Remove the #new# prefix that identifies new tags $tagText = str_replace('#new#', '', $tag); if ($tagText === $tag) { $newTags[] = (int) $tag; } else { // Clear old data if exist $tagTable->reset(); // Try to load the selected tag if ($tagTable->load(['title' => $tagText])) { $newTags[] = (int) $tagTable->id; } else { // Prepare tag data $tagTable->id = 0; $tagTable->title = $tagText; $tagTable->published = 1; $tagTable->description = ''; // $tagTable->language = property_exists ($item, 'language') ? $item->language : '*'; $tagTable->language = '*'; $tagTable->access = 1; // Make this item a child of the root tag $tagTable->setLocation($tagTable->getRootId(), 'last-child'); // Try to store tag if ($tagTable->check()) { // Assign the alias as path (autogenerated tags have always level 1) $tagTable->path = $tagTable->alias; if ($tagTable->store()) { $newTags[] = (int) $tagTable->id; } } } } } // At this point $tags is an array of all tag ids $this->tags = $newTags; $result = $newTags; } return $result; } /** * Method to delete the tag mappings and #__ucm_content record for an item * * @param TableInterface $table Table object of content table where delete occurred * @param integer|array $contentItemId ID of the content item. Or an array of key/value pairs with array key * being a primary key name and value being the content item ID. Note * multiple primary keys are not supported * * @return boolean true on success, false on failure * * @since 3.1 * @throws \InvalidArgumentException */ public function deleteTagData(TableInterface $table, $contentItemId) { $key = $table->getKeyName(); if (!\is_array($contentItemId)) { $contentItemId = [$key => $contentItemId]; } // If we have multiple items for the content item primary key we currently don't support this so // throw an InvalidArgumentException for now if (\count($contentItemId) != 1) { throw new \InvalidArgumentException('Multiple primary keys are not supported as a content item id'); } $result = $this->unTagItem($contentItemId[$key], $table); /** @var CoreContent $ucmContentTable */ $ucmContentTable = Table::getInstance('Corecontent'); return $result && $ucmContentTable->deleteByContentId($contentItemId[$key], $this->typeAlias); } /** * Method to get a list of tags for an item, optionally with the tag data. * * @param string $contentType Content type alias. Dot separated. * @param integer $id Id of the item to retrieve tags for. * @param boolean $getTagData If true, data from the tags table will be included, defaults to true. * * @return array Array of tag objects * * @since 3.1 */ public function getItemTags($contentType, $id, $getTagData = true) { // Cast as integer until method is typehinted. $id = (int) $id; // Initialize some variables. $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('m.tag_id')) ->from($db->quoteName('#__contentitem_tag_map', 'm')) ->where( [ $db->quoteName('m.type_alias') . ' = :contentType', $db->quoteName('m.content_item_id') . ' = :id', $db->quoteName('t.published') . ' = 1', ] ) ->bind(':contentType', $contentType) ->bind(':id', $id, ParameterType::INTEGER); $user = Factory::getUser(); $groups = $user->getAuthorisedViewLevels(); $query->whereIn($db->quoteName('t.access'), $groups); // Optionally filter on language $language = ComponentHelper::getParams('com_tags')->get('tag_list_language_filter', 'all'); if ($language !== 'all') { if ($language === 'current_language') { $language = $this->getCurrentLanguage(); } $query->whereIn($db->quoteName('language'), [$language, '*'], ParameterType::STRING); } if ($getTagData) { $query->select($db->quoteName('t') . '.*'); } $query->join('INNER', $db->quoteName('#__tags', 't'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')); $db->setQuery($query); $this->itemTags = $db->loadObjectList(); return $this->itemTags; } /** * Method to get a list of tags for multiple items, optionally with the tag data. * * @param string $contentType Content type alias. Dot separated. * @param array $ids Id of the item to retrieve tags for. * @param boolean $getTagData If true, data from the tags table will be included, defaults to true. * * @return array Array of tag objects grouped by Id. * * @since 4.2.0 */ public function getMultipleItemTags($contentType, array $ids, $getTagData = true) { $data = []; $ids = array_map('intval', $ids); /** @var DatabaseInterface $db */ $db = Factory::getContainer()->get(DatabaseInterface::class); $query = $db->getQuery(true) ->select($db->quoteName(['m.tag_id', 'm.content_item_id'])) ->from($db->quoteName('#__contentitem_tag_map', 'm')) ->where( [ $db->quoteName('m.type_alias') . ' = :contentType', $db->quoteName('t.published') . ' = 1', ] ) ->whereIn($db->quoteName('m.content_item_id'), $ids) ->bind(':contentType', $contentType); $query->join('INNER', $db->quoteName('#__tags', 't'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')); $groups = Factory::getUser()->getAuthorisedViewLevels(); $query->whereIn($db->quoteName('t.access'), $groups); // Optionally filter on language $language = ComponentHelper::getParams('com_tags')->get('tag_list_language_filter', 'all'); if ($language !== 'all') { if ($language === 'current_language') { $language = $this->getCurrentLanguage(); } $query->whereIn($db->quoteName('language'), [$language, '*'], ParameterType::STRING); } if ($getTagData) { $query->select($db->quoteName('t') . '.*'); } $db->setQuery($query); $rows = $db->loadObjectList(); // Group data by item Id. foreach ($rows as $row) { $data[$row->content_item_id][] = $row; unset($row->content_item_id); } return $data; } /** * Method to get a list of tags for a given item. * Normally used for displaying a list of tags within a layout * * @param mixed $ids The id or array of ids (primary key) of the item to be tagged. * @param string $prefix Dot separated string with the option and view to be used for a url. * * @return string Comma separated list of tag Ids. * * @since 3.1 */ public function getTagIds($ids, $prefix) { if (empty($ids)) { return; } /** * Ids possible formats: * --------------------- * $id = 1; * $id = array(1,2); * $id = array('1,3,4,19'); * $id = '1,3'; */ $ids = (array) $ids; $ids = implode(',', $ids); $ids = explode(',', $ids); $ids = ArrayHelper::toInteger($ids); $db = Factory::getDbo(); // Load the tags. $query = $db->getQuery(true) ->select($db->quoteName('t.id')) ->from($db->quoteName('#__tags', 't')) ->join('INNER', $db->quoteName('#__contentitem_tag_map', 'm'), $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id')) ->where($db->quoteName('m.type_alias') . ' = :prefix') ->whereIn($db->quoteName('m.content_item_id'), $ids) ->bind(':prefix', $prefix); $db->setQuery($query); // Add the tags to the content data. $tagsList = $db->loadColumn(); $this->tags = implode(',', $tagsList); return $this->tags; } /** * Method to get a query to retrieve a detailed list of items for a tag. * * @param mixed $tagId Tag or array of tags to be matched * @param mixed $typesr Null, type or array of type aliases for content types to be included in the results * @param boolean $includeChildren True to include the results from child tags * @param string $orderByOption Column to order the results by * @param string $orderDir Direction to sort the results in * @param boolean $anyOrAll True to include items matching at least one tag, false to include * items all tags in the array. * @param string $languageFilter Optional filter on language. Options are 'all', 'current' or any string. * @param string $stateFilter Optional filtering on publication state, defaults to published or unpublished. * * @return \Joomla\Database\DatabaseQuery Query to retrieve a list of tags * * @since 3.1 */ public function getTagItemsQuery( $tagId, $typesr = null, $includeChildren = false, $orderByOption = 'c.core_title', $orderDir = 'ASC', $anyOrAll = true, $languageFilter = 'all', $stateFilter = '0,1' ) { // Create a new query object. $db = Factory::getDbo(); $query = $db->getQuery(true); $user = Factory::getUser(); $nullDate = $db->getNullDate(); $nowDate = Factory::getDate()->toSql(); // Force ids to array and sanitize $tagIds = (array) $tagId; $tagIds = implode(',', $tagIds); $tagIds = explode(',', $tagIds); $tagIds = ArrayHelper::toInteger($tagIds); $ntagsr = \count($tagIds); // If we want to include children we have to adjust the list of tags. // We do not search child tags when the match all option is selected. if ($includeChildren) { $tagTreeArray = []; foreach ($tagIds as $tag) { $this->getTagTreeArray($tag, $tagTreeArray); } $tagIds = array_values(array_unique(array_merge($tagIds, $tagTreeArray))); } // Sanitize filter states $stateFilters = explode(',', $stateFilter); $stateFilters = ArrayHelper::toInteger($stateFilters); // M is the mapping table. C is the core_content table. Ct is the content_types table. $query->select( [ $db->quoteName('m.type_alias'), $db->quoteName('m.content_item_id'), $db->quoteName('m.core_content_id'), 'COUNT(' . $db->quoteName('m.tag_id') . ') AS ' . $db->quoteName('match_count'), 'MAX(' . $db->quoteName('m.tag_date') . ') AS ' . $db->quoteName('tag_date'), 'MAX(' . $db->quoteName('c.core_title') . ') AS ' . $db->quoteName('core_title'), 'MAX(' . $db->quoteName('c.core_params') . ') AS ' . $db->quoteName('core_params'), 'MAX(' . $db->quoteName('c.core_alias') . ') AS ' . $db->quoteName('core_alias'), 'MAX(' . $db->quoteName('c.core_body') . ') AS ' . $db->quoteName('core_body'), 'MAX(' . $db->quoteName('c.core_state') . ') AS ' . $db->quoteName('core_state'), 'MAX(' . $db->quoteName('c.core_access') . ') AS ' . $db->quoteName('core_access'), 'MAX(' . $db->quoteName('c.core_metadata') . ') AS ' . $db->quoteName('core_metadata'), 'MAX(' . $db->quoteName('c.core_created_user_id') . ') AS ' . $db->quoteName('core_created_user_id'), 'MAX(' . $db->quoteName('c.core_created_by_alias') . ') AS' . $db->quoteName('core_created_by_alias'), 'MAX(' . $db->quoteName('c.core_created_time') . ') AS ' . $db->quoteName('core_created_time'), 'MAX(' . $db->quoteName('c.core_images') . ') AS ' . $db->quoteName('core_images'), 'CASE WHEN ' . $db->quoteName('c.core_modified_time') . ' = :nullDate THEN ' . $db->quoteName('c.core_created_time') . ' ELSE ' . $db->quoteName('c.core_modified_time') . ' END AS ' . $db->quoteName('core_modified_time'), 'MAX(' . $db->quoteName('c.core_language') . ') AS ' . $db->quoteName('core_language'), 'MAX(' . $db->quoteName('c.core_catid') . ') AS ' . $db->quoteName('core_catid'), 'MAX(' . $db->quoteName('c.core_publish_up') . ') AS ' . $db->quoteName('core_publish_up'), 'MAX(' . $db->quoteName('c.core_publish_down') . ') AS ' . $db->quoteName('core_publish_down'), 'MAX(' . $db->quoteName('ct.type_title') . ') AS ' . $db->quoteName('content_type_title'), 'MAX(' . $db->quoteName('ct.router') . ') AS ' . $db->quoteName('router'), 'CASE WHEN ' . $db->quoteName('c.core_created_by_alias') . ' > ' . $db->quote(' ') . ' THEN ' . $db->quoteName('c.core_created_by_alias') . ' ELSE ' . $db->quoteName('ua.name') . ' END AS ' . $db->quoteName('author'), $db->quoteName('ua.email', 'author_email'), ] ) ->bind(':nullDate', $nullDate) ->from($db->quoteName('#__contentitem_tag_map', 'm')) ->join( 'INNER', $db->quoteName('#__ucm_content', 'c'), $db->quoteName('m.type_alias') . ' = ' . $db->quoteName('c.core_type_alias') . ' AND ' . $db->quoteName('m.core_content_id') . ' = ' . $db->quoteName('c.core_content_id') ) ->join('INNER', $db->quoteName('#__content_types', 'ct'), $db->quoteName('ct.type_alias') . ' = ' . $db->quoteName('m.type_alias')); // Join over categories to get only tags from published categories $query->join('LEFT', $db->quoteName('#__categories', 'tc'), $db->quoteName('tc.id') . ' = ' . $db->quoteName('c.core_catid')); // Join over the users for the author and email $query->join('LEFT', $db->quoteName('#__users', 'ua'), $db->quoteName('ua.id') . ' = ' . $db->quoteName('c.core_created_user_id')) ->whereIn($db->quoteName('c.core_state'), $stateFilters) ->whereIn($db->quoteName('m.tag_id'), $tagIds) ->extendWhere( 'AND', [ $db->quoteName('c.core_catid') . ' = 0', $db->quoteName('tc.published') . ' = 1', ], 'OR' ); // Get the type data, limited to types in the request if there are any specified. $typesarray = self::getTypes('assocList', $typesr, false); $typeAliases = \array_column($typesarray, 'type_alias'); $query->whereIn($db->quoteName('m.type_alias'), $typeAliases, ParameterType::STRING); $groups = array_values(array_unique($user->getAuthorisedViewLevels())); $groups[] = 0; $query->whereIn($db->quoteName('c.core_access'), $groups); if (!\in_array(0, $stateFilters, true)) { $query->extendWhere( 'AND', [ $db->quoteName('c.core_publish_up') . ' = :nullDate1', $db->quoteName('c.core_publish_up') . ' IS NULL', $db->quoteName('c.core_publish_up') . ' <= :nowDate1', ], 'OR' ) ->extendWhere( 'AND', [ $db->quoteName('c.core_publish_down') . ' = :nullDate2', $db->quoteName('c.core_publish_down') . ' IS NULL', $db->quoteName('c.core_publish_down') . ' >= :nowDate2', ], 'OR' ) ->bind([':nullDate1', ':nullDate2'], $nullDate) ->bind([':nowDate1', ':nowDate2'], $nowDate); } // Optionally filter on language if ($languageFilter !== 'all') { if ($languageFilter === 'current_language') { $languageFilter = $this->getCurrentLanguage(); } $query->whereIn($db->quoteName('c.core_language'), [$languageFilter, '*'], ParameterType::STRING); } $query->group( [ $db->quoteName('m.type_alias'), $db->quoteName('m.content_item_id'), $db->quoteName('m.core_content_id'), $db->quoteName('core_modified_time'), $db->quoteName('core_created_time'), $db->quoteName('core_created_by_alias'), $db->quoteName('author'), $db->quoteName('author_email'), ] ); // Use HAVING if matching all tags and we are matching more than one tag. if ($ntagsr > 1 && $anyOrAll != 1 && $includeChildren != 1) { // The number of results should equal the number of tags requested. $query->having('COUNT(' . $db->quoteName('m.tag_id') . ') = :ntagsr') ->bind(':ntagsr', $ntagsr, ParameterType::INTEGER); } // Set up the order by using the option chosen if ($orderByOption === 'match_count') { $orderBy = 'COUNT(' . $db->quoteName('m.tag_id') . ')'; } else { $orderBy = 'MAX(' . $db->quoteName($orderByOption) . ')'; } $query->order($orderBy . ' ' . $orderDir); return $query; } /** * Function that converts tag ids to their tag names * * @param array $tagIds Array of integer tag ids. * * @return array An array of tag names. * * @since 3.1 */ public function getTagNames($tagIds) { $tagNames = []; if (\is_array($tagIds) && \count($tagIds) > 0) { $tagIds = ArrayHelper::toInteger($tagIds); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__tags')) ->whereIn($db->quoteName('id'), $tagIds) ->order($db->quoteName('title')); $db->setQuery($query); $tagNames = $db->loadColumn(); } return $tagNames; } /** * Method to get an array of tag ids for the current tag and its children * * @param integer $id An optional ID * @param array &$tagTreeArray Array containing the tag tree * * @return mixed * * @since 3.1 */ public function getTagTreeArray($id, &$tagTreeArray = []) { // Get a level row instance. $table = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); if ($table->isLeaf($id)) { $tagTreeArray[] = $id; return $tagTreeArray; } $tagTree = $table->getTree($id); // Attempt to load the tree if ($tagTree) { foreach ($tagTree as $tag) { $tagTreeArray[] = $tag->id; } return $tagTreeArray; } } /** * Method to get a list of types with associated data. * * @param string $arrayType Optionally specify that the returned list consist of objects, associative arrays, or arrays. * Options are: rowList, assocList, and objectList * @param array $selectTypes Optional array of type ids or aliases to limit the results to. Often from a request. * @param boolean $useAlias If true, the alias is used to match, if false the type_id is used. * * @return array Array of types * * @since 3.1 */ public static function getTypes($arrayType = 'objectList', $selectTypes = null, $useAlias = true) { // Initialize some variables. $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*'); if (!empty($selectTypes)) { $selectTypes = (array) $selectTypes; if ($useAlias) { $query->whereIn($db->quoteName('type_alias'), $selectTypes, ParameterType::STRING); } else { $selectTypes = ArrayHelper::toInteger($selectTypes); $query->whereIn($db->quoteName('type_id'), $selectTypes); } } $query->from($db->quoteName('#__content_types')); $db->setQuery($query); switch ($arrayType) { case 'assocList': $types = $db->loadAssocList(); break; case 'rowList': $types = $db->loadRowList(); break; case 'objectList': default: $types = $db->loadObjectList(); break; } return $types; } /** * Function that handles saving tags used in a table class after a store() * * @param TableInterface $table Table being processed * @param array $newTags Array of new tags * @param boolean $replace Flag indicating if all existing tags should be replaced * * @return boolean * * @since 3.1 */ public function postStoreProcess(TableInterface $table, $newTags = [], $replace = true) { if (!empty($table->newTags) && empty($newTags)) { $newTags = $table->newTags; } // If existing row, check to see if tags have changed. $newTable = clone $table; $newTable->reset(); $result = true; // Process ucm_content and ucm_base if either tags have changed or we have some tags. if ($this->tagsChanged || (!empty($newTags) && $newTags[0] != '')) { if (!$newTags && $replace == true) { // Delete all tags data $key = $table->getKeyName(); $result = $this->deleteTagData($table, $table->$key); } else { // Process the tags $data = $this->getRowData($table); $ucmContentTable = Table::getInstance('Corecontent'); $ucm = new UCMContent($table, $this->typeAlias); $ucmData = $data ? $ucm->mapData($data) : $ucm->ucmData; $primaryId = $ucm->getPrimaryKey($ucmData['common']['core_type_id'], $ucmData['common']['core_content_item_id']); $result = $ucmContentTable->load($primaryId); $result = $result && $ucmContentTable->bind($ucmData['common']); $result = $result && $ucmContentTable->check(); $result = $result && $ucmContentTable->store(); $ucmId = $ucmContentTable->core_content_id; // Store the tag data if the article data was saved and run related methods. $result = $result && $this->tagItem($ucmId, $table, $newTags, $replace); } } return $result; } /** * Function that preProcesses data from a table prior to a store() to ensure proper tag handling * * @param TableInterface $table Table being processed * @param array $newTags Array of new tags * * @return null * * @since 3.1 */ public function preStoreProcess(TableInterface $table, $newTags = []) { if ($newTags != []) { $this->newTags = $newTags; } // If existing row, check to see if tags have changed. $oldTable = clone $table; $oldTable->reset(); $key = $oldTable->getKeyName(); $typeAlias = $this->typeAlias; if ($oldTable->$key && $oldTable->load()) { $this->oldTags = $this->getTagIds($oldTable->$key, $typeAlias); } // New items with no tags bypass this step. if ((!empty($newTags) && \is_string($newTags) || (isset($newTags[0]) && $newTags[0] != '')) || isset($this->oldTags)) { if (\is_array($newTags)) { $newTags = implode(',', $newTags); } // We need to process tags if the tags have changed or if we have a new row $this->tagsChanged = (empty($this->oldTags) && !empty($newTags)) || (!empty($this->oldTags) && $this->oldTags != $newTags) || !$table->$key; } } /** * Function to search tags * * @param array $filters Filter to apply to the search * * @return array * * @since 3.1 */ public static function searchTags($filters = []) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('a.id', 'value'), $db->quoteName('a.path', 'text'), $db->quoteName('a.path'), ] ) ->from($db->quoteName('#__tags', 'a')) ->join( 'LEFT', $db->quoteName('#__tags', 'b'), $db->quoteName('a.lft') . ' > ' . $db->quoteName('b.lft') . ' AND ' . $db->quoteName('a.rgt') . ' < ' . $db->quoteName('b.rgt') ); // Do not return root $query->where($db->quoteName('a.alias') . ' <> ' . $db->quote('root')); // Filter language if (!empty($filters['flanguage'])) { $query->whereIn($db->quoteName('a.language'), [$filters['flanguage'], '*'], ParameterType::STRING); } // Search in title or path if (!empty($filters['like'])) { $search = '%' . $filters['like'] . '%'; $query->extendWhere( 'AND', [ $db->quoteName('a.title') . ' LIKE :search1', $db->quoteName('a.path') . ' LIKE :search2', ], 'OR' ) ->bind([':search1', ':search2'], $search); } // Filter title if (!empty($filters['title'])) { $query->where($db->quoteName('a.title') . ' = :title') ->bind(':title', $filters['title']); } // Filter on the published state if (isset($filters['published']) && is_numeric($filters['published'])) { $published = (int) $filters['published']; $query->where($db->quoteName('a.published') . ' = :published') ->bind(':published', $published, ParameterType::INTEGER); } // Filter on the access level if (isset($filters['access']) && \is_array($filters['access']) && \count($filters['access'])) { $groups = ArrayHelper::toInteger($filters['access']); $query->whereIn($db->quoteName('a.access'), $groups); } // Filter by parent_id if (isset($filters['parent_id']) && is_numeric($filters['parent_id'])) { $tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator'); if ($children = $tagTable->getTree($filters['parent_id'])) { $childrenIds = \array_column($children, 'id'); $query->whereIn($db->quoteName('a.id'), $childrenIds); } } $query->group( [ $db->quoteName('a.id'), $db->quoteName('a.title'), $db->quoteName('a.level'), $db->quoteName('a.lft'), $db->quoteName('a.rgt'), $db->quoteName('a.parent_id'), $db->quoteName('a.published'), $db->quoteName('a.path'), ] ) ->order($db->quoteName('a.lft') . ' ASC'); // Get the options. $db->setQuery($query); try { $results = $db->loadObjectList(); } catch (\RuntimeException $e) { return []; } // We will replace path aliases with tag names return self::convertPathsToNames($results); } /** * Method to delete all instances of a tag from the mapping table. Generally used when a tag is deleted. * * @param integer $tagId The tag_id (primary key) for the deleted tag. * * @return void * * @since 3.1 */ public function tagDeleteInstances($tagId) { // Cast as integer until method is typehinted. $tag_id = (int) $tagId; // Delete the old tag maps. $db = Factory::getDbo(); $query = $db->getQuery(true) ->delete($db->quoteName('#__contentitem_tag_map')) ->where($db->quoteName('tag_id') . ' = :id') ->bind(':id', $tagId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); } /** * Method to add or update tags associated with an item. * * @param integer $ucmId Id of the #__ucm_content item being tagged * @param TableInterface $table Table object being tagged * @param array $tags Array of tags to be applied. * @param boolean $replace Flag indicating if all existing tags should be replaced * * @return boolean true on success, otherwise false. * * @since 3.1 */ public function tagItem($ucmId, TableInterface $table, $tags = [], $replace = true) { $key = $table->getKeyName(); $oldTags = $this->getTagIds((int) $table->$key, $this->typeAlias); $oldTags = explode(',', $oldTags); $result = $this->unTagItem($ucmId, $table); if ($replace) { $newTags = $tags; } else { if ($tags == []) { $newTags = $table->newTags; } else { $newTags = $tags; } if ($oldTags[0] != '') { $newTags = array_unique(array_merge($newTags, $oldTags)); } } if (\is_array($newTags) && \count($newTags) > 0 && $newTags[0] != '') { $result = $result && $this->addTagMapping($ucmId, $table, $newTags); } return $result; } /** * Method to untag an item * * @param integer $contentId ID of the content item being untagged * @param TableInterface $table Table object being untagged * @param array $tags Array of tags to be untagged. Use an empty array to untag all existing tags. * * @return boolean true on success, otherwise false. * * @since 3.1 */ public function unTagItem($contentId, TableInterface $table, $tags = []) { $key = $table->getKeyName(); $id = (int) $table->$key; $db = Factory::getDbo(); $query = $db->getQuery(true) ->delete($db->quoteName('#__contentitem_tag_map')) ->where( [ $db->quoteName('type_alias') . ' = :type', $db->quoteName('content_item_id') . ' = :id', ] ) ->bind(':type', $this->typeAlias) ->bind(':id', $id, ParameterType::INTEGER); if (\is_array($tags) && \count($tags) > 0) { $tags = ArrayHelper::toInteger($tags); $query->whereIn($db->quoteName('tag_id'), $tags); } $db->setQuery($query); return (bool) $db->execute(); } /** * Function that converts tag ids to their tag id and tag names * * @param array $tagIds Array of integer tag ids. * * @return array An array of tag id and name. * * @since 4.4.0 */ public function getTags($tagIds) { $tagNames = []; if (\is_array($tagIds) && \count($tagIds) > 0) { $tagIds = ArrayHelper::toInteger($tagIds); $db = Factory::getDbo(); $query = $db->getQuery(true) ->select([$db->quoteName('id'), $db->quoteName('title')]) ->from($db->quoteName('#__tags')) ->whereIn($db->quoteName('id'), $tagIds) ->order($db->quoteName('title')); $db->setQuery($query); $tagNames = $db->loadAssocList('id', 'title'); } return $tagNames; } } PK1A#]������src/Helper/UserGroupsHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Factory; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Helper to deal with user groups. * * @since 3.6.3 */ final class UserGroupsHelper { /** * Indicates the current helper instance is the singleton instance. * * @var integer * @since 3.6.3 */ public const MODE_SINGLETON = 1; /** * Indicates the current helper instance is a standalone class instance. * * @var integer * @since 3.6.3 */ public const MODE_INSTANCE = 2; /** * Singleton instance. * * @var UserGroupsHelper * @since 3.6.3 */ private static $instance; /** * Available user groups * * @var array * @since 3.6.3 */ private $groups = []; /** * Mode this class is working: singleton or std instance * * @var integer * @since 3.6.3 */ private $mode; /** * Total available groups * * @var integer * @since 3.6.3 */ private $total; /** * Constructor * * @param array $groups Array of groups * @param integer $mode Working mode for this class * * @since 3.6.3 */ public function __construct(array $groups = [], $mode = self::MODE_INSTANCE) { $this->mode = (int) $mode; if ($groups) { $this->setGroups($groups); } } /** * Count loaded user groups. * * @return integer * * @since 3.6.3 */ public function count() { return \count($this->groups); } /** * Get the helper instance. * * @return self * * @since 3.6.3 */ public static function getInstance() { if (static::$instance === null) { // Only here to avoid code style issues... $groups = []; static::$instance = new static($groups, static::MODE_SINGLETON); } return static::$instance; } /** * Get a user group by its id. * * @param integer $id Group identifier * * @return mixed stdClass on success. False otherwise * * @since 3.6.3 */ public function get($id) { if ($this->has($id)) { return $this->groups[$id]; } // Singleton will load groups as they are requested if ($this->isSingleton()) { $this->groups[$id] = $this->load($id); return $this->groups[$id]; } return false; } /** * Get the list of existing user groups. * * @return array * * @since 3.6.3 */ public function getAll() { if ($this->isSingleton() && $this->total() !== $this->count()) { $this->loadAll(); } return $this->groups; } /** * Check if a group is in the list. * * @param integer $id Group identifier * * @return boolean * * @since 3.6.3 */ public function has($id) { return (\array_key_exists($id, $this->groups) && $this->groups[$id] !== false); } /** * Check if this instance is a singleton. * * @return boolean * * @since 3.6.3 */ private function isSingleton() { return $this->mode === static::MODE_SINGLETON; } /** * Get total available user groups in database. * * @return integer * * @since 3.6.3 */ public function total() { if ($this->total === null) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('COUNT(' . $db->quoteName('id') . ')') ->from($db->quoteName('#__usergroups')); $db->setQuery($query); $this->total = (int) $db->loadResult(); } return $this->total; } /** * Load a group from database. * * @param integer $id Group identifier * * @return mixed * * @since 3.6.3 */ public function load($id) { // Cast as integer until method is typehinted. $id = (int) $id; $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :id') ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); $group = $db->loadObject(); if (!$group) { return false; } return $this->populateGroupData($group); } /** * Load all user groups from the database. * * @return self * * @since 3.6.3 */ public function loadAll() { $this->groups = []; $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__usergroups')) ->order($db->quoteName('lft') . ' ASC'); $db->setQuery($query); $groups = $db->loadObjectList('id'); $this->groups = $groups ?: []; $this->populateGroupsData(); return $this; } /** * Populates extra information for groups. * * @return array * * @since 3.6.3 */ private function populateGroupsData() { foreach ($this->groups as $group) { $this->populateGroupData($group); } return $this->groups; } /** * Populate data for a specific user group. * * @param \stdClass $group Group * * @return \stdClass * * @since 3.6.3 */ public function populateGroupData($group) { if (!$group || property_exists($group, 'path')) { return $group; } $parentId = (int) $group->parent_id; if ($parentId === 0) { $group->path = [$group->id]; $group->level = 0; return $group; } $parentGroup = $this->has($parentId) ? $this->get($parentId) : $this->load($parentId); if (!property_exists($parentGroup, 'path')) { $parentGroup = $this->populateGroupData($parentGroup); } $group->path = array_merge($parentGroup->path, [$group->id]); $group->level = \count($group->path) - 1; return $group; } /** * Set the groups to be used as source. * * @param array $groups Array of user groups. * * @return self * * @since 3.6.3 */ public function setGroups(array $groups) { $this->groups = $groups; $this->populateGroupsData(); $this->total = \count($groups); return $this; } } PK1A#] ��%""src/Helper/RouteHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Categories\Categories; use Joomla\CMS\Categories\CategoryNode; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Multilanguage; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Route Helper * * A class providing basic routing for urls that are for content types found in * the #__content_types table and rows found in the #__ucm_content table. * * @since 3.1 */ class RouteHelper { /** * @var array Holds the reverse lookup * @since 3.1 */ protected static $lookup; /** * @var string Option for the extension (such as com_content) * @since 3.1 */ protected $extension; /** * @var string Value of the primary key in the content type table * @since 3.1 */ protected $id; /** * @var string Name of the view for the url * @since 3.1 */ protected $view; /** * A method to get the route for a specific item * * @param integer $id Value of the primary key for the item in its content table * @param string $typealias The type_alias for the item being routed. Of the form extension.view. * @param string $link The link to be routed * @param string $language The language of the content for multilingual sites * @param integer $catid Optional category id * * @return string The route of the item * * @since 3.1 */ public function getRoute($id, $typealias, $link = '', $language = null, $catid = null) { $typeExploded = explode('.', $typealias); if (isset($typeExploded[1])) { $this->view = $typeExploded[1]; $this->extension = $typeExploded[0]; } else { $this->view = Factory::getApplication()->getInput()->getString('view'); $this->extension = Factory::getApplication()->getInput()->getCmd('option'); } $name = ucfirst(substr_replace($this->extension, '', 0, 4)); $needles = []; if (isset($this->view)) { $needles[$this->view] = [(int) $id]; } if (empty($link)) { // Create the link $link = 'index.php?option=' . $this->extension . '&view=' . $this->view . '&id=' . $id; } if ($catid > 1) { $categories = Categories::getInstance($name); if ($categories) { $category = $categories->get((int) $catid); if ($category) { $needles['category'] = array_reverse($category->getPath()); $needles['categories'] = $needles['category']; $link .= '&catid=' . $catid; } } } // Deal with languages only if needed if (!empty($language) && $language !== '*' && Multilanguage::isEnabled()) { $link .= '&lang=' . $language; $needles['language'] = $language; } if ($item = $this->findItem($needles)) { $link .= '&Itemid=' . $item; } return $link; } /** * Method to find the item in the menu structure * * @param array $needles Array of lookup values * * @return mixed * * @since 3.1 */ protected function findItem($needles = []) { $app = Factory::getApplication(); $menus = $app->getMenu('site'); $language = $needles['language'] ?? '*'; // $this->extension may not be set if coming from a static method, check it if ($this->extension === null) { $this->extension = $app->getInput()->getCmd('option'); } // Prepare the reverse lookup array. if (!isset(static::$lookup[$language])) { static::$lookup[$language] = []; $component = ComponentHelper::getComponent($this->extension); $attributes = ['component_id']; $values = [$component->id]; if ($language !== '*') { $attributes[] = 'language'; $values[] = [$needles['language'], '*']; } $items = $menus->getItems($attributes, $values); foreach ($items as $item) { if (isset($item->query) && isset($item->query['view'])) { $view = $item->query['view']; if (!isset(static::$lookup[$language][$view])) { static::$lookup[$language][$view] = []; } if (isset($item->query['id'])) { if (\is_array($item->query['id'])) { $item->query['id'] = $item->query['id'][0]; } /* * Here it will become a bit tricky * $language != * can override existing entries * $language == * cannot override existing entries */ if ($item->language !== '*' || !isset(static::$lookup[$language][$view][$item->query['id']])) { static::$lookup[$language][$view][$item->query['id']] = $item->id; } } } } } if ($needles) { foreach ($needles as $view => $ids) { if (isset(static::$lookup[$language][$view])) { foreach ($ids as $id) { if (isset(static::$lookup[$language][$view][(int) $id])) { return static::$lookup[$language][$view][(int) $id]; } } } } } $active = $menus->getActive(); if ($active && $active->component === $this->extension && ($active->language === '*' || !Multilanguage::isEnabled())) { return $active->id; } // If not found, return language specific home link $default = $menus->getDefault($language); return !empty($default->id) ? $default->id : null; } /** * Fetches the category route * * @param mixed $catid Category ID or CategoryNode instance * @param mixed $language Language code * @param string $extension Extension to lookup * * @return string * * @since 3.2 * * @throws \InvalidArgumentException */ public static function getCategoryRoute($catid, $language = 0, $extension = '') { // Note: $extension is required but has to be an optional argument in the function call due to argument order if (empty($extension)) { throw new \InvalidArgumentException(sprintf('$extension is a required argument in %s()', __METHOD__)); } if ($catid instanceof CategoryNode) { $id = $catid->id; $category = $catid; } else { $extensionName = ucfirst(substr($extension, 4)); $id = (int) $catid; $category = Categories::getInstance($extensionName)->get($id); } if ($id < 1) { $link = ''; } else { $link = 'index.php?option=' . $extension . '&view=category&id=' . $id; $needles = [ 'category' => [$id], ]; if ($language && $language !== '*' && Multilanguage::isEnabled()) { $link .= '&lang=' . $language; $needles['language'] = $language; } // Create the link if ($category) { $catids = array_reverse($category->getPath()); $needles['category'] = $catids; $needles['categories'] = $catids; } if ($item = static::lookupItem($needles)) { $link .= '&Itemid=' . $item; } } return $link; } /** * Static alias to findItem() used to find the item in the menu structure * * @param array $needles Array of lookup values * * @return mixed * * @since 3.2 */ protected static function lookupItem($needles = []) { $instance = new static(); return $instance->findItem($needles); } } PK1A#]žȀTTsrc/Helper/HelperFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Namespace based implementation of the HelperFactoryInterface * * @since 4.0.0 */ class HelperFactory implements HelperFactoryInterface { use DatabaseAwareTrait; /** * The extension namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * HelperFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Returns a helper instance for the given name. * * @param string $name The name * @param array $config The config * * @return \stdClass * * @since 4.0.0 */ public function getHelper(string $name, array $config = []) { $className = '\\' . trim($this->namespace, '\\') . '\\' . $name; if (!class_exists($className)) { return null; } $helper = new $className($config); if ($helper instanceof DatabaseAwareInterface) { $helper->setDatabase($this->getDatabase()); } return $helper; } } PK1A#]�HC�)F)Fsrc/Helper/MediaHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use enshrined\svgSanitize\Sanitizer; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Media helper class * * @since 3.2 */ class MediaHelper { /** * A special list of blocked executable extensions, skipping executables that are * typically executable in the webserver context as those are fetched from * Joomla\CMS\Filter\InputFilter * * @var string[] * @since 4.0.0 */ public const EXECUTABLES = [ 'js', 'exe', 'dll', 'go', 'ade', 'adp', 'bat', 'chm', 'cmd', 'com', 'cpl', 'hta', 'ins', 'isp', 'jse', 'lib', 'mde', 'msc', 'msp', 'mst', 'pif', 'scr', 'sct', 'shb', 'sys', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh', 'html', 'htm', 'msi', ]; /** * Checks if the file is an image * * @param string $fileName The filename * * @return boolean * * @since 3.2 */ public static function isImage($fileName) { static $imageTypes = 'xcf|odg|gif|jpg|jpeg|png|bmp|webp'; return preg_match("/\.(?:$imageTypes)$/i", $fileName); } /** * Gets the file extension for purposed of using an icon * * @param string $fileName The filename * * @return string File extension to determine icon * * @since 3.2 */ public static function getTypeIcon($fileName) { return strtolower(substr($fileName, strrpos($fileName, '.') + 1)); } /** * Get the Mime type * * @param string $file The link to the file to be checked * @param boolean $isImage True if the passed file is an image else false * * @return mixed the mime type detected false on error * * @since 3.7.2 */ public static function getMimeType($file, $isImage = false) { // If we can't detect anything mime is false $mime = false; try { if ($isImage && \function_exists('exif_imagetype')) { $mime = image_type_to_mime_type(exif_imagetype($file)); } elseif ($isImage && \function_exists('getimagesize')) { $imagesize = getimagesize($file); $mime = $imagesize['mime'] ?? false; } elseif (\function_exists('mime_content_type')) { // We have mime magic. $mime = mime_content_type($file); } elseif (\function_exists('finfo_open')) { // We have fileinfo $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file); finfo_close($finfo); } } catch (\Exception $e) { // If we have any kind of error here => false; return false; } // If we can't detect the mime try it again if ($mime === 'application/octet-stream' && $isImage === true) { $mime = static::getMimeType($file, false); } if ( ($mime === 'application/octet-stream' || $mime === 'image/svg' || $mime === 'image/svg+xml') && !$isImage && strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'svg' && self::isValidSvg($file, false) ) { return 'image/svg+xml'; } // We have a mime here return $mime; } /** * Checks the Mime type * * @param string $mime The mime to be checked * @param string $component The optional name for the component storing the parameters * * @return boolean true if mime type checking is disabled or it passes the checks else false * * @since 3.7 */ private function checkMimeType($mime, $component = 'com_media'): bool { $params = ComponentHelper::getParams($component); if ($params->get('check_mime', 1)) { $allowedMime = $params->get( 'upload_mime', 'image/jpeg,image/gif,image/png,image/bmp,image/webp,application/msword,application/excel,' . 'application/pdf,application/powerpoint,text/plain,application/x-zip' ); // Get the mime type configuration $allowedMime = array_map('trim', explode(',', str_replace('\\', '', $allowedMime))); // Mime should be available and in the allowed list return !empty($mime) && \in_array($mime, $allowedMime); } // We don't check mime at all or it passes the checks return true; } /** * Checks the file extension * * @param string $extension The extension to be checked * @param string $component The optional name for the component storing the parameters * * @return boolean true if it passes the checks else false * * @since 4.0.0 */ public static function checkFileExtension($extension, $component = 'com_media', $allowedExecutables = []): bool { $params = ComponentHelper::getParams($component); // Media file names should never have executable extensions buried in them. $executables = array_merge(self::EXECUTABLES, InputFilter::FORBIDDEN_FILE_EXTENSIONS); // Remove allowed executables from array if (count($allowedExecutables)) { $executables = array_diff($executables, $allowedExecutables); } if (in_array($extension, $executables, true)) { return false; } $allowable = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,ppt,txt,xcf,xls,csv'))); $ignored = array_map('trim', explode(',', $params->get('ignore_extensions', ''))); if ($extension == '' || $extension == false || (!\in_array($extension, $allowable, true) && !\in_array($extension, $ignored, true))) { return false; } // We don't check mime at all or it passes the checks return true; } /** * Checks if the file can be uploaded * * @param array $file File information * @param string $component The option name for the component storing the parameters * @param string[] $allowedExecutables Array of executable file types that shall be whitelisted * * @return boolean * * @since 3.2 */ public function canUpload($file, $component = 'com_media', $allowedExecutables = []) { $app = Factory::getApplication(); $params = ComponentHelper::getParams($component); if (empty($file['name'])) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'), 'error'); return false; } if ($file['name'] !== File::makeSafe($file['name'])) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILENAME'), 'error'); return false; } $filetypes = explode('.', $file['name']); if (\count($filetypes) < 2) { // There seems to be no extension $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); return false; } array_shift($filetypes); // Media file names should never have executable extensions buried in them. $executables = array_merge(self::EXECUTABLES, InputFilter::FORBIDDEN_FILE_EXTENSIONS); // Remove allowed executables from array if (count($allowedExecutables)) { $executables = array_diff($executables, $allowedExecutables); } // Ensure lowercase extension $filetypes = array_map('strtolower', $filetypes); $check = array_intersect($filetypes, $executables); if (!empty($check)) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); return false; } $filetype = array_pop($filetypes); $allowable = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv'))); $ignored = array_map('trim', explode(',', $params->get('ignore_extensions', ''))); if ($filetype == '' || $filetype == false || (!\in_array($filetype, $allowable) && !\in_array($filetype, $ignored))) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); return false; } $maxSize = (int) ($params->get('upload_maxsize', 0) * 1024 * 1024); if ($maxSize > 0 && (int) $file['size'] > $maxSize) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'), 'error'); return false; } if ($params->get('restrict_uploads', 1)) { $allowedExtensions = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv'))); if (\in_array($filetype, $allowedExtensions)) { // If tmp_name is empty, then the file was bigger than the PHP limit if (!empty($file['tmp_name'])) { // Get the mime type this is an image file $mime = static::getMimeType($file['tmp_name'], static::isImage($file['tmp_name'])); // Did we get anything useful? if ($mime != false) { $result = $this->checkMimeType($mime, $component); // If the mime type is not allowed we don't upload it and show the mime code error to the user if ($result === false) { $app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE', $mime), 'error'); return false; } } else { // We can't detect the mime type so it looks like an invalid image $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_IMG'), 'error'); return false; } } else { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'), 'error'); return false; } } elseif (!\in_array($filetype, $ignored)) { // Get the mime type this is not an image file $mime = static::getMimeType($file['tmp_name'], false); // Did we get anything useful? if ($mime != false) { $result = $this->checkMimeType($mime, $component); // If the mime type is not allowed we don't upload it and show the mime code error to the user if ($result === false) { $app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE', $mime), 'error'); return false; } } else { // We can't detect the mime type so it looks like an invalid file $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_MIME'), 'error'); return false; } if (!Factory::getUser()->authorise('core.manage', $component)) { $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNNOTADMIN'), 'error'); return false; } } } if ($filetype === 'svg') { return self::isValidSvg($file['tmp_name'], true); } return true; } /** * Calculate the size of a resized image * * @param integer $width Image width * @param integer $height Image height * @param integer $target Target size * * @return array The new width and height * * @since 3.2 */ public static function imageResize($width, $height, $target) { /* * Takes the larger size of the width and height and applies the * formula accordingly. This is so this script will work * dynamically with any size image */ if ($width > $height) { $percentage = ($target / $width); } else { $percentage = ($target / $height); } // Gets the new value and applies the percentage, then rounds the value $width = round($width * $percentage); $height = round($height * $percentage); return [$width, $height]; } /** * Counts the files and directories in a directory that are not php or html files. * * @param string $dir Directory name * * @return array The number of media files and directories in the given directory * * @since 3.2 */ public function countFiles($dir) { $total_file = 0; $total_dir = 0; if (is_dir($dir)) { $d = dir($dir); while (($entry = $d->read()) !== false) { if ($entry[0] !== '.' && strpos($entry, '.html') === false && strpos($entry, '.php') === false && is_file($dir . DIRECTORY_SEPARATOR . $entry)) { $total_file++; } if ($entry[0] !== '.' && is_dir($dir . DIRECTORY_SEPARATOR . $entry)) { $total_dir++; } } $d->close(); } return [$total_file, $total_dir]; } /** * Small helper function that properly converts any * configuration options to their byte representation. * * @param string|integer $val The value to be converted to bytes. * * @return integer The calculated bytes value from the input. * * @since 3.3 */ public function toBytes($val) { switch ($val[\strlen($val) - 1]) { case 'M': case 'm': return (int) $val * 1048576; case 'K': case 'k': return (int) $val * 1024; case 'G': case 'g': return (int) $val * 1073741824; default: return $val; } } /** * Method to check if the given directory is a directory configured in FileSystem - Local plugin * * @param string $directory * * @return boolean * * @since 4.0.0 */ public static function isValidLocalDirectory($directory) { $plugin = PluginHelper::getPlugin('filesystem', 'local'); if ($plugin) { $params = new Registry($plugin->params); $directories = $params->get('directories', '[{"directory": "images"}]'); // Do a check if default settings are not saved by user // If not initialize them manually if (is_string($directories)) { $directories = json_decode($directories); } foreach ($directories as $directoryEntity) { if ($directoryEntity->directory === $directory) { return true; } } } return false; } /** * Helper method get clean data for value stores in a Media form field by removing adapter information * from the value if available (in this case, the value will have this format: * images/headers/blue-flower.jpg#joomlaImage://local-images/headers/blue-flower.jpg?width=700&height=180) * * @param string $value * * @return string * * @since 4.0.0 */ public static function getCleanMediaFieldValue($value) { if ($pos = strpos($value, '#')) { return substr($value, 0, $pos); } return $value; } /** * Check if a file is a valid SVG * * @param string $file * @param bool $shouldLogErrors * * @return boolean * * @since 4.3.0 */ private static function isValidSvg($file, $shouldLogErrors = true): bool { $sanitizer = new Sanitizer(); $isValid = $sanitizer->sanitize(file_get_contents($file)); $svgErrors = $sanitizer->getXmlIssues(); /** * We allow comments and temp fix for bugs in svg-santitizer * https://github.com/darylldoyle/svg-sanitizer/issues/64 * https://github.com/darylldoyle/svg-sanitizer/issues/63 * https://github.com/darylldoyle/svg-sanitizer/pull/65 * https://github.com/darylldoyle/svg-sanitizer/issues/82 */ foreach ($svgErrors as $i => $error) { if ( ($error['message'] === 'Suspicious node \'#comment\'') || ($error['message'] === 'Suspicious attribute \'space\'') || ($error['message'] === 'Suspicious attribute \'enable-background\'') || ($error['message'] === 'Suspicious node \'svg\'') ) { unset($svgErrors[$i]); } } if ($isValid === false || count($svgErrors)) { if ($shouldLogErrors) { Factory::getApplication()->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNIEXSS'), 'error'); } return false; } return true; } } PK1A#]΄�=��%src/Helper/HelperFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Factory to load helper classes. * * @since 4.0.0 */ interface HelperFactoryInterface { /** * Returns a helper instance for the given name. * * @param string $name The name * @param array $config The config * * @return \stdClass * * @since 4.0.0 */ public function getHelper(string $name, array $config = []); } PK1A#]oR2�!�!src/Helper/ContentHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Access\Access; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Table; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Helper for standard content style extensions. * This class mainly simplifies static helper methods often repeated in individual components * * @since 3.1 */ class ContentHelper { /** * Configure the Linkbar. Must be implemented by each extension. * * @param string $vName The name of the active view. * * @return void * * @since 3.1 */ public static function addSubmenu($vName) { } /** * Adds Count relations for Category and Tag Managers * * @param \stdClass[] &$items The category or tag objects * @param \stdClass $config Configuration object allowing to use a custom relations table * * @return \stdClass[] * * @since 3.9.1 */ public static function countRelations(&$items, $config) { $db = Factory::getDbo(); // Allow custom state / condition values and custom column names to support custom components $counter_names = isset($config->counter_names) ? $config->counter_names : [ '-2' => 'count_trashed', '0' => 'count_unpublished', '1' => 'count_published', '2' => 'count_archived', ]; // Index category objects by their ID $records = []; foreach ($items as $item) { $records[(int) $item->id] = $item; } // The relation query does not return a value for cases without relations of a particular state / condition, set zero as default foreach ($items as $item) { foreach ($counter_names as $n) { $item->{$n} = 0; } } // Table alias for related data table below will be 'c', and state / condition column is inside related data table $related_tbl = '#__' . $config->related_tbl; $state_col = 'c.' . $config->state_col; // Supported cases switch ($config->relation_type) { case 'tag_assigments': $recid_col = 'ct.' . $config->group_col; $query = $db->getQuery(true) ->from($db->quoteName('#__contentitem_tag_map', 'ct')) ->join( 'INNER', $db->quoteName($related_tbl, 'c'), $db->quoteName('ct.content_item_id') . ' = ' . $db->quoteName('c.id') . ' AND ' . $db->quoteName('ct.type_alias') . ' = :extension' ) ->bind(':extension', $config->extension); break; case 'category_or_group': $recid_col = 'c.' . $config->group_col; $query = $db->getQuery(true) ->from($db->quoteName($related_tbl, 'c')); break; default: return $items; } /** * Get relation counts for all category objects with single query * NOTE: 'state IN', allows counting specific states / conditions only, also prevents warnings with custom states / conditions, do not remove */ $query ->select( [ $db->quoteName($recid_col, 'catid'), $db->quoteName($state_col, 'state'), 'COUNT(*) AS ' . $db->quoteName('count'), ] ) ->whereIn($db->quoteName($recid_col), array_keys($records)) ->whereIn($db->quoteName($state_col), array_keys($counter_names)) ->group($db->quoteName([$recid_col, $state_col])); $relationsAll = $db->setQuery($query)->loadObjectList(); // Loop through the DB data overwriting the above zeros with the found count foreach ($relationsAll as $relation) { // Sanity check in case someone removes the state IN above ... and some views may start throwing warnings if (isset($counter_names[$relation->state])) { $id = (int) $relation->catid; $cn = $counter_names[$relation->state]; $records[$id]->{$cn} = $relation->count; } } return $items; } /** * Gets a list of the actions that can be performed. * * @param string $component The component name. * @param string $section The access section name. * @param integer $id The item ID. * * @return CMSObject * * @since 3.2 */ public static function getActions($component = '', $section = '', $id = 0) { $assetName = $component; if ($section && $id) { $assetName .= '.' . $section . '.' . (int) $id; } $result = new CMSObject(); $user = Factory::getUser(); $actions = Access::getActionsFromFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml', '/access/section[@name="component"]/' ); if ($actions === false) { Log::add( Text::sprintf('JLIB_ERROR_COMPONENTS_ACL_CONFIGURATION_FILE_MISSING_OR_IMPROPERLY_STRUCTURED', $component), Log::ERROR, 'jerror' ); return $result; } foreach ($actions as $action) { $result->set($action->name, $user->authorise($action->name, $assetName)); } return $result; } /** * Gets the current language * * @param boolean $detectBrowser Flag indicating whether to use the browser language as a fallback. * * @return string The language string * * @since 3.1 * @note CmsHelper::getCurrentLanguage is the preferred method */ public static function getCurrentLanguage($detectBrowser = true) { $app = Factory::getApplication(); $langCode = null; // Get the languagefilter parameters if (Multilanguage::isEnabled()) { $plugin = PluginHelper::getPlugin('system', 'languagefilter'); $pluginParams = new Registry($plugin->params); if ((int) $pluginParams->get('lang_cookie', 1) === 1) { $langCode = $app->getInput()->cookie->getString(ApplicationHelper::getHash('language')); } else { $langCode = $app->getSession()->get('plg_system_languagefilter.language'); } } // No cookie - let's try to detect browser language or use site default if (!$langCode) { if ($detectBrowser) { $langCode = LanguageHelper::detectLanguage(); } else { $langCode = ComponentHelper::getParams('com_languages')->get('site', 'en-GB'); } } return $langCode; } /** * Gets the associated language ID * * @param string $langCode The language code to look up * * @return integer The language ID * * @since 3.1 * @note CmsHelper::getLanguage() is the preferred method. */ public static function getLanguageId($langCode) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('lang_id')) ->from($db->quoteName('#__languages')) ->where($db->quoteName('lang_code') . ' = :language') ->bind(':language', $langCode); $db->setQuery($query); return $db->loadResult(); } /** * Gets a row of data from a table * * @param Table $table Table instance for a row. * * @return array Associative array of all columns and values for a row in a table. * * @since 3.1 */ public function getRowData(Table $table) { $data = new CMSHelper(); return $data->getRowData($table); } } PK1A#]A���src/Helper/CMSHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\TableInterface; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base Helper class. * * @since 3.2 */ class CMSHelper { /** * Gets the current language * * @param boolean $detectBrowser Flag indicating whether to use the browser language as a fallback. * * @return string The language string * * @since 3.2 */ public function getCurrentLanguage($detectBrowser = true) { $app = Factory::getApplication(); $langCode = null; // Get the languagefilter parameters if (Multilanguage::isEnabled()) { $plugin = PluginHelper::getPlugin('system', 'languagefilter'); $pluginParams = new Registry($plugin->params); if ((int) $pluginParams->get('lang_cookie', 1) === 1) { $langCode = $app->getInput()->cookie->getString(ApplicationHelper::getHash('language')); } else { $langCode = $app->getSession()->get('plg_system_languagefilter.language'); } } // No cookie - let's try to detect browser language or use site default if (!$langCode) { if ($detectBrowser) { $langCode = LanguageHelper::detectLanguage(); } else { $langCode = ComponentHelper::getParams('com_languages')->get('site', 'en-GB'); } } return $langCode; } /** * Gets the associated language ID * * @param string $langCode The language code to look up * * @return integer The language ID * * @since 3.2 */ public function getLanguageId($langCode) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('lang_id')) ->from($db->quoteName('#__languages')) ->where($db->quoteName('lang_code') . ' = :language') ->bind(':language', $langCode); $db->setQuery($query); return $db->loadResult(); } /** * Gets a row of data from a table * * @param TableInterface $table Table instance for a row. * * @return array Associative array of all columns and values for a row in a table. * * @since 3.2 */ public function getRowData(TableInterface $table) { $fields = $table->getFields(); $data = []; foreach ($fields as &$field) { $columnName = $field->Field; $value = $table->$columnName; $data[$columnName] = $value; } return $data; } /** * Method to get an object containing all of the table columns and values. * * @param TableInterface $table Table object. * * @return \stdClass Contains all of the columns and values. * * @since 3.2 */ public function getDataObject(TableInterface $table) { $fields = $table->getFields(); $dataObject = new \stdClass(); foreach ($fields as $field) { $fieldName = $field->Field; $dataObject->$fieldName = $table->$fieldName ?? null; } return $dataObject; } } PK1A#]Ō�g>\>\src/Helper/ModuleHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\LayoutHelper; use Joomla\CMS\Log\Log; use Joomla\CMS\Profiler\Profiler; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module helper class * * @since 1.5 */ abstract class ModuleHelper { /** * Get module by name (real, eg 'Breadcrumbs' or folder, eg 'mod_breadcrumbs') * * @param string $name The name of the module * @param string $title The title of the module, optional * * @return \stdClass The Module object * * @since 1.5 */ public static function &getModule($name, $title = null) { $result = null; $modules =& static::load(); $total = \count($modules); for ($i = 0; $i < $total; $i++) { // Match the name of the module if ($modules[$i]->name === $name || $modules[$i]->module === $name) { // Match the title if we're looking for a specific instance of the module if (!$title || $modules[$i]->title === $title) { // Found it $result = &$modules[$i]; break; } } } // If we didn't find it, and the name is mod_something, create a dummy object if ($result === null && strpos($name, 'mod_') === 0) { $result = static::createDummyModule(); $result->module = $name; } return $result; } /** * Get modules by position * * @param string $position The position of the module * * @return array An array of module objects * * @since 1.5 */ public static function &getModules($position) { $position = strtolower($position); $result = []; $input = Factory::getApplication()->getInput(); $modules = &static::load(); $total = \count($modules); for ($i = 0; $i < $total; $i++) { if ($modules[$i]->position === $position) { $result[] = &$modules[$i]; } } // Prepend a dummy module for template preview if no module is published in the position if (empty($result) && $input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) { $dummy = static::createDummyModule(); $dummy->title = $position; $dummy->position = $position; $dummy->content = $position; $dummy->contentRendered = true; array_unshift($result, $dummy); } return $result; } /** * Checks if a module is enabled. A given module will only be returned * if it meets the following criteria: it is enabled, it is assigned to * the current menu item or all items, and the user meets the access level * requirements. * * @param string $module The module name * * @return boolean See description for conditions. * * @since 1.5 */ public static function isEnabled($module) { $result = static::getModule($module); return $result !== null && $result->id !== 0; } /** * Render the module. * * @param object $module A module object. * @param array $attribs An array of attributes for the module (probably from the XML). * * @return string The HTML content of the module output. * * @since 1.5 */ public static function renderModule($module, $attribs = []) { $app = Factory::getApplication(); // Check that $module is a valid module object if (!\is_object($module) || !isset($module->module) || !isset($module->params)) { if (JDEBUG) { Log::addLogger(['text_file' => 'jmodulehelper.log.php'], Log::ALL, ['modulehelper']); $app->getLogger()->debug( __METHOD__ . '() - The $module parameter should be a module object.', ['category' => 'modulehelper'] ); } return ''; } // Get module parameters $params = new Registry($module->params); // Render the module content static::renderRawModule($module, $params, $attribs); // Return early if only the content is required if (!empty($attribs['contentOnly'])) { return $module->content; } if (JDEBUG) { Profiler::getInstance('Application')->mark('beforeRenderModule ' . $module->module . ' (' . $module->title . ')'); } // Record the scope. $scope = $app->scope; // Set scope to component name $app->scope = $module->module; // Get the template $template = $app->getTemplate(); // Check if the current module has a style param to override template module style $paramsChromeStyle = $params->get('style'); $basePath = ''; if ($paramsChromeStyle) { $paramsChromeStyle = explode('-', $paramsChromeStyle, 2); $ChromeStyleTemplate = strtolower($paramsChromeStyle[0]); $attribs['style'] = $paramsChromeStyle[1]; // Only set $basePath if the specified template isn't the current or system one. if ($ChromeStyleTemplate !== $template && $ChromeStyleTemplate !== 'system') { $basePath = JPATH_THEMES . '/' . $ChromeStyleTemplate . '/html/layouts'; } } // Make sure a style is set if (!isset($attribs['style'])) { $attribs['style'] = 'none'; } // Dynamically add outline style if ($app->getInput()->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) { $attribs['style'] .= ' outline'; } $module->style = $attribs['style']; // If the $module is nulled it will return an empty content, otherwise it will render the module normally. $app->triggerEvent('onRenderModule', [&$module, &$attribs]); if ($module === null || !isset($module->content)) { return ''; } // Prevent double modification of the module content by chrome style $module = clone $module; $displayData = [ 'module' => $module, 'params' => $params, 'attribs' => $attribs, ]; foreach (explode(' ', $attribs['style']) as $style) { $moduleContent = LayoutHelper::render('chromes.' . $style, $displayData, $basePath); if ($moduleContent) { $module->content = $moduleContent; } } // Revert the scope $app->scope = $scope; $app->triggerEvent('onAfterRenderModule', [&$module, &$attribs]); if (JDEBUG) { Profiler::getInstance('Application')->mark('afterRenderModule ' . $module->module . ' (' . $module->title . ')'); } return $module->content; } /** * Render the module content. * * @param object $module A module object * @param Registry $params A module parameters * @param array $attribs An array of attributes for the module (probably from the XML). * * @return string * * @since 4.0.0 */ public static function renderRawModule($module, Registry $params, $attribs = []) { if (!empty($module->contentRendered)) { return $module->content; } if (JDEBUG) { Profiler::getInstance('Application')->mark('beforeRenderRawModule ' . $module->module . ' (' . $module->title . ')'); } $app = Factory::getApplication(); // Record the scope. $scope = $app->scope; // Set scope to component name $app->scope = $module->module; // Get module path $module->module = preg_replace('/[^A-Z0-9_\.-]/i', '', $module->module); $dispatcher = $app->bootModule($module->module, $app->getName())->getDispatcher($module, $app); // Check if we have a dispatcher if ($dispatcher) { ob_start(); $dispatcher->dispatch(); $module->content = ob_get_clean(); } // Add the flag that the module content has been rendered $module->contentRendered = true; // Revert the scope $app->scope = $scope; if (JDEBUG) { Profiler::getInstance('Application')->mark('afterRenderRawModule ' . $module->module . ' (' . $module->title . ')'); } return $module->content; } /** * Get the path to a layout for a module * * @param string $module The name of the module * @param string $layout The name of the module layout. If alternative layout, in the form template:filename. * * @return string The path to the module layout * * @since 1.5 */ public static function getLayoutPath($module, $layout = 'default') { $templateObj = Factory::getApplication()->getTemplate(true); $defaultLayout = $layout; $template = $templateObj->template; if (strpos($layout, ':') !== false) { // Get the template and file name from the string $temp = explode(':', $layout); $template = $temp[0] === '_' ? $template : $temp[0]; $layout = $temp[1]; $defaultLayout = $temp[1] ?: 'default'; } $dPath = JPATH_BASE . '/modules/' . $module . '/tmpl/default.php'; try { // Build the template and base path for the layout $tPath = Path::check(JPATH_THEMES . '/' . $template . '/html/' . $module . '/' . $layout . '.php'); $iPath = Path::check(JPATH_THEMES . '/' . $templateObj->parent . '/html/' . $module . '/' . $layout . '.php'); $bPath = Path::check(JPATH_BASE . '/modules/' . $module . '/tmpl/' . $defaultLayout . '.php'); } catch (\Exception $e) { // On error fallback to the default path return $dPath; } // If the template has a layout override use it if (is_file($tPath)) { return $tPath; } if (!empty($templateObj->parent) && is_file($iPath)) { return $iPath; } if (is_file($bPath)) { return $bPath; } return $dPath; } /** * Load published modules. * * @return array * * @since 3.2 */ protected static function &load() { static $modules; if (isset($modules)) { return $modules; } $app = Factory::getApplication(); $modules = null; $app->triggerEvent('onPrepareModuleList', [&$modules]); // If the onPrepareModuleList event returns an array of modules, then ignore the default module list creation if (!\is_array($modules)) { $modules = static::getModuleList(); } $app->triggerEvent('onAfterModuleList', [&$modules]); $modules = static::cleanModuleList($modules); $app->triggerEvent('onAfterCleanModuleList', [&$modules]); return $modules; } /** * Module list * * @return array */ public static function getModuleList() { $app = Factory::getApplication(); $itemId = $app->getInput()->getInt('Itemid', 0); $groups = $app->getIdentity()->getAuthorisedViewLevels(); $clientId = (int) $app->getClientId(); // Build a cache ID for the resulting data object $cacheId = implode(',', $groups) . '.' . $clientId . '.' . $itemId; $db = Factory::getDbo(); $query = $db->getQuery(true); $nowDate = Factory::getDate()->toSql(); $query->select($db->quoteName(['m.id', 'm.title', 'm.module', 'm.position', 'm.content', 'm.showtitle', 'm.params', 'mm.menuid'])) ->from($db->quoteName('#__modules', 'm')) ->join( 'LEFT', $db->quoteName('#__modules_menu', 'mm'), $db->quoteName('mm.moduleid') . ' = ' . $db->quoteName('m.id') ) ->join( 'LEFT', $db->quoteName('#__extensions', 'e'), $db->quoteName('e.element') . ' = ' . $db->quoteName('m.module') . ' AND ' . $db->quoteName('e.client_id') . ' = ' . $db->quoteName('m.client_id') ) ->where( [ $db->quoteName('m.published') . ' = 1', $db->quoteName('e.enabled') . ' = 1', $db->quoteName('m.client_id') . ' = :clientId', ] ) ->bind(':clientId', $clientId, ParameterType::INTEGER) ->whereIn($db->quoteName('m.access'), $groups) ->extendWhere( 'AND', [ $db->quoteName('m.publish_up') . ' IS NULL', $db->quoteName('m.publish_up') . ' <= :publishUp', ], 'OR' ) ->bind(':publishUp', $nowDate) ->extendWhere( 'AND', [ $db->quoteName('m.publish_down') . ' IS NULL', $db->quoteName('m.publish_down') . ' >= :publishDown', ], 'OR' ) ->bind(':publishDown', $nowDate) ->extendWhere( 'AND', [ $db->quoteName('mm.menuid') . ' = :itemId', $db->quoteName('mm.menuid') . ' <= 0', ], 'OR' ) ->bind(':itemId', $itemId, ParameterType::INTEGER); // Filter by language if ($app->isClient('site') && $app->getLanguageFilter() || $app->isClient('administrator') && static::isAdminMultilang()) { $language = $app->getLanguage()->getTag(); $query->whereIn($db->quoteName('m.language'), [$language, '*'], ParameterType::STRING); $cacheId .= $language . '*'; } $query->order($db->quoteName(['m.position', 'm.ordering'])); // Set the query $db->setQuery($query); try { /** @var CallbackController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('callback', ['defaultgroup' => 'com_modules']); $modules = $cache->get([$db, 'loadObjectList'], [], md5($cacheId), false); } catch (\RuntimeException $e) { $app->getLogger()->warning( Text::sprintf('JLIB_APPLICATION_ERROR_MODULE_LOAD', $e->getMessage()), ['category' => 'jerror'] ); return []; } return $modules; } /** * Clean the module list * * @param array $modules Array with module objects * * @return array */ public static function cleanModuleList($modules) { // Apply negative selections and eliminate duplicates $Itemid = Factory::getApplication()->getInput()->getInt('Itemid'); $negId = $Itemid ? -(int) $Itemid : false; $clean = []; $dupes = []; foreach ($modules as $i => $module) { // The module is excluded if there is an explicit prohibition $negHit = ($negId === (int) $module->menuid); if (isset($dupes[$module->id])) { // If this item has been excluded, keep the duplicate flag set, // but remove any item from the modules array. if ($negHit) { unset($clean[$module->id]); } continue; } $dupes[$module->id] = true; // Only accept modules without explicit exclusions. if ($negHit) { continue; } $module->name = substr($module->module, 4); $module->style = null; $module->position = strtolower($module->position); $clean[$module->id] = $module; } unset($dupes); // Return to simple indexing that matches the query order. return array_values($clean); } /** * Module cache helper * * Caching modes: * To be set in XML: * 'static' One cache file for all pages with the same module parameters * 'itemid' Changes on itemid change, to be called from inside the module: * 'safeuri' Id created from $cacheparams->modeparams array, * 'id' Module sets own cache id's * * @param object $module Module object * @param object $moduleparams Module parameters * @param object $cacheparams Module cache parameters - id or URL parameters, depending on the module cache mode * * @return string * * @see InputFilter::clean() * @since 1.6 */ public static function moduleCache($module, $moduleparams, $cacheparams) { if (!isset($cacheparams->modeparams)) { $cacheparams->modeparams = null; } if (!isset($cacheparams->cachegroup)) { $cacheparams->cachegroup = $module->module; } if (!isset($cacheparams->cachesuffix)) { $cacheparams->cachesuffix = ''; } $user = Factory::getUser(); $app = Factory::getApplication(); /** @var CallbackController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('callback', ['defaultgroup' => $cacheparams->cachegroup]); // Turn cache off for internal callers if parameters are set to off and for all logged in users $ownCacheDisabled = $moduleparams->get('owncache') === 0 || $moduleparams->get('owncache') === '0'; $cacheDisabled = $moduleparams->get('cache') === 0 || $moduleparams->get('cache') === '0'; if ($ownCacheDisabled || $cacheDisabled || $app->get('caching') == 0 || $user->get('id')) { $cache->setCaching(false); } // Module cache is set in seconds, global cache in minutes, setLifeTime works in minutes $cache->setLifeTime($moduleparams->get('cache_time', $app->get('cachetime') * 60) / 60); $wrkaroundoptions = ['nopathway' => 1, 'nohead' => 0, 'nomodules' => 1, 'modulemode' => 1, 'mergehead' => 1]; $wrkarounds = true; $view_levels = md5(serialize($user->getAuthorisedViewLevels())); switch ($cacheparams->cachemode) { case 'id': $ret = $cache->get( [$cacheparams->class, $cacheparams->method], $cacheparams->methodparams, $cacheparams->modeparams . $cacheparams->cachesuffix, $wrkarounds, $wrkaroundoptions ); break; case 'safeuri': $safeuri = new \stdClass(); if (\is_array($cacheparams->modeparams)) { $input = $app->getInput(); $uri = $input->getArray(); $noHtmlFilter = InputFilter::getInstance(); foreach ($cacheparams->modeparams as $key => $value) { // Use int filter for id/catid to clean out spamy slugs if (isset($uri[$key])) { $safeuri->$key = $noHtmlFilter->clean($uri[$key], $value); } } } $secureid = md5(serialize([$safeuri, $cacheparams->method, $moduleparams])); $ret = $cache->get( [$cacheparams->class, $cacheparams->method], $cacheparams->methodparams, $module->id . $view_levels . $secureid . $cacheparams->cachesuffix, $wrkarounds, $wrkaroundoptions ); break; case 'static': $ret = $cache->get( [$cacheparams->class, $cacheparams->method], $cacheparams->methodparams, $module->module . md5(serialize($cacheparams->methodparams)) . $cacheparams->cachesuffix, $wrkarounds, $wrkaroundoptions ); break; case 'itemid': default: $ret = $cache->get( [$cacheparams->class, $cacheparams->method], $cacheparams->methodparams, $module->id . $view_levels . $app->getInput()->getInt('Itemid', null) . $cacheparams->cachesuffix, $wrkarounds, $wrkaroundoptions ); break; } return $ret; } /** * Method to determine if filtering by language is enabled in back-end for modules. * * @return boolean True if enabled; false otherwise. * * @since 3.8.0 */ public static function isAdminMultilang() { static $enabled = false; if (\count(LanguageHelper::getInstalledLanguages(1)) > 1) { $enabled = (bool) ComponentHelper::getParams('com_modules')->get('adminlangfilter', 0); } return $enabled; } /** * Get module by id * * @param string $id The id of the module * * @return \stdClass The Module object * * @since 3.9.0 */ public static function &getModuleById($id) { $modules =& static::load(); $total = \count($modules); for ($i = 0; $i < $total; $i++) { // Match the id of the module if ((string) $modules[$i]->id === $id) { // Found it return $modules[$i]; } } // If we didn't find it, create a dummy object $result = static::createDummyModule(); return $result; } /** * Method to create a dummy module. * * @return \stdClass The Module object * * @since 4.0.0 */ protected static function createDummyModule(): \stdClass { $module = new \stdClass(); $module->id = 0; $module->title = ''; $module->module = ''; $module->position = ''; $module->content = ''; $module->showtitle = 0; $module->control = ''; $module->params = ''; return $module; } } PK1A#]�g�#src/Helper/AuthenticationHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Authentication helper class * * @since 3.6.3 */ abstract class AuthenticationHelper { /** * No longer used * * @return array Always empty * * @since 3.6.3 * * @deprecated 4.2 will be removed in 6.0 * Will be removed without replacement */ public static function getTwoFactorMethods() { return []; } /** * Get additional login buttons to add in a login module. These buttons can be used for * authentication methods external to Joomla such as WebAuthn, login with social media * providers, login with third party providers or even login with third party Single Sign On * (SSO) services. * * Button definitions are returned by the onUserLoginButtons event handlers in plugins. By * default, only system and user plugins are taken into account. The former because they are * always loaded. The latter are explicitly loaded in this method. * * The onUserLoginButtons event handlers must conform to the following method definition: * * public function onUserLoginButtons(string $formId): array * * The onUserLoginButtons event handlers must return a simple array containing 0 or more button * definitions. * * Each button definition is a hash array with the following keys: * * * `label` The translation string used as the label and title of the button. Required * * `id` The HTML ID of the button. Required. * * `tooltip` (optional) The translation string used as the alt tag of the button's image * * `onclick` (optional) The onclick attribute, used to fire a JavaScript event. Not * recommended. * * `data-*` (optional) Data attributes to pass verbatim. Use these and JavaScript to handle * the button. * * `icon` (optional) A CSS class for an optional icon displayed before the label; has * precedence over 'image' * * `image` (optional) An image path for an optional icon displayed before the label * * `class` (optional) CSS class(es) to be added to the button * * You can find a real world implementation of the onUserLoginButtons plugin event in the * system/webauthn plugin. * * You can find a real world implementation of consuming the output of this method in the * modules/mod_login module. * * Third party developers implementing a login module or a login form in their component are * strongly advised to call this method and consume its results to display additional login * buttons. Not doing that means that you are not fully compatible with Joomla 4. * * @param string $formId The HTML ID of the login form container. Use it to filter when and * where to show your additional login button(s) * * @return array Button definitions. * * @since 4.0.0 */ public static function getLoginButtons(string $formId): array { // Get all the User plugins. PluginHelper::importPlugin('user'); // Trigger the onUserLoginButtons event and return the button definitions. try { /** @var CMSApplication $app */ $app = Factory::getApplication(); } catch (\Exception $e) { return []; } $results = $app->triggerEvent('onUserLoginButtons', [$formId]); $buttons = []; foreach ($results as $result) { // Did we get garbage back from the plugin? if (!is_array($result) || empty($result)) { continue; } // Did the developer accidentally return a single button definition instead of an array? if (array_key_exists('label', $result)) { $result = [$result]; } // Process each button, making sure it conforms to the required definition foreach ($result as $item) { // Force mandatory fields $defaultButtonDefinition = [ 'label' => '', 'tooltip' => '', 'icon' => '', 'image' => '', 'class' => '', 'id' => '', 'onclick' => '', ]; $button = array_merge($defaultButtonDefinition, $item); // Unset anything that doesn't conform to a button definition foreach (array_keys($button) as $key) { if (substr($key, 0, 5) == 'data-') { continue; } if (!in_array($key, ['label', 'tooltip', 'icon', 'image', 'svg', 'class', 'id', 'onclick'])) { unset($button[$key]); } } // We need a label and an ID as the bare minimum if (empty($button['label']) || empty($button['id'])) { continue; } $buttons[] = $button; } } return $buttons; } } PK1A#]AG::&src/Helper/HelperFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Helper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a HelperFactory Aware Class. * * @since 4.2.0 */ trait HelperFactoryAwareTrait { /** * HelperFactory * * @var HelperFactory * * @since 4.2.0 */ private $helper; /** * Get the HelperFactory. * * @return HelperFactory * * @since 4.2.0 * * @throws \UnexpectedValueException May be thrown if the HelperFactory has not been set. */ public function getHelperFactory(): HelperFactory { if ($this->helper) { return $this->helper; } throw new \UnexpectedValueException('HelperFactory not set in ' . __CLASS__); } /** * Sets the helper factory to use. * * @param HelperFactory $helperFactory The helper factory to use. * * @return void * * @since 4.2.0 */ public function setHelperFactory(HelperFactory $helperFactory) { $this->helper = $helperFactory; } } PK1A#]4 3��'src/Installer/LegacyInstallerScript.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Factory; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\Exception\DatabaseNotFoundException; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Legacy installer script which delegates the methods to the internal instance when possible. * * @since 4.2.0 */ class LegacyInstallerScript implements InstallerScriptInterface, DatabaseAwareInterface { use DatabaseAwareTrait; /** * @var \stdClass * @since 4.2.0 */ private $installerScript; /** * @param \stdClass $installerScript The script instance */ public function __construct($installerScript) { $this->installerScript = $installerScript; } /** * Function called after the extension is installed. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function install(InstallerAdapter $adapter): bool { return $this->callOnScript('install', [$adapter]); } /** * Function called after the extension is updated. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function update(InstallerAdapter $adapter): bool { return $this->callOnScript('update', [$adapter]); } /** * Function called after the extension is uninstalled. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function uninstall(InstallerAdapter $adapter): bool { return $this->callOnScript('uninstall', [$adapter]); } /** * Function called before extension installation/update/removal procedure commences. * * @param string $type The type of change (install or discover_install, update, uninstall) * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function preflight(string $type, InstallerAdapter $adapter): bool { return $this->callOnScript('preflight', [$type, $adapter]); } /** * Function called after extension installation/update/removal procedure commences. * * @param string $type The type of change (install or discover_install, update, uninstall) * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function postflight(string $type, InstallerAdapter $adapter): bool { return $this->callOnScript('postflight', [$type, $adapter]); } /** * Sets the variable to the internal script. * * @param string $name The name of the variable * @param mixed $value The value of the variable * * @return void * * @since 4.2.0 */ public function __set(string $name, $value) { $this->installerScript->$name = $value; } /** * Returns the variable from the internal script. * * @param string $name The name of the variable * * @return mixed * * @since 4.2.0 */ public function __get(string $name) { return $this->installerScript->$name; } /** * Calls the function with the given name on the internal script with * the given name and arguments. * * @param string $name The name of the function * @param array $arguments The arguments * * @return mixed * * @since 4.2.0 */ public function __call(string $name, array $arguments) { return call_user_func_array([$this->installerScript, $name], $arguments); } /** * Calls the function with the given name on the internal script with * some condition checking. * * @param string $name The name of the function * @param array $arguments The arguments * * @return bool * * @since 4.2.0 */ private function callOnScript(string $name, array $arguments): bool { if (!method_exists($this->installerScript, $name)) { return true; } if ($this->installerScript instanceof DatabaseAwareInterface) { try { $this->installerScript->setDatabase($this->getDatabase()); } catch (DatabaseNotFoundException $e) { @trigger_error(sprintf('Database must be set, this will not be caught anymore in 6.0 in %s.', __METHOD__), E_USER_DEPRECATED); $this->installerScript->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } } $return = $this->__call($name, $arguments); // When function doesn't have a return value, assume it succeeded if ($return === null) { return true; } return (bool) $return; } } PK1A#]���M *src/Installer/InstallerScriptInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base install script interface for use by extensions providing helper methods for common behaviours. * * @since 4.2.0 */ interface InstallerScriptInterface { /** * Function called after the extension is installed. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function install(InstallerAdapter $adapter): bool; /** * Function called after the extension is updated. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function update(InstallerAdapter $adapter): bool; /** * Function called after the extension is uninstalled. * * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function uninstall(InstallerAdapter $adapter): bool; /** * Function called before extension installation/update/removal procedure commences. * * @param string $type The type of change (install or discover_install, update, uninstall) * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function preflight(string $type, InstallerAdapter $adapter): bool; /** * Function called after extension installation/update/removal procedure commences. * * @param string $type The type of change (install or discover_install, update, uninstall) * @param InstallerAdapter $adapter The adapter calling this method * * @return boolean True on success * * @since 4.2.0 */ public function postflight(string $type, InstallerAdapter $adapter): bool; } PK1A#]:Z��1�1!src/Installer/InstallerScript.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\Database\ParameterType; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base install script for use by extensions providing helper methods for common behaviours. * * @since 3.6 */ class InstallerScript { /** * The version number of the extension. * * @var string * @since 3.6 */ protected $release; /** * The table the parameters are stored in. * * @var string * @since 3.6 */ protected $paramTable; /** * The extension name. This should be set in the installer script. * * @var string * @since 3.6 */ protected $extension; /** * A list of files to be deleted * * @var array * @since 3.6 */ protected $deleteFiles = []; /** * A list of folders to be deleted * * @var array * @since 3.6 */ protected $deleteFolders = []; /** * A list of CLI script files to be copied to the cli directory * * @var array * @since 3.6 */ protected $cliScriptFiles = []; /** * Minimum PHP version required to install the extension * * @var string * @since 3.6 */ protected $minimumPhp; /** * Minimum Joomla! version required to install the extension * * @var string * @since 3.6 */ protected $minimumJoomla; /** * Allow downgrades of your extension * * Use at your own risk as if there is a change in functionality people may wish to downgrade. * * @var boolean * @since 3.6 */ protected $allowDowngrades = false; /** * Function called before extension installation/update/removal procedure commences * * @param string $type The type of change (install, update or discover_install, not uninstall) * @param InstallerAdapter $parent The class calling this method * * @return boolean True on success * * @since 3.6 */ public function preflight($type, $parent) { // Check for the minimum PHP version before continuing if (!empty($this->minimumPhp) && version_compare(PHP_VERSION, $this->minimumPhp, '<')) { Log::add(Text::sprintf('JLIB_INSTALLER_MINIMUM_PHP', $this->minimumPhp), Log::WARNING, 'jerror'); return false; } // Check for the minimum Joomla version before continuing if (!empty($this->minimumJoomla) && version_compare(JVERSION, $this->minimumJoomla, '<')) { Log::add(Text::sprintf('JLIB_INSTALLER_MINIMUM_JOOMLA', $this->minimumJoomla), Log::WARNING, 'jerror'); return false; } // Extension manifest file version $this->extension = $parent->getName(); $this->release = $parent->getManifest()->version; $extensionType = substr($this->extension, 0, 3); // Modules parameters are located in the module table - else in the extension table if ($extensionType === 'mod') { $this->paramTable = '#__modules'; } else { $this->paramTable = '#__extensions'; } // Abort if the extension being installed is not newer than the currently installed version if (!$this->allowDowngrades && strtolower($type) === 'update') { $manifest = $this->getItemArray('manifest_cache', '#__extensions', 'name', $this->extension); // Check whether we have an old release installed and skip this check when this here is the initial install. if (!isset($manifest['version'])) { return true; } $oldRelease = $manifest['version']; if (version_compare($this->release, $oldRelease, '<')) { Factory::getApplication()->enqueueMessage(Text::sprintf('JLIB_INSTALLER_INCORRECT_SEQUENCE', $oldRelease, $this->release), 'error'); return false; } } return true; } /** * Gets each instance of a module in the #__modules table * * @param boolean $isModule True if the extension is a module as this can have multiple instances * * @return array An array of ID's of the extension * * @since 3.6 */ public function getInstances($isModule) { $extension = $this->extension; $db = Factory::getDbo(); $query = $db->getQuery(true); // Select the item(s) and retrieve the id if ($isModule) { $query->select($db->quoteName('id')) ->from($db->quoteName('#__modules')) ->where($db->quoteName('module') . ' = :extension'); } else { $query->select($db->quoteName('extension_id', 'id')) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('element') . ' = :extension'); } $query->bind(':extension', $extension); // Set the query and obtain an array of id's return $db->setQuery($query)->loadColumn(); } /** * Gets parameter value in the extensions row of the extension table * * @param string $name The name of the parameter to be retrieved * @param integer $id The id of the item in the Param Table * * @return string The parameter desired * * @since 3.6 */ public function getParam($name, $id = 0) { if (!\is_int($id) || $id == 0) { // Return false if there is no item given return false; } $params = $this->getItemArray('params', $this->paramTable, 'id', $id); return $params[$name]; } /** * Sets parameter values in the extensions row of the extension table. Note that the * this must be called separately for deleting and editing. Note if edit is called as a * type then if the param doesn't exist it will be created * * @param array $paramArray The array of parameters to be added/edited/removed * @param string $type The type of change to be made to the param (edit/remove) * @param integer $id The id of the item in the relevant table * * @return boolean True on success * * @since 3.6 */ public function setParams($paramArray = null, $type = 'edit', $id = 0) { if (!\is_int($id) || $id == 0) { // Return false if there is no valid item given return false; } $params = $this->getItemArray('params', $this->paramTable, 'id', $id); if ($paramArray) { foreach ($paramArray as $name => $value) { if ($type === 'edit') { // Add or edit the new variable(s) to the existing params if (\is_array($value)) { // Convert an array into a json encoded string $params[(string) $name] = array_values($value); } else { $params[(string) $name] = (string) $value; } } elseif ($type === 'remove') { // Unset the parameter from the array unset($params[(string) $name]); } } } // Store the combined new and existing values back as a JSON string $paramsString = json_encode($params); $db = Factory::getDbo(); $query = $db->getQuery(true) ->update($db->quoteName($this->paramTable)) ->set('params = :params') ->where('id = :id') ->bind(':params', $paramsString) ->bind(':id', $id, ParameterType::INTEGER); // Update table $db->setQuery($query)->execute(); return true; } /** * Builds a standard select query to produce better DRY code in this script. * This should produce a single unique cell which is json encoded - it will then * return an associated array with this data in. * * @param string $element The element to get from the query * @param string $table The table to search for the data in * @param string $column The column of the database to search from * @param mixed $identifier The integer id or the string * * @return array Associated array containing data from the cell * * @since 3.6 */ public function getItemArray($element, $table, $column, $identifier) { // Get the DB and query objects $db = Factory::getDbo(); $paramType = is_numeric($identifier) ? ParameterType::INTEGER : ParameterType::STRING; // Build the query $query = $db->getQuery(true) ->select($db->quoteName($element)) ->from($db->quoteName($table)) ->where($db->quoteName($column) . ' = :id') ->bind(':id', $identifier, $paramType); $db->setQuery($query); // Load the single cell and json_decode data $result = $db->loadResult(); return $result === null ? [] : json_decode($result, true); } /** * Remove the files and folders in the given array from * * @return void * * @since 3.6 */ public function removeFiles() { if (!empty($this->deleteFiles)) { foreach ($this->deleteFiles as $file) { if (is_file(JPATH_ROOT . $file) && !File::delete(JPATH_ROOT . $file)) { echo Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $file) . '<br>'; } } } if (!empty($this->deleteFolders)) { foreach ($this->deleteFolders as $folder) { if (Folder::exists(JPATH_ROOT . $folder) && !Folder::delete(JPATH_ROOT . $folder)) { echo Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $folder) . '<br>'; } } } } /** * Moves the CLI scripts into the CLI folder in the CMS * * @return void * * @since 3.6 */ public function moveCliFiles() { if (!empty($this->cliScriptFiles)) { foreach ($this->cliScriptFiles as $file) { $name = basename($file); if (file_exists(JPATH_ROOT . $file) && !File::move(JPATH_ROOT . $file, JPATH_ROOT . '/cli/' . $name)) { echo Text::sprintf('JLIB_INSTALLER_FILE_ERROR_MOVE', $name); } } } } /** * Creates the dashboard menu module * * @param string $dashboard The name of the dashboard * @param string $preset The name of the menu preset * * @return void * * @throws \Exception * @since 4.0.0 */ public function addDashboardMenu(string $dashboard, string $preset) { $model = Factory::getApplication()->bootComponent('com_modules')->getMVCFactory()->createModel('Module', 'Administrator', ['ignore_request' => true]); $module = [ 'id' => 0, 'asset_id' => 0, 'language' => '*', 'note' => '', 'published' => 1, 'assignment' => 0, 'client_id' => 1, 'showtitle' => 0, 'content' => '', 'module' => 'mod_submenu', 'position' => 'cpanel-' . $dashboard, ]; // Try to get a translated module title, otherwise fall back to a fixed string. $titleKey = strtoupper('COM_' . $this->extension . '_DASHBOARD_' . $dashboard . '_TITLE'); $title = Text::_($titleKey); $module['title'] = ($title === $titleKey) ? ucfirst($dashboard) . ' Dashboard' : $title; $module['access'] = (int) Factory::getApplication()->get('access', 1); $module['params'] = [ 'menutype' => '*', 'preset' => $preset, 'style' => 'System-none', ]; if (!$model->save($module)) { Factory::getApplication()->enqueueMessage(Text::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_FAILED_TO_CREATE_DASHBOARD', $model->getError())); } } } PK1A#]�*Ov$src/Installer/InstallerExtension.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Extension object * * @since 3.1 */ class InstallerExtension extends CMSObject { /** * Client ID of the extension * * @var int * @since 4.3.0 */ public $client_id; /** * Filename of the extension * * @var string * @since 3.1 */ public $filename = ''; /** * Type of the extension * * @var string * @since 3.1 */ public $type = ''; /** * Unique Identifier for the extension * * @var string * @since 3.1 */ public $id = ''; /** * The status of the extension * * @var boolean * @since 3.1 */ public $published = false; /** * String representation of client. Valid for modules, templates and languages. * Set by default to site. * * @var string * @since 3.1 */ public $client = 'site'; /** * The group name of the plugin. Not used for other known extension types (only plugins) * * @var string * @since 3.1 */ public $group = ''; /** * An object representation of the manifest file stored metadata * * @var object * @since 3.1 */ public $manifest_cache = null; /** * An object representation of the extension params * * @var object * @since 3.1 */ public $params = null; /** * The namespace of the extension * * @var string * @since 4.0.0 */ public $namespace = null; /** * Constructor * * @param \SimpleXMLElement $element A SimpleXMLElement from which to load data from * * @since 3.1 */ public function __construct(\SimpleXMLElement $element = null) { if ($element) { $this->type = (string) $element->attributes()->type; $this->id = (string) $element->attributes()->id; switch ($this->type) { case 'component': // By default a component doesn't have anything break; case 'module': case 'template': case 'language': $this->client = (string) $element->attributes()->client; $tmp_client_id = ApplicationHelper::getClientInfo($this->client, 1); if ($tmp_client_id == null) { Log::add(Text::_('JLIB_INSTALLER_ERROR_EXTENSION_INVALID_CLIENT_IDENTIFIER'), Log::WARNING, 'jerror'); } else { $this->client_id = $tmp_client_id->id; } break; case 'plugin': $this->group = (string) $element->attributes()->group; break; default: // Catch all // Get and set client and group if we don't recognise the extension if ($element->attributes()->client) { $this->client_id = ApplicationHelper::getClientInfo($this->client, 1); $this->client_id = $this->client_id->id; } if ($element->attributes()->group) { $this->group = (string) $element->attributes()->group; } break; } $this->filename = (string) $element; } } } PK1A#]�~��� � src/Installer/Manifest.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Package Manifest File * * @since 3.1 */ abstract class Manifest { /** * The error messages * * @var array * @since 4.3.0 */ public $_errors; /** * Path to the manifest file * * @var string * @since 3.1 */ public $manifest_file = ''; /** * Name of the extension * * @var string * @since 3.1 */ public $name = ''; /** * Version of the extension * * @var string * @since 3.1 */ public $version = ''; /** * Description of the extension * * @var string * @since 3.1 */ public $description = ''; /** * Packager of the extension * * @var string * @since 3.1 */ public $packager = ''; /** * Packager's URL of the extension * * @var string * @since 3.1 */ public $packagerurl = ''; /** * Update site for the extension * * @var string * @since 3.1 */ public $update = ''; /** * List of files in the extension * * @var array * @since 3.1 */ public $filelist = []; /** * Constructor * * @param string $xmlpath Path to XML manifest file. * * @since 3.1 */ public function __construct($xmlpath = '') { if ($xmlpath !== '') { $this->loadManifestFromXml($xmlpath); } } /** * Load a manifest from a file * * @param string $xmlfile Path to file to load * * @return boolean * * @since 3.1 */ public function loadManifestFromXml($xmlfile) { $this->manifest_file = basename($xmlfile, '.xml'); $xml = simplexml_load_file($xmlfile); if (!$xml) { $this->_errors[] = Text::sprintf('JLIB_INSTALLER_ERROR_LOAD_XML', $xmlfile); return false; } else { $this->loadManifestFromData($xml); return true; } } /** * Apply manifest data from a \SimpleXMLElement to the object. * * @param \SimpleXMLElement $xml Data to load * * @return void * * @since 3.1 */ abstract protected function loadManifestFromData(\SimpleXMLElement $xml); } PK1A#]$)�-�,�,!src/Installer/InstallerHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\Archive\Archive; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Updater\Update; use Joomla\CMS\Version; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Installer helper class * * @since 3.1 */ abstract class InstallerHelper { /** * Hash not validated identifier. * * @var integer * @since 3.9.0 */ public const HASH_NOT_VALIDATED = 0; /** * Hash validated identifier. * * @var integer * @since 3.9.0 */ public const HASH_VALIDATED = 1; /** * Hash not provided identifier. * * @var integer * @since 3.9.0 */ public const HASH_NOT_PROVIDED = 2; /** * Downloads a package * * @param string $url URL of file to download * @param string|bool $target Download target filename or false to get the filename from the URL * * @return string|boolean Path to downloaded package or boolean false on failure * * @since 3.1 */ public static function downloadPackage($url, $target = false) { // Capture PHP errors $track_errors = ini_get('track_errors'); ini_set('track_errors', true); // Set user agent $version = new Version(); ini_set('user_agent', $version->getUserAgent('Installer')); // Load installer plugins, and allow URL and headers modification $headers = []; PluginHelper::importPlugin('installer'); Factory::getApplication()->triggerEvent('onInstallerBeforePackageDownload', [&$url, &$headers]); try { $response = HttpFactory::getHttp()->get($url, $headers); } catch (\RuntimeException $exception) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $exception->getMessage()), Log::WARNING, 'jerror'); return false; } // Convert keys of headers to lowercase, to accommodate for case variations $headers = array_change_key_case($response->headers, CASE_LOWER); if (302 == $response->code && !empty($headers['location'])) { return self::downloadPackage($headers['location']); } elseif (200 != $response->code) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $response->code), Log::WARNING, 'jerror'); return false; } // Parse the Content-Disposition header to get the file name if ( !empty($headers['content-disposition']) && preg_match("/\s*filename\s?=\s?(.*)/", $headers['content-disposition'][0], $parts) ) { $flds = explode(';', $parts[1]); $target = trim($flds[0], '"'); } $tmpPath = Factory::getApplication()->get('tmp_path'); // Set the target path if not given if (!$target) { $target = $tmpPath . '/' . self::getFilenameFromUrl($url); } else { $target = $tmpPath . '/' . basename($target); } // Fix Indirect Modification of Overloaded Property $body = $response->body; // Write buffer to file File::write($target, $body); // Restore error tracking to what it was before ini_set('track_errors', $track_errors); // Bump the max execution time because not using built in php zip libs are slow if (\function_exists('set_time_limit')) { set_time_limit(ini_get('max_execution_time')); } // Return the name of the downloaded package return basename($target); } /** * Unpacks a file and verifies it as a Joomla element package * Supports .gz .tar .tar.gz and .zip * * @param string $packageFilename The uploaded package filename or install directory * @param boolean $alwaysReturnArray If should return false (and leave garbage behind) or return $retval['type']=false * * @return array|boolean Array on success or boolean false on failure * * @since 3.1 */ public static function unpack($packageFilename, $alwaysReturnArray = false) { // Path to the archive $archivename = $packageFilename; // Temporary folder to extract the archive into $tmpdir = uniqid('install_'); // Clean the paths to use for archive extraction $extractdir = Path::clean(\dirname($packageFilename) . '/' . $tmpdir); $archivename = Path::clean($archivename); // Do the unpacking of the archive try { $archive = new Archive(['tmp_path' => Factory::getApplication()->get('tmp_path')]); $extract = $archive->extract($archivename, $extractdir); } catch (\Exception $e) { if ($alwaysReturnArray) { return [ 'extractdir' => null, 'packagefile' => $archivename, 'type' => false, ]; } return false; } if (!$extract) { if ($alwaysReturnArray) { return [ 'extractdir' => null, 'packagefile' => $archivename, 'type' => false, ]; } return false; } /* * Let's set the extraction directory and package file in the result array so we can * cleanup everything properly later on. */ $retval = []; $retval['extractdir'] = $extractdir; $retval['packagefile'] = $archivename; /* * Try to find the correct install directory. In case the package is inside a * subdirectory detect this and set the install directory to the correct path. * * List all the items in the installation directory. If there is only one, and * it is a folder, then we will set that folder to be the installation folder. */ $dirList = array_merge((array) Folder::files($extractdir, ''), (array) Folder::folders($extractdir, '')); if (\count($dirList) === 1) { if (Folder::exists($extractdir . '/' . $dirList[0])) { $extractdir = Path::clean($extractdir . '/' . $dirList[0]); } } /* * We have found the install directory so lets set it and then move on * to detecting the extension type. */ $retval['dir'] = $extractdir; /* * Get the extension type and return the directory/type array on success or * false on fail. */ $retval['type'] = self::detectType($extractdir); if ($alwaysReturnArray || $retval['type']) { return $retval; } else { return false; } } /** * Method to detect the extension type from a package directory * * @param string $packageDirectory Path to package directory * * @return mixed Extension type string or boolean false on fail * * @since 3.1 */ public static function detectType($packageDirectory) { // Search the install dir for an XML file $files = Folder::files($packageDirectory, '\.xml$', 1, true); if (!$files || !\count($files)) { Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE'), Log::WARNING, 'jerror'); return false; } foreach ($files as $file) { $xml = simplexml_load_file($file); if (!$xml) { continue; } if ($xml->getName() !== 'extension') { unset($xml); continue; } $type = (string) $xml->attributes()->type; // Free up memory unset($xml); return $type; } Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE'), Log::WARNING, 'jerror'); return false; } /** * Gets a file name out of a url * * @param string $url URL to get name from * * @return string Clean version of the filename or a unique id * * @since 3.1 */ public static function getFilenameFromUrl($url) { $default = uniqid(); if (!\is_string($url) || strpos($url, '/') === false) { return $default; } // Get last part of the url (after the last slash). $parts = explode('/', $url); $filename = array_pop($parts); // Replace special characters with underscores. $filename = preg_replace('/[^a-z0-9\_\-\.]/i', '_', $filename); // Replace multiple underscores with just one. $filename = preg_replace('/__+/', '_', trim($filename, '_')); // Return the cleaned filename or, if it is empty, a unique id. return $filename ?: $default; } /** * Clean up temporary uploaded package and unpacked extension * * @param string $package Path to the uploaded package file * @param string $resultdir Path to the unpacked extension * * @return boolean True on success * * @since 3.1 */ public static function cleanupInstall($package, $resultdir) { // Does the unpacked extension directory exist? if ($resultdir && is_dir($resultdir)) { Folder::delete($resultdir); } // Is the package file a valid file? if (is_file($package)) { File::delete($package); } elseif (is_file(Path::clean(Factory::getApplication()->get('tmp_path') . '/' . $package))) { // It might also be just a base filename File::delete(Path::clean(Factory::getApplication()->get('tmp_path') . '/' . $package)); } } /** * Return the result of the checksum of a package with the SHA256/SHA384/SHA512 tags in the update server manifest * * @param string $packagefile Location of the package to be installed * @param Update $updateObject The Update Object * * @return integer one if the hashes match, zero if hashes doesn't match, two if hashes not found * * @since 3.9.0 */ public static function isChecksumValid($packagefile, $updateObject) { $hashes = ['sha256', 'sha384', 'sha512']; $hashOnFile = false; foreach ($hashes as $hash) { if ($updateObject->get($hash, false)) { $hashPackage = hash_file($hash, $packagefile); $hashRemote = $updateObject->$hash->_data; $hashOnFile = true; if ($hashPackage !== strtolower($hashRemote)) { return self::HASH_NOT_VALIDATED; } } } if ($hashOnFile) { return self::HASH_VALIDATED; } return self::HASH_NOT_PROVIDED; } } PK1A#]h_�M����"src/Installer/InstallerAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Installer\Manifest\PackageManifest; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Extension; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseInterface; use Joomla\DI\Container; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; use Joomla\DI\Exception\ContainerNotFoundException; use Joomla\DI\ServiceProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract adapter for the installer. * * @since 3.4 */ abstract class InstallerAdapter implements ContainerAwareInterface, DatabaseAwareInterface { use ContainerAwareTrait; use DatabaseAwareTrait; /** * Changelog URL of extensions * * @var string * @since 4.0.0 * */ protected $changelogurl = null; /** * ID for the currently installed extension if present * * @var integer * @since 3.4 */ protected $currentExtensionId = null; /** * The unique identifier for the extension (e.g. mod_login) * * @var string * @since 3.4 * */ protected $element = null; /** * Extension object. * * @var Extension * @since 3.4 * */ protected $extension = null; /** * Messages rendered by custom scripts * * @var string * @since 3.4 */ protected $extensionMessage = ''; /** * Copy of the XML manifest file. * * Making this object public allows extensions to customize the manifest in custom scripts. * * @var \SimpleXMLElement * @since 3.4 */ public $manifest = null; /** * A path to the PHP file that the scriptfile declaration in the manifest refers to. * * @var string * @since 3.4 */ protected $manifest_script = null; /** * Name of the extension * * @var string * @since 3.4 */ protected $name = null; /** * Installer used with this adapter * * @var Installer * @since 4.0.0 */ protected $parent = null; /** * Install function routing * * @var string * @since 3.4 */ protected $route = 'install'; /** * Flag if the adapter supports discover installs * * Adapters should override this and set to false if discover install is unsupported * * @var boolean * @since 3.4 */ protected $supportsDiscoverInstall = true; /** * The type of adapter in use * * @var string * @since 3.4 */ protected $type; /** * Constructor * * @param Installer $parent Parent object * @param DatabaseDriver $db Database object * @param array $options Configuration Options * * @since 3.4 */ public function __construct(Installer $parent, DatabaseDriver $db, array $options = []) { $this->parent = $parent; $this->setDatabase($db); foreach ($options as $key => $value) { if (property_exists($this, $key)) { $this->$key = $value; } } // Get a generic TableExtension instance for use if not already loaded if (!($this->extension instanceof TableInterface)) { $this->extension = Table::getInstance('extension'); } // Sanity check, make sure the type is set by taking the adapter name from the class name if (!$this->type) { // This assumes the adapter short class name in its namespace is `<foo>Adapter`, replace this logic in subclasses if needed $reflection = new \ReflectionClass(\get_called_class()); $this->type = str_replace('Adapter', '', $reflection->getShortName()); } // Extension type is stored as lowercase in the database $this->type = strtolower($this->type); } /** * Check if a package extension allows its child extensions to be uninstalled individually * * @param integer $packageId The extension ID of the package to check * * @return boolean * * @since 3.7.0 * @note This method defaults to true to emulate the behavior of 3.6 and earlier which did not support this lookup */ protected function canUninstallPackageChild($packageId) { $package = Table::getInstance('extension'); // If we can't load this package ID, we have a corrupt database if (!$package->load((int) $packageId)) { return true; } $manifestFile = JPATH_MANIFESTS . '/packages/' . $package->element . '.xml'; $xml = $this->parent->isManifest($manifestFile); // If the manifest doesn't exist, we've got some major issues if (!$xml) { return true; } $manifest = new PackageManifest($manifestFile); return $manifest->blockChildUninstall === false; } /** * Method to check if the extension is already present in the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExistingExtension() { try { $this->currentExtensionId = $this->extension->find( ['element' => $this->element, 'type' => $this->type] ); // If it does exist, load it if ($this->currentExtensionId) { $this->extension->load(['element' => $this->element, 'type' => $this->type]); } } catch (\RuntimeException $e) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $e->getMessage() ), $e->getCode(), $e ); } } /** * Method to check if the extension is present in the filesystem, flags the route as update if so * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExtensionInFilesystem() { if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->isOverwrite() || $this->parent->isUpgrade())) { // Look for an update function or update tag $updateElement = $this->getManifest()->update; // Upgrade manually set or update function available or update tag detected if ( $updateElement || $this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) ) { // Force this one $this->parent->setOverwrite(true); $this->parent->setUpgrade(true); if ($this->currentExtensionId) { // If there is a matching extension mark this as an update $this->setRoute('update'); } } elseif (!$this->parent->isOverwrite()) { // We didn't have overwrite set, find an update function or find an update tag so lets call it safe throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_DIRECTORY', Text::_('JLIB_INSTALLER_' . $this->route), $this->type, $this->parent->getPath('extension_root') ) ); } } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ abstract protected function copyBaseFiles(); /** * Method to create the extension root path if necessary * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function createExtensionRoot() { // If the extension directory does not exist, lets create it $created = false; if (!file_exists($this->parent->getPath('extension_root'))) { if (!$created = Folder::create($this->parent->getPath('extension_root'))) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_' . $this->route), $this->parent->getPath('extension_root') ) ); } } /* * Since we created the extension directory and will want to remove it if * we have to roll back the installation, let's add it to the * installation step stack */ if ($created) { $this->parent->pushStep( [ 'type' => 'folder', 'path' => $this->parent->getPath('extension_root'), ] ); } } /** * Generic discover_install method for extensions * * @return boolean True on success * * @since 3.4 */ public function discover_install() { // Get the extension's description $description = (string) $this->getManifest()->description; if ($description) { $this->parent->message = Text::_($description); } else { $this->parent->message = ''; } // Set the extension's name and element $this->name = $this->getName(); $this->element = $this->getElement(); /* * --------------------------------------------------------------------------------------------- * Extension Precheck and Setup Section * --------------------------------------------------------------------------------------------- */ // Setup the install paths and perform other prechecks as necessary try { $this->setupInstallPaths(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } /* * --------------------------------------------------------------------------------------------- * Installer Trigger Loading * --------------------------------------------------------------------------------------------- */ $this->setupScriptfile(); try { $this->triggerManifestScript('preflight'); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } /* * --------------------------------------------------------------------------------------------- * Database Processing Section * --------------------------------------------------------------------------------------------- */ try { $this->storeExtension(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } try { $this->parseQueries(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Run the custom install method try { $this->triggerManifestScript('install'); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } /* * --------------------------------------------------------------------------------------------- * Finalization and Cleanup Section * --------------------------------------------------------------------------------------------- */ try { $this->finaliseInstall(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // And now we run the postflight try { $this->triggerManifestScript('postflight'); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } return $this->extension->extension_id; } /** * Method to handle database transactions for the installer * * @return boolean True on success * * @since 3.4 * @throws \RuntimeException */ protected function doDatabaseTransactions() { $route = $this->route === 'discover_install' ? 'install' : $this->route; // Let's run the install queries for the component if (isset($this->getManifest()->{$route}->sql)) { $result = $this->parent->parseSQLFiles($this->getManifest()->{$route}->sql); if ($result === false) { // Only rollback if installing if ($route === 'install') { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_INSTALL_ABORTED')); } return false; } // If installing with success and there is an uninstall script, add an installer rollback step to rollback if needed if ($route === 'install' && isset($this->getManifest()->uninstall->sql)) { $this->parent->pushStep(['type' => 'query', 'script' => $this->getManifest()->uninstall->sql]); } } return true; } /** * Load language files * * @param string $extension The name of the extension * @param string $source Path to the extension * @param string $base Base path for the extension language * * @return void * * @since 3.4 */ protected function doLoadLanguage($extension, $source, $base = JPATH_ADMINISTRATOR) { $lang = Factory::getLanguage(); $lang->load($extension . '.sys', $source) || $lang->load($extension . '.sys', $base); } /** * Method to finalise the installation processing * * @return void * * @since 4.0.0 * @throws \RuntimeException */ abstract protected function finaliseInstall(); /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ abstract protected function finaliseUninstall(): bool; /** * Checks if the adapter supports discover_install * * @return boolean * * @since 3.4 */ public function getDiscoverInstallSupported() { return $this->supportsDiscoverInstall; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { if (!$element) { // Ensure the element is a string $element = (string) $this->getManifest()->element; } if (!$element) { $element = $this->getName(); } // Filter the name for illegal characters return strtolower(InputFilter::getInstance()->clean($element, 'cmd')); } /** * Get the manifest object. * * @return \SimpleXMLElement Manifest object * * @since 3.4 */ public function getManifest() { return $this->manifest; } /** * Get the filtered component name from the manifest * * @return string The filtered name * * @since 3.4 */ public function getName() { // Ensure the name is a string $name = (string) $this->getManifest()->name; // Filter the name for illegal characters $name = InputFilter::getInstance()->clean($name, 'string'); return $name; } /** * Retrieves the parent installer * * @return Installer * * @since 4.0.0 */ public function getParent() { return $this->parent; } /** * Get the install route being followed * * @return string The install route * * @since 3.4 */ public function getRoute() { return $this->route; } /** * Get the class name for the install adapter script. * * @return string The class name. * * @since 3.4 */ protected function getScriptClassName() { // Support element names like 'en-GB' $className = InputFilter::getInstance()->clean($this->element, 'cmd') . 'InstallerScript'; // Cannot have - in class names $className = str_replace('-', '', $className); return $className; } /** * Generic install method for extensions * * @return boolean|integer The extension ID on success, boolean false on failure * * @since 3.4 */ public function install() { // Get the extension's description $description = (string) $this->getManifest()->description; $this->parent->message = ''; if ($description) { $this->parent->message = Text::_($description); } // Set the extension's name and element $this->name = $this->getName(); $this->element = $this->getElement(); $this->changelogurl = (string) $this->getManifest()->changelogurl; /* * --------------------------------------------------------------------------------------------- * Extension Precheck and Setup Section * --------------------------------------------------------------------------------------------- */ // Setup the install paths and perform other prechecks as necessary try { $this->setupInstallPaths(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Check to see if an extension by the same name is already installed. try { $this->checkExistingExtension(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Check if the extension is present in the filesystem try { $this->checkExtensionInFilesystem(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // If we are on the update route, run any custom setup routines if ($this->route === 'update') { try { $this->setupUpdates(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } } /* * --------------------------------------------------------------------------------------------- * Installer Trigger Loading * --------------------------------------------------------------------------------------------- */ $this->setupScriptfile(); try { $this->triggerManifestScript('preflight'); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } /* * --------------------------------------------------------------------------------------------- * Filesystem Processing Section * --------------------------------------------------------------------------------------------- */ // If the extension directory does not exist, lets create it try { $this->createExtensionRoot(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Copy all necessary files try { $this->copyBaseFiles(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Parse optional tags $this->parseOptionalTags(); /* * --------------------------------------------------------------------------------------------- * Database Processing Section * --------------------------------------------------------------------------------------------- */ try { $this->storeExtension(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } try { $this->parseQueries(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // Run the custom method based on the route try { $this->triggerManifestScript($this->route); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } /* * --------------------------------------------------------------------------------------------- * Finalization and Cleanup Section * --------------------------------------------------------------------------------------------- */ try { $this->finaliseInstall(); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } // And now we run the postflight try { $this->triggerManifestScript('postflight'); } catch (\RuntimeException $e) { // Install failed, roll back changes $this->parent->abort($e->getMessage()); return false; } return $this->extension->extension_id; } /** * Method to parse the queries specified in the `<sql>` tags * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function parseQueries() { // Let's run the queries for the extension if (\in_array($this->route, ['install', 'discover_install', 'uninstall'])) { // This method may throw an exception, but it is caught by the parent caller if (!$this->doDatabaseTransactions()) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_INSTALL_ABORTED')); } // Set the schema version to be the latest update version if ($this->getManifest()->update) { $this->parent->setSchemaVersion($this->getManifest()->update->schemas, $this->extension->extension_id); } } elseif ($this->route === 'update') { if ($this->getManifest()->update) { $result = $this->parent->parseSchemaUpdates($this->getManifest()->update->schemas, $this->extension->extension_id); if ($result === false) { // Install failed, rollback changes throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_INSTALL_ABORTED')); } } } } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.1 */ protected function parseOptionalTags() { // Some extensions may not have optional tags } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 */ public function prepareDiscoverInstall() { // Adapters may not support discover install or may have overridden the default task and aren't using this } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ abstract protected function removeExtensionFiles(); /** * Set the manifest object. * * @param object $manifest The manifest object * * @return InstallerAdapter Instance of this class to support chaining * * @since 3.4 */ public function setManifest($manifest) { $this->manifest = $manifest; return $this; } /** * Set the install route being followed * * @param string $route The install route being followed * * @return InstallerAdapter Instance of this class to support chaining * * @since 3.4 */ public function setRoute($route) { $this->route = $route; return $this; } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 */ abstract protected function setupInstallPaths(); /** * Setup the manifest script file for those adapters that use it. * * @return void * * @since 3.4 */ protected function setupScriptfile() { // If there is a manifest class file, lets load it; we'll copy it later (don't have dest yet) $manifestScript = (string) $this->getManifest()->scriptfile; // When no script file, do nothing if (!$manifestScript) { return; } // Build a child container, so we do not overwrite the global one // and start from scratch when multiple extensions are installed try { $container = new Container($this->getContainer()); } catch (ContainerNotFoundException $e) { @trigger_error('Container must be set.', E_USER_DEPRECATED); // Fallback to the global container $container = new Container(Factory::getContainer()); } // The real location of the file $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; // Load the installer from the file if (!file_exists($manifestScriptFile)) { @trigger_error( 'Installer file must exist when defined. In version 5.0 this will crash.', E_USER_DEPRECATED ); return; } $installer = require_once $manifestScriptFile; // When the instance is a service provider, then register the container with it if ($installer instanceof ServiceProviderInterface) { $installer->register($container); } // When the returned object is an installer instance, use it directly if ($installer instanceof InstallerScriptInterface) { $container->set(InstallerScriptInterface::class, $installer); } // When none is set, then use the legacy way if (!$container->has(InstallerScriptInterface::class)) { @trigger_error( 'Legacy installer files are deprecated and will be removed in 6.0. Use a service provider instead.', E_USER_DEPRECATED ); $classname = $this->getScriptClassName(); \JLoader::register($classname, $manifestScriptFile); if (!class_exists($classname)) { return; } $container->set( InstallerScriptInterface::class, function (Container $container) use ($classname) { return new LegacyInstallerScript(new $classname($this)); } ); } // Create a new instance $this->parent->manifestClass = $container->get(InstallerScriptInterface::class); // Set the database if ($this->parent->manifestClass instanceof DatabaseAwareInterface) { $this->parent->manifestClass->setDatabase($container->get(DatabaseInterface::class)); } // And set this so we can copy it later $this->manifest_script = $manifestScript; } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ abstract protected function setupUninstall(); /** * Method to setup the update routine for the adapter * * @return void * * @since 3.4 */ protected function setupUpdates() { // Some extensions may not have custom setup routines for updates } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ abstract protected function storeExtension(); /** * Executes a custom install script method * * @param string $method The install method to execute * * @return boolean True on success * * @since 3.4 * @throws \RuntimeException */ protected function triggerManifestScript($method) { ob_start(); ob_implicit_flush(false); if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $method)) { switch ($method) { // The preflight and postflight take the route as a param case 'preflight': case 'postflight': if ($this->parent->manifestClass->$method($this->route, $this) === false) { if ($method !== 'postflight') { // Clean and close the output buffer ob_end_clean(); // The script failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_INSTALL_CUSTOM_INSTALL_FAILURE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } } break; // The install, uninstall, and update methods only pass this object as a param case 'install': case 'uninstall': case 'update': if ($this->parent->manifestClass->$method($this) === false) { if ($method !== 'uninstall') { // Clean and close the output buffer ob_end_clean(); // The script failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_INSTALL_CUSTOM_INSTALL_FAILURE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } } break; } } // Append to the message object $this->extensionMessage .= ob_get_clean(); // If in postflight or uninstall, set the message for display if (($method === 'uninstall' || $method === 'postflight') && $this->extensionMessage !== '') { $this->parent->set('extension_message', $this->extensionMessage); } return true; } /** * Generic update method for extensions * * @param integer $id The extension ID * * @return boolean True on success * * @since 4.0.0 */ public function uninstall($id) { if (!$this->extension->load((int) $id)) { Log::add(Text::_('JLIB_INSTALLER_ERROR_UNKNOWN_EXTENSION'), Log::WARNING, 'jerror'); return false; } // Joomla 4: Locked extensions cannot be removed. if (isset($this->extension->locked) && $this->extension->locked) { Log::add(Text::_('JLIB_INSTALLER_ERROR_UNINSTALL_LOCKED_EXTENSION'), Log::WARNING, 'jerror'); return false; } elseif (!isset($this->extension->locked) && $this->extension->protected) { // Joomla 3 ('locked' property does not exist yet): Protected extensions cannot be removed. Log::add(Text::_('JLIB_INSTALLER_ERROR_UNINSTALL_PROTECTED_EXTENSION'), Log::WARNING, 'jerror'); return false; } /* * Does this extension have a parent package? * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled */ if ($this->extension->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($this->extension->package_id)) { Log::add( Text::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $this->extension->name), Log::WARNING, 'jerror' ); return false; } // Setup the uninstall job as required try { $this->setupUninstall(); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); return false; } // Set the extension's name and element $this->name = $this->getName(); $this->element = $this->getElement(); /* * --------------------------------------------------------------------------------------------- * Installer Trigger Loading and Uninstall * --------------------------------------------------------------------------------------------- */ $this->setupScriptfile(); try { $this->triggerManifestScript('preflight'); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); return false; } try { $this->triggerManifestScript('uninstall'); } catch (\RuntimeException $e) { // Ignore errors for now } // Tasks from here may fail but we will still attempt to finish the uninstall process $retval = true; /* * --------------------------------------------------------------------------------------------- * Database Processing Section * --------------------------------------------------------------------------------------------- */ try { $this->parseQueries(); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); $retval = false; } /* * --------------------------------------------------------------------------------------------- * Filesystem Processing Section * --------------------------------------------------------------------------------------------- */ try { $this->removeExtensionFiles(); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); $retval = false; } /* * --------------------------------------------------------------------------------------------- * Finalization and Cleanup Section * --------------------------------------------------------------------------------------------- */ try { $retval |= $this->finaliseUninstall(); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); $retval = false; } // And now we run the postflight try { $this->triggerManifestScript('postflight'); } catch (\RuntimeException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); $retval = false; } return $retval; } /** * Generic update method for extensions * * @return boolean|integer The extension ID on success, boolean false on failure * * @since 3.4 */ public function update() { // Set the overwrite setting $this->parent->setOverwrite(true); $this->parent->setUpgrade(true); // And make sure the route is set correctly $this->setRoute('update'); // Now jump into the install method to run the update return $this->install(); } /** * Proxy for db variable. * * @param string $name The name of the element * * @return mixed The value of the element if set, null otherwise * * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Use getDatabase() instead of directly accessing _db */ public function __get($name) { if ($name === 'db') { return $this->getDatabase(); } // Default the variable if (!isset($this->$name)) { $this->$name = null; } return $this->$name; } } PK1A#]XU�?3?3src/Installer/Installer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer; use Joomla\CMS\Adapter\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Extension; use Joomla\CMS\Table\Table; use Joomla\Database\DatabaseAwareInterface; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\DI\ContainerAwareInterface; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla base installer class * * @since 3.1 */ class Installer extends Adapter implements DatabaseAwareInterface { use DatabaseAwareTrait; /** * Array of paths needed by the installer * * @var array * @since 3.1 */ protected $paths = []; /** * True if package is an upgrade * * @var boolean * @since 3.1 */ protected $upgrade = null; /** * The manifest trigger class * * @var object * @since 3.1 */ public $manifestClass = null; /** * True if existing files can be overwritten * * @var boolean * @since 3.0.0 */ protected $overwrite = false; /** * Stack of installation steps * - Used for installation rollback * * @var array * @since 3.1 */ protected $stepStack = []; /** * Extension Table Entry * * @var Extension * @since 3.1 */ public $extension = null; /** * The output from the install/uninstall scripts * * @var string * @since 3.1 * */ public $message = null; /** * The installation manifest XML object * * @var object * @since 3.1 */ public $manifest = null; /** * The extension message that appears * * @var string * @since 3.1 */ protected $extension_message = null; /** * The redirect URL if this extension (can be null if no redirect) * * @var string * @since 3.1 */ protected $redirect_url = null; /** * Flag if the uninstall process was triggered by uninstalling a package * * @var boolean * @since 3.7.0 */ protected $packageUninstall = false; /** * Backup extra_query during update_sites rebuild * * @var string * @since 3.9.26 */ public $extraQuery = ''; /** * JInstaller instances container. * * @var Installer[] * @since 3.4 */ protected static $instances; /** * A comment marker to indicate that an update SQL query may fail without triggering an update error. * * @since 4.2.0 */ protected const CAN_FAIL_MARKER = '/** CAN FAIL **/'; /** * The length of the CAN_FAIL_MARKER string * * @since 4.2.0 */ protected const CAN_FAIL_MARKER_LENGTH = 16; /** * Constructor * * @param string $basepath Base Path of the adapters * @param string $classprefix Class prefix of adapters * @param string $adapterfolder Name of folder to append to base path * * @since 3.1 */ public function __construct($basepath = __DIR__, $classprefix = '\\Joomla\\CMS\\Installer\\Adapter', $adapterfolder = 'Adapter') { parent::__construct($basepath, $classprefix, $adapterfolder); $this->extension = Table::getInstance('extension'); } /** * Returns the global Installer object, only creating it if it doesn't already exist. * * @param string $basepath Base Path of the adapters * @param string $classprefix Class prefix of adapters * @param string $adapterfolder Name of folder to append to base path * * @return Installer An installer object * * @since 3.1 */ public static function getInstance($basepath = __DIR__, $classprefix = '\\Joomla\\CMS\\Installer\\Adapter', $adapterfolder = 'Adapter') { if (!isset(self::$instances[$basepath])) { self::$instances[$basepath] = new static($basepath, $classprefix, $adapterfolder); self::$instances[$basepath]->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } return self::$instances[$basepath]; } /** * Splits a string of multiple queries into an array of individual queries. * * This is different than DatabaseDriver::splitSql. It supports the special CAN FAIL comment * marker which indicates that a SQL statement could fail without raising an error during the * installation. * * @param string|null $sql Input SQL string with which to split into individual queries. * * @return array * * @since 4.2.0 */ public static function splitSql(?string $sql): array { if (empty($sql)) { return []; } $start = 0; $open = false; $comment = false; $endString = ''; $end = \strlen($sql); $queries = []; $query = ''; for ($i = 0; $i < $end; $i++) { $current = substr($sql, $i, 1); $current2 = substr($sql, $i, 2); $current3 = substr($sql, $i, 3); $lenEndString = \strlen($endString); $testEnd = substr($sql, $i, $lenEndString); if ( $current === '"' || $current === "'" || $current2 === '--' || ($current2 === '/*' && $current3 !== '/*!' && $current3 !== '/*+') || ($current === '#' && $current3 !== '#__') || ($comment && $testEnd === $endString) ) { // Check if quoted with previous backslash $n = 2; while (substr($sql, $i - $n + 1, 1) === '\\' && $n < $i) { $n++; } // Not quoted if ($n % 2 === 0) { if ($open) { if ($testEnd === $endString) { if ($comment) { $comment = false; if ($lenEndString > 1) { $i += ($lenEndString - 1); $current = substr($sql, $i, 1); } $start = $i + 1; } $open = false; $endString = ''; } } else { $open = true; if ($current2 === '--') { $endString = "\n"; $comment = true; } elseif ($current2 === '/*') { $endString = '*/'; $comment = true; } elseif ($current === '#') { $endString = "\n"; $comment = true; } else { $endString = $current; } if ($comment && $start < $i) { $query .= substr($sql, $start, $i - $start); } } } } if ($comment) { $start = $i + 1; } if (($current === ';' && !$open) || $i === $end - 1) { if ($current === ';' && !$open && $start <= $i && $start > self::CAN_FAIL_MARKER_LENGTH) { $possibleMarker = substr($sql, $start - self::CAN_FAIL_MARKER_LENGTH, $i - $start + self::CAN_FAIL_MARKER_LENGTH); if (strtoupper($possibleMarker) === self::CAN_FAIL_MARKER) { $start -= self::CAN_FAIL_MARKER_LENGTH; } } if ($start <= $i) { $query .= substr($sql, $start, $i - $start + 1); } $query = trim($query); if ($query) { if (($i === $end - 1) && ($current !== ';')) { $query .= ';'; } $queries[] = $query; } $query = ''; $start = $i + 1; } $endComment = false; } return $queries; } /** * Get the allow overwrite switch * * @return boolean Allow overwrite switch * * @since 3.1 */ public function isOverwrite() { return $this->overwrite; } /** * Set the allow overwrite switch * * @param boolean $state Overwrite switch state * * @return boolean True it state is set, false if it is not * * @since 3.1 */ public function setOverwrite($state = false) { $tmp = $this->overwrite; if ($state) { $this->overwrite = true; } else { $this->overwrite = false; } return $tmp; } /** * Get the redirect location * * @return string Redirect location (or null) * * @since 3.1 */ public function getRedirectUrl() { return $this->redirect_url; } /** * Set the redirect location * * @param string $newurl New redirect location * * @return void * * @since 3.1 */ public function setRedirectUrl($newurl) { $this->redirect_url = $newurl; } /** * Get whether this installer is uninstalling extensions which are part of a package * * @return boolean * * @since 3.7.0 */ public function isPackageUninstall() { return $this->packageUninstall; } /** * Set whether this installer is uninstalling extensions which are part of a package * * @param boolean $uninstall True if a package triggered the uninstall, false otherwise * * @return void * * @since 3.7.0 */ public function setPackageUninstall($uninstall) { $this->packageUninstall = $uninstall; } /** * Get the upgrade switch * * @return boolean * * @since 3.1 */ public function isUpgrade() { return $this->upgrade; } /** * Set the upgrade switch * * @param boolean $state Upgrade switch state * * @return boolean True if upgrade, false otherwise * * @since 3.1 */ public function setUpgrade($state = false) { $tmp = $this->upgrade; if ($state) { $this->upgrade = true; } else { $this->upgrade = false; } return $tmp; } /** * Get the installation manifest object * * @return \SimpleXMLElement Manifest object * * @since 3.1 */ public function getManifest() { if (!\is_object($this->manifest)) { $this->findManifest(); } return $this->manifest; } /** * Get an installer path by name * * @param string $name Path name * @param string $default Default value * * @return string Path * * @since 3.1 */ public function getPath($name, $default = null) { return (!empty($this->paths[$name])) ? $this->paths[$name] : $default; } /** * Sets an installer path by name * * @param string $name Path name * @param string $value Path * * @return void * * @since 3.1 */ public function setPath($name, $value) { $this->paths[$name] = $value; } /** * Pushes a step onto the installer stack for rolling back steps * * @param array $step Installer step * * @return void * * @since 3.1 */ public function pushStep($step) { $this->stepStack[] = $step; } /** * Installation abort method * * @param string $msg Abort message from the installer * @param string $type Package type if defined * * @return boolean True if successful * * @since 3.1 */ public function abort($msg = null, $type = null) { $retval = true; $step = array_pop($this->stepStack); // Raise abort warning if ($msg) { Log::add($msg, Log::WARNING, 'jerror'); } while ($step != null) { switch ($step['type']) { case 'file': // Remove the file if (is_file($step['path']) && !($stepval = File::delete($step['path']))) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $step['path']), Log::WARNING, 'jerror'); } break; case 'folder': // Remove the folder if (Folder::exists($step['path']) && !($stepval = Folder::delete($step['path']))) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $step['path']), Log::WARNING, 'jerror'); } break; case 'query': // Execute the query. $stepval = $this->parseSQLFiles($step['script']); break; case 'extension': // Get database connector object $db = $this->getDatabase(); $query = $db->getQuery(true); $stepId = (int) $step['id']; // Remove the entry from the #__extensions table $query->delete($db->quoteName('#__extensions')) ->where($db->quoteName('extension_id') . ' = :step_id') ->bind(':step_id', $stepId, ParameterType::INTEGER); $db->setQuery($query); try { $db->execute(); $stepval = true; } catch (ExecutionFailureException $e) { // The database API will have already logged the error it caught, we just need to alert the user to the issue Log::add(Text::_('JLIB_INSTALLER_ABORT_ERROR_DELETING_EXTENSIONS_RECORD'), Log::WARNING, 'jerror'); $stepval = false; } break; default: if ($type && \is_object($this->_adapters[$type])) { // Build the name of the custom rollback method for the type $method = '_rollback_' . $step['type']; // Custom rollback method handler if (method_exists($this->_adapters[$type], $method)) { $stepval = $this->_adapters[$type]->$method($step); } } else { // Set it to false $stepval = false; } break; } // Only set the return value if it is false if ($stepval === false) { $retval = false; } // Get the next step and continue $step = array_pop($this->stepStack); } return $retval; } // Adapter functions /** * Package installation method * * @param string $path Path to package source folder * * @return boolean True if successful * * @since 3.1 */ public function install($path = null) { if ($path && Folder::exists($path)) { $this->setPath('source', $path); } else { $this->abort(Text::_('JLIB_INSTALLER_ABORT_NOINSTALLPATH')); return false; } if (!$adapter = $this->setupInstall('install', true)) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST')); return false; } if (!\is_object($adapter)) { return false; } // Add the languages from the package itself if (method_exists($adapter, 'loadLanguage')) { $adapter->loadLanguage($path); } // Fire the onExtensionBeforeInstall event. PluginHelper::importPlugin('extension'); Factory::getApplication()->triggerEvent( 'onExtensionBeforeInstall', [ 'method' => 'install', 'type' => $this->manifest->attributes()->type, 'manifest' => $this->manifest, 'extension' => 0, ] ); // Run the install $result = $adapter->install(); // Make sure Joomla can figure out what has changed clearstatcache(); // Fire the onExtensionAfterInstall Factory::getApplication()->triggerEvent( 'onExtensionAfterInstall', ['installer' => clone $this, 'eid' => $result] ); if ($result !== false) { // Refresh versionable assets cache Factory::getApplication()->flushAssets(); return true; } return false; } /** * Discovered package installation method * * @param integer $eid Extension ID * * @return boolean True if successful * * @since 3.1 */ public function discover_install($eid = null) { if (!$eid) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_EXTENSIONNOTVALID')); return false; } if (!$this->extension->load($eid)) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS')); return false; } if ($this->extension->state != -1) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_ALREADYINSTALLED')); return false; } // Load the adapter(s) for the install manifest $type = $this->extension->type; $params = ['extension' => $this->extension, 'route' => 'discover_install']; $adapter = $this->loadAdapter($type, $params); if (!\is_object($adapter)) { return false; } if (!method_exists($adapter, 'discover_install') || !$adapter->getDiscoverInstallSupported()) { $this->abort(Text::sprintf('JLIB_INSTALLER_ERROR_DISCOVER_INSTALL_UNSUPPORTED', $type)); return false; } // The adapter needs to prepare itself if (method_exists($adapter, 'prepareDiscoverInstall')) { try { $adapter->prepareDiscoverInstall(); } catch (\RuntimeException $e) { $this->abort($e->getMessage()); return false; } } // Add the languages from the package itself if (method_exists($adapter, 'loadLanguage')) { $adapter->loadLanguage(); } // Fire the onExtensionBeforeInstall event. PluginHelper::importPlugin('extension'); Factory::getApplication()->triggerEvent( 'onExtensionBeforeInstall', [ 'method' => 'discover_install', 'type' => $this->extension->get('type'), 'manifest' => null, 'extension' => $this->extension->get('extension_id'), ] ); // Run the install $result = $adapter->discover_install(); // Fire the onExtensionAfterInstall Factory::getApplication()->triggerEvent( 'onExtensionAfterInstall', ['installer' => clone $this, 'eid' => $result] ); if ($result !== false) { // Refresh versionable assets cache Factory::getApplication()->flushAssets(); return true; } return false; } /** * Extension discover method * * Asks each adapter to find extensions * * @return InstallerExtension[] * * @since 3.1 */ public function discover() { $results = []; foreach ($this->getAdapters() as $adapter) { $instance = $this->loadAdapter($adapter); // Joomla! 1.5 installation adapter legacy support if (method_exists($instance, 'discover')) { $tmp = $instance->discover(); // If its an array and has entries if (\is_array($tmp) && \count($tmp)) { // Merge it into the system $results = array_merge($results, $tmp); } } } return $results; } /** * Package update method * * @param string $path Path to package source folder * * @return boolean True if successful * * @since 3.1 */ public function update($path = null) { if ($path && Folder::exists($path)) { $this->setPath('source', $path); } else { $this->abort(Text::_('JLIB_INSTALLER_ABORT_NOUPDATEPATH')); return false; } if (!$adapter = $this->setupInstall('update', true)) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST')); return false; } if (!\is_object($adapter)) { return false; } // Add the languages from the package itself if (method_exists($adapter, 'loadLanguage')) { $adapter->loadLanguage($path); } // Fire the onExtensionBeforeUpdate event. PluginHelper::importPlugin('extension'); Factory::getApplication()->triggerEvent( 'onExtensionBeforeUpdate', ['type' => $this->manifest->attributes()->type, 'manifest' => $this->manifest] ); // Run the update $result = $adapter->update(); // Fire the onExtensionAfterUpdate Factory::getApplication()->triggerEvent( 'onExtensionAfterUpdate', ['installer' => clone $this, 'eid' => $result] ); if ($result !== false) { return true; } return false; } /** * Package uninstallation method * * @param string $type Package type * @param mixed $identifier Package identifier for adapter * * @return boolean True if successful * * @since 3.1 */ public function uninstall($type, $identifier) { $params = ['extension' => $this->extension, 'route' => 'uninstall']; $adapter = $this->loadAdapter($type, $params); if (!\is_object($adapter)) { return false; } // We don't load languages here, we get the extension adapter to work it out // Fire the onExtensionBeforeUninstall event. PluginHelper::importPlugin('extension'); Factory::getApplication()->triggerEvent( 'onExtensionBeforeUninstall', ['eid' => $identifier] ); // Run the uninstall $result = $adapter->uninstall($identifier); // Fire the onExtensionAfterInstall Factory::getApplication()->triggerEvent( 'onExtensionAfterUninstall', ['installer' => clone $this, 'eid' => $identifier, 'removed' => $result] ); // Refresh versionable assets cache Factory::getApplication()->flushAssets(); return $result; } /** * Refreshes the manifest cache stored in #__extensions * * @param integer $eid Extension ID * * @return boolean * * @since 3.1 */ public function refreshManifestCache($eid) { if ($eid) { if (!$this->extension->load($eid)) { $this->abort(Text::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS')); return false; } if ($this->extension->state == -1) { $this->abort(Text::sprintf('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE', $this->extension->name)); return false; } // Fetch the adapter $adapter = $this->loadAdapter($this->extension->type); if (!\is_object($adapter)) { return false; } if (!method_exists($adapter, 'refreshManifestCache')) { $this->abort(Text::sprintf('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED_TYPE', $this->extension->type)); return false; } $result = $adapter->refreshManifestCache(); if ($result !== false) { return true; } else { return false; } } $this->abort(Text::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE_VALID')); return false; } // Utility functions /** * Prepare for installation: this method sets the installation directory, finds * and checks the installation file and verifies the installation type. * * @param string $route The install route being followed * @param boolean $returnAdapter Flag to return the instantiated adapter * * @return boolean|InstallerAdapter InstallerAdapter object if explicitly requested otherwise boolean * * @since 3.1 */ public function setupInstall($route = 'install', $returnAdapter = false) { // We need to find the installation manifest file if (!$this->findManifest()) { return false; } // Load the adapter(s) for the install manifest $type = (string) $this->manifest->attributes()->type; $params = ['route' => $route, 'manifest' => $this->getManifest()]; // Load the adapter $adapter = $this->loadAdapter($type, $params); if ($returnAdapter) { return $adapter; } return true; } /** * Backward compatible method to parse through a queries element of the * installation manifest file and take appropriate action. * * @param \SimpleXMLElement $element The XML node to process * * @return mixed Number of queries processed or False on error * * @since 3.1 */ public function parseQueries(\SimpleXMLElement $element) { // Get the database connector object $db = & $this->_db; if (!$element || !\count($element->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return 0; } // Get the array of query nodes to process $queries = $element->children(); if (\count($queries) === 0) { // No queries to process return 0; } $update_count = 0; // Process each query in the $queries array (children of $tagName). foreach ($queries as $query) { try { $db->setQuery($query)->execute(); } catch (ExecutionFailureException $e) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $e->getMessage()), Log::WARNING, 'jerror'); return false; } $update_count++; } return $update_count; } /** * Method to extract the name of a discreet installation sql file from the installation manifest file. * * @param object $element The XML node to process * * @return mixed Number of queries processed or False on error * * @since 3.1 */ public function parseSQLFiles($element) { if (!$element || !\count($element->children())) { // The tag does not exist. return 0; } $db = &$this->_db; $dbDriver = $db->getServerType(); $updateCount = 0; // Get the name of the sql file to process foreach ($element->children() as $file) { $fCharset = strtolower($file->attributes()->charset) === 'utf8' ? 'utf8' : ''; $fDriver = strtolower($file->attributes()->driver); if ($fDriver === 'mysqli' || $fDriver === 'pdomysql') { $fDriver = 'mysql'; } elseif ($fDriver === 'pgsql') { $fDriver = 'postgresql'; } if ($fCharset !== 'utf8' || $fDriver != $dbDriver) { continue; } $sqlfile = $this->getPath('extension_root') . '/' . trim($file); // Check that sql files exists before reading. Otherwise raise error for rollback if (!file_exists($sqlfile)) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_FILENOTFOUND', $sqlfile), Log::WARNING, 'jerror'); return false; } $buffer = file_get_contents($sqlfile); // Graceful exit and rollback if read not successful if ($buffer === false) { Log::add(Text::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER'), Log::WARNING, 'jerror'); return false; } // Create an array of queries from the sql file $queries = self::splitSql($buffer); if (\count($queries) === 0) { // No queries to process continue; } // Process each query in the $queries array (split out of sql file). foreach ($queries as $query) { $canFail = strlen($query) > self::CAN_FAIL_MARKER_LENGTH + 1 && strtoupper(substr($query, -self::CAN_FAIL_MARKER_LENGTH - 1)) === (self::CAN_FAIL_MARKER . ';'); $query = $canFail ? (substr($query, 0, -self::CAN_FAIL_MARKER_LENGTH - 1) . ';') : $query; try { $db->setQuery($query)->execute(); } catch (ExecutionFailureException $e) { if (!$canFail) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $e->getMessage()), Log::WARNING, 'jerror'); return false; } } $updateCount++; } } return $updateCount; } /** * Set the schema version for an extension by looking at its latest update * * @param \SimpleXMLElement $schema Schema Tag * @param integer $eid Extension ID * * @return void * * @since 3.1 */ public function setSchemaVersion(\SimpleXMLElement $schema, $eid) { if ($eid && $schema) { $db = $this->getDatabase(); $schemapaths = $schema->children(); if (!$schemapaths) { return; } if (\count($schemapaths)) { $dbDriver = $db->getServerType(); $schemapath = ''; foreach ($schemapaths as $entry) { $attrs = $entry->attributes(); if ($attrs['type'] == $dbDriver) { $schemapath = $entry; break; } } if ($schemapath !== '') { $files = str_replace('.sql', '', Folder::files($this->getPath('extension_root') . '/' . $schemapath, '\.sql$')); usort($files, 'version_compare'); // Update the database $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $eid, ParameterType::INTEGER); $db->setQuery($query); if ($db->execute()) { $schemaVersion = end($files); $query->clear() ->insert($db->quoteName('#__schemas')) ->columns([$db->quoteName('extension_id'), $db->quoteName('version_id')]) ->values(':extension_id, :version_id') ->bind(':extension_id', $eid, ParameterType::INTEGER) ->bind(':version_id', $schemaVersion); $db->setQuery($query); $db->execute(); } } } } } /** * Method to process the updates for an item * * @param \SimpleXMLElement $schema The XML node to process * @param integer $eid Extension Identifier * * @return boolean|int Number of SQL updates executed; false on failure. * * @since 3.1 */ public function parseSchemaUpdates(\SimpleXMLElement $schema, $eid) { $updateCount = 0; // Ensure we have an XML element and a valid extension id if (!$eid || !$schema) { return $updateCount; } $db = $this->getDatabase(); $schemapaths = $schema->children(); if (!\count($schemapaths)) { return $updateCount; } $dbDriver = $db->getServerType(); $schemapath = ''; foreach ($schemapaths as $entry) { $attrs = $entry->attributes(); // Assuming that the type is a mandatory attribute but if it is not mandatory then there should be a discussion for it. $uDriver = strtolower($attrs['type']); if ($uDriver === 'mysqli' || $uDriver === 'pdomysql') { $uDriver = 'mysql'; } elseif ($uDriver === 'pgsql') { $uDriver = 'postgresql'; } if ($uDriver == $dbDriver) { $schemapath = $entry; break; } } if ($schemapath === '') { return $updateCount; } $files = Folder::files($this->getPath('extension_root') . '/' . $schemapath, '\.sql$'); if (empty($files)) { return $updateCount; } Log::add(Text::_('JLIB_INSTALLER_SQL_BEGIN'), Log::INFO, 'Update'); $files = str_replace('.sql', '', $files); usort($files, 'version_compare'); $query = $db->getQuery(true) ->select('version_id') ->from('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $eid, ParameterType::INTEGER); $db->setQuery($query); $hasVersion = true; try { $version = $db->loadResult(); // No version - use initial version. if (!$version) { $version = '0.0.0'; $hasVersion = false; } } catch (ExecutionFailureException $e) { $version = '0.0.0'; } Log::add(Text::sprintf('JLIB_INSTALLER_SQL_BEGIN_SCHEMA', $version), Log::INFO, 'Update'); foreach ($files as $file) { // Skip over files earlier or equal to the latest schema version recorded for this extension. if (version_compare($file, $version) <= 0) { continue; } $buffer = file_get_contents(sprintf("%s/%s/%s.sql", $this->getPath('extension_root'), $schemapath, $file)); // Graceful exit and rollback if read not successful if ($buffer === false) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_READBUFFER'), Log::WARNING, 'jerror'); return false; } // Create an array of queries from the sql file $queries = self::splitSql($buffer); // Process each query in the $queries array (split out of sql file). foreach ($queries as $query) { $canFail = strlen($query) > self::CAN_FAIL_MARKER_LENGTH + 1 && strtoupper(substr($query, -self::CAN_FAIL_MARKER_LENGTH - 1)) === (self::CAN_FAIL_MARKER . ';'); $query = $canFail ? (substr($query, 0, -self::CAN_FAIL_MARKER_LENGTH - 1) . ';') : $query; $queryString = (string) $query; $queryString = str_replace(["\r", "\n"], ['', ' '], substr($queryString, 0, 80)); try { $db->setQuery($query)->execute(); } catch (\RuntimeException $e) { if (!$canFail) { $errorMessage = Text::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $e->getMessage()); // Log the error in the update log file Log::add(Text::sprintf('JLIB_INSTALLER_UPDATE_LOG_QUERY', $file, $queryString), Log::INFO, 'Update'); Log::add($errorMessage, Log::INFO, 'Update'); Log::add(Text::_('JLIB_INSTALLER_SQL_END_NOT_COMPLETE'), Log::INFO, 'Update'); // Show the error message to the user Log::add($errorMessage, Log::WARNING, 'jerror'); return false; } } Log::add(Text::sprintf('JLIB_INSTALLER_UPDATE_LOG_QUERY', $file, $queryString), Log::INFO, 'Update'); $updateCount++; } // Update the schema version for this extension try { $this->updateSchemaTable($eid, $file, $hasVersion); $hasVersion = true; } catch (ExecutionFailureException $e) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $e->getMessage()), Log::WARNING, 'jerror'); return false; } } Log::add(Text::_('JLIB_INSTALLER_SQL_END'), Log::INFO, 'Update'); return $updateCount; } /** * Update the schema table with the latest version * * @param int $eid Extension ID. * @param string $version Latest schema version ID. * @param boolean $update Should I run an update against an existing record or insert a new one? * * @return void * * @since 4.2.0 */ protected function updateSchemaTable(int $eid, string $version, bool $update = false): void { $db = $this->getDatabase(); $o = (object) [ 'extension_id' => $eid, 'version_id' => $version, ]; try { if ($update) { $db->updateObject('#__schemas', $o, 'extension_id'); } else { $db->insertObject('#__schemas', $o); } } catch (ExecutionFailureException $e) { /** * Safe fallback: delete any existing record and insert afresh. * * It is possible that the schema version may be populated after we detected it does not * exist (or removed after we detected it exists) and before we finish executing the SQL * update script. This could happen e.g. if the update SQL script messes with it, or if * another process is also tinkering with the #__schemas table. * * The safe fallback below even runs inside a transaction to prevent interference from * another process. */ $db->transactionStart(); $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $eid, ParameterType::INTEGER); $db->setQuery($query)->execute(); $db->insertObject('#__schemas', $o); $db->transactionCommit(); } } /** * Method to parse through a files element of the installation manifest and take appropriate * action. * * @param \SimpleXMLElement $element The XML node to process * @param integer $cid Application ID of application to install to * @param array $oldFiles List of old files (SimpleXMLElement's) * @param array $oldMD5 List of old MD5 sums (indexed by filename with value as MD5) * * @return boolean True on success * * @since 3.1 */ public function parseFiles(\SimpleXMLElement $element, $cid = 0, $oldFiles = null, $oldMD5 = null) { // Get the array of file nodes to process; we checked whether this had children above. if (!$element || !\count($element->children())) { // Either the tag does not exist or has no children (hence no files to process) therefore we return zero files processed. return 0; } $copyfiles = []; // Get the client info $client = ApplicationHelper::getClientInfo($cid); /* * Here we set the folder we are going to remove the files from. */ if ($client) { $pathname = 'extension_' . $client->name; $destination = $this->getPath($pathname); } else { $pathname = 'extension_root'; $destination = $this->getPath($pathname); } /* * Here we set the folder we are going to copy the files from. * * Does the element have a folder attribute? * * If so this indicates that the files are in a subdirectory of the source * folder and we should append the folder attribute to the source path when * copying files. */ $folder = (string) $element->attributes()->folder; if ($folder && file_exists($this->getPath('source') . '/' . $folder)) { $source = $this->getPath('source') . '/' . $folder; } else { $source = $this->getPath('source'); } // Work out what files have been deleted if ($oldFiles && ($oldFiles instanceof \SimpleXMLElement)) { $oldEntries = $oldFiles->children(); if (\count($oldEntries)) { $deletions = $this->findDeletedFiles($oldEntries, $element->children()); foreach ($deletions['folders'] as $deleted_folder) { $folder = $destination . '/' . $deleted_folder; if (Folder::exists($folder) && !Folder::delete($folder)) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $folder), Log::WARNING, 'jerror'); } } foreach ($deletions['files'] as $deleted_file) { $file = $destination . '/' . $deleted_file; if (is_file($file) && !File::delete($file)) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FILE_FOLDER', $file), Log::WARNING, 'jerror'); } } } } $path = []; // Copy the MD5SUMS file if it exists if (file_exists($source . '/MD5SUMS')) { $path['src'] = $source . '/MD5SUMS'; $path['dest'] = $destination . '/MD5SUMS'; $path['type'] = 'file'; $copyfiles[] = $path; } // Process each file in the $files array (children of $tagName). foreach ($element->children() as $file) { $path['src'] = $source . '/' . $file; $path['dest'] = $destination . '/' . $file; // Is this path a file or folder? $path['type'] = $file->getName() === 'folder' ? 'folder' : 'file'; /* * Before we can add a file to the copyfiles array we need to ensure * that the folder we are copying our file to exists and if it doesn't, * we need to create it. */ if (basename($path['dest']) !== $path['dest']) { $newdir = \dirname($path['dest']); if (!Folder::create($newdir)) { Log::add( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_INSTALL'), $newdir ), Log::WARNING, 'jerror' ); return false; } } // Add the file to the copyfiles array $copyfiles[] = $path; } return $this->copyFiles($copyfiles); } /** * Method to parse through a languages element of the installation manifest and take appropriate * action. * * @param \SimpleXMLElement $element The XML node to process * @param integer $cid Application ID of application to install to * * @return boolean True on success * * @since 3.1 */ public function parseLanguages(\SimpleXMLElement $element, $cid = 0) { // TODO: work out why the below line triggers 'node no longer exists' errors with files if (!$element || !\count($element->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return 0; } $copyfiles = []; // Get the client info $client = ApplicationHelper::getClientInfo($cid); // Here we set the folder we are going to copy the files to. // 'languages' Files are copied to JPATH_BASE/language/ folder $destination = $client->path . '/language'; /* * Here we set the folder we are going to copy the files from. * * Does the element have a folder attribute? * * If so this indicates that the files are in a subdirectory of the source * folder and we should append the folder attribute to the source path when * copying files. */ $folder = (string) $element->attributes()->folder; if ($folder && file_exists($this->getPath('source') . '/' . $folder)) { $source = $this->getPath('source') . '/' . $folder; } else { $source = $this->getPath('source'); } // Process each file in the $files array (children of $tagName). foreach ($element->children() as $file) { /* * Language files go in a subfolder based on the language code, ie. * <language tag="en-US">en-US.mycomponent.ini</language> * would go in the en-US subdirectory of the language folder. */ // We will only install language files where a core language pack // already exists. if ((string) $file->attributes()->tag !== '') { $path = []; $path['src'] = $source . '/' . $file; if ((string) $file->attributes()->client !== '') { // Override the client $langclient = ApplicationHelper::getClientInfo((string) $file->attributes()->client, true); $path['dest'] = $langclient->path . '/language/' . $file->attributes()->tag . '/' . basename((string) $file); } else { // Use the default client $path['dest'] = $destination . '/' . $file->attributes()->tag . '/' . basename((string) $file); } // If the language folder is not present, then the core pack hasn't been installed... ignore if (!Folder::exists(\dirname($path['dest']))) { continue; } } else { $path = []; $path['src'] = $source . '/' . $file; $path['dest'] = $destination . '/' . $file; } /* * Before we can add a file to the copyfiles array we need to ensure * that the folder we are copying our file to exists and if it doesn't, * we need to create it. */ if (basename($path['dest']) !== $path['dest']) { $newdir = \dirname($path['dest']); if (!Folder::create($newdir)) { Log::add( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_INSTALL'), $newdir ), Log::WARNING, 'jerror' ); return false; } } // Add the file to the copyfiles array $copyfiles[] = $path; } return $this->copyFiles($copyfiles); } /** * Method to parse through a media element of the installation manifest and take appropriate * action. * * @param \SimpleXMLElement $element The XML node to process * @param integer $cid Application ID of application to install to * * @return boolean True on success * * @since 3.1 */ public function parseMedia(\SimpleXMLElement $element, $cid = 0) { if (!$element || !\count($element->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return 0; } $copyfiles = []; // Here we set the folder we are going to copy the files to. // Default 'media' Files are copied to the JPATH_BASE/media folder $folder = ((string) $element->attributes()->destination) ? '/' . $element->attributes()->destination : null; $destination = Path::clean(JPATH_ROOT . '/media' . $folder); // Here we set the folder we are going to copy the files from. /* * Does the element have a folder attribute? * If so this indicates that the files are in a subdirectory of the source * folder and we should append the folder attribute to the source path when * copying files. */ $folder = (string) $element->attributes()->folder; if ($folder && file_exists($this->getPath('source') . '/' . $folder)) { $source = $this->getPath('source') . '/' . $folder; } else { $source = $this->getPath('source'); } // Process each file in the $files array (children of $tagName). foreach ($element->children() as $file) { $path = []; $path['src'] = $source . '/' . $file; $path['dest'] = $destination . '/' . $file; // Is this path a file or folder? $path['type'] = $file->getName() === 'folder' ? 'folder' : 'file'; /* * Before we can add a file to the copyfiles array we need to ensure * that the folder we are copying our file to exists and if it doesn't, * we need to create it. */ if (basename($path['dest']) !== $path['dest']) { $newdir = \dirname($path['dest']); if (!Folder::create($newdir)) { Log::add( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_INSTALL'), $newdir ), Log::WARNING, 'jerror' ); return false; } } // Add the file to the copyfiles array $copyfiles[] = $path; } return $this->copyFiles($copyfiles); } /** * Method to parse the parameters of an extension, build the JSON string for its default parameters, and return the JSON string. * * @return string JSON string of parameter values * * @since 3.1 * @note This method must always return a JSON compliant string */ public function getParams() { // Validate that we have a fieldset to use if (!isset($this->manifest->config->fields->fieldset)) { return '{}'; } // Getting the fieldset tags $fieldsets = $this->manifest->config->fields->fieldset; // Creating the data collection variable: $ini = []; // Iterating through the fieldsets: foreach ($fieldsets as $fieldset) { if (!\count($fieldset->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return '{}'; } // Iterating through the fields and collecting the name/default values: foreach ($fieldset as $field) { // Check against the null value since otherwise default values like "0" // cause entire parameters to be skipped. if (($name = $field->attributes()->name) === null) { continue; } if (($value = $field->attributes()->default) === null) { continue; } $ini[(string) $name] = (string) $value; } } return json_encode($ini); } /** * Copyfiles * * Copy files from source directory to the target directory * * @param array $files Array with filenames * @param boolean $overwrite True if existing files can be replaced * * @return boolean True on success * * @since 3.1 */ public function copyFiles($files, $overwrite = null) { /* * To allow for manual override on the overwriting flag, we check to see if * the $overwrite flag was set and is a boolean value. If not, use the object * allowOverwrite flag. */ if ($overwrite === null || !\is_bool($overwrite)) { $overwrite = $this->overwrite; } /* * $files must be an array of filenames. Verify that it is an array with * at least one file to copy. */ if (\is_array($files) && \count($files) > 0) { foreach ($files as $file) { // Get the source and destination paths $filesource = Path::clean($file['src']); $filedest = Path::clean($file['dest']); $filetype = \array_key_exists('type', $file) ? $file['type'] : 'file'; if (!file_exists($filesource)) { /* * The source file does not exist. Nothing to copy so set an error * and return false. */ Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_NO_FILE', $filesource), Log::WARNING, 'jerror'); return false; } elseif (($exists = file_exists($filedest)) && !$overwrite) { // It's okay if the manifest already exists if ($this->getPath('manifest') === $filesource) { continue; } // The destination file already exists and the overwrite flag is false. // Set an error and return false. Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FILE_EXISTS', $filedest), Log::WARNING, 'jerror'); return false; } else { // Copy the folder or file to the new location. if ($filetype === 'folder') { if (!Folder::copy($filesource, $filedest, null, $overwrite)) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FOLDER', $filesource, $filedest), Log::WARNING, 'jerror'); return false; } $step = ['type' => 'folder', 'path' => $filedest]; } else { if (!File::copy($filesource, $filedest, null)) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FILE', $filesource, $filedest), Log::WARNING, 'jerror'); // In 3.2, TinyMCE language handling changed. Display a special notice in case an older language pack is installed. if (strpos($filedest, 'media/editors/tinymce/jscripts/tiny_mce/langs')) { Log::add(Text::_('JLIB_INSTALLER_NOT_ERROR'), Log::WARNING, 'jerror'); } return false; } $step = ['type' => 'file', 'path' => $filedest]; } /* * Since we copied a file/folder, we want to add it to the installation step stack so that * in case we have to roll back the installation we can remove the files copied. */ if (!$exists) { $this->stepStack[] = $step; } } } } else { // The $files variable was either not an array or an empty array return false; } return \count($files); } /** * Method to parse through a files element of the installation manifest and remove * the files that were installed * * @param object $element The XML node to process * @param integer $cid Application ID of application to remove from * * @return boolean True on success * * @since 3.1 */ public function removeFiles($element, $cid = 0) { if (!$element || !\count($element->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return true; } $retval = true; // Get the client info if we're using a specific client if ($cid > -1) { $client = ApplicationHelper::getClientInfo($cid); } else { $client = null; } // Get the array of file nodes to process $files = $element->children(); if (\count($files) === 0) { // No files to process return true; } $folder = ''; /* * Here we set the folder we are going to remove the files from. There are a few * special cases that need to be considered for certain reserved tags. */ switch ($element->getName()) { case 'media': if ((string) $element->attributes()->destination) { $folder = (string) $element->attributes()->destination; } else { $folder = ''; } $source = $client->path . '/media/' . $folder; break; case 'languages': $lang_client = (string) $element->attributes()->client; if ($lang_client) { $client = ApplicationHelper::getClientInfo($lang_client, true); $source = $client->path . '/language'; } else { if ($client) { $source = $client->path . '/language'; } else { $source = ''; } } break; default: if ($client) { $pathname = 'extension_' . $client->name; $source = $this->getPath($pathname); } else { $pathname = 'extension_root'; $source = $this->getPath($pathname); } break; } // Process each file in the $files array (children of $tagName). foreach ($files as $file) { /* * If the file is a language, we must handle it differently. Language files * go in a subdirectory based on the language code, ie. * <language tag="en_US">en_US.mycomponent.ini</language> * would go in the en_US subdirectory of the languages directory. */ if ($file->getName() === 'language' && (string) $file->attributes()->tag !== '') { if ($source) { $path = $source . '/' . $file->attributes()->tag . '/' . basename((string) $file); } else { $target_client = ApplicationHelper::getClientInfo((string) $file->attributes()->client, true); $path = $target_client->path . '/language/' . $file->attributes()->tag . '/' . basename((string) $file); } // If the language folder is not present, then the core pack hasn't been installed... ignore if (!Folder::exists(\dirname($path))) { continue; } } else { $path = $source . '/' . $file; } // Actually delete the files/folders if (is_dir($path)) { $val = Folder::delete($path); } else { $val = File::delete($path); } if ($val === false) { Log::add('Failed to delete ' . $path, Log::WARNING, 'jerror'); $retval = false; } } if (!empty($folder)) { Folder::delete($source); } return $retval; } /** * Copies the installation manifest file to the extension folder in the given client * * @param integer $cid Where to copy the installfile [optional: defaults to 1 (admin)] * * @return boolean True on success, False on error * * @since 3.1 */ public function copyManifest($cid = 1) { // Get the client info $client = ApplicationHelper::getClientInfo($cid); $path = ['src' => $this->getPath('manifest')]; if ($client) { $pathname = 'extension_' . $client->name; $path['dest'] = $this->getPath($pathname) . '/' . basename($this->getPath('manifest')); } else { $pathname = 'extension_root'; $path['dest'] = $this->getPath($pathname) . '/' . basename($this->getPath('manifest')); } return $this->copyFiles([$path], true); } /** * Tries to find the package manifest file * * @return boolean True on success, False on error * * @since 3.1 */ public function findManifest() { // Do nothing if folder does not exist for some reason if (!Folder::exists($this->getPath('source'))) { return false; } // Main folder manifests (higher priority) $parentXmlfiles = Folder::files($this->getPath('source'), '.xml$', false, true); // Search for children manifests (lower priority) $allXmlFiles = Folder::files($this->getPath('source'), '.xml$', 1, true); // Create an unique array of files ordered by priority $xmlfiles = array_unique(array_merge($parentXmlfiles, $allXmlFiles)); // If at least one XML file exists if (!empty($xmlfiles)) { foreach ($xmlfiles as $file) { // Is it a valid Joomla installation manifest file? $manifest = $this->isManifest($file); if ($manifest !== null) { // If the root method attribute is set to upgrade, allow file overwrite if ((string) $manifest->attributes()->method === 'upgrade') { $this->upgrade = true; $this->overwrite = true; } // If the overwrite option is set, allow file overwriting if ((string) $manifest->attributes()->overwrite === 'true') { $this->overwrite = true; } // Set the manifest object and path $this->manifest = $manifest; $this->setPath('manifest', $file); // Set the installation source path to that of the manifest file $this->setPath('source', \dirname($file)); return true; } } // None of the XML files found were valid install files Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE'), Log::WARNING, 'jerror'); return false; } else { // No XML files were found in the install folder Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE'), Log::WARNING, 'jerror'); return false; } } /** * Is the XML file a valid Joomla installation manifest file. * * @param string $file An xmlfile path to check * * @return \SimpleXMLElement|null A \SimpleXMLElement, or null if the file failed to parse * * @since 3.1 */ public function isManifest($file) { $xml = simplexml_load_file($file); // If we cannot load the XML file return null if (!$xml) { return; } // Check for a valid XML root tag. if ($xml->getName() !== 'extension') { return; } // Valid manifest file return the object return $xml; } /** * Generates a manifest cache * * @return string serialised manifest data * * @since 3.1 */ public function generateManifestCache() { return json_encode(self::parseXMLInstallFile($this->getPath('manifest'))); } /** * Cleans up discovered extensions if they're being installed some other way * * @param string $type The type of extension (component, etc) * @param string $element Unique element identifier (e.g. com_content) * @param string $folder The folder of the extension (plugins; e.g. system) * @param integer $client The client application (administrator or site) * * @return object Result of query * * @since 3.1 */ public function cleanDiscoveredExtension($type, $element, $folder = '', $client = 0) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->delete($db->quoteName('#__extensions')) ->where('type = :type') ->where('element = :element') ->where('folder = :folder') ->where('client_id = :client_id') ->where('state = -1') ->bind(':type', $type) ->bind(':element', $element) ->bind(':folder', $folder) ->bind(':client_id', $client, ParameterType::INTEGER); $db->setQuery($query); return $db->execute(); } /** * Compares two "files" entries to find deleted files/folders * * @param array $oldFiles An array of \SimpleXMLElement objects that are the old files * @param array $newFiles An array of \SimpleXMLElement objects that are the new files * * @return array An array with the delete files and folders in findDeletedFiles[files] and findDeletedFiles[folders] respectively * * @since 3.1 */ public function findDeletedFiles($oldFiles, $newFiles) { // The magic find deleted files function! // The files that are new $files = []; // The folders that are new $folders = []; // The folders of the files that are new $containers = []; // A list of files to delete $files_deleted = []; // A list of folders to delete $folders_deleted = []; foreach ($newFiles as $file) { switch ($file->getName()) { case 'folder': // Add any folders to the list $folders[] = (string) $file; break; case 'file': default: // Add any files to the list $files[] = (string) $file; // Now handle the folder part of the file to ensure we get any containers // Break up the parts of the directory $container_parts = explode('/', \dirname((string) $file)); // Make sure this is clean and empty $container = ''; foreach ($container_parts as $part) { // Iterate through each part // Add a slash if its not empty if (!empty($container)) { $container .= '/'; } // Append the folder part $container .= $part; if (!\in_array($container, $containers)) { // Add the container if it doesn't already exist $containers[] = $container; } } break; } } foreach ($oldFiles as $file) { switch ($file->getName()) { case 'folder': if (!\in_array((string) $file, $folders)) { // See whether the folder exists in the new list if (!\in_array((string) $file, $containers)) { // Check if the folder exists as a container in the new list // If it's not in the new list or a container then delete it $folders_deleted[] = (string) $file; } } break; case 'file': default: if (!\in_array((string) $file, $files)) { // Look if the file exists in the new list if (!\in_array(\dirname((string) $file), $folders)) { // Look if the file is now potentially in a folder $files_deleted[] = (string) $file; } } break; } } return ['files' => $files_deleted, 'folders' => $folders_deleted]; } /** * Loads an MD5SUMS file into an associative array * * @param string $filename Filename to load * * @return array Associative array with filenames as the index and the MD5 as the value * * @since 3.1 */ public function loadMD5Sum($filename) { if (!file_exists($filename)) { // Bail if the file doesn't exist return false; } $data = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $retval = []; foreach ($data as $row) { // Split up the data $results = explode(' ', $row); // Cull any potential prefix $results[1] = str_replace('./', '', $results[1]); // Throw into the array $retval[$results[1]] = $results[0]; } return $retval; } /** * Parse a XML install manifest file. * * XML Root tag should be 'install' except for languages which use meta file. * * @param string $path Full path to XML file. * * @return array XML metadata. * * @since 3.0.0 */ public static function parseXMLInstallFile($path) { // Check if xml file exists. if (!file_exists($path)) { return false; } // Read the file to see if it's a valid component XML file $xml = simplexml_load_file($path); if (!$xml) { return false; } // Check for a valid XML root tag. // Extensions use 'extension' as the root tag. Languages use 'metafile' instead $name = $xml->getName(); if ($name !== 'extension' && $name !== 'metafile') { unset($xml); return false; } $data = []; $data['name'] = (string) $xml->name; // Check if we're a language. If so use metafile. $data['type'] = $xml->getName() === 'metafile' ? 'language' : (string) $xml->attributes()->type; $data['creationDate'] = ((string) $xml->creationDate) ?: Text::_('JLIB_UNKNOWN'); $data['author'] = ((string) $xml->author) ?: Text::_('JLIB_UNKNOWN'); $data['copyright'] = (string) $xml->copyright; $data['authorEmail'] = (string) $xml->authorEmail; $data['authorUrl'] = (string) $xml->authorUrl; $data['version'] = (string) $xml->version; $data['description'] = (string) $xml->description; $data['group'] = (string) $xml->group; // Child template specific fields. if (isset($xml->inheritable)) { $data['inheritable'] = (string) $xml->inheritable === '0' ? false : true; } // Child template specific fields. if (isset($xml->namespace) && (string) $xml->namespace !== '') { $data['namespace'] = (string) $xml->namespace; } if (isset($xml->parent) && (string) $xml->parent !== '') { $data['parent'] = (string) $xml->parent; } if ($xml->files && \count($xml->files->children())) { $filename = basename($path); $data['filename'] = File::stripExt($filename); foreach ($xml->files->children() as $oneFile) { if ((string) $oneFile->attributes()->plugin) { $data['filename'] = (string) $oneFile->attributes()->plugin; break; } } } return $data; } /** * Gets a list of available install adapters. * * @param array $options An array of options to inject into the adapter * @param array $custom Array of custom install adapters * * @return string[] An array of the class names of available install adapters. * * @since 3.4 */ public function getAdapters($options = [], array $custom = []) { $files = new \DirectoryIterator($this->_basepath . '/' . $this->_adapterfolder); $adapters = []; // Process the core adapters foreach ($files as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() !== 'php') { continue; } // Derive the class name from the filename. $name = str_ireplace('.php', '', trim($fileName)); $name = str_ireplace('adapter', '', trim($name)); $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name) . 'Adapter'; if (!class_exists($class)) { // Not namespaced $class = $this->_classprefix . ucfirst($name); } // Core adapters should autoload based on classname, keep this fallback just in case if (!class_exists($class)) { // Try to load the adapter object \JLoader::register($class, $this->_basepath . '/' . $this->_adapterfolder . '/' . $fileName); if (!class_exists($class)) { // Skip to next one continue; } } $adapters[] = $name; } // Add any custom adapters if specified if (\count($custom) >= 1) { foreach ($custom as $adapter) { // Setup the class name // TODO - Can we abstract this to not depend on the Joomla class namespace without PHP namespaces? $class = $this->_classprefix . ucfirst(trim($adapter)); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } $adapters[] = str_ireplace('.php', '', $fileName); } } return $adapters; } /** * Method to load an adapter instance * * @param string $adapter Adapter name * @param array $options Adapter options * * @return InstallerAdapter * * @since 3.4 * @throws \InvalidArgumentException */ public function loadAdapter($adapter, $options = []) { $class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($adapter) . 'Adapter'; if (!class_exists($class)) { // Not namespaced $class = $this->_classprefix . ucfirst($adapter); } if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf('The %s install adapter does not exist.', $adapter)); } // Ensure the adapter type is part of the options array $options['type'] = $adapter; // Check for a possible service from the container otherwise manually instantiate the class if (Factory::getContainer()->has($class)) { return Factory::getContainer()->get($class); } $adapter = new $class($this, $this->getDatabase(), $options); if ($adapter instanceof ContainerAwareInterface) { $adapter->setContainer(Factory::getContainer()); } return $adapter; } } PK1A#]i�)S S *src/Installer/Manifest/LibraryManifest.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Manifest; use Joomla\CMS\Installer\Manifest; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Library Manifest File * * @since 3.1 */ class LibraryManifest extends Manifest { /** * Creation date of the library * * @var string * @since 4.3.0 */ public $creationdate; /** * File system name of the library * * @var string * @since 3.1 */ public $libraryname = ''; /** * Creation Date of the library * * @var string * @since 3.1 */ public $creationDate = ''; /** * Copyright notice for the library * * @var string * @since 3.1 */ public $copyright = ''; /** * License for the library * * @var string * @since 3.1 */ public $license = ''; /** * Author for the library * * @var string * @since 3.1 */ public $author = ''; /** * Author email for the library * * @var string * @since 3.1 */ public $authoremail = ''; /** * Author URL for the library * * @var string * @since 3.1 */ public $authorurl = ''; /** * Apply manifest data from a \SimpleXMLElement to the object. * * @param \SimpleXMLElement $xml Data to load * * @return void * * @since 3.1 */ protected function loadManifestFromData(\SimpleXMLElement $xml) { $this->name = (string) $xml->name; $this->libraryname = (string) $xml->libraryname; $this->version = (string) $xml->version; $this->description = (string) $xml->description; $this->creationdate = (string) $xml->creationDate; $this->author = (string) $xml->author; $this->authoremail = (string) $xml->authorEmail; $this->authorurl = (string) $xml->authorUrl; $this->packager = (string) $xml->packager; $this->packagerurl = (string) $xml->packagerurl; $this->update = (string) $xml->update; if (isset($xml->files) && isset($xml->files->file) && \count($xml->files->file)) { foreach ($xml->files->file as $file) { $this->filelist[] = (string) $file; } } } } PK1A#]��">��*src/Installer/Manifest/PackageManifest.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Manifest; use Joomla\CMS\Installer\InstallerExtension; use Joomla\CMS\Installer\Manifest; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Package Manifest File * * @since 3.1 */ class PackageManifest extends Manifest { /** * Author url of the package * * @var string * @since 4.3.0 */ public $authorurl; /** * Author of the package * * @var string * @since 4.3.0 */ public $author; /** * Author email of the package * * @var string * @since 4.3.0 */ public $authoremail; /** * Unique name of the package * * @var string * @since 3.1 */ public $packagename = ''; /** * Website for the package * * @var string * @since 3.1 */ public $url = ''; /** * Scriptfile for the package * * @var string * @since 3.1 */ public $scriptfile = ''; /** * Flag if the package blocks individual child extensions from being uninstalled * * @var boolean * @since 3.7.0 */ public $blockChildUninstall = false; /** * Apply manifest data from a \SimpleXMLElement to the object. * * @param \SimpleXMLElement $xml Data to load * * @return void * * @since 3.1 */ protected function loadManifestFromData(\SimpleXMLElement $xml) { $this->name = (string) $xml->name; $this->packagename = (string) $xml->packagename; $this->update = (string) $xml->update; $this->authorurl = (string) $xml->authorUrl; $this->author = (string) $xml->author; $this->authoremail = (string) $xml->authorEmail; $this->description = (string) $xml->description; $this->packager = (string) $xml->packager; $this->packagerurl = (string) $xml->packagerurl; $this->scriptfile = (string) $xml->scriptfile; $this->version = (string) $xml->version; if (isset($xml->blockChildUninstall)) { $value = (string) $xml->blockChildUninstall; if ($value === '1' || $value === 'true') { $this->blockChildUninstall = true; } } if (isset($xml->files->file) && \count($xml->files->file)) { foreach ($xml->files->file as $file) { // NOTE: JInstallerExtension doesn't expect a string. // DO NOT CAST $file $this->filelist[] = new InstallerExtension($file); } } // Handle cases where package contains folders if (isset($xml->files->folder) && \count($xml->files->folder)) { foreach ($xml->files->folder as $folder) { // NOTE: JInstallerExtension doesn't expect a string. // DO NOT CAST $folder $this->filelist[] = new InstallerExtension($folder); } } } } PK1A#])�\\)src/Installer/Adapter/TemplateAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Template installer * * @since 3.1 */ class TemplateAdapter extends InstallerAdapter { /** * The install client ID * * @var integer * @since 3.4 */ protected $clientId; /** * Method to check if the extension is already present in the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExistingExtension() { try { $this->currentExtensionId = $this->extension->find( [ 'element' => $this->element, 'type' => $this->type, 'client_id' => $this->clientId, ] ); } catch (\RuntimeException $e) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $e->getMessage() ), $e->getCode(), $e ); } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // Copy all the necessary files if ($this->parent->parseFiles($this->getManifest()->files, -1) === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 'files' ) ); } if ($this->parent->parseFiles($this->getManifest()->images, -1) === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 'images' ) ); } if ($this->parent->parseFiles($this->getManifest()->css, -1) === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 'css' ) ); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->getRoute())) ) ); } } } } /** * Method to finalise the installation processing * * @return void * * @since 3.1 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates /** @var Update $update */ $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => $this->type, 'client_id' => $this->clientId, ] ); if ($uid) { $update->delete($uid); } // Lastly, we will copy the manifest file to its appropriate place. if ($this->route !== 'discover_install') { if (!$this->parent->copyManifest(-1)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $db = $this->getDatabase(); $query = $db->getQuery(true); $element = $this->extension->element; $clientId = $this->extension->client_id; $extensionId = $this->extension->extension_id; // Set menu that assigned to the template back to default template $subQuery = $db->getQuery(true) ->select($db->quoteName('s.id')) ->from($db->quoteName('#__template_styles', 's')) ->where( [ $db->quoteName('s.template') . ' = :element', $db->quoteName('s.client_id') . ' = :clientId', ] ); $query->bind(':element', $element) ->bind(':clientId', $clientId, ParameterType::INTEGER); $query->update($db->quoteName('#__menu')) ->set($db->quoteName('template_style_id') . ' = 0') ->where($db->quoteName('template_style_id') . ' IN (' . (string) $subQuery . ')'); $db->setQuery($query); $db->execute(); // Remove the template's styles $query = $db->getQuery(true) ->delete($db->quoteName('#__template_styles')) ->where( [ $db->quoteName('template') . ' = :template', $db->quoteName('client_id') . ' = :client_id', ] ) ->bind(':template', $element) ->bind(':client_id', $clientId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Remove the schema version $query = $db->getQuery(true) ->delete($db->quoteName('#__schemas')) ->where($db->quoteName('extension_id') . ' = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Remove any overrides $query = $db->getQuery(true) ->delete($db->quoteName('#__template_overrides')) ->where($db->quoteName('template') . ' = :template') ->bind(':template', $element); $db->setQuery($query); $db->execute(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => $this->type, 'client_id' => $this->extension->client_id, ] ); if ($uid) { $update->delete($uid); } $this->extension->delete(); return true; } /** * Custom loadLanguage method * * @param string $path The path where to find language files. * * @return void * * @since 3.1 */ public function loadLanguage($path = null) { $source = $this->parent->getPath('source'); $basePath = $this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE; if (!$source) { $this->parent->setPath('source', $basePath . '/templates/' . $this->parent->extension->element); } $this->setManifest($this->parent->getManifest()); $client = (string) $this->getManifest()->attributes()->client; // Load administrator language if not set. if (!$client) { $client = 'ADMINISTRATOR'; } $base = \constant('JPATH_' . strtoupper($client)); $extension = 'tpl_' . $this->getName(); $source = $path ?: $base . '/templates/' . $this->getName(); $this->doLoadLanguage($extension, $source, $base); } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { $this->parent->parseMedia($this->getManifest()->media); $this->parent->parseLanguages($this->getManifest()->languages, $this->clientId); } /** * Overloaded method to parse queries for template installations * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function parseQueries() { if (\in_array($this->route, ['install', 'discover_install'])) { $db = $this->getDatabase(); $query = $db->getQuery(true); $lang = Factory::getLanguage(); $debug = $lang->setDebug(false); $columns = [ $db->quoteName('template'), $db->quoteName('client_id'), $db->quoteName('home'), $db->quoteName('title'), $db->quoteName('params'), $db->quoteName('inheritable'), $db->quoteName('parent'), ]; $values = $query->bindArray( [ $this->extension->element, $this->extension->client_id, '0', Text::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', Text::_($this->extension->name)), $this->extension->params, (int) $this->manifest->inheritable, (string) $this->manifest->parent ?: '', ], [ ParameterType::STRING, ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER, ParameterType::STRING, ] ); $lang->setDebug($debug); // Insert record in #__template_styles $query->insert($db->quoteName('#__template_styles')) ->columns($columns) ->values(implode(',', $values)); // There is a chance this could fail but we don't care... $db->setQuery($query)->execute(); } } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 */ public function prepareDiscoverInstall() { $client = ApplicationHelper::getClientInfo($this->extension->client_id); $manifestPath = $client->path . '/templates/' . $this->extension->element . '/templateDetails.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->setManifest($this->parent->getManifest()); } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { // Remove files $this->parent->removeFiles($this->getManifest()->media); $this->parent->removeFiles($this->getManifest()->languages, $this->extension->client_id); // Delete the template directory if (Folder::exists($this->parent->getPath('extension_root'))) { Folder::delete($this->parent->getPath('extension_root')); } else { Log::add(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DIRECTORY'), Log::WARNING, 'jerror'); } } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { // Get the client application target $cname = (string) $this->getManifest()->attributes()->client; if ($cname) { // Attempt to map the client to a base path $client = ApplicationHelper::getClientInfo($cname, true); if ($client === false) { throw new \RuntimeException(Text::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_UNKNOWN_CLIENT', $cname)); } $basePath = $client->path; $this->clientId = $client->id; } else { // No client attribute was found so we assume the site as the client $basePath = JPATH_SITE; $this->clientId = 0; } // Set the template root path if (empty($this->element)) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } $this->parent->setPath('extension_root', $basePath . '/templates/' . $this->element); } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { $this->parent->extension = $this->extension; $db = $this->getDatabase(); $name = $this->extension->element; $clientId = $this->extension->client_id; // For a template the id will be the template name which represents the subfolder of the templates folder that the template resides in. if (!$name) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_ID_EMPTY')); } // Deny removing a parent template if there are children $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->quoteName('#__template_styles')) ->where( [ $db->quoteName('parent') . ' = :template', $db->quoteName('client_id') . ' = :client_id', ] ) ->bind(':template', $name) ->bind(':client_id', $clientId); $db->setQuery($query); if ($db->loadResult() != 0) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_PARENT_TEMPLATE')); } // Deny remove default template $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->quoteName('#__template_styles')) ->where( [ $db->quoteName('home') . ' = ' . $db->quote('1'), $db->quoteName('template') . ' = :template', $db->quoteName('client_id') . ' = :client_id', ] ) ->bind(':template', $name) ->bind(':client_id', $clientId); $db->setQuery($query); if ($db->loadResult() != 0) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DEFAULT')); } // Get the template root path $client = ApplicationHelper::getClientInfo($clientId); if (!$client) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_CLIENT')); } $this->parent->setPath('extension_root', $client->path . '/templates/' . strtolower($name)); $this->parent->setPath('source', $this->parent->getPath('extension_root')); // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file $this->parent->findManifest(); $manifest = $this->parent->getManifest(); if (!($manifest instanceof \SimpleXMLElement)) { // Kill the extension entry $this->extension->delete($this->extension->extension_id); // Make sure we delete the folders Folder::delete($this->parent->getPath('extension_root')); throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); } // Attempt to load the language file; might have uninstall strings $this->loadLanguage(); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { // Discover installs are stored a little differently if ($this->route === 'discover_install') { $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->extension->manifest_cache = json_encode($manifest_details); $this->extension->state = 0; $this->extension->name = $manifest_details['name']; $this->extension->enabled = 1; $this->extension->params = $this->parent->getParams(); $this->extension->changelogurl = (string) $this->manifest->changelogurl; if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_DISCOVER_STORE_DETAILS')); } return; } // Was there a template already installed with the same name? if ($this->currentExtensionId) { if (!$this->parent->isOverwrite()) { // Install failed, roll back changes throw new \RuntimeException( Text::_('JLIB_INSTALLER_ABORT_TPL_INSTALL_ALREADY_INSTALLED') ); } // Load the entry and update the manifest_cache $this->extension->load($this->currentExtensionId); } else { $this->extension->type = 'template'; $this->extension->element = $this->element; // There is no folder for templates $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = 1; $this->extension->client_id = $this->clientId; $this->extension->params = $this->parent->getParams(); $this->extension->changelogurl = $this->changelogurl; } // Name might change in an update $this->extension->name = $this->name; // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); $this->extension->changelogurl = $this->changelogurl; if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->extension->getError() ) ); } } /** * Discover existing but uninstalled templates * * @return array Extension list */ public function discover() { $results = []; $site_list = Folder::folders(JPATH_SITE . '/templates'); $admin_list = Folder::folders(JPATH_ADMINISTRATOR . '/templates'); $site_info = ApplicationHelper::getClientInfo('site', true); $admin_info = ApplicationHelper::getClientInfo('administrator', true); foreach ($site_list as $template) { if (file_exists(JPATH_SITE . "/templates/$template/templateDetails.xml")) { if ($template === 'system') { // Ignore special system template continue; } $manifest_details = Installer::parseXMLInstallFile(JPATH_SITE . "/templates/$template/templateDetails.xml"); $extension = Table::getInstance('extension'); $extension->set('type', 'template'); $extension->set('client_id', $site_info->id); $extension->set('element', $template); $extension->set('folder', ''); $extension->set('name', $template); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } foreach ($admin_list as $template) { if (file_exists(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml")) { if ($template === 'system') { // Ignore special system template continue; } $manifest_details = Installer::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml"); $extension = Table::getInstance('extension'); $extension->set('type', 'template'); $extension->set('client_id', $admin_info->id); $extension->set('element', $template); $extension->set('folder', ''); $extension->set('name', $template); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } return $results; } /** * Refreshes the extension table cache * * @return boolean Result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { // Need to find to find where the XML file is since we don't store this normally. $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; try { return $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_TPL_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } } PK1A#]ܳ�t^^'src/Installer/Adapter/ModuleAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module installer * * @since 3.1 */ class ModuleAdapter extends InstallerAdapter { /** * The install client ID * * @var integer * @since 3.4 */ protected $clientId; /** * `<scriptfile>` element of the extension manifest * * @var object * @since 3.1 */ protected $scriptElement = null; /** * Method to check if the extension is already present in the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExistingExtension() { try { $this->currentExtensionId = $this->extension->find( [ 'element' => $this->element, 'type' => $this->type, 'client_id' => $this->clientId, ] ); } catch (\RuntimeException $e) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $e->getMessage() ), $e->getCode(), $e ); } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // Copy all necessary files if ($this->parent->parseFiles($this->getManifest()->files, -1) === false) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_MOD_COPY_FILES')); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } } /** * Custom discover method * * @return array Extension list of extensions available * * @since 3.1 */ public function discover() { $results = []; $site_list = Folder::folders(JPATH_SITE . '/modules'); $admin_list = Folder::folders(JPATH_ADMINISTRATOR . '/modules'); $site_info = ApplicationHelper::getClientInfo('site', true); $admin_info = ApplicationHelper::getClientInfo('administrator', true); foreach ($site_list as $module) { if (file_exists(JPATH_SITE . "/modules/$module/$module.xml")) { $manifest_details = Installer::parseXMLInstallFile(JPATH_SITE . "/modules/$module/$module.xml"); $extension = Table::getInstance('extension'); $extension->set('type', 'module'); $extension->set('client_id', $site_info->id); $extension->set('element', $module); $extension->set('folder', ''); $extension->set('name', $module); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = clone $extension; } } foreach ($admin_list as $module) { if (file_exists(JPATH_ADMINISTRATOR . "/modules/$module/$module.xml")) { $manifest_details = Installer::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/modules/$module/$module.xml"); $extension = Table::getInstance('extension'); $extension->set('type', 'module'); $extension->set('client_id', $admin_info->id); $extension->set('element', $module); $extension->set('folder', ''); $extension->set('name', $module); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = clone $extension; } } return $results; } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => 'module', 'client_id' => $this->clientId, ] ); if ($uid) { $update->delete($uid); } // Lastly, we will copy the manifest file to its appropriate place. if ($this->route !== 'discover_install') { if (!$this->parent->copyManifest(-1)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $extensionId = $this->extension->extension_id; $db = $this->getDatabase(); $retval = true; // Remove the schema version $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); $element = $this->extension->element; $clientId = $this->extension->client_id; // Let's delete all the module copies for the type we are uninstalling $query->clear() ->select($db->quoteName('id')) ->from($db->quoteName('#__modules')) ->where($db->quoteName('module') . ' = :element') ->where($db->quoteName('client_id') . ' = :client_id') ->bind(':element', $element) ->bind(':client_id', $clientId, ParameterType::INTEGER); $db->setQuery($query); try { $modules = $db->loadColumn(); } catch (\RuntimeException $e) { $modules = []; } // Do we have any module copies? if (\count($modules)) { // Ensure the list is sane $modules = ArrayHelper::toInteger($modules); // Wipe out any items assigned to menus $query = $db->getQuery(true) ->delete($db->quoteName('#__modules_menu')) ->whereIn($db->quoteName('moduleid'), $modules); $db->setQuery($query); try { $db->execute(); } catch (\RuntimeException $e) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $e->getMessage()), Log::WARNING, 'jerror'); $retval = false; } // Wipe out any instances in the modules table /** @var \Joomla\CMS\Table\Module $module */ $module = Table::getInstance('Module'); foreach ($modules as $modInstanceId) { $module->load($modInstanceId); if (!$module->delete()) { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $module->getError()), Log::WARNING, 'jerror'); $retval = false; } } } // Now we will no longer need the module object, so let's delete it and free up memory $this->extension->delete($this->extension->extension_id); $query = $db->getQuery(true) ->delete($db->quoteName('#__modules')) ->where($db->quoteName('module') . ' = :element') ->where($db->quoteName('client_id') . ' = :client_id') ->bind(':element', $element) ->bind(':client_id', $clientId, ParameterType::INTEGER); $db->setQuery($query); try { // Clean up any other ones that might exist as well $db->execute(); } catch (\RuntimeException $e) { // Ignore the error... } // Remove the installation folder if (!Folder::delete($this->parent->getPath('extension_root'))) { // Folder should raise an error $retval = false; } return $retval; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string|null The filtered element * * @since 3.4 */ public function getElement($element = null) { if ($element) { return $element; } // Joomla 4 Module. if ((string) $this->getManifest()->element) { return (string) $this->getManifest()->element; } if (!\count($this->getManifest()->files->children())) { return $element; } foreach ($this->getManifest()->files->children() as $file) { if ((string) $file->attributes()->module) { // Joomla 3 (legacy) Module. return strtolower((string) $file->attributes()->module); } } return $element; } /** * Custom loadLanguage method * * @param string $path The path where we find language files * * @return void * * @since 3.4 */ public function loadLanguage($path = null) { $source = $this->parent->getPath('source'); $client = $this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE; if (!$source) { $this->parent->setPath('source', $client . '/modules/' . $this->parent->extension->element); } $this->setManifest($this->parent->getManifest()); if ($this->getManifest()->files) { $extension = $this->getElement(); if ($extension) { $source = $path ?: ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $extension; $folder = (string) $this->getManifest()->files->attributes()->folder; if ($folder && file_exists($path . '/' . $folder)) { $source = $path . '/' . $folder; } $client = (string) $this->getManifest()->attributes()->client ?: 'site'; $this->doLoadLanguage($extension, $source, \constant('JPATH_' . strtoupper($client))); } } } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { // Parse optional tags $this->parent->parseMedia($this->getManifest()->media, $this->clientId); $this->parent->parseLanguages($this->getManifest()->languages, $this->clientId); } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 */ public function prepareDiscoverInstall() { $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->setManifest($this->parent->getManifest()); } /** * Refreshes the extension table cache * * @return boolean Result of operation, true if updated, false on failure. * * @since 3.1 */ public function refreshManifestCache() { $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; if ($this->parent->extension->store()) { return true; } else { Log::add(Text::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { $this->parent->removeFiles($this->getManifest()->media); $this->parent->removeFiles($this->getManifest()->languages, $this->extension->client_id); } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { // Get the target application $cname = (string) $this->getManifest()->attributes()->client; if ($cname) { // Attempt to map the client to a base path $client = ApplicationHelper::getClientInfo($cname, true); if ($client === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MOD_UNKNOWN_CLIENT', Text::_('JLIB_INSTALLER_' . $this->route), $client->name ) ); } $basePath = $client->path; $this->clientId = $client->id; } else { // No client attribute was found so we assume the site as the client $basePath = JPATH_SITE; $this->clientId = 0; } // Set the installation path if (empty($this->element)) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } $this->parent->setPath('extension_root', $basePath . '/modules/' . $this->element); } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { // Get the extension root path $element = $this->extension->element; $client = ApplicationHelper::getClientInfo($this->extension->client_id); if ($client === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ERROR_MOD_UNINSTALL_UNKNOWN_CLIENT', $this->extension->client_id ) ); } $this->parent->setPath('extension_root', $client->path . '/modules/' . $element); $this->parent->setPath('source', $this->parent->getPath('extension_root')); // Get the module's manifest object // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file. $this->parent->findManifest(); $this->setManifest($this->parent->getManifest()); // Attempt to load the language file; might have uninstall strings $this->loadLanguage(($this->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $element); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { // Discover installs are stored a little differently if ($this->route === 'discover_install') { $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->extension->manifest_cache = json_encode($manifest_details); $this->extension->state = 0; $this->extension->name = $manifest_details['name']; $this->extension->enabled = 1; $this->extension->params = $this->parent->getParams(); $this->extension->changelogurl = (string) $this->manifest->changelogurl; if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_MOD_DISCOVER_STORE_DETAILS')); } return; } // Was there a module already installed with the same name? if ($this->currentExtensionId) { if (!$this->parent->isOverwrite()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ALREADY_EXISTS', Text::_('JLIB_INSTALLER_' . $this->route), $this->name ) ); } // Load the entry and update the manifest_cache $this->extension->load($this->currentExtensionId); // Update name $this->extension->name = $this->name; // Update namespace $this->extension->namespace = (string) $this->manifest->namespace; // Update changelogurl $this->extension->changelogurl = (string) $this->manifest->changelogurl; // Update manifest $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MOD_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $this->extension->getError() ) ); } } else { $this->extension->name = $this->name; $this->extension->type = 'module'; $this->extension->element = $this->element; $this->extension->namespace = (string) $this->manifest->namespace; $this->extension->changelogurl = $this->changelogurl; // There is no folder for modules $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = $this->clientId == 1 ? 2 : 0; $this->extension->client_id = $this->clientId; $this->extension->params = $this->parent->getParams(); // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MOD_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $this->extension->getError() ) ); } // Since we have created a module item, we add it to the installation step stack // so that if we have to rollback the changes we can undo it. $this->parent->pushStep( [ 'type' => 'extension', 'extension_id' => $this->extension->extension_id, ] ); // Create unpublished module $name = preg_replace('#[\*?]#', '', Text::_($this->name)); /** @var \Joomla\CMS\Table\Module $module */ $module = Table::getInstance('module'); $module->title = $name; $module->content = ''; $module->module = $this->element; $module->access = '1'; $module->showtitle = '1'; $module->params = ''; $module->client_id = $this->clientId; $module->language = '*'; $module->position = ''; $module->store(); } } /** * Custom rollback method * - Roll back the menu item * * @param array $arg Installation step to rollback * * @return boolean True on success * * @since 3.1 */ protected function _rollback_menu($arg) { // Get database connector object $db = $this->getDatabase(); $moduleId = $arg['id']; // Remove the entry from the #__modules_menu table $query = $db->getQuery(true) ->delete($db->quoteName('#__modules_menu')) ->where($db->quoteName('moduleid') . ' = :module_id') ->bind(':module_id', $moduleId, ParameterType::INTEGER); $db->setQuery($query); try { return $db->execute(); } catch (\RuntimeException $e) { return false; } } /** * Custom rollback method * - Roll back the module item * * @param array $arg Installation step to rollback * * @return boolean True on success * * @since 3.1 */ protected function _rollback_module($arg) { // Get database connector object $db = $this->getDatabase(); $moduleId = $arg['id']; // Remove the entry from the #__modules table $query = $db->getQuery(true) ->delete($db->quoteName('#__modules')) ->where($db->quoteName('id') . ' = :module_id') ->bind(':module_id', $moduleId, ParameterType::INTEGER); $db->setQuery($query); try { return $db->execute(); } catch (\RuntimeException $e) { return false; } } } PK1A#]2��n._._(src/Installer/Adapter/PackageAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Installer\InstallerHelper; use Joomla\CMS\Installer\Manifest\PackageManifest; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Event\Event; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Package installer * * @since 3.1 */ class PackageAdapter extends InstallerAdapter { /** * An array of extension IDs for each installed extension * * @var array * @since 3.7.0 */ protected $installedIds = []; /** * The results of each installed extensions * * @var array * @since 3.1 */ protected $results = []; /** * Flag if the adapter supports discover installs * * Adapters should override this and set to false if discover install is unsupported * * @var boolean * @since 3.4 */ protected $supportsDiscoverInstall = false; /** * Method to check if the extension is present in the filesystem, flags the route as update if so * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExtensionInFilesystem() { // If the package manifest already exists, then we will assume that the package is already installed. if (file_exists(JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest')))) { // Look for an update function or update tag $updateElement = $this->manifest->update; // Upgrade manually set or update function available or update tag detected if ( $updateElement || $this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) ) { // Force this one $this->parent->setOverwrite(true); $this->parent->setUpgrade(true); if ($this->currentExtensionId) { // If there is a matching extension mark this as an update $this->setRoute('update'); } } elseif (!$this->parent->isOverwrite()) { // We didn't have overwrite set, find an update function or find an update tag so lets call it safe throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_DIRECTORY', Text::_('JLIB_INSTALLER_' . $this->route), $this->type, $this->parent->getPath('extension_root') ) ); } } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { $folder = (string) $this->getManifest()->files->attributes()->folder; $source = $this->parent->getPath('source'); if ($folder) { $source .= '/' . $folder; } // Install all necessary files if (!\count($this->getManifest()->files->children())) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } $dispatcher = Factory::getApplication()->getDispatcher(); // Add a callback for the `onExtensionAfterInstall` event so we can receive the installed extension ID if (!$dispatcher->hasListener([$this, 'onExtensionAfterInstall'], 'onExtensionAfterInstall')) { $dispatcher->addListener('onExtensionAfterInstall', [$this, 'onExtensionAfterInstall']); } foreach ($this->getManifest()->files->children() as $child) { $file = $source . '/' . (string) $child; if (is_dir($file)) { // If it's actually a directory then fill it up $package = []; $package['dir'] = $file; $package['type'] = InstallerHelper::detectType($file); } else { // If it's an archive $package = InstallerHelper::unpack($file); } $tmpInstaller = new Installer(); $tmpInstaller->setDatabase($this->getDatabase()); $installResult = $tmpInstaller->install($package['dir']); if (!$installResult) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), basename($file) ) ); } $this->results[] = [ 'name' => (string) $tmpInstaller->manifest->name, 'result' => $installResult, ]; } } /** * Method to create the extension root path if necessary * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function createExtensionRoot() { /* * For packages, we only need the extension root if copying manifest files; this step will be handled * at that point if necessary */ } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates /** @var Update $update */ $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } // Set the package ID for each of the installed extensions to track the relationship if (!empty($this->installedIds)) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('package_id') . ' = :id') ->whereIn($db->quoteName('extension_id'), $this->installedIds) ->bind(':id', $this->extension->extension_id, ParameterType::INTEGER); try { $db->setQuery($query)->execute(); } catch (ExecutionFailureException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_PACK_SETTING_PACKAGE_ID'), Log::WARNING, 'jerror'); } } // Lastly, we will copy the manifest file to its appropriate place. $manifest = []; $manifest['src'] = $this->parent->getPath('manifest'); $manifest['dest'] = JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest')); if (!$this->parent->copyFiles([$manifest], true)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { // First, we have to create a folder for the script if one isn't present if (!file_exists($this->parent->getPath('extension_root'))) { if (!Folder::create($this->parent->getPath('extension_root'))) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_' . $this->route), $this->parent->getPath('extension_root') ) ); } /* * Since we created the extension directory and will want to remove it if * we have to roll back the installation, let's add it to the * installation step stack */ $this->parent->pushStep( [ 'type' => 'folder', 'path' => $this->parent->getPath('extension_root'), ] ); } $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $db = $this->getDatabase(); // Remove the schema version $query = $db->getQuery(true) ->delete($db->quoteName('#__schemas')) ->where($db->quoteName('extension_id') . ' = :extension_id') ->bind(':extension_id', $this->extension->extension_id, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } $file = JPATH_MANIFESTS . '/packages/' . $this->extension->element . '.xml'; if (is_file($file)) { File::delete($file); } $folder = $this->parent->getPath('extension_root'); if (Folder::exists($folder)) { Folder::delete($folder); } $this->extension->delete(); return true; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { if (!$element) { // Ensure the element is a string $element = (string) $this->getManifest()->packagename; // Filter the name for illegal characters $element = 'pkg_' . InputFilter::getInstance()->clean($element, 'cmd'); } return $element; } /** * Load language from a path * * @param string $path The path of the language. * * @return void * * @since 3.1 */ public function loadLanguage($path) { $this->doLoadLanguage($this->getElement(), $path); } /** * Handler for the `onExtensionAfterInstall` event * * @param Event $event The event * * @return void * * @since 3.7.0 */ public function onExtensionAfterInstall(Event $event) { if ($event->getArgument('eid', false) !== false) { $this->installedIds[] = $event->getArgument('eid'); } } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { $this->parent->parseLanguages($this->getManifest()->languages); } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { $manifest = new PackageManifest(JPATH_MANIFESTS . '/packages/' . $this->extension->element . '.xml'); $error = false; foreach ($manifest->filelist as $extension) { $tmpInstaller = new Installer(); $tmpInstaller->setDatabase($this->getDatabase()); $tmpInstaller->setPackageUninstall(true); $id = $this->_getExtensionId($extension->type, $extension->id, $extension->client, $extension->group); if ($id) { if (!$tmpInstaller->uninstall($extension->type, $id)) { $error = true; Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_NOT_PROPER', basename($extension->filename)), Log::WARNING, 'jerror'); } } else { Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MISSING_EXTENSION', basename($extension->filename)), Log::WARNING, 'jerror'); } } // Remove any language files $this->parent->removeFiles($this->getManifest()->languages); // Clean up manifest file after we're done if there were no errors if ($error) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MANIFEST_NOT_REMOVED')); } } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { $packagepath = (string) $this->getManifest()->packagename; if (empty($packagepath)) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_PACK', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . $packagepath); } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { $manifestFile = JPATH_MANIFESTS . '/packages/' . $this->extension->element . '.xml'; $manifest = new PackageManifest($manifestFile); // Set the package root path $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . $manifest->packagename); // Set the source path for compatibility with the API $this->parent->setPath('source', $this->parent->getPath('extension_root')); // Because packages may not have their own folders we cannot use the standard method of finding an installation manifest if (!file_exists($manifestFile)) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MISSINGMANIFEST')); } $xml = simplexml_load_file($manifestFile); if (!$xml) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_LOAD_MANIFEST')); } // Check for a valid XML root tag. if ($xml->getName() !== 'extension') { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_INVALID_MANIFEST')); } $this->setManifest($xml); // Attempt to load the language file; might have uninstall strings $this->loadLanguage(JPATH_SITE); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { if ($this->currentExtensionId) { if (!$this->parent->isOverwrite()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ALREADY_EXISTS', Text::_('JLIB_INSTALLER_' . $this->route), $this->name ) ); } $this->extension->load($this->currentExtensionId); $this->extension->name = $this->name; } else { $this->extension->name = $this->name; $this->extension->type = 'package'; $this->extension->element = $this->element; $this->extension->changelogurl = $this->changelogurl; // There is no folder for packages $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = 1; $this->extension->client_id = 0; $this->extension->params = $this->parent->getParams(); } // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PACK_INSTALL_ROLLBACK', $this->extension->getError() ) ); } // Since we have created a package item, we add it to the installation step stack // so that if we have to rollback the changes we can undo it. $this->parent->pushStep(['type' => 'extension', 'id' => $this->extension->extension_id]); } /** * Executes a custom install script method * * @param string $method The install method to execute * * @return boolean True on success * * @since 3.4 */ protected function triggerManifestScript($method) { ob_start(); ob_implicit_flush(false); if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $method)) { switch ($method) { // The preflight method takes the route as a param case 'preflight': if ($this->parent->manifestClass->$method($this->route, $this) === false) { // The script failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_INSTALL_CUSTOM_INSTALL_FAILURE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } break; // The postflight method takes the route and a results array as params case 'postflight': $this->parent->manifestClass->$method($this->route, $this, $this->results); break; // The install, uninstall, and update methods only pass this object as a param case 'install': case 'uninstall': case 'update': if ($this->parent->manifestClass->$method($this) === false) { if ($method !== 'uninstall') { // The script failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_INSTALL_CUSTOM_INSTALL_FAILURE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } } break; } } // Append to the message object $this->extensionMessage .= ob_get_clean(); // If in postflight or uninstall, set the message for display if (($method === 'uninstall' || $method === 'postflight') && $this->extensionMessage !== '') { $this->parent->set('extension_message', $this->extensionMessage); } return true; } /** * Gets the extension id. * * @param string $type The extension type. * @param string $id The name of the extension (the element field). * @param integer $client The application id (0: Joomla CMS site; 1: Joomla CMS administrator). * @param string $group The extension group (mainly for plugins). * * @return integer * * @since 3.1 */ protected function _getExtensionId($type, $id, $client, $group) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('type') . ' = :type', $db->quoteName('element') . ' = :element', ] ) ->bind(':type', $type) ->bind(':element', $id); switch ($type) { case 'plugin': // Plugins have a folder but not a client $query->where('folder = :folder') ->bind(':folder', $group); break; case 'library': case 'package': case 'component': // Components, packages and libraries don't have a folder or client. // Included for completeness. break; case 'language': case 'module': case 'template': // Languages, modules and templates have a client but not a folder $clientId = ApplicationHelper::getClientInfo($client, true)->id; $query->where('client_id = :client_id') ->bind(':client_id', $clientId, ParameterType::INTEGER); break; } $db->setQuery($query); // Note: For templates, libraries and packages their unique name is their key. // This means they come out the same way they came in. return $db->loadResult(); } /** * Refreshes the extension table cache * * @return boolean Result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { // Need to find to find where the XML file is since we don't store this normally $manifestPath = JPATH_MANIFESTS . '/packages/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; try { return $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } } PK1A#]+�v����*src/Installer/Adapter/ComponentAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Asset; use Joomla\CMS\Table\Extension; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Component installer * * @since 3.1 */ class ComponentAdapter extends InstallerAdapter { /** * The list of current files for the Joomla! CMS administrator that are installed and is read * from the manifest on disk in the update area to handle doing a diff * and deleting files that are in the old files list and not in the new * files list. * * @var array * @since 3.1 * */ protected $oldAdminFiles = null; /** * The list of current files for the Joomla! CMS API that are installed and is read * from the manifest on disk in the update area to handle doing a diff * and deleting files that are in the old files list and not in the new * files list. * * @var array * @since 4.0.0 * */ protected $oldApiFiles = null; /** * The list of current files that are installed and is read * from the manifest on disk in the update area to handle doing a diff * and deleting files that are in the old files list and not in the new * files list. * * @var array * @since 3.1 * */ protected $oldFiles = null; /** * A path to the PHP file that the scriptfile declaration in * the manifest refers to. * * @var string * @since 3.1 * */ protected $manifest_script = null; /** * For legacy installations this is a path to the PHP file that the scriptfile declaration in the * manifest refers to. * * @var string * @since 3.1 * */ protected $install_script = null; /** * Method to check if the extension is present in the filesystem * * @return boolean * * @since 3.4 * @throws \RuntimeException */ protected function checkExtensionInFilesystem() { /* * If the component site or admin directory already exists, then we will assume that the component is already * installed or another component is using that directory. */ if ( file_exists($this->parent->getPath('extension_site')) || file_exists($this->parent->getPath('extension_administrator')) || file_exists($this->parent->getPath('extension_api')) ) { // Look for an update function or update tag $updateElement = $this->getManifest()->update; // Upgrade manually set or update function available or update tag detected if ( $updateElement || $this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) ) { // If there is a matching extension mark this as an update $this->setRoute('update'); } elseif (!$this->parent->isOverwrite()) { // We didn't have overwrite set, find an update function or find an update tag so lets call it safe if (file_exists($this->parent->getPath('extension_site'))) { // If the site exists say so. throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_SITE', $this->parent->getPath('extension_site') ) ); } if (file_exists($this->parent->getPath('extension_administrator'))) { // If the admin exists say so throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_ADMIN', $this->parent->getPath('extension_administrator') ) ); } // If the API exists say so throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_API', $this->parent->getPath('extension_api') ) ); } } return false; } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // Copy site files if ($this->getManifest()->files) { if ($this->route === 'update') { $result = $this->parent->parseFiles($this->getManifest()->files, 0, $this->oldFiles); } else { $result = $this->parent->parseFiles($this->getManifest()->files); } if ($result === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COMP_FAIL_SITE_FILES', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } // Copy admin files if ($this->getManifest()->administration->files) { if ($this->route === 'update') { $result = $this->parent->parseFiles($this->getManifest()->administration->files, 1, $this->oldAdminFiles); } else { $result = $this->parent->parseFiles($this->getManifest()->administration->files, 1); } if ($result === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COMP_FAIL_ADMIN_FILES', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } // Copy API files if ($this->getManifest()->api->files) { if ($this->route === 'update') { $result = $this->parent->parseFiles($this->getManifest()->api->files, 3, $this->oldApiFiles); } else { $result = $this->parent->parseFiles($this->getManifest()->api->files, 3); } if ($result === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COMP_FAIL_API_FILES', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } // If there is a manifest script, let's copy it. if ($this->manifest_script) { $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_administrator') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } } /** * Method to create the extension root path if necessary * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function createExtensionRoot() { // If the component directory does not exist, let's create it $created = false; if (!file_exists($this->parent->getPath('extension_site'))) { if (!$created = Folder::create($this->parent->getPath('extension_site'))) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->parent->getPath('extension_site') ) ); } } /* * Since we created the component directory and we will want to remove it if we have to roll back * the installation, let's add it to the installation step stack */ if ($created) { $this->parent->pushStep( [ 'type' => 'folder', 'path' => $this->parent->getPath('extension_site'), ] ); } // If the component admin directory does not exist, let's create it $created = false; if (!file_exists($this->parent->getPath('extension_administrator'))) { if (!$created = Folder::create($this->parent->getPath('extension_administrator'))) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->parent->getPath('extension_administrator') ) ); } } /* * Since we created the component admin directory and we will want to remove it if we have to roll * back the installation, let's add it to the installation step stack */ if ($created) { $this->parent->pushStep( [ 'type' => 'folder', 'path' => $this->parent->getPath('extension_administrator'), ] ); } // If the component API directory does not exist, let's create it $created = false; if (!file_exists($this->parent->getPath('extension_api'))) { if (!$created = Folder::create($this->parent->getPath('extension_api'))) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_CREATE_DIRECTORY', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->parent->getPath('extension_api') ) ); } } /* * Since we created the component API directory and we will want to remove it if we have to roll * back the installation, let's add it to the installation step stack */ if ($created) { $this->parent->pushStep( [ 'type' => 'folder', 'path' => $this->parent->getPath('extension_api'), ] ); } } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { /** @var Update $update */ $update = Table::getInstance('update'); // Clobber any possible pending updates $uid = $update->find( [ 'element' => $this->element, 'type' => $this->extension->type, 'client_id' => 1, ] ); if ($uid) { $update->delete($uid); } // We will copy the manifest file to its appropriate place. if ($this->route !== 'discover_install') { if (!$this->parent->copyManifest()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } // Time to build the admin menus if (!$this->_buildAdminMenus($this->extension->extension_id)) { Log::add(Text::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), Log::WARNING, 'jerror'); } // Make sure that menu items pointing to the component have correct component id assigned to them. // Prevents message "Component 'com_extension' does not exist." after uninstalling / re-installing component. if (!$this->_updateMenus($this->extension->extension_id)) { Log::add(Text::_('JLIB_INSTALLER_ABORT_COMP_UPDATESITEMENUS_FAILED'), Log::WARNING, 'jerror'); } /** @var Asset $asset */ $asset = Table::getInstance('Asset'); // Check if an asset already exists for this extension and create it if not if (!$asset->loadByName($this->extension->element)) { // Register the component container just under root in the assets table. $asset->name = $this->extension->element; $asset->parent_id = 1; $asset->rules = '{}'; $asset->title = $this->extension->name; $asset->setLocation(1, 'last-child'); if (!$asset->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->extension->getError() ) ); } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $extensionId = $this->extension->extension_id; $db = $this->getDatabase(); // Remove the schema version $query = $db->getQuery(true) ->delete($db->quoteName('#__schemas')) ->where($db->quoteName('extension_id') . ' = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Remove the component container in the assets table. $asset = Table::getInstance('Asset'); if ($asset->loadByName($this->getElement())) { $asset->delete(); } $extensionName = $this->element; $extensionNameWithWildcard = $extensionName . '.%'; // Remove categories for this component $query = $db->getQuery(true) ->delete($db->quoteName('#__categories')) ->where( [ $db->quoteName('extension') . ' = :extension', $db->quoteName('extension') . ' LIKE :wildcard', ], 'OR' ) ->bind(':extension', $extensionName) ->bind(':wildcard', $extensionNameWithWildcard); $db->setQuery($query); $db->execute(); // Rebuild the categories for correct lft/rgt Table::getInstance('category')->rebuild(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => 'component', 'client_id' => 1, 'folder' => '', ] ); if ($uid) { $update->delete($uid); } // Now we need to delete the installation directories. This is the final step in uninstalling the component. if (trim($this->extension->element)) { $retval = true; // Delete the component site directory if (is_dir($this->parent->getPath('extension_site'))) { if (!Folder::delete($this->parent->getPath('extension_site'))) { Log::add(Text::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_SITE'), Log::WARNING, 'jerror'); $retval = false; } } // Delete the component admin directory if (is_dir($this->parent->getPath('extension_administrator'))) { if (!Folder::delete($this->parent->getPath('extension_administrator'))) { Log::add(Text::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_ADMIN'), Log::WARNING, 'jerror'); $retval = false; } } // Delete the component API directory if (is_dir($this->parent->getPath('extension_api'))) { if (!Folder::delete($this->parent->getPath('extension_api'))) { Log::add(Text::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_API'), Log::WARNING, 'jerror'); $retval = false; } } // Now we will no longer need the extension object, so let's delete it $this->extension->delete($this->extension->extension_id); return $retval; } // No component option defined... cannot delete what we don't know about Log::add(Text::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_NO_OPTION'), Log::WARNING, 'jerror'); return false; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { $element = parent::getElement($element); if (strpos($element, 'com_') !== 0) { $element = 'com_' . $element; } return $element; } /** * Custom loadLanguage method * * @param string $path The path language files are on. * * @return void * * @since 3.1 */ public function loadLanguage($path = null) { $source = $this->parent->getPath('source'); switch ($this->parent->extension->client_id) { case 0: $client = JPATH_SITE; break; case 1: $client = JPATH_ADMINISTRATOR; break; case 3: $client = JPATH_API; break; default: throw new \InvalidArgumentException( sprintf( 'Unsupported client ID %d for component %s', $this->parent->extension->client_id, $this->parent->extension->element ) ); } if (!$source) { $this->parent->setPath('source', $client . '/components/' . $this->parent->extension->element); } $extension = $this->getElement(); $source = $path ?: $client . '/components/' . $extension; if ($this->getManifest()->administration->files) { $element = $this->getManifest()->administration->files; } elseif ($this->getManifest()->api->files) { $element = $this->getManifest()->api->files; } elseif ($this->getManifest()->files) { $element = $this->getManifest()->files; } else { $element = null; } if ($element) { $folder = (string) $element->attributes()->folder; if ($folder && file_exists($path . '/' . $folder)) { $source = $path . '/' . $folder; } } $this->doLoadLanguage($extension, $source); } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { // Parse optional tags $this->parent->parseMedia($this->getManifest()->media); $this->parent->parseLanguages($this->getManifest()->languages); $this->parent->parseLanguages($this->getManifest()->administration->languages, 1); } /** * Method to parse the queries specified in the `<sql>` tags * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function parseQueries() { parent::parseQueries(); // We have extra tasks to run for the uninstall path if ($this->route === 'uninstall') { $this->_removeAdminMenus($this->extension->extension_id); } } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 * @throws \RuntimeException */ public function prepareDiscoverInstall() { // Need to find to find where the XML file is since we don't store this normally $client = ApplicationHelper::getClientInfo($this->extension->client_id); $short_element = str_replace('com_', '', $this->extension->element); $manifestPath = $client->path . '/components/' . $this->extension->element . '/' . $short_element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->parent->setPath('source', $client->path . '/components/' . $this->extension->element); $this->parent->setPath('extension_root', $this->parent->getPath('source')); $this->setManifest($this->parent->getManifest()); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->extension->manifest_cache = json_encode($manifest_details); $this->extension->state = 0; $this->extension->name = $manifest_details['name']; $this->extension->enabled = 1; $this->extension->params = $this->parent->getParams(); $stored = false; try { $this->extension->store(); $stored = true; } catch (\RuntimeException $e) { $name = $this->extension->name; $type = $this->extension->type; $element = $this->extension->element; // Try to delete existing failed records before retrying $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('name') . ' = :name', $db->quoteName('type') . ' = :type', $db->quoteName('element') . ' = :element', ] ) ->bind(':name', $name) ->bind(':type', $type) ->bind(':element', $element); $db->setQuery($query); $extension_ids = $db->loadColumn(); if (!empty($extension_ids)) { foreach ($extension_ids as $eid) { // Remove leftover admin menus for this extension ID $this->_removeAdminMenus($eid); // Remove the extension record itself /** @var Extension $extensionTable */ $extensionTable = Table::getInstance('extension'); $extensionTable->delete($eid); } } } if (!$stored) { try { $this->extension->store(); } catch (\RuntimeException $e) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS'), $e->getCode(), $e); } } } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { // Let's remove those language files and media in the JROOT/images/ folder that are associated with the component we are uninstalling $this->parent->removeFiles($this->getManifest()->media); $this->parent->removeFiles($this->getManifest()->languages); $this->parent->removeFiles($this->getManifest()->administration->languages, 1); } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { // Set the installation target paths $this->parent->setPath('extension_site', Path::clean(JPATH_SITE . '/components/' . $this->element)); $this->parent->setPath('extension_administrator', Path::clean(JPATH_ADMINISTRATOR . '/components/' . $this->element)); $this->parent->setPath('extension_api', Path::clean(JPATH_API . '/components/' . $this->element)); // Copy the admin path as it's used as a common base $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator')); // Make sure that we have an admin element if (!$this->getManifest()->administration) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT')); } } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { // Get the admin and site paths for the component $this->parent->setPath('extension_administrator', Path::clean(JPATH_ADMINISTRATOR . '/components/' . $this->extension->element)); $this->parent->setPath('extension_api', Path::clean(JPATH_API . '/components/' . $this->extension->element)); $this->parent->setPath('extension_site', Path::clean(JPATH_SITE . '/components/' . $this->extension->element)); // Copy the admin path as it's used as a common base $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator')); // Find and load the XML install file for the component $this->parent->setPath('source', $this->parent->getPath('extension_administrator')); // Get the package manifest object // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file $this->parent->findManifest(); $this->setManifest($this->parent->getManifest()); if (!$this->getManifest()) { // Make sure we delete the folders if no manifest exists Folder::delete($this->parent->getPath('extension_administrator')); Folder::delete($this->parent->getPath('extension_api')); Folder::delete($this->parent->getPath('extension_site')); // Remove the menu $this->_removeAdminMenus($this->extension->extension_id); // Raise a warning throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_ERRORREMOVEMANUALLY')); } // Attempt to load the admin language file; might have uninstall strings $this->loadLanguage(JPATH_ADMINISTRATOR . '/components/' . $this->extension->element); } /** * Method to setup the update routine for the adapter * * @return void * * @since 3.4 */ protected function setupUpdates() { // Hunt for the original XML file $old_manifest = null; // Use a temporary instance due to side effects; start in the administrator first $tmpInstaller = new Installer(); $tmpInstaller->setDatabase($this->getDatabase()); $tmpInstaller->setPath('source', $this->parent->getPath('extension_administrator')); if (!$tmpInstaller->findManifest()) { // Then the site $tmpInstaller->setPath('source', $this->parent->getPath('extension_site')); if ($tmpInstaller->findManifest()) { $old_manifest = $tmpInstaller->getManifest(); } } else { $old_manifest = $tmpInstaller->getManifest(); } if ($old_manifest) { $this->oldAdminFiles = $old_manifest->administration->files; $this->oldApiFiles = $old_manifest->api->files; $this->oldFiles = $old_manifest->files; } } /** * Method to store the extension to the database * * @param bool $deleteExisting Should I try to delete existing records of the same component? * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension($deleteExisting = false) { // The extension is stored during prepareDiscoverInstall for discover installs if ($this->route === 'discover_install') { return; } // Add or update an entry to the extension table $this->extension->name = $this->name; $this->extension->type = 'component'; $this->extension->element = $this->element; $this->extension->changelogurl = $this->changelogurl; // If we are told to delete existing extension entries then do so. if ($deleteExisting) { $name = $this->extension->name; $type = $this->extension->type; $element = $this->extension->element; // Try to delete existing failed records before retrying $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('name') . ' = :name', $db->quoteName('type') . ' = :type', $db->quoteName('element') . ' = :element', ] ) ->bind(':name', $name) ->bind(':type', $type) ->bind(':element', $element); $db->setQuery($query); $extension_ids = $db->loadColumn(); if (!empty($extension_ids)) { foreach ($extension_ids as $eid) { // Remove leftover admin menus for this extension ID $this->_removeAdminMenus($eid); // Remove the extension record itself /** @var Extension $extensionTable */ $extensionTable = Table::getInstance('extension'); $extensionTable->delete($eid); } } } // Namespace is optional if (isset($this->manifest->namespace)) { $this->extension->namespace = (string) $this->manifest->namespace; } // If there is not already a row, generate a heap of defaults if (!$this->currentExtensionId) { $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = 0; $this->extension->client_id = 1; $this->extension->params = $this->parent->getParams(); } $this->extension->manifest_cache = $this->parent->generateManifestCache(); $couldStore = $this->extension->store(); if (!$couldStore && $deleteExisting) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $this->extension->getError() ) ); } if (!$couldStore && !$deleteExisting) { // Maybe we have a failed installation (e.g. timeout). Let's retry after deleting old records. $this->storeExtension(true); } } /** * Method to build menu database entries for a component * * @param int|null $componentId The component ID for which I'm building menus * * @return boolean True if successful * * @since 3.1 */ protected function _buildAdminMenus($componentId = null) { $db = $this->getDatabase(); $option = $this->element; // If a component exists with this option in the table within the protected menutype 'main' then we don't need to add menus $query = $db->getQuery(true) ->select( [ $db->quoteName('m.id'), $db->quoteName('e.extension_id'), ] ) ->from($db->quoteName('#__menu', 'm')) ->join('LEFT', $db->quoteName('#__extensions', 'e'), $db->quoteName('m.component_id') . ' = ' . $db->quoteName('e.extension_id')) ->where( [ $db->quoteName('m.parent_id') . ' = 1', $db->quoteName('m.client_id') . ' = 1', $db->quoteName('m.menutype') . ' = ' . $db->quote('main'), $db->quoteName('e.element') . ' = :element', ] ) ->bind(':element', $option); $db->setQuery($query); // In case of a failed installation (e.g. timeout error) we may have duplicate menu item and extension records. $componentrows = $db->loadObjectList(); // Check if menu items exist if (!empty($componentrows)) { // Don't do anything if overwrite has not been enabled if (!$this->parent->isOverwrite()) { return true; } // Remove all menu items foreach ($componentrows as $componentrow) { // Remove existing menu items if overwrite has been enabled if ($option) { // If something goes wrong, there's no way to rollback @todo: Search for better solution $this->_removeAdminMenus($componentrow->extension_id); } } } // Only try to detect the component ID if it's not provided if (empty($componentId)) { // Lets find the extension id $query->clear() ->select($db->quoteName('e.extension_id')) ->from($db->quoteName('#__extensions', 'e')) ->where( [ $db->quoteName('e.type') . ' = ' . $db->quote('component'), $db->quoteName('e.element') . ' = :element', ] ) ->bind(':element', $option); $db->setQuery($query); $componentId = $db->loadResult(); } // Ok, now its time to handle the menus. Start with the component root menu, then handle submenus. $menuElement = $this->getManifest()->administration->menu; // Just do not create the menu if $menuElement not exist if (!$menuElement) { return true; } // If the menu item is hidden do nothing more, just return if (\in_array((string) $menuElement['hidden'], ['true', 'hidden'])) { return true; } // Let's figure out what the menu item data should look like $data = []; // I have a menu element, use this information $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($menuElement); $data['alias'] = (string) $menuElement; $data['type'] = 'component'; $data['published'] = 1; $data['parent_id'] = 1; $data['component_id'] = $componentId; $data['img'] = ((string) $menuElement->attributes()->img) ?: 'class:component'; $data['home'] = 0; $data['path'] = ''; $data['params'] = ''; if ($params = $menuElement->params) { // Pass $params through Registry to convert to JSON. $params = new Registry($params); $data['params'] = $params->toString(); } // Set the menu link $request = []; if ((string) $menuElement->attributes()->task) { $request[] = 'task=' . $menuElement->attributes()->task; } if ((string) $menuElement->attributes()->view) { $request[] = 'view=' . $menuElement->attributes()->view; } $qstring = \count($request) ? '&' . implode('&', $request) : ''; $data['link'] = 'index.php?option=' . $option . $qstring; // Try to create the menu item in the database $parent_id = $this->_createAdminMenuItem($data, 1); if ($parent_id === false) { return false; } /* * Process SubMenus */ if (!$this->getManifest()->administration->submenu) { // No submenu? We're done. return true; } foreach ($this->getManifest()->administration->submenu->menu as $child) { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($child); $data['alias'] = ((string) $child->attributes()->alias) ?: (string) $child; $data['type'] = ((string) $child->attributes()->type) ?: 'component'; $data['published'] = 1; $data['parent_id'] = $parent_id; $data['component_id'] = $componentId; $data['img'] = ((string) $child->attributes()->img) ?: 'class:component'; $data['home'] = 0; $data['params'] = ''; if ($params = $child->params) { // Pass $params through Registry to convert to JSON. $params = new Registry($params); $data['params'] = $params->toString(); } // Set the sub menu link if ((string) $child->attributes()->link) { $data['link'] = 'index.php?' . $child->attributes()->link; } else { $request = []; if ((string) $child->attributes()->act) { $request[] = 'act=' . $child->attributes()->act; } if ((string) $child->attributes()->task) { $request[] = 'task=' . $child->attributes()->task; } if ((string) $child->attributes()->controller) { $request[] = 'controller=' . $child->attributes()->controller; } if ((string) $child->attributes()->view) { $request[] = 'view=' . $child->attributes()->view; } if ((string) $child->attributes()->layout) { $request[] = 'layout=' . $child->attributes()->layout; } if ((string) $child->attributes()->sub) { $request[] = 'sub=' . $child->attributes()->sub; } $qstring = \count($request) ? '&' . implode('&', $request) : ''; $data['link'] = 'index.php?option=' . $option . $qstring; } $submenuId = $this->_createAdminMenuItem($data, $parent_id); if ($submenuId === false) { return false; } /* * Since we have created a menu item, we add it to the installation step stack * so that if we have to rollback the changes we can undo it. */ $this->parent->pushStep(['type' => 'menu', 'id' => $componentId]); } return true; } /** * Method to remove admin menu references to a component * * @param int $id The ID of the extension whose admin menus will be removed * * @return boolean True if successful. * * @throws \Exception * * @since 3.1 */ protected function _removeAdminMenus($id) { $db = $this->getDatabase(); /** @var \Joomla\CMS\Table\Menu $table */ $table = Table::getInstance('menu'); // Get the ids of the menu items $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__menu')) ->where( [ $db->quoteName('client_id') . ' = 1', $db->quoteName('menutype') . ' = ' . $db->quote('main'), $db->quoteName('component_id') . ' = :id', ] ) ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); $ids = $db->loadColumn(); $result = true; // Check for error if (!empty($ids)) { // Iterate the items to delete each one. foreach ($ids as $menuid) { if (!$table->delete((int) $menuid, false)) { Factory::getApplication()->enqueueMessage($table->getError(), 'error'); $result = false; } } // Rebuild the whole tree $table->rebuild(); } return $result; } /** * Method to update menu database entries for a component in case the component has been uninstalled before. * NOTE: This will not update admin menus. Use _updateMenus() instead to update admin menus ase well. * * @param int|null $componentId The component ID. * * @return boolean True if successful * * @since 3.4.2 */ protected function _updateSiteMenus($componentId = null) { return $this->_updateMenus($componentId, 0); } /** * Method to update menu database entries for a component in case if the component has been uninstalled before. * * @param int|null $componentId The component ID. * @param int $clientId The client id * * @return boolean True if successful * * @since 3.7.0 */ protected function _updateMenus($componentId, $clientId = null) { $db = $this->getDatabase(); $option = $this->element; $link = 'index.php?option=' . $option; $linkMatch = 'index.php?option=' . $option . '&%'; // Update all menu items which contain 'index.php?option=com_extension' or 'index.php?option=com_extension&...' // to use the new component id. $query = $db->getQuery(true) ->update($db->quoteName('#__menu')) ->set($db->quoteName('component_id') . ' = :componentId') ->where($db->quoteName('type') . ' = ' . $db->quote('component')) ->extendWhere( 'AND', [ $db->quoteName('link') . ' LIKE :link', $db->quoteName('link') . ' LIKE :linkMatch', ], 'OR' ) ->bind(':componentId', $componentId, ParameterType::INTEGER) ->bind(':link', $link) ->bind(':linkMatch', $linkMatch); if (isset($clientId)) { $query->where($db->quoteName('client_id') . ' = :clientId') ->bind(':clientId', $clientId, ParameterType::INTEGER); } try { $db->setQuery($query); $db->execute(); } catch (\RuntimeException $e) { return false; } return true; } /** * Custom rollback method * - Roll back the component menu item * * @param array $step Installation step to rollback. * * @return boolean True on success * * @throws \Exception * * @since 3.1 */ protected function _rollback_menu($step) { return $this->_removeAdminMenus($step['id']); } /** * Discover unregistered extensions. * * @return array A list of extensions. * * @since 3.1 */ public function discover() { $results = []; $site_components = Folder::folders(JPATH_SITE . '/components'); $admin_components = Folder::folders(JPATH_ADMINISTRATOR . '/components'); $api_components = Folder::folders(JPATH_API . '/components'); foreach ($site_components as $component) { if (file_exists(JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml')) { $manifest_details = Installer::parseXMLInstallFile( JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml' ); $extension = Table::getInstance('extension'); $extension->set('type', 'component'); $extension->set('client_id', 0); $extension->set('element', $component); $extension->set('folder', ''); $extension->set('name', $component); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } foreach ($admin_components as $component) { if (file_exists(JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml')) { $manifest_details = Installer::parseXMLInstallFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml' ); $extension = Table::getInstance('extension'); $extension->set('type', 'component'); $extension->set('client_id', 1); $extension->set('element', $component); $extension->set('folder', ''); $extension->set('name', $component); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } foreach ($api_components as $component) { if (file_exists(JPATH_API . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml')) { $manifest_details = Installer::parseXMLInstallFile( JPATH_API . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml' ); $extension = Table::getInstance('extension'); $extension->set('type', 'component'); $extension->set('client_id', 3); $extension->set('element', $component); $extension->set('folder', ''); $extension->set('name', $component); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } return $results; } /** * Refreshes the extension table cache * * @return boolean Result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { // Need to find to find where the XML file is since we don't store this normally $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $short_element = str_replace('com_', '', $this->parent->extension->element); $manifestPath = $client->path . '/components/' . $this->parent->extension->element . '/' . $short_element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; // Namespace is optional if (isset($manifest_details['namespace'])) { $this->parent->extension->namespace = $manifest_details['namespace']; } try { return $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_COMP_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } /** * Creates the menu item in the database. If the item already exists it tries to remove it and create it afresh. * * @param array &$data The menu item data to create * @param integer $parentId The parent menu item ID * * @return boolean|integer Menu item ID on success, false on failure * * @throws \Exception * * @since 3.1 */ protected function _createAdminMenuItem(array &$data, $parentId) { $db = $this->getDatabase(); /** @var \Joomla\CMS\Table\Menu $table */ $table = Table::getInstance('menu'); try { $table->setLocation($parentId, 'last-child'); } catch (\InvalidArgumentException $e) { Log::add($e->getMessage(), Log::WARNING, 'jerror'); return false; } if (!$table->bind($data) || !$table->check() || !$table->store()) { $menutype = $data['menutype']; $link = $data['link']; $type = $data['type']; $menuParentId = $data['parent_id']; $home = $data['home']; // The menu item already exists. Delete it and retry instead of throwing an error. $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__menu')) ->where( [ $db->quoteName('menutype') . ' = :menutype', $db->quoteName('client_id') . ' = 1', $db->quoteName('link') . ' = :link', $db->quoteName('type') . ' = :type', $db->quoteName('parent_id') . ' = :parent_id', $db->quoteName('home') . ' = :home', ] ) ->bind(':menutype', $menutype) ->bind(':link', $link) ->bind(':type', $type) ->bind(':parent_id', $menuParentId, ParameterType::INTEGER) ->bind(':home', $home, ParameterType::BOOLEAN); $db->setQuery($query); $menu_id = $db->loadResult(); if (!$menu_id) { // Oops! Could not get the menu ID. Go back and rollback changes. Factory::getApplication()->enqueueMessage($table->getError(), 'error'); return false; } else { /** @var \Joomla\CMS\Table\Menu $temporaryTable */ $temporaryTable = Table::getInstance('menu'); $temporaryTable->delete($menu_id, true); $temporaryTable->load($parentId); $temporaryTable->rebuild($parentId, $temporaryTable->lft, $temporaryTable->level, $temporaryTable->path); // Retry creating the menu item $table->setLocation($parentId, 'last-child'); if (!$table->bind($data) || !$table->check() || !$table->store()) { // Install failed, warn user and rollback changes Factory::getApplication()->enqueueMessage($table->getError(), 'error'); return false; } } } return $table->id; } } PK1A#]"��vQvQ'src/Installer/Adapter/PluginAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\ParameterType; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Plugin installer * * @since 3.1 */ class PluginAdapter extends InstallerAdapter { /** * Group of the plugin * * @var string * @since 4.3.0 */ protected $group; /** * `<scriptfile>` element of the extension manifest * * @var object * @since 3.1 */ protected $scriptElement = null; /** * `<files>` element of the old extension manifest * * @var object * @since 3.1 */ protected $oldFiles = null; /** * Method to check if the extension is already present in the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExistingExtension() { try { $this->currentExtensionId = $this->extension->find( ['type' => $this->type, 'element' => $this->element, 'folder' => $this->group] ); } catch (\RuntimeException $e) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $e->getMessage() ), $e->getCode(), $e ); } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // Copy all necessary files if ($this->parent->parseFiles($this->getManifest()->files, -1, $this->oldFiles) === false) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PLG_COPY_FILES', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } } } } /** * Method to create the extension root path if necessary * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function createExtensionRoot() { // Run the common create code first parent::createExtensionRoot(); // If we're updating at this point when there is always going to be an extension_root find the old XML files if ($this->route === 'update') { // Create a new installer because findManifest sets stuff; side effects! $tmpInstaller = new Installer(); $tmpInstaller->setDatabase($this->getDatabase()); // Look in the extension root $tmpInstaller->setPath('source', $this->parent->getPath('extension_root')); if ($tmpInstaller->findManifest()) { $old_manifest = $tmpInstaller->getManifest(); $this->oldFiles = $old_manifest->files; } } } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates /** @var Update $update */ $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => $this->type, 'folder' => $this->group, ] ); if ($uid) { $update->delete($uid); } // Lastly, we will copy the manifest file to its appropriate place. if ($this->route !== 'discover_install') { if (!$this->parent->copyManifest(-1)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $extensionId = $this->extension->extension_id; $db = $this->getDatabase(); // Remove the schema version $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Now we will no longer need the plugin object, so let's delete it $this->extension->delete($this->extension->extension_id); // Remove the plugin's folder Folder::delete($this->parent->getPath('extension_root')); return true; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { if ($element || !$this->getManifest()) { return $element; } // Backward Compatibility // @todo Deprecate in future version if (!\count($this->getManifest()->files->children())) { return $element; } $type = (string) $this->getManifest()->attributes()->type; foreach ($this->getManifest()->files->children() as $file) { if ((string) $file->attributes()->$type) { $element = (string) $file->attributes()->$type; break; } } return $element; } /** * Get the class name for the install adapter script. * * @return string The class name. * * @since 3.4 */ protected function getScriptClassName() { return 'Plg' . str_replace('-', '', $this->group) . $this->element . 'InstallerScript'; } /** * Custom loadLanguage method * * @param string $path The path where to find language files. * * @return void * * @since 3.1 */ public function loadLanguage($path = null) { $source = $this->parent->getPath('source'); if (!$source) { $this->parent->setPath( 'source', JPATH_PLUGINS . '/' . $this->parent->extension->folder . '/' . $this->parent->extension->element ); } $element = $this->getManifest()->files; if ($element) { $group = strtolower((string) $this->getManifest()->attributes()->group); $name = ''; if (\count($element->children())) { foreach ($element->children() as $file) { if ((string) $file->attributes()->plugin) { $name = strtolower((string) $file->attributes()->plugin); break; } } } if ($name) { $extension = "plg_{$group}_{$name}"; $source = $path ?: JPATH_PLUGINS . "/$group/$name"; $folder = (string) $element->attributes()->folder; if ($folder && file_exists("$path/$folder")) { $source = "$path/$folder"; } $this->doLoadLanguage($extension, $source, JPATH_ADMINISTRATOR); } } } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { // Parse optional tags -- media and language files for plugins go in admin app $this->parent->parseMedia($this->getManifest()->media, 1); $this->parent->parseLanguages($this->getManifest()->languages, 1); } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 */ public function prepareDiscoverInstall() { $client = ApplicationHelper::getClientInfo($this->extension->client_id); $basePath = $client->path . '/plugins/' . $this->extension->folder; $manifestPath = $basePath . '/' . $this->extension->element . '/' . $this->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->setManifest($this->parent->getManifest()); } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { // Remove the plugin files $this->parent->removeFiles($this->getManifest()->files, -1); // Remove all media and languages as well $this->parent->removeFiles($this->getManifest()->media); $this->parent->removeFiles($this->getManifest()->languages, 1); } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { $this->group = (string) $this->getManifest()->attributes()->group; if (empty($this->element) && empty($this->group)) { throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PLG_INSTALL_NO_FILE', Text::_('JLIB_INSTALLER_' . $this->route) ) ); } $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $this->group . '/' . $this->element); } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { // Get the plugin folder so we can properly build the plugin path if (trim($this->extension->folder) === '') { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_FOLDER_FIELD_EMPTY')); } // Set the plugin root path $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $this->extension->folder . '/' . $this->extension->element); $this->parent->setPath('source', $this->parent->getPath('extension_root')); $this->parent->findManifest(); $this->setManifest($this->parent->getManifest()); if ($this->getManifest()) { $this->group = (string) $this->getManifest()->attributes()->group; } // Attempt to load the language file; might have uninstall strings $this->loadLanguage($this->parent->getPath('source')); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { // Discover installs are stored a little differently if ($this->route === 'discover_install') { $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->extension->manifest_cache = json_encode($manifest_details); $this->extension->state = 0; $this->extension->name = $manifest_details['name']; $this->extension->enabled = 'editors' === $this->extension->folder ? 1 : 0; $this->extension->params = $this->parent->getParams(); $this->extension->changelogurl = (string) $this->manifest->changelogurl; if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PLG_DISCOVER_STORE_DETAILS')); } return; } // Was there a plugin with the same name already installed? if ($this->currentExtensionId) { if (!$this->parent->isOverwrite()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ALREADY_EXISTS', Text::_('JLIB_INSTALLER_' . $this->route), $this->name ) ); } // Load the entry and update the manifest_cache $this->extension->load($this->currentExtensionId); // Update name $this->extension->name = $this->name; // Update namespace $this->extension->namespace = (string) $this->manifest->namespace; // Update changelogurl $this->extension->changelogurl = (string) $this->manifest->changelogurl; // Update manifest $this->extension->manifest_cache = $this->parent->generateManifestCache(); // Update the manifest cache and name $this->extension->store(); } else { // Store in the extensions table (1.6) $this->extension->name = $this->name; $this->extension->type = 'plugin'; $this->extension->ordering = 0; $this->extension->element = $this->element; $this->extension->folder = $this->group; $this->extension->enabled = 0; $this->extension->protected = 0; $this->extension->access = 1; $this->extension->client_id = 0; $this->extension->params = $this->parent->getParams(); $this->extension->changelogurl = $this->changelogurl; // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); // Editor plugins are published by default if ($this->group === 'editors') { $this->extension->enabled = 1; } if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', Text::_('JLIB_INSTALLER_' . $this->route), $this->extension->getError() ) ); } // Since we have created a plugin item, we add it to the installation step stack // so that if we have to rollback the changes we can undo it. $this->parent->pushStep(['type' => 'extension', 'id' => $this->extension->extension_id]); } } /** * Custom discover method * * @return array Extension) list of extensions available * * @since 3.1 */ public function discover() { $results = []; $folder_list = Folder::folders(JPATH_SITE . '/plugins'); foreach ($folder_list as $folder) { $file_list = Folder::files(JPATH_SITE . '/plugins/' . $folder, '\.xml$'); foreach ($file_list as $file) { $manifest_details = Installer::parseXMLInstallFile(JPATH_SITE . '/plugins/' . $folder . '/' . $file); $file = File::stripExt($file); // Ignore example plugins if ($file === 'example' || $manifest_details === false) { continue; } $element = empty($manifest_details['filename']) ? $file : $manifest_details['filename']; $extension = Table::getInstance('extension'); $extension->set('type', 'plugin'); $extension->set('client_id', 0); $extension->set('element', $element); $extension->set('folder', $folder); $extension->set('name', $manifest_details['name']); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } $folder_list = Folder::folders(JPATH_SITE . '/plugins/' . $folder); foreach ($folder_list as $plugin_folder) { $file_list = Folder::files(JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder, '\.xml$'); foreach ($file_list as $file) { $manifest_details = Installer::parseXMLInstallFile( JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder . '/' . $file ); $file = File::stripExt($file); if ($file === 'example' || $manifest_details === false) { continue; } $element = empty($manifest_details['filename']) ? $file : $manifest_details['filename']; // Ignore example plugins $extension = Table::getInstance('extension'); $extension->set('type', 'plugin'); $extension->set('client_id', 0); $extension->set('element', $element); $extension->set('folder', $folder); $extension->set('name', $manifest_details['name']); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } } return $results; } /** * Refreshes the extension table cache. * * @return boolean Result of operation, true if updated, false on failure. * * @since 3.1 */ public function refreshManifestCache() { /* * Plugins use the extensions table as their primary store * Similar to modules and templates, rather easy * If it's not in the extensions table we just add it */ $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; if ($this->parent->extension->store()) { return true; } else { Log::add(Text::_('JLIB_INSTALLER_ERROR_PLG_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } } PK1A#]y�J�J%src/Installer/Adapter/FileAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\Database\ParameterType; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * File installer * * @since 3.1 */ class FileAdapter extends InstallerAdapter { /** * `<scriptfile>` element of the extension manifest * * @var object * @since 3.1 */ protected $scriptElement = null; /** * Flag if the adapter supports discover installs * * Adapters should override this and set to false if discover install is unsupported * * @var boolean * @since 3.4 */ protected $supportsDiscoverInstall = false; /** * List of processed folders * * @var array * @since 3.4 */ protected $folderList; /** * List of processed files * * @var array * @since 3.4 */ protected $fileList; /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // Populate File and Folder List to copy $this->populateFilesAndFolderList(); // Now that we have folder list, lets start creating them foreach ($this->folderList as $folder) { if (!Folder::exists($folder)) { if (!$created = Folder::create($folder)) { throw new \RuntimeException( Text::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $folder) ); } // Since we created a directory and will want to remove it if we have to roll back. // The installation due to some errors, let's add it to the installation step stack. if ($created) { $this->parent->pushStep(['type' => 'folder', 'path' => $folder]); } } } // Now that we have file list, let's start copying them $this->parent->copyFiles($this->fileList); } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } // Lastly, we will copy the manifest file to its appropriate place. $manifest = []; $manifest['src'] = $this->parent->getPath('manifest'); $manifest['dest'] = JPATH_MANIFESTS . '/files/' . basename($this->parent->getPath('manifest')); if (!$this->parent->copyFiles([$manifest], true)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { // First, we have to create a folder for the script if one isn't present if (!file_exists($this->parent->getPath('extension_root'))) { Folder::create($this->parent->getPath('extension_root')); } $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $manifest = JPATH_MANIFESTS . '/files/' . $this->extension->element . '.xml'; if (is_file($manifest)) { File::delete($manifest); } $extensionId = $this->extension->extension_id; $db = $this->getDatabase(); // Remove the schema version $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } $this->extension->delete(); return true; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { if (!$element) { $manifestPath = Path::clean($this->parent->getPath('manifest', '')); $element = preg_replace('/\.xml/', '', basename($manifestPath)); } return $element; } /** * Custom loadLanguage method * * @param string $path The path on which to find language files. * * @return void * * @since 3.1 */ public function loadLanguage($path) { $extension = 'files_' . strtolower(str_replace('files_', '', $this->getElement())); $this->doLoadLanguage($extension, $path, JPATH_SITE); } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { // Parse optional tags $this->parent->parseLanguages($this->getManifest()->languages); } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { // Loop through all elements and get list of files and folders foreach ($this->getManifest()->fileset->files as $eFiles) { $target = (string) $eFiles->attributes()->target; // Create folder path if (empty($target)) { $targetFolder = JPATH_ROOT; } else { $targetFolder = JPATH_ROOT . '/' . $target; } $folderList = []; // Check if all children exists if (\count($eFiles->children()) > 0) { // Loop through all filenames elements foreach ($eFiles->children() as $eFileName) { if ($eFileName->getName() === 'folder') { $folderList[] = $targetFolder . '/' . $eFileName; } else { $fileName = $targetFolder . '/' . $eFileName; if (is_file($fileName)) { File::delete($fileName); } } } } // Delete any folders that don't have any content in them. foreach ($folderList as $folder) { $files = Folder::files($folder); if ($files !== false && !\count($files)) { Folder::delete($folder); } } } // Lastly, remove the extension_root $folder = $this->parent->getPath('extension_root'); if (Folder::exists($folder)) { Folder::delete($folder); } $this->parent->removeFiles($this->getManifest()->languages); } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 */ protected function setupInstallPaths() { // Set the file root path if ($this->name === 'files_joomla') { // If we are updating the Joomla core, set the root path to the root of Joomla $this->parent->setPath('extension_root', JPATH_ROOT); } else { $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/files/' . $this->element); } } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { $manifestFile = JPATH_MANIFESTS . '/files/' . $this->extension->element . '.xml'; // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest if (!file_exists($manifestFile)) { // Remove this row entry since its invalid $this->extension->delete($this->extension->extension_id); throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); } // Set the files root path $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/files/' . $this->extension->element); // Set the source path for compatibility with the API $this->parent->setPath('source', $this->parent->getPath('extension_root')); $xml = simplexml_load_file($manifestFile); // If we cannot load the XML file return null if (!$xml) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_LOAD_MANIFEST')); } // Check for a valid XML root tag. if ($xml->getName() !== 'extension') { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_MANIFEST')); } $this->setManifest($xml); // Attempt to load the language file; might have uninstall strings $this->loadLanguage(JPATH_MANIFESTS . '/files'); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { if ($this->currentExtensionId) { // Load the entry and update the manifest_cache $this->extension->load($this->currentExtensionId); // Update name $this->extension->name = $this->name; // Update manifest $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->extension->getError() ) ); } } else { // Add an entry to the extension table with a whole heap of defaults $this->extension->name = $this->name; $this->extension->type = 'file'; $this->extension->element = $this->element; $this->extension->changelogurl = $this->changelogurl; // There is no folder for files so leave it blank $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = 0; $this->extension->client_id = 0; $this->extension->params = ''; // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_ROLLBACK', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), $this->extension->getError() ) ); } // Since we have created a module item, we add it to the installation step stack // so that if we have to rollback the changes we can undo it. $this->parent->pushStep(['type' => 'extension', 'extension_id' => $this->extension->extension_id]); } } /** * Function used to check if extension is already installed * * @param string $extension The element name of the extension to install * * @return boolean True if extension exists * * @since 3.1 */ protected function extensionExistsInSystem($extension = null) { // Get a database connector object $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('type') . ' = ' . $db->quote('file')) ->where($db->quoteName('element') . ' = :extension') ->bind(':extension', $extension); $db->setQuery($query); try { $db->execute(); } catch (\RuntimeException $e) { // Install failed, rollback changes - error logged by the installer return false; } $id = $db->loadResult(); if (empty($id)) { return false; } return true; } /** * Function used to populate files and folder list * * @return boolean none * * @since 3.1 */ protected function populateFilesAndFolderList() { // Initialise variable $this->folderList = []; $this->fileList = []; // Set root folder names $packagePath = $this->parent->getPath('source'); $jRootPath = Path::clean(JPATH_ROOT); // Loop through all elements and get list of files and folders foreach ($this->getManifest()->fileset->files as $eFiles) { // Check if the element is files element $folder = (string) $eFiles->attributes()->folder; $target = (string) $eFiles->attributes()->target; // Split folder names into array to get folder names. This will help in creating folders $arrList = preg_split("#/|\\/#", $target); $folderName = $jRootPath; foreach ($arrList as $dir) { if (empty($dir)) { continue; } $folderName .= '/' . $dir; // Check if folder exists, if not then add to the array for folder creation if (!Folder::exists($folderName)) { $this->folderList[] = $folderName; } } // Create folder path $sourceFolder = empty($folder) ? $packagePath : $packagePath . '/' . $folder; $targetFolder = empty($target) ? $jRootPath : $jRootPath . '/' . $target; // Check if source folder exists if (!Folder::exists($sourceFolder)) { Log::add(Text::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $sourceFolder), Log::WARNING, 'jerror'); // If installation fails, rollback $this->parent->abort(); return false; } // Check if all children exists if (\count($eFiles->children())) { // Loop through all filenames elements foreach ($eFiles->children() as $eFileName) { $path = []; $path['src'] = $sourceFolder . '/' . $eFileName; $path['dest'] = $targetFolder . '/' . $eFileName; $path['type'] = 'file'; if ($eFileName->getName() === 'folder') { $folderName = $targetFolder . '/' . $eFileName; $this->folderList[] = $folderName; $path['type'] = 'folder'; } $this->fileList[] = $path; } } else { $files = Folder::files($sourceFolder); foreach ($files as $file) { $path = []; $path['src'] = $sourceFolder . '/' . $file; $path['dest'] = $targetFolder . '/' . $file; $this->fileList[] = $path; } } } } /** * Refreshes the extension table cache * * @return boolean result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { // Need to find to find where the XML file is since we don't store this normally $manifestPath = JPATH_MANIFESTS . '/files/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; try { return $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } } PK1A#]��X��y�y)src/Installer/Adapter/LanguageAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Language; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Language installer * * @since 3.1 */ class LanguageAdapter extends InstallerAdapter { /** * Core language pack flag * * @var boolean * @since 3.0.0 */ protected $core = false; /** * The language tag for the package * * @var string * @since 4.0.0 */ protected $tag; /** * Flag indicating the uninstall process should not run SQL queries * * @var boolean * @since 4.0.0 */ protected $ignoreUninstallQueries = false; /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { // @todo - Refactor adapter to use common code } /** * Method to finalise the installation processing * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseInstall() { // @todo - Refactor adapter to use common code } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { if ($this->ignoreUninstallQueries) { return false; } $this->resetUserLanguage(); $extensionId = $this->extension->extension_id; // Remove the schema version $db = $this->getDatabase(); $query = $db->getQuery(true) ->delete($db->quoteName('#__schemas')) ->where($db->quoteName('extension_id') . ' = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } // Clean installed languages cache. Factory::getCache()->clean('com_languages'); // Remove the extension table entry $this->extension->delete(); return true; } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { $this->parent->removeFiles($this->getManifest()->media); // Construct the path from the client, the language and the extension element name $path = ApplicationHelper::getClientInfo($this->extension->client_id)->path . '/language/' . $this->extension->element; if (!Folder::delete($path)) { // If deleting failed we'll leave the extension entry in tact just in case Log::add(Text::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DIRECTORY'), Log::WARNING, 'jerror'); $this->ignoreUninstallQueries = true; } } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 */ protected function setupInstallPaths() { // @todo - Refactor adapter to use common code } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { // Grab a copy of the client details $client = ApplicationHelper::getClientInfo($this->extension->client_id); // Check the element isn't blank to prevent nuking the languages directory...just in case if (empty($this->extension->element)) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_ELEMENT_EMPTY')); } // Verify that it's not the default language for that client $params = ComponentHelper::getParams('com_languages'); if ($params->get($client->name) === $this->extension->element) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DEFAULT')); } // Construct the path from the client, the language and the extension element name $path = $client->path . '/language/' . $this->extension->element; // Get the package manifest object and remove media $this->parent->setPath('source', $path); // Check it exists if (!Folder::exists($path)) { // If the folder doesn't exist lets just nuke the row as well and presume the user killed it for us $this->extension->delete(); throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PATH_EMPTY')); } // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file $this->parent->findManifest(); $this->setManifest($this->parent->getManifest()); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { // @todo - Refactor adapter to use common code } /** * Custom install method * * Note: This behaves badly due to hacks made in the middle of 1.5.x to add * the ability to install multiple distinct packs in one install. The * preferred method is to use a package to install multiple language packs. * * @return boolean|integer The extension ID on success, boolean false on failure * * @since 3.1 */ public function install() { $source = $this->parent->getPath('source'); if (!$source) { $this->parent ->setPath( 'source', ApplicationHelper::getClientInfo($this->parent->extension->client_id)->path . '/language/' . $this->parent->extension->element ); } $this->setManifest($this->parent->getManifest()); // Get the client application target if ($cname = (string) $this->getManifest()->attributes()->client) { // Attempt to map the client to a base path $client = ApplicationHelper::getClientInfo($cname, true); if ($client === null) { $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); return false; } $basePath = $client->path; $clientId = $client->id; $element = $this->getManifest()->files; return $this->_install($cname, $basePath, $clientId, $element); } else { // No client attribute was found so we assume the site as the client $cname = 'site'; $basePath = JPATH_SITE; $clientId = 0; $element = $this->getManifest()->files; return $this->_install($cname, $basePath, $clientId, $element); } } /** * Install function that is designed to handle individual clients * * @param string $cname Cname @todo: not used * @param string $basePath The base name. * @param integer $clientId The client id. * @param object &$element The XML element. * * @return boolean|integer The extension ID on success, boolean false on failure * * @since 3.1 */ protected function _install($cname, $basePath, $clientId, &$element) { $this->setManifest($this->parent->getManifest()); // Get the language name // Set the extensions name $this->name = InputFilter::getInstance()->clean((string) $this->getManifest()->name, 'string'); // Get the Language tag [ISO tag, eg. en-GB] $tag = (string) $this->getManifest()->tag; // Check if we found the tag - if we didn't, we may be trying to install from an older language package if (!$tag) { $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', Text::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); return false; } $this->tag = $tag; // Set the language installation path $this->parent->setPath('extension_site', $basePath . '/language/' . $tag); // Do we have a meta file in the file list? In other words... is this a core language pack? if ($element && \count($element->children())) { $files = $element->children(); foreach ($files as $file) { if ((string) $file->attributes()->file === 'meta') { $this->core = true; break; } } } // If the language directory does not exist, let's create it $created = false; if (!file_exists($this->parent->getPath('extension_site'))) { if (!$created = Folder::create($this->parent->getPath('extension_site'))) { $this->parent ->abort( Text::sprintf( 'JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_CREATE_FOLDER_FAILED', $this->parent->getPath('extension_site')) ) ); return false; } } else { // Look for an update function or update tag $updateElement = $this->getManifest()->update; // Upgrade manually set or update tag detected if ($updateElement || $this->parent->isUpgrade()) { // Transfer control to the update function return $this->update(); } elseif (!$this->parent->isOverwrite()) { // Overwrite is set // We didn't have overwrite set, find an update function or find an update tag so lets call it safe if (file_exists($this->parent->getPath('extension_site'))) { // If the site exists say so. Log::add( Text::sprintf('JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_site'))), Log::WARNING, 'jerror' ); } elseif (file_exists($this->parent->getPath('extension_administrator'))) { // If the admin exists say so. Log::add( Text::sprintf( 'JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_administrator')) ), Log::WARNING, 'jerror' ); } else { // If the api exists say so. Log::add( Text::sprintf( 'JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_api')) ), Log::WARNING, 'jerror' ); } return false; } } /* * If we created the language directory we will want to remove it if we * have to roll back the installation, so let's add it to the installation * step stack */ if ($created) { $this->parent->pushStep(['type' => 'folder', 'path' => $this->parent->getPath('extension_site')]); } // Copy all the necessary files if ($this->parent->parseFiles($element) === false) { // Install failed, rollback changes $this->parent->abort(); return false; } // Parse optional tags $this->parent->parseMedia($this->getManifest()->media); // Get the language description $description = (string) $this->getManifest()->description; if ($description) { $this->parent->set('message', Text::_($description)); } else { $this->parent->set('message', ''); } // Add an entry to the extension table with a whole heap of defaults $row = Table::getInstance('extension'); $row->set('name', $this->name); $row->set('type', 'language'); $row->set('element', $this->tag); $row->set('changelogurl', (string) $this->getManifest()->changelogurl); // There is no folder for languages $row->set('folder', ''); $row->set('enabled', 1); $row->set('protected', 0); $row->set('access', 0); $row->set('client_id', $clientId); $row->set('params', $this->parent->getParams()); $row->set('manifest_cache', $this->parent->generateManifestCache()); if (!$row->check() || !$row->store()) { // Install failed, roll back changes $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); return false; } if ((int) $clientId === 0) { $this->createContentLanguage($this->tag); } // Clobber any possible pending updates /** @var Update $update */ $update = Table::getInstance('update'); $uid = $update->find(['element' => $this->tag, 'type' => 'language', 'folder' => '']); if ($uid) { $update->delete($uid); } // Clean installed languages cache. Factory::getCache()->clean('com_languages'); return $row->get('extension_id'); } /** * Gets a unique language SEF string. * * This function checks other existing language with the same code, if they exist provides a unique SEF name. * For instance: en-GB, en-US and en-AU will share the same SEF code by default: www.mywebsite.com/en/ * To avoid this conflict, this function creates a specific SEF in case of existing conflict: * For example: www.mywebsite.com/en-au/ * * @param string $itemLanguageTag Language Tag. * * @return string * * @since 3.7.0 */ protected function getSefString($itemLanguageTag) { $langs = explode('-', $itemLanguageTag); $prefixToFind = $langs[0]; $numberPrefixesFound = 0; // Get the sef value of all current content languages. $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('sef')) ->from($db->quoteName('#__languages')); $db->setQuery($query); $siteLanguages = $db->loadObjectList(); foreach ($siteLanguages as $siteLang) { if ($siteLang->sef === $prefixToFind) { $numberPrefixesFound++; } } return $numberPrefixesFound === 0 ? $prefixToFind : strtolower($itemLanguageTag); } /** * Custom update method * * @return boolean True on success, false on failure * * @since 3.1 */ public function update() { $xml = $this->parent->getManifest(); $this->setManifest($xml); $cname = $xml->attributes()->client; // Attempt to map the client to a base path $client = ApplicationHelper::getClientInfo($cname, true); if ($client === null || (empty($cname) && $cname !== 0)) { $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', Text::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); return false; } $basePath = $client->path; $clientId = $client->id; // Get the language name // Set the extensions name $name = (string) $this->getManifest()->name; $name = InputFilter::getInstance()->clean($name, 'string'); $this->name = $name; // Get the Language tag [ISO tag, eg. en-GB] $tag = (string) $xml->tag; // Check if we found the tag - if we didn't, we may be trying to install from an older language package if (!$tag) { $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', Text::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); return false; } $this->tag = $tag; // Set the language installation path $this->parent->setPath('extension_site', $basePath . '/language/' . $tag); // Do we have a meta file in the file list? In other words... is this a core language pack? if (\count($xml->files->children())) { foreach ($xml->files->children() as $file) { if ((string) $file->attributes()->file === 'meta') { $this->core = true; break; } } } // Copy all the necessary files if ($this->parent->parseFiles($xml->files) === false) { // Install failed, rollback changes $this->parent->abort(); return false; } // Parse optional tags $this->parent->parseMedia($xml->media); // Get the language description and set it as message $this->parent->set('message', (string) $xml->description); /** * --------------------------------------------------------------------------------------------- * Finalization and Cleanup Section * --------------------------------------------------------------------------------------------- */ // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find(['element' => $this->tag, 'type' => 'language', 'client_id' => $clientId]); if ($uid) { $update->delete($uid); } // Update an entry to the extension table $row = Table::getInstance('extension'); $eid = $row->find(['element' => $this->tag, 'type' => 'language', 'client_id' => $clientId]); if ($eid) { $row->load($eid); } else { // Set the defaults // There is no folder for language $row->set('folder', ''); $row->set('enabled', 1); $row->set('protected', 0); $row->set('access', 0); $row->set('client_id', $clientId); $row->set('params', $this->parent->getParams()); } $row->set('name', $this->name); $row->set('type', 'language'); $row->set('element', $this->tag); $row->set('manifest_cache', $this->parent->generateManifestCache()); $row->set('changelogurl', (string) $this->getManifest()->changelogurl); // Clean installed languages cache. Factory::getCache()->clean('com_languages'); if (!$row->check() || !$row->store()) { // Install failed, roll back changes $this->parent->abort(Text::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); return false; } if ($clientId === 0) { $this->createContentLanguage($this->tag); } return $row->get('extension_id'); } /** * Custom discover method * Finds language files * * @return \Joomla\CMS\Table\Extension[] Array of discovered extensions. * * @since 3.1 */ public function discover() { $results = []; $clients = [0 => JPATH_SITE, 1 => JPATH_ADMINISTRATOR, 3 => JPATH_API]; foreach ($clients as $clientId => $basePath) { $languages = Folder::folders($basePath . '/language'); foreach ($languages as $language) { $manifestfile = $basePath . '/language/' . $language . '/langmetadata.xml'; if (!is_file($manifestfile)) { $manifestfile = $basePath . '/language/' . $language . '/' . $language . '.xml'; if (!is_file($manifestfile)) { continue; } } $manifest_details = Installer::parseXMLInstallFile($manifestfile); $extension = Table::getInstance('extension'); $extension->set('type', 'language'); $extension->set('client_id', $clientId); $extension->set('element', $language); $extension->set('folder', ''); $extension->set('name', $language); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifest_details)); $extension->set('params', '{}'); $results[] = $extension; } } return $results; } /** * Custom discover install method * Basically updates the manifest cache and leaves everything alone * * @return integer The extension id * * @since 3.1 */ public function discover_install() { // Need to find to find where the XML file is since we don't store this normally $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $short_element = $this->parent->extension->element; $manifestPath = $client->path . '/language/' . $short_element . '/langmetadata.xml'; if (!is_file($manifestPath)) { $manifestPath = $client->path . '/language/' . $short_element . '/' . $short_element . '.xml'; } $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->parent->setPath('source', $client->path . '/language/' . $short_element); $this->parent->setPath('extension_root', $this->parent->getPath('source')); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->state = 0; $this->parent->extension->name = $manifest_details['name']; $this->parent->extension->enabled = 1; // @todo remove code: $this->parent->extension->params = $this->parent->getParams(); try { $this->parent->extension->check(); $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_LANG_DISCOVER_STORE_DETAILS'), Log::WARNING, 'jerror'); return false; } if ($client->id === 0) { $this->createContentLanguage($short_element); } // Clean installed languages cache. Factory::getCache()->clean('com_languages'); return $this->parent->extension->get('extension_id'); } /** * Refreshes the extension table cache * * @return boolean result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); $manifestPath = $client->path . '/language/' . $this->parent->extension->element . '/langmetadata.xml'; if (!is_file($manifestPath)) { $manifestPath = $client->path . '/language/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; } $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; if ($this->parent->extension->store()) { return true; } Log::add(Text::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } /** * Resets user language to default language * * @return void * * @since 4.0.0 */ private function resetUserLanguage(): void { $client = ApplicationHelper::getClientInfo($this->extension->client_id); if ($client->name !== 'site' && $client->name !== 'administrator') { return; } // Setting the language of users which have this language as the default language $db = $this->getDatabase(); $query = $db->getQuery(true) ->select( [ $db->quoteName('id'), $db->quoteName('params'), ] ) ->from($db->quoteName('#__users')); $db->setQuery($query); $users = $db->loadObjectList(); if ($client->name === 'administrator') { $param_name = 'admin_language'; } else { $param_name = 'language'; } $count = 0; // Prepare the query. $query = $db->getQuery(true) ->update($db->quoteName('#__users')) ->set($db->quoteName('params') . ' = :registry') ->where($db->quoteName('id') . ' = :userId') ->bind(':registry', $registry) ->bind(':userId', $userId, ParameterType::INTEGER); $db->setQuery($query); foreach ($users as $user) { $registry = new Registry($user->params); if ($registry->get($param_name) === $this->extension->element) { // Update query parameters. $registry->set($param_name, ''); $userId = $user->id; $db->execute(); $count++; } } if (!empty($count)) { Log::add(Text::plural('JLIB_INSTALLER_NOTICE_LANG_RESET_USERS', $count), Log::NOTICE, 'jerror'); } } /** * Create an unpublished content language. * * @param $tag string The language tag * * @throws \Exception * @since 4.0.0 */ protected function createContentLanguage($tag) { $tableLanguage = Table::getInstance('language'); // Check if content language already exists. if ($tableLanguage->load(['lang_code' => $tag])) { return; } $manifestfile = JPATH_SITE . '/language/' . $tag . '/langmetadata.xml'; if (!is_file($manifestfile)) { $manifestfile = JPATH_SITE . '/language/' . $tag . '/' . $tag . '.xml'; } // Load the site language manifest. $siteLanguageManifest = LanguageHelper::parseXMLLanguageFile($manifestfile); // Set the content language title as the language metadata name. $contentLanguageTitle = $siteLanguageManifest['name']; // Set, as fallback, the content language native title to the language metadata name. $contentLanguageNativeTitle = $contentLanguageTitle; // If exist, load the native title from the language xml metadata. if (isset($siteLanguageManifest['nativeName']) && $siteLanguageManifest['nativeName']) { $contentLanguageNativeTitle = $siteLanguageManifest['nativeName']; } // Try to load a language string from the installation language var. Will be removed in 4.0. if ($contentLanguageNativeTitle === $contentLanguageTitle) { $manifestfile = JPATH_INSTALLATION . '/language/' . $tag . '/langmetadata.xml'; if (!is_file($manifestfile)) { $manifestfile = JPATH_INSTALLATION . '/language/' . $tag . '/' . $tag . '.xml'; } if (file_exists($manifestfile)) { $installationLanguage = new Language($tag); $installationLanguage->load('', JPATH_INSTALLATION); if ($installationLanguage->hasKey('INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME')) { // Make sure it will not use the en-GB fallback. $defaultLanguage = new Language('en-GB'); $defaultLanguage->load('', JPATH_INSTALLATION); $defaultLanguageNativeTitle = $defaultLanguage->_('INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME'); $installationLanguageNativeTitle = $installationLanguage->_('INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME'); if ($defaultLanguageNativeTitle !== $installationLanguageNativeTitle) { $contentLanguageNativeTitle = $installationLanguage->_('INSTL_DEFAULTLANGUAGE_NATIVE_LANGUAGE_NAME'); } } } } // Prepare language data for store. $languageData = [ 'lang_id' => 0, 'lang_code' => $tag, 'title' => $contentLanguageTitle, 'title_native' => $contentLanguageNativeTitle, 'sef' => $this->getSefString($tag), 'image' => strtolower(str_replace('-', '_', $tag)), 'published' => 0, 'ordering' => 0, 'access' => (int) Factory::getApplication()->get('access', 1), 'description' => '', 'metadesc' => '', 'sitename' => '', ]; if (!$tableLanguage->bind($languageData) || !$tableLanguage->check() || !$tableLanguage->store() || !$tableLanguage->reorder()) { Log::add( Text::sprintf('JLIB_INSTALLER_WARNING_UNABLE_TO_INSTALL_CONTENT_LANGUAGE', $siteLanguageManifest['name'], $tableLanguage->getError()), Log::NOTICE, 'jerror' ); } } } PK1A#]Gܵ�A�A(src/Installer/Adapter/LibraryAdapter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installer\Adapter; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Installer\Manifest\LibraryManifest; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\Update; use Joomla\Database\ParameterType; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Library installer * * @since 3.1 */ class LibraryAdapter extends InstallerAdapter { /** * Method to check if the extension is present in the filesystem, flags the route as update if so * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function checkExtensionInFilesystem() { if ($this->currentExtensionId) { // Already installed, can we upgrade? if ($this->parent->isOverwrite() || $this->parent->isUpgrade()) { // We can upgrade, so uninstall the old one // We don't want to compromise this instance! $installer = new Installer(); $installer->setDatabase($this->getDatabase()); $installer->setPackageUninstall(true); $installer->uninstall('library', $this->currentExtensionId); // Clear the cached data $this->currentExtensionId = null; $this->extension = Table::getInstance('Extension', 'JTable', ['dbo' => $this->getDatabase()]); // From this point we'll consider this an update $this->setRoute('update'); } else { // Stop the install, no upgrade possible throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_ALREADY_INSTALLED')); } } } /** * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function copyBaseFiles() { if ($this->parent->parseFiles($this->getManifest()->files, -1) === false) { throw new \RuntimeException(Text::sprintf('JLIB_INSTALLER_ABORT_LIB_COPY_FILES', $this->element)); } } /** * Method to finalise the installation processing * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function finaliseInstall() { // Clobber any possible pending updates /** @var Update $update */ $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } // Lastly, we will copy the manifest file to its appropriate place. if ($this->route !== 'discover_install') { $manifest = []; $manifest['src'] = $this->parent->getPath('manifest'); $manifest['dest'] = JPATH_MANIFESTS . '/libraries/' . $this->element . '.xml'; $destFolder = \dirname($manifest['dest']); if (!is_dir($destFolder) && !@mkdir($destFolder)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } if (!$this->parent->copyFiles([$manifest], true)) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_COPY_SETUP', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } // If there is a manifest script, let's copy it. if ($this->manifest_script) { $path = []; $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { if (!$this->parent->copyFiles([$path])) { // Install failed, rollback changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_MANIFEST', Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) ) ); } } } } } /** * Method to finalise the uninstallation processing * * @return boolean * * @since 4.0.0 * @throws \RuntimeException */ protected function finaliseUninstall(): bool { $extensionId = $this->extension->extension_id; $db = $this->getDatabase(); // Remove the schema version $query = $db->getQuery(true) ->delete('#__schemas') ->where('extension_id = :extension_id') ->bind(':extension_id', $extensionId, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); // Clobber any possible pending updates $update = Table::getInstance('update'); $uid = $update->find( [ 'element' => $this->extension->element, 'type' => $this->type, ] ); if ($uid) { $update->delete($uid); } $this->extension->delete(); return true; } /** * Get the filtered extension element from the manifest * * @param string $element Optional element name to be converted * * @return string The filtered element * * @since 3.4 */ public function getElement($element = null) { if (!$element) { $element = (string) $this->getManifest()->libraryname; } return $element; } /** * Custom loadLanguage method * * @param string $path The path where to find language files. * * @return void * * @since 3.1 */ public function loadLanguage($path = null) { $source = $this->parent->getPath('source'); if (!$source) { $this->parent->setPath('source', JPATH_PLATFORM . '/' . $this->getElement()); } $extension = 'lib_' . str_replace('/', '_', $this->getElement()); $librarypath = (string) $this->getManifest()->libraryname; $source = $path ?: JPATH_PLATFORM . '/' . $librarypath; $this->doLoadLanguage($extension, $source, JPATH_SITE); } /** * Method to parse optional tags in the manifest * * @return void * * @since 3.4 */ protected function parseOptionalTags() { $this->parent->parseLanguages($this->getManifest()->languages); $this->parent->parseMedia($this->getManifest()->media); } /** * Prepares the adapter for a discover_install task * * @return void * * @since 3.4 */ public function prepareDiscoverInstall() { $manifestPath = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $this->setManifest($this->parent->getManifest()); } /** * Removes this extension's files * * @return void * * @since 4.0.0 * @throws \RuntimeException */ protected function removeExtensionFiles() { $this->parent->removeFiles($this->getManifest()->files, -1); $manifest = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml'; if (is_file($manifest)) { File::delete($manifest); } // @todo: Change this so it walked up the path backwards so we clobber multiple empties // If the folder is empty, let's delete it if (Folder::exists($this->parent->getPath('extension_root'))) { if (is_dir($this->parent->getPath('extension_root'))) { $files = Folder::files($this->parent->getPath('extension_root')); if (!\count($files)) { Folder::delete($this->parent->getPath('extension_root')); } } } $this->parent->removeFiles($this->getManifest()->media); $this->parent->removeFiles($this->getManifest()->languages); $elementParts = explode('/', $this->extension->element); // Delete empty vendor folders if (2 === \count($elementParts)) { $folders = Folder::folders(JPATH_PLATFORM . '/' . $elementParts[0]); if (empty($folders)) { Folder::delete(JPATH_MANIFESTS . '/libraries/' . $elementParts[0]); Folder::delete(JPATH_PLATFORM . '/' . $elementParts[0]); } } } /** * Method to do any prechecks and setup the install paths for the extension * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function setupInstallPaths() { $group = (string) $this->getManifest()->libraryname; if (!$group) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_NOFILE')); } // Don't install libraries which would override core folders $restrictedFolders = ['php-encryption', 'phpass', 'src', 'vendor']; if (in_array($group, $restrictedFolders)) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_CORE_FOLDER')); } $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . implode(DIRECTORY_SEPARATOR, explode('/', $group))); } /** * Method to do any prechecks and setup the uninstall job * * @return void * * @since 4.0.0 */ protected function setupUninstall() { $manifestFile = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml'; // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest if (!file_exists($manifestFile)) { // Remove this row entry since its invalid $this->extension->delete($this->extension->extension_id); throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); } $manifest = new LibraryManifest($manifestFile); // Set the library root path $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . $manifest->libraryname); // Set the source path to the library root, the manifest script may be found $this->parent->setPath('source', $this->parent->getPath('extension_root')); $xml = simplexml_load_file($manifestFile); // If we cannot load the XML file return null if (!$xml) { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_LOAD_MANIFEST')); } // Check for a valid XML root tag. if ($xml->getName() !== 'extension') { throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_MANIFEST')); } $this->setManifest($xml); // Attempt to load the language file; might have uninstall strings $this->loadLanguage(); } /** * Method to store the extension to the database * * @return void * * @since 3.4 * @throws \RuntimeException */ protected function storeExtension() { // Discover installs are stored a little differently if ($this->route === 'discover_install') { $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->extension->manifest_cache = json_encode($manifest_details); $this->extension->state = 0; $this->extension->name = $manifest_details['name']; $this->extension->enabled = 1; $this->extension->params = $this->parent->getParams(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_DISCOVER_STORE_DETAILS')); } return; } $this->extension->name = $this->name; $this->extension->type = 'library'; $this->extension->element = $this->element; $this->extension->changelogurl = $this->changelogurl; // There is no folder for libraries $this->extension->folder = ''; $this->extension->enabled = 1; $this->extension->protected = 0; $this->extension->access = 1; $this->extension->client_id = 0; $this->extension->params = $this->parent->getParams(); // Update the manifest cache for the entry $this->extension->manifest_cache = $this->parent->generateManifestCache(); if (!$this->extension->store()) { // Install failed, roll back changes throw new \RuntimeException( Text::sprintf( 'JLIB_INSTALLER_ABORT_LIB_INSTALL_ROLLBACK', $this->extension->getError() ) ); } // Since we have created a library item, we add it to the installation step stack // so that if we have to rollback the changes we can undo it. $this->parent->pushStep(['type' => 'extension', 'id' => $this->extension->extension_id]); } /** * Custom discover method * * @return array Extension list of extensions available * * @since 3.1 */ public function discover() { $results = []; $mainFolder = JPATH_MANIFESTS . '/libraries'; $folder = new \RecursiveDirectoryIterator($mainFolder); $iterator = new \RegexIterator( new \RecursiveIteratorIterator($folder), '/\.xml$/i', \RecursiveRegexIterator::GET_MATCH ); foreach ($iterator as $file => $pattern) { $element = str_replace([$mainFolder . DIRECTORY_SEPARATOR, '.xml'], '', $file); $manifestCache = Installer::parseXMLInstallFile($file); $extension = Table::getInstance('extension'); $extension->set('type', 'library'); $extension->set('client_id', 0); $extension->set('element', $element); $extension->set('folder', ''); $extension->set('name', $element); $extension->set('state', -1); $extension->set('manifest_cache', json_encode($manifestCache)); $extension->set('params', '{}'); $results[] = $extension; } return $results; } /** * Refreshes the extension table cache * * @return boolean Result of operation, true if updated, false on failure * * @since 3.1 */ public function refreshManifestCache() { // Need to find to find where the XML file is since we don't store this normally $manifestPath = JPATH_MANIFESTS . '/libraries/' . $this->parent->extension->element . '.xml'; $this->parent->manifest = $this->parent->isManifest($manifestPath); $this->parent->setPath('manifest', $manifestPath); $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); $this->parent->extension->manifest_cache = json_encode($manifest_details); $this->parent->extension->name = $manifest_details['name']; try { return $this->parent->extension->store(); } catch (\RuntimeException $e) { Log::add(Text::_('JLIB_INSTALLER_ERROR_LIB_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); return false; } } } PK1A#]g��#src/Serializer/JoomlaSerializer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Serializer; use Joomla\CMS\Factory; use Joomla\CMS\Object\CMSObject; use Tobscure\JsonApi\AbstractSerializer; use Tobscure\JsonApi\Relationship; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * This class does the messy job of sanitising all the classes Joomla has that contain data and converting them * into a standard array that can be consumed by the Tobscure library. It also throws appropriate plugin events * to allow 3rd party extensions to add custom data and relations into these properties before they are rendered * * @since 4.0.0 */ class JoomlaSerializer extends AbstractSerializer { /** * Constructor. * * @param string $type The content type to be loaded * * @since 4.0.0 */ public function __construct(string $type) { $this->type = $type; } /** * Get the attributes array. * * @param array|\stdClass|CMSObject $post The data container * @param array|null $fields The requested fields to be rendered * * @return array * * @since 4.0.0 */ public function getAttributes($post, array $fields = null) { if (!($post instanceof \stdClass) && !(\is_array($post)) && !($post instanceof CMSObject)) { $message = sprintf( 'Invalid argument for %s. Expected array or %s. Got %s', static::class, CMSObject::class, \gettype($post) ); throw new \InvalidArgumentException($message); } // The response from a standard ListModel query if ($post instanceof \stdClass) { $post = (array) $post; } // The response from a standard AdminModel query also works for Table which extends CMSObject if ($post instanceof CMSObject) { $post = $post->getProperties(); } $event = new Events\OnGetApiAttributes('onGetApiAttributes', ['attributes' => $post, 'context' => $this->type]); /** @var Events\OnGetApiAttributes $eventResult */ $eventResult = Factory::getApplication()->getDispatcher()->dispatch('onGetApiAttributes', $event); $combinedData = array_merge($post, $eventResult->getAttributes()); return \is_array($fields) ? array_intersect_key($combinedData, array_flip($fields)) : $combinedData; } /** * Get a relationship. * * @param mixed $model The model of the entity being rendered * @param string $name The name of the relationship to return * * @return \Tobscure\JsonApi\Relationship|null * * @since 4.0.0 */ public function getRelationship($model, $name) { $result = parent::getRelationship($model, $name); // If we found a result in the content type serializer return now. Else trigger plugins. if ($result instanceof Relationship) { return $result; } $eventData = ['model' => $model, 'field' => $name, 'context' => $this->type]; $event = new Events\OnGetApiRelation('onGetApiRelation', $eventData); /** @var Events\OnGetApiRelation $eventResult */ $eventResult = Factory::getApplication()->getDispatcher()->dispatch('onGetApiRelation', $event); $relationship = $eventResult->getRelationship(); if ($relationship instanceof Relationship) { return $relationship; } return null; } } PK1A#]��(T T *src/Serializer/Events/OnGetApiRelation.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Serializer\Events; use Joomla\CMS\Event\AbstractImmutableEvent; use Tobscure\JsonApi\Relationship; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event for getting information on an API Relationship * * @since 4.0.0 */ final class OnGetApiRelation extends AbstractImmutableEvent { /** * The relationship * * @var Relationship * @since 4.0.0 */ private $relationship; /** * Constructor. * * Mandatory arguments: * model mixed The model being used to render the resource. * field string The field name we wish to obtain a relationship for. * context string The content type of the api resource. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 4.0.0 * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if (!\array_key_exists('model', $arguments)) { throw new \BadMethodCallException("Argument 'model' is required for event $name"); } if (!\array_key_exists('field', $arguments)) { throw new \BadMethodCallException("Argument 'field' is required for event $name"); } if (!\array_key_exists('context', $arguments)) { throw new \BadMethodCallException("Argument 'context' is required for event $name"); } parent::__construct($name, $arguments); } /** * Get properties to render. * * @return Relationship * * @since 4.0.0 */ public function getRelationship(): ?Relationship { return $this->relationship; } /** * Set relationship object that should be rendered. * * @param Relationship $relationship The relationship object that should be rendered. * * @return void * @since 4.0.0 */ public function setRelationship(Relationship $relationship): void { $this->relationship = $relationship; } } PK1A#]ti��J J ,src/Serializer/Events/OnGetApiAttributes.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Serializer\Events; use Joomla\CMS\Event\AbstractImmutableEvent; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Event for getting extra data attributes for an API Entity * * @since 4.0.0 */ final class OnGetApiAttributes extends AbstractImmutableEvent { /** * The attributes * * @var array * @since 4.0.0 */ private $attributes = []; /** * Constructor. * * Mandatory arguments: * attributes array The main data for the object. * context string The content type of the api resource. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 4.0.0 * @throws \BadMethodCallException */ public function __construct($name, array $arguments = []) { if ( !\array_key_exists('attributes', $arguments) || \array_key_exists('attributes', $arguments) && !is_array($arguments['attributes']) ) { throw new \BadMethodCallException("Argument 'attributes' as an array is required for event $name"); } if (!\array_key_exists('context', $arguments)) { throw new \BadMethodCallException("Argument 'context' is required for event $name"); } parent::__construct($name, $arguments); } /** * The properties to be rendered. * * @return array * * @since 4.0.0 */ public function getAttributes(): array { return $this->attributes; } /** * Set a named attribute to be rendered in the API. * * @param string $name The name of the property to be rendered in the api * @param mixed $value The value of the named property to be rendered in the api. * * @return void * @since 4.0.0 */ public function addAttribute($name, $value): void { $this->attributes[$name] = $value; } /** * Set attributes to be rendered in the API. * * @param array $value An array of key/value pairs for properties to be added to the api. * * @return void * @since 4.0.0 */ public function addAttributes(array $value): void { $this->attributes = array_merge($this->attributes, $value); } } PK1A#]�31�.src/Association/AssociationExtensionHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Association; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Association Extension Helper * * @since 3.7.0 */ abstract class AssociationExtensionHelper implements AssociationExtensionInterface { /** * The extension name * * @var array $extension * * @since 3.7.0 */ protected $extension = 'com_??'; /** * Array of item types * * @var array $itemTypes * * @since 3.7.0 */ protected $itemTypes = []; /** * Has the extension association support * * @var boolean $associationsSupport * * @since 3.7.0 */ protected $associationsSupport = false; /** * Checks if the extension supports associations * * @return boolean Supports the extension associations * * @since 3.7.0 */ public function hasAssociationsSupport() { return $this->associationsSupport; } /** * Get the item types * * @return array Array of item types * * @since 3.7.0 */ public function getItemTypes() { return $this->itemTypes; } /** * Get the associated items for an item * * @param string $typeName The item type * @param int $itemId The id of item for which we need the associated items * * @return array * * @since 3.7.0 */ public function getAssociationList($typeName, $itemId) { $items = []; $associations = $this->getAssociations($typeName, $itemId); foreach ($associations as $key => $association) { $items[$key] = ArrayHelper::fromObject($this->getItem($typeName, (int) $association->id), false); } return $items; } /** * Get information about the type * * @param string $typeName The item type * * @return array Array of item types * * @since 3.7.0 */ public function getType($typeName = '') { $fields = $this->getFieldsTemplate(); $tables = []; $joins = []; $support = $this->getSupportTemplate(); $title = ''; return [ 'fields' => $fields, 'support' => $support, 'tables' => $tables, 'joins' => $joins, 'title' => $title, ]; } /** * Get information about the fields the type provides * * @param string $typeName The item type * * @return array Array of support information * * @since 3.7.0 */ public function getTypeFields($typeName) { return $this->getTypeInformation($typeName, 'fields'); } /** * Get information about the fields the type provides * * @param string $typeName The item type * * @return array Array of support information * * @since 3.7.0 */ public function getTypeSupport($typeName) { return $this->getTypeInformation($typeName, 'support'); } /** * Get information about the tables the type use * * @param string $typeName The item type * * @return array Array of support information * * @since 3.7.0 */ public function getTypeTables($typeName) { return $this->getTypeInformation($typeName, 'tables'); } /** * Get information about the table joins for the type * * @param string $typeName The item type * * @return array Array of support information * * @since 3.7.0 */ public function getTypeJoins($typeName) { return $this->getTypeInformation($typeName, 'joins'); } /** * Get the type title * * @param string $typeName The item type * * @return string The type title * * @since 3.7.0 */ public function getTypeTitle($typeName) { $type = $this->getType($typeName); if (!\array_key_exists('title', $type)) { return ''; } return $type['title']; } /** * Get information about the type * * @param string $typeName The item type * @param string $part part of the information * * @return array Array of support information * * @since 3.7.0 */ private function getTypeInformation($typeName, $part = 'support') { $type = $this->getType($typeName); if (!\array_key_exists($part, $type)) { return []; } return $type[$part]; } /** * Get a table field name for a type * * @param string $typeName The item type * @param string $fieldName The item type * * @return string * * @since 3.7.0 */ public function getTypeFieldName($typeName, $fieldName) { $fields = $this->getTypeFields($typeName); if (!\array_key_exists($fieldName, $fields)) { return ''; } $tmp = $fields[$fieldName]; $pos = strpos($tmp, '.'); if ($pos === false) { return $tmp; } return substr($tmp, $pos + 1); } /** * Get default values for support array * * @return array * * @since 3.7.0 */ protected function getSupportTemplate() { return [ 'state' => false, 'acl' => false, 'checkout' => false, ]; } /** * Get default values for fields array * * @return array * * @since 3.7.0 */ protected function getFieldsTemplate() { return [ 'id' => 'a.id', 'title' => 'a.title', 'alias' => 'a.alias', 'ordering' => 'a.ordering', 'menutype' => '', 'level' => '', 'catid' => 'a.catid', 'language' => 'a.language', 'access' => 'a.access', 'state' => 'a.state', 'created_user_id' => 'a.created_by', 'checked_out' => 'a.checked_out', 'checked_out_time' => 'a.checked_out_time', ]; } } PK1A#]1t-S��/src/Association/AssociationServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Association; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The association service. * * @since 4.0.0 */ interface AssociationServiceInterface { /** * Returns the associations extension helper class. * * @return AssociationExtensionInterface * * @since 4.0.0 */ public function getAssociationsExtension(): AssociationExtensionInterface; } PK1A#]G1src/Association/AssociationExtensionInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Association; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Association Extension Interface for the helper classes * * @since 3.7.0 */ interface AssociationExtensionInterface { /** * Checks if the extension supports associations * * @return boolean Supports the extension associations * * @since 3.7.0 */ public function hasAssociationsSupport(); /** * Method to get the associations for a given item. * * @param integer $id Id of the item * @param string $view Name of the view * * @return array Array of associations for the item * * @since 4.0.0 */ public function getAssociationsForItem($id = 0, $view = null); } PK1A#]~��E+src/Association/AssociationServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Association; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait to implement AssociationServiceInterface * * @since 4.0.0 */ trait AssociationServiceTrait { /** * The association extension. * * @var AssociationExtensionInterface * * @since 4.0.0 */ private $associationExtension = null; /** * Returns the associations extension helper class. * * @return AssociationExtensionInterface * * @since 4.0.0 */ public function getAssociationsExtension(): AssociationExtensionInterface { return $this->associationExtension; } /** * The association extension. * * @param AssociationExtensionInterface $associationExtension The extension * * @return void * * @since 4.0.0 */ public function setAssociationExtension(AssociationExtensionInterface $associationExtension) { $this->associationExtension = $associationExtension; } } PK1A#]sQ����%src/Fields/FieldsServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Fields; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The fields service. * * @since 4.0.0 */ interface FieldsServiceInterface { /** * Returns a valid section for the given section. If it is not valid then null * is returned. * * @param string $section The section to get the mapping for * @param object $item The item * * @return string|null The new section * * @since 4.0.0 */ public function validateSection($section, $item = null); /** * Returns valid contexts. * * @return array * * @since 4.0.0 */ public function getContexts(): array; } PK1A#]�����src/Document/XmlDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Factory as CmsFactory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * XmlDocument class, provides an easy interface to parse and display XML output * * @since 1.7.0 */ class XmlDocument extends Document { /** * Document name * * @var string * @since 3.0.0 */ protected $name = 'joomla'; /** * Flag indicating the document should be downloaded (Content-Disposition = attachment) versus displayed inline * * @var boolean * @since 3.9.0 */ protected $isDownload = false; /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set mime type $this->_mime = 'application/xml'; // Set document type $this->_type = 'xml'; } /** * Render the document. * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { parent::render($cache, $params); $disposition = $this->isDownload ? 'attachment' : 'inline'; CmsFactory::getApplication()->setHeader('Content-disposition', $disposition . '; filename="' . $this->getName() . '.xml"', true); return $this->getBuffer(); } /** * Returns the document name * * @return string * * @since 1.7.0 */ public function getName() { return $this->name; } /** * Sets the document name * * @param string $name Document name * * @return XmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function setName($name = 'joomla') { $this->name = $name; return $this; } /** * Check if this document is intended for download * * @return string * * @since 3.9.0 */ public function isDownload() { return $this->isDownload; } /** * Sets the document's download state * * @param boolean $download If true, this document will be downloaded; if false, this document will be displayed inline * * @return XmlDocument instance of $this to allow chaining * * @since 3.9.0 */ public function setDownload($download = false) { $this->isDownload = $download; return $this; } } PK1A#]{��%xx#src/Document/Feed/FeedEnclosure.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Feed; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing a feed enclosure * * @since 1.7.0 */ class FeedEnclosure { /** * URL enclosure element * * required * * @var string * @since 1.7.0 */ public $url = ''; /** * Length enclosure element * * required * * @var string * @since 1.7.0 */ public $length = ''; /** * Type enclosure element * * required * * @var string * @since 1.7.0 */ public $type = ''; } PK1A#]��src/Document/Feed/FeedImage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Feed; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing a feed image * * @since 1.7.0 */ class FeedImage { /** * Title image attribute * * required * * @var string * @since 1.7.0 */ public $title = ''; /** * URL image attribute * * required * * @var string * @since 1.7.0 */ public $url = ''; /** * Link image attribute * * required * * @var string * @since 1.7.0 */ public $link = ''; /** * Width image attribute * * optional * * @var string * @since 1.7.0 */ public $width; /** * Title feed attribute * * optional * * @var string * @since 1.7.0 */ public $height; /** * Title feed attribute * * optional * * @var string * @since 1.7.0 */ public $description; } PK1A#]$�p� src/Document/Feed/FeedItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Feed; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing a feed item * * @since 1.7.0 */ class FeedItem { /** * Title item element * * required * * @var string * @since 1.7.0 */ public $title; /** * Link item element * * required * * @var string * @since 1.7.0 */ public $link; /** * Description item element * * required * * @var string * @since 1.7.0 */ public $description; /** * Author item element * * optional * * @var string * @since 1.7.0 */ public $author; /** * Author email element * * optional * * @var string * @since 1.7.0 */ public $authorEmail; /** * Category element * * optional * * @var array or string * @since 1.7.0 */ public $category; /** * Comments element * * optional * * @var string * @since 1.7.0 */ public $comments; /** * Enclosure element * * @var FeedEnclosure * @since 1.7.0 */ public $enclosure = null; /** * Guid element * * optional * * @var string * @since 1.7.0 */ public $guid; /** * Published date * * optional * * May be in one of the following formats: * * RFC 822: * "Mon, 20 Jan 03 18:05:41 +0400" * "20 Jan 03 18:05:41 +0000" * * ISO 8601: * "2003-01-20T18:05:41+04:00" * * Unix: * 1043082341 * * @var string * @since 1.7.0 */ public $date; /** * Source element * * optional * * @var string * @since 1.7.0 */ public $source; /** * Set the FeedEnclosure for this item * * @param FeedEnclosure $enclosure The FeedEnclosure to add to the feed. * * @return FeedItem instance of $this to allow chaining * * @since 1.7.0 */ public function setEnclosure(FeedEnclosure $enclosure) { $this->enclosure = $enclosure; return $this; } } PK1A#]N�$�ddsrc/Document/HtmlDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Factory as CmsFactory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Helper\ModuleHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Utility\Utility; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HtmlDocument class, provides an easy interface to parse and display a HTML document * * @since 1.7.0 */ class HtmlDocument extends Document implements CacheControllerFactoryAwareInterface { use CacheControllerFactoryAwareTrait; /** * Array of Header `<link>` tags * * @var array * @since 1.7.0 */ public $_links = []; /** * Array of custom tags * * @var array * @since 1.7.0 */ public $_custom = []; /** * Name of the template * * @var string * @since 1.7.0 */ public $template = null; /** * Base url * * @var string * @since 1.7.0 */ public $baseurl = null; /** * Array of template parameters * * @var array * @since 1.7.0 */ public $params = null; /** * File name * * @var array * @since 1.7.0 */ public $_file = null; /** * Script nonce (string if set, null otherwise) * * @var string|null * @since 4.0.0 */ public $cspNonce = null; /** * String holding parsed template * * @var string * @since 1.7.0 */ protected $_template = ''; /** * Array of parsed template JDoc tags * * @var array * @since 1.7.0 */ protected $_template_tags = []; /** * Integer with caching setting * * @var integer * @since 1.7.0 */ protected $_caching = null; /** * Set to true when the document should be output as HTML5 * * @var boolean * @since 4.0.0 */ private $html5 = true; /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set document type $this->_type = 'html'; // Set default mime type and document metadata (metadata syncs with mime type by default) $this->setMimeEncoding('text/html'); } /** * Get the HTML document head data * * @return array The document head data in array form * * @since 1.7.0 */ public function getHeadData() { $data = []; $data['title'] = $this->title; $data['description'] = $this->description; $data['link'] = $this->link; $data['metaTags'] = $this->_metaTags; $data['links'] = $this->_links; $data['styleSheets'] = $this->_styleSheets; $data['style'] = $this->_style; $data['scripts'] = $this->_scripts; $data['script'] = $this->_script; $data['custom'] = $this->_custom; /** * @deprecated 4.0 will be removed in 6.0 * This property is for backwards compatibility. Pass text through script options in the future */ $data['scriptText'] = Text::getScriptStrings(); $data['scriptOptions'] = $this->scriptOptions; // Get Asset manager state $wa = $this->getWebAssetManager(); $waState = $wa->getManagerState(); // Get asset objects and filter only manually added/enabled assets, // Dependencies will be picked up from registry files $waState['assets'] = []; foreach ($waState['activeAssets'] as $assetType => $assetNames) { foreach ($assetNames as $assetName => $assetState) { $waState['assets'][$assetType][] = $wa->getAsset($assetType, $assetName); } } // We have loaded asset objects, now can remove unused stuff unset($waState['activeAssets']); $data['assetManager'] = $waState; return $data; } /** * Reset the HTML document head data * * @param mixed $types type or types of the heads elements to reset * * @return HtmlDocument instance of $this to allow chaining * * @since 3.7.0 */ public function resetHeadData($types = null) { if (\is_null($types)) { $this->title = ''; $this->description = ''; $this->link = ''; $this->_metaTags = []; $this->_links = []; $this->_styleSheets = []; $this->_style = []; $this->_scripts = []; $this->_script = []; $this->_custom = []; $this->scriptOptions = []; } if (\is_array($types)) { foreach ($types as $type) { $this->resetHeadDatum($type); } } if (\is_string($types)) { $this->resetHeadDatum($types); } return $this; } /** * Reset a part the HTML document head data * * @param string $type type of the heads elements to reset * * @return void * * @since 3.7.0 */ private function resetHeadDatum($type) { switch ($type) { case 'title': case 'description': case 'link': $this->{$type} = ''; break; case 'metaTags': case 'links': case 'styleSheets': case 'style': case 'scripts': case 'script': case 'custom': $realType = '_' . $type; $this->{$realType} = []; break; case 'scriptOptions': $this->{$type} = []; break; } } /** * Set the HTML document head data * * @param array $data The document head data in array form * * @return HtmlDocument|null instance of $this to allow chaining or null for empty input data * * @since 1.7.0 */ public function setHeadData($data) { if (empty($data) || !\is_array($data)) { return null; } $this->title = $data['title'] ?? $this->title; $this->description = $data['description'] ?? $this->description; $this->link = $data['link'] ?? $this->link; $this->_metaTags = $data['metaTags'] ?? $this->_metaTags; $this->_links = $data['links'] ?? $this->_links; $this->_styleSheets = $data['styleSheets'] ?? $this->_styleSheets; $this->_style = $data['style'] ?? $this->_style; $this->_scripts = $data['scripts'] ?? $this->_scripts; $this->_script = $data['script'] ?? $this->_script; $this->_custom = $data['custom'] ?? $this->_custom; $this->scriptOptions = (isset($data['scriptOptions']) && !empty($data['scriptOptions'])) ? $data['scriptOptions'] : $this->scriptOptions; // Restore asset manager state $wa = $this->getWebAssetManager(); if (!empty($data['assetManager']['registryFiles'])) { $waRegistry = $wa->getRegistry(); foreach ($data['assetManager']['registryFiles'] as $registryFile) { $waRegistry->addRegistryFile($registryFile); } } if (!empty($data['assetManager']['assets'])) { foreach ($data['assetManager']['assets'] as $assetType => $assets) { foreach ($assets as $asset) { $wa->registerAsset($assetType, $asset)->useAsset($assetType, $asset->getName()); } } } return $this; } /** * Merge the HTML document head data * * @param array $data The document head data in array form * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function mergeHeadData($data) { if (empty($data) || !\is_array($data)) { return $this; } $this->title = (isset($data['title']) && !empty($data['title']) && !stristr($this->title, $data['title'])) ? $this->title . $data['title'] : $this->title; $this->description = (isset($data['description']) && !empty($data['description']) && !stristr($this->description, $data['description'])) ? $this->description . $data['description'] : $this->description; $this->link = $data['link'] ?? $this->link; if (isset($data['metaTags'])) { foreach ($data['metaTags'] as $type1 => $data1) { $booldog = $type1 === 'http-equiv'; foreach ($data1 as $name2 => $data2) { $this->setMetaData($name2, $data2, $booldog); } } } $this->_links = (isset($data['links']) && !empty($data['links']) && \is_array($data['links'])) ? array_unique(array_merge($this->_links, $data['links']), SORT_REGULAR) : $this->_links; $this->_styleSheets = (isset($data['styleSheets']) && !empty($data['styleSheets']) && \is_array($data['styleSheets'])) ? array_merge($this->_styleSheets, $data['styleSheets']) : $this->_styleSheets; if (isset($data['style'])) { foreach ($data['style'] as $type => $styles) { foreach ($styles as $hash => $style) { if (!isset($this->_style[strtolower($type)][$hash])) { $this->addStyleDeclaration($style, $type); } } } } $this->_scripts = (isset($data['scripts']) && !empty($data['scripts']) && \is_array($data['scripts'])) ? array_merge($this->_scripts, $data['scripts']) : $this->_scripts; if (isset($data['script'])) { foreach ($data['script'] as $type => $scripts) { foreach ($scripts as $hash => $script) { if (!isset($this->_script[strtolower($type)][$hash])) { $this->addScriptDeclaration($script, $type); } } } } $this->_custom = (isset($data['custom']) && !empty($data['custom']) && \is_array($data['custom'])) ? array_unique(array_merge($this->_custom, $data['custom'])) : $this->_custom; if (!empty($data['scriptOptions'])) { foreach ($data['scriptOptions'] as $key => $scriptOptions) { $this->addScriptOptions($key, $scriptOptions, true); } } // Restore asset manager state $wa = $this->getWebAssetManager(); if (!empty($data['assetManager']['registryFiles'])) { $waRegistry = $wa->getRegistry(); foreach ($data['assetManager']['registryFiles'] as $registryFile) { $waRegistry->addRegistryFile($registryFile); } } if (!empty($data['assetManager']['assets'])) { foreach ($data['assetManager']['assets'] as $assetType => $assets) { foreach ($assets as $asset) { $wa->registerAsset($assetType, $asset)->useAsset($assetType, $asset->getName()); } } } return $this; } /** * Adds `<link>` tags to the head of the document * * $relType defaults to 'rel' as it is the most common relation type used. * ('rev' refers to reverse relation, 'rel' indicates normal, forward relation.) * Typical tag: `<link href="index.php" rel="Start">` * * @param string $href The link that is being related. * @param string $relation Relation of link. * @param string $relType Relation type attribute. Either rel or rev (default: 'rel'). * @param array $attribs Associative array of remaining attributes. * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addHeadLink($href, $relation, $relType = 'rel', $attribs = []) { $this->_links[$href]['relation'] = $relation; $this->_links[$href]['relType'] = $relType; $this->_links[$href]['attribs'] = $attribs; return $this; } /** * Adds a shortcut icon (favicon) * * This adds a link to the icon shown in the favorites list or on * the left of the url in the address bar. Some browsers display * it on the tab, as well. * * @param string $href The link that is being related. * @param string $type File type * @param string $relation Relation of link * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addFavicon($href, $type = 'image/vnd.microsoft.icon', $relation = 'icon') { $href = str_replace('\\', '/', $href); $this->addHeadLink($href, $relation, 'rel', ['type' => $type]); return $this; } /** * Adds a custom HTML string to the head block * * @param string $html The HTML to add to the head * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addCustomTag($html) { $this->_custom[] = trim($html); return $this; } /** * Returns whether the document is set up to be output as HTML5 * * @return boolean true when HTML5 is used * * @since 3.0.0 */ public function isHtml5() { return $this->html5; } /** * Sets whether the document should be output as HTML5 * * @param bool $state True when HTML5 should be output * * @return void * * @since 3.0.0 */ public function setHtml5($state) { if (\is_bool($state)) { $this->html5 = $state; } } /** * Get the contents of a document include * * @param string $type The type of renderer * @param string $name The name of the element to render * @param array $attribs Associative array of remaining attributes. * * @return mixed|string The output of the renderer * * @since 1.7.0 */ public function getBuffer($type = null, $name = null, $attribs = []) { // If no type is specified, return the whole buffer if ($type === null) { return parent::$_buffer; } $title = $attribs['title'] ?? null; if (isset(parent::$_buffer[$type][$name][$title])) { return parent::$_buffer[$type][$name][$title]; } $renderer = $this->loadRenderer($type); if ($this->_caching == true && $type === 'modules' && $name !== 'debug') { /** @var \Joomla\CMS\Document\Renderer\Html\ModulesRenderer $renderer */ /** @var \Joomla\CMS\Cache\Controller\OutputController $cache */ $cache = $this->getCacheControllerFactory()->createCacheController('output', ['defaultgroup' => 'com_modules']); $itemId = (int) CmsFactory::getApplication()->getInput()->get('Itemid', 0, 'int'); $hash = md5( serialize( [ $name, $attribs, \get_class($renderer), $itemId, ] ) ); $cbuffer = $cache->get('cbuffer_' . $type) ?: []; if (isset($cbuffer[$hash])) { return Cache::getWorkarounds($cbuffer[$hash], ['mergehead' => 1]); } $options = []; $options['nopathway'] = 1; $options['nomodules'] = 1; $options['modulemode'] = 1; $this->setBuffer($renderer->render($name, $attribs, null), $type, $name); $data = parent::$_buffer[$type][$name][$title]; $tmpdata = Cache::setWorkarounds($data, $options); $cbuffer[$hash] = $tmpdata; $cache->store($cbuffer, 'cbuffer_' . $type); } else { $this->setBuffer($renderer->render($name, $attribs, null), $type, $name, $title); } return parent::$_buffer[$type][$name][$title]; } /** * Set the contents a document includes * * @param string $content The content to be set in the buffer. * @param array $options Array of optional elements. * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function setBuffer($content, $options = []) { // The following code is just for backward compatibility. if (\func_num_args() > 1 && !\is_array($options)) { $args = \func_get_args(); $options = []; $options['type'] = $args[1]; $options['name'] = $args[2] ?? null; $options['title'] = $args[3] ?? null; } $type = $options['type'] ?? ''; $name = $options['name'] ?? ''; $title = $options['title'] ?? ''; parent::$_buffer[$type][$name][$title] = $content; return $this; } /** * Parses the template and populates the buffer * * @param array $params Parameters for fetching the template * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ public function parse($params = []) { return $this->_fetchTemplate($params)->_parseTemplate(); } /** * Outputs the template to the browser. * * @param boolean $caching If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($caching = false, $params = []) { $this->_caching = $caching; if (empty($this->_template)) { $this->parse($params); } if (\array_key_exists('csp_nonce', $params) && $params['csp_nonce'] !== null) { $this->cspNonce = $params['csp_nonce']; } $data = $this->_renderTemplate(); parent::render($caching, $params); return $data; } /** * Count the modules in the given position * * @param string $positionName The position to use * @param boolean $withContentOnly Count only a modules which actually has a content * * @return integer Number of modules found * * @since 1.7.0 */ public function countModules(string $positionName, bool $withContentOnly = false) { if ((isset(parent::$_buffer['modules'][$positionName])) && (parent::$_buffer['modules'][$positionName] === false)) { return 0; } $modules = ModuleHelper::getModules($positionName); if (!$withContentOnly) { return \count($modules); } // Now we need to count only modules which actually have a content $result = 0; $renderer = $this->loadRenderer('module'); foreach ($modules as $module) { if (empty($module->contentRendered)) { $renderer->render($module, ['contentOnly' => true]); } if (trim($module->content) !== '') { $result++; } } return $result; } /** * Count the number of child menu items of the current active menu item * * @return integer Number of child menu items * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 * Load the active menu item directly and count the children with the php count function * `$children = count($app->getMenu()->getActive()->getChildren())` beware getActive could be `null` */ public function countMenuChildren() { $active = CmsFactory::getApplication()->getMenu()->getActive(); return $active ? count($active->getChildren()) : 0; } /** * Load a template file * * @param string $directory The name of the template * @param string $filename The actual filename * * @return string The contents of the template * * @since 1.7.0 */ protected function _loadTemplate($directory, $filename) { $contents = ''; // Check to see if we have a valid template file if (is_file($directory . '/' . $filename)) { // Store the file path $this->_file = $directory . '/' . $filename; // Get the file content ob_start(); require $directory . '/' . $filename; $contents = ob_get_contents(); ob_end_clean(); } return $contents; } /** * Fetch the template, and initialise the params * * @param array $params Parameters to determine the template * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ protected function _fetchTemplate($params = []) { // Check $directory = $params['directory'] ?? 'templates'; $filter = InputFilter::getInstance(); $template = $filter->clean($params['template'], 'cmd'); $file = $filter->clean($params['file'], 'cmd'); $inherits = $params['templateInherits'] ?? ''; $baseDir = $directory . '/' . $template; if (!is_file($directory . '/' . $template . '/' . $file)) { if ($inherits !== '' && is_file($directory . '/' . $inherits . '/' . $file)) { $baseDir = $directory . '/' . $inherits; } else { $baseDir = $directory . '/system'; $template = 'system'; if ($file !== 'index.php' && !is_file($baseDir . '/' . $file)) { $file = 'index.php'; } } } // Load the language file for the template $lang = CmsFactory::getLanguage(); // 1.5 or core then 1.6 $lang->load('tpl_' . $template, JPATH_BASE) || ($inherits !== '' && $lang->load('tpl_' . $inherits, JPATH_BASE)) || $lang->load('tpl_' . $template, $directory . '/' . $template) || ($inherits !== '' && $lang->load('tpl_' . $inherits, $directory . '/' . $inherits)); // Assign the variables $this->baseurl = Uri::base(true); $this->params = $params['params'] ?? new Registry(); $this->template = $template; // Load $this->_template = $this->_loadTemplate($baseDir, $file); return $this; } /** * Parse a document template * * @return HtmlDocument instance of $this to allow chaining * * @since 1.7.0 */ protected function _parseTemplate() { $matches = []; if (preg_match_all('#<jdoc:include\ type="([^"]+)"(.*)\/>#iU', $this->_template, $matches)) { $messages = []; $template_tags_first = []; $template_tags_last = []; // Step through the jdocs in reverse order. for ($i = \count($matches[0]) - 1; $i >= 0; $i--) { $type = $matches[1][$i]; $attribs = empty($matches[2][$i]) ? [] : Utility::parseAttributes($matches[2][$i]); $name = $attribs['name'] ?? null; // Separate buffers to be executed first and last if ($type === 'module' || $type === 'modules') { $template_tags_first[$matches[0][$i]] = ['type' => $type, 'name' => $name, 'attribs' => $attribs]; } elseif ($type === 'message') { $messages = [$matches[0][$i] => ['type' => $type, 'name' => $name, 'attribs' => $attribs]]; } else { $template_tags_last[$matches[0][$i]] = ['type' => $type, 'name' => $name, 'attribs' => $attribs]; } } $this->_template_tags = $template_tags_first + $messages + array_reverse($template_tags_last); } return $this; } /** * Render pre-parsed template * * @return string rendered template * * @since 1.7.0 */ protected function _renderTemplate() { $replace = []; $with = []; foreach ($this->_template_tags as $jdoc => $args) { $replace[] = $jdoc; $with[] = $this->getBuffer($args['type'], $args['name'], $args['attribs']); } return str_replace($replace, $with, $this->_template); } } PK1A#]��D��#src/Document/DocumentAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a document aware class. * * @since 4.4.0 */ trait DocumentAwareTrait { /** * Document * * @var Document * @since 4.4.0 */ private $document; /** * Get the Document. * * @return Document * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the document has not been set. */ protected function getDocument(): Document { if ($this->document) { return $this->document; } throw new \UnexpectedValueException('Document not set in ' . __CLASS__); } /** * Set the document to use. * * @param Document $document The document to use * * @return void * * @since 4.4.0 */ public function setDocument(Document $document): void { $this->document = $document; } } PK1A#]g��c c src/Document/JsonDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Factory as CmsFactory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JsonDocument class, provides an easy interface to parse and display JSON output * * @link http://www.json.org/ * @since 1.7.0 */ class JsonDocument extends Document { /** * Document name * * @var string * @since 1.7.0 */ protected $_name = 'joomla'; /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set mime type if ( isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') === false && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false ) { // Internet Explorer < 10 $this->_mime = 'text/plain'; } else { $this->_mime = 'application/json'; } // Set document type $this->_type = 'json'; } /** * Render the document. * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { /** @var \Joomla\CMS\Application\CMSApplication $app */ $app = CmsFactory::getApplication(); $app->allowCache($cache); if ($this->_mime === 'application/json') { // Browser other than Internet Explorer < 10 $app->setHeader('Content-Disposition', 'attachment; filename="' . $this->getName() . '.json"', true); } parent::render($cache, $params); return $this->getBuffer(); } /** * Returns the document name * * @return string * * @since 1.7.0 */ public function getName() { return $this->_name; } /** * Sets the document name * * @param string $name Document name * * @return JsonDocument instance of $this to allow chaining * * @since 1.7.0 */ public function setName($name = 'joomla') { $this->_name = $name; return $this; } } PK1A#]烅.. src/Document/JsonapiDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Factory; use Tobscure\JsonApi\Document; use Tobscure\JsonApi\ElementInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JsonapiDocument class, provides an easy interface to parse output in JSON-API format. * * @link http://www.jsonapi.org/ * @since 4.0.0 */ class JsonapiDocument extends JsonDocument implements \JsonSerializable { /** * The JsonApi Document object. * * @var Document * @since 4.0.0 */ protected $document; /** * Class constructor. * * @param array $options Associative array of options * * @since 4.0.0 */ public function __construct($options = []) { parent::__construct($options); // Set mime type to JSON-API $this->_mime = 'application/vnd.api+json'; $this->_type = 'jsonapi'; if (\array_key_exists('api_document', $options) && $options['api_document'] instanceof Document) { $this->document = $options['api_document']; } else { $this->document = new Document(); } } /** * Set the data object. * * @param ElementInterface $element Element interface. * * @return $this * * @since 4.0.0 */ public function setData(ElementInterface $element) { $this->document->setData($element); return $this; } /** * Set the errors array. * * @param array $errors Error array. * * @return $this * * @since 4.0.0 */ public function setErrors($errors) { $this->document->setErrors($errors); return $this; } /** * Set the JSON-API array. * * @param array $jsonapi JSON-API array. * * @return $this * * @since 4.0.0 */ public function setJsonapi($jsonapi) { $this->document->setJsonapi($jsonapi); return $this; } /** * Map everything to arrays. * * @return array * * @since 4.0.0 */ public function toArray() { return $this->document->toArray(); } /** * Map to string. * * @return string * * @since 4.0.0 */ public function __toString() { return json_encode($this->toArray()); } /** * Outputs the document. * * @param boolean $cache If true, cache the output. * @param array $params Associative array of attributes. * * @return string The rendered data. * * @since 4.0.0 */ public function render($cache = false, $params = []) { $app = Factory::getApplication(); if ($mdate = $this->getModifiedDate()) { $app->modifiedDate = $mdate; } $app->mimeType = $this->_mime; $app->charSet = $this->_charset; return json_encode($this->document); } /** * Serialize for JSON usage. * * @return array * * @since 4.0.0 */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); } /** * Add a link to the output. * * @param string $key The name of the link * @param string $value The link * * @return $this * * @since 4.0.0 */ public function addLink($key, $value) { $this->document->addLink($key, $value); return $this; } /** * Add a link to the output. * * @param string $key The name of the metadata key * @param string $value The value * * @return $this * * @since 4.0.0 */ public function addMeta($key, $value) { $this->document->addMeta($key, $value); return $this; } } PK1A#]�!:+src/Document/Opensearch/OpensearchImage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Opensearch; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing an OpenSearch image * * @since 1.7.0 */ class OpensearchImage { /** * The images MIME type * * required * * @var string * @since 1.7.0 */ public $type = ''; /** * URL of the image or the image as base64 encoded value * * required * * @var string * @since 1.7.0 */ public $data = ''; /** * The image's width * * required * * @var string * @since 1.7.0 */ public $width; /** * The image's height * * required * * @var string * @since 1.7.0 */ public $height; } PK1A#]�㨴�)src/Document/Opensearch/OpensearchUrl.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Opensearch; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Data object representing an OpenSearch URL * * @since 1.7.0 */ class OpensearchUrl { /** * Type item element * * required * * @var string * @since 1.7.0 */ public $type = 'text/html'; /** * Rel item element * * required * * @var string * @since 1.7.0 */ public $rel = 'results'; /** * Template item element. Has to contain the {searchTerms} parameter to work. * * required * * @var string * @since 1.7.0 */ public $template; } PK1A#]��4=vv#src/Document/OpensearchDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Document\Opensearch\OpensearchImage; use Joomla\CMS\Document\Opensearch\OpensearchUrl; use Joomla\CMS\Factory; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Opensearch class, provides an easy interface to display an Opensearch document * * @link http://www.opensearch.org/ * @since 1.7.0 */ class OpensearchDocument extends Document { /** * ShortName element * * required * * @var string * @since 1.7.0 */ private $_shortName = ''; /** * Images collection * * optional * * @var object[] * @since 1.7.0 */ private $_images = []; /** * The url collection * * @var array * @since 1.7.0 */ private $_urls = []; /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set document type $this->_type = 'opensearch'; // Set mime type $this->_mime = 'application/opensearchdescription+xml'; // Add the URL for self updating $update = new OpensearchUrl(); $update->type = 'application/opensearchdescription+xml'; $update->rel = 'self'; $update->template = Route::_(Uri::getInstance()); $this->addUrl($update); // Add the favicon as the default image // Try to find a favicon by checking the template and root folder $app = Factory::getApplication(); $dirs = [JPATH_THEMES . '/' . $app->getTemplate(), JPATH_BASE]; foreach ($dirs as $dir) { if (is_file($dir . '/favicon.ico')) { $path = str_replace(JPATH_BASE, '', $dir); $path = str_replace('\\', '/', $path); $favicon = new OpensearchImage(); if ($path == '') { $favicon->data = Uri::base() . 'favicon.ico'; } else { if ($path[0] == '/') { $path = substr($path, 1); } $favicon->data = Uri::base() . $path . '/favicon.ico'; } $favicon->height = '16'; $favicon->width = '16'; $favicon->type = 'image/vnd.microsoft.icon'; $this->addImage($favicon); break; } } } /** * Render the document * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { $xml = new \DOMDocument('1.0', 'utf-8'); if (\defined('JDEBUG') && JDEBUG) { $xml->formatOutput = true; } // The Opensearch Namespace $osns = 'http://a9.com/-/spec/opensearch/1.1/'; // Create the root element $elOs = $xml->createElementNS($osns, 'OpenSearchDescription'); $elShortName = $xml->createElementNS($osns, 'ShortName'); $elShortName->appendChild($xml->createTextNode(htmlspecialchars($this->_shortName))); $elOs->appendChild($elShortName); $elDescription = $xml->createElementNS($osns, 'Description'); $elDescription->appendChild($xml->createTextNode(htmlspecialchars($this->description))); $elOs->appendChild($elDescription); // Always set the accepted input encoding to UTF-8 $elInputEncoding = $xml->createElementNS($osns, 'InputEncoding'); $elInputEncoding->appendChild($xml->createTextNode('UTF-8')); $elOs->appendChild($elInputEncoding); foreach ($this->_images as $image) { $elImage = $xml->createElementNS($osns, 'Image'); $elImage->setAttribute('type', $image->type); $elImage->setAttribute('width', $image->width); $elImage->setAttribute('height', $image->height); $elImage->appendChild($xml->createTextNode(htmlspecialchars($image->data))); $elOs->appendChild($elImage); } foreach ($this->_urls as $url) { $elUrl = $xml->createElementNS($osns, 'Url'); $elUrl->setAttribute('type', $url->type); // Results is the default value so we don't need to add it if ($url->rel !== 'results') { $elUrl->setAttribute('rel', $url->rel); } $elUrl->setAttribute('template', $url->template); $elOs->appendChild($elUrl); } $xml->appendChild($elOs); parent::render($cache, $params); return $xml->saveXML(); } /** * Sets the short name * * @param string $name The name. * * @return OpensearchDocument instance of $this to allow chaining * * @since 1.7.0 */ public function setShortName($name) { $this->_shortName = $name; return $this; } /** * Adds a URL to the Opensearch description. * * @param OpensearchUrl $url The url to add to the description. * * @return OpensearchDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addUrl(OpensearchUrl $url) { $this->_urls[] = $url; return $this; } /** * Adds an image to the Opensearch description. * * @param OpensearchImage $image The image to add to the description. * * @return OpensearchDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addImage(OpensearchImage $image) { $this->_images[] = $image; return $this; } } PK1A#]�f'���'src/Document/DocumentAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a document. * * @since 4.4.0 */ interface DocumentAwareInterface { /** * Set the document to use. * * @param Document $document The document to use. * * @return void * * @since 4.4.0 */ public function setDocument(Document $document): void; } PK1A#]+�k���src/Document/ImageDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * ImageDocument class, provides an easy interface to output image data * * @since 3.0.0 */ class ImageDocument extends Document { /** * Class constructor * * @param array $options Associative array of options * * @since 3.0.0 */ public function __construct($options = []) { parent::__construct($options); // Set mime type $this->_mime = 'image/png'; // Set document type $this->_type = 'image'; } /** * Render the document. * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 3.0.0 */ public function render($cache = false, $params = []) { // Get the image type $type = Factory::getApplication()->getInput()->get('type', 'png'); switch ($type) { case 'jpg': case 'jpeg': $this->_mime = 'image/jpeg'; break; case 'gif': $this->_mime = 'image/gif'; break; case 'png': default: $this->_mime = 'image/png'; break; } $this->_charset = null; parent::render($cache, $params); return $this->getBuffer(); } } PK1A#]��X src/Document/Factory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating Document objects * * @since 4.0.0 */ class Factory implements FactoryInterface { use CacheControllerFactoryAwareTrait; /** * Creates a new Document object for the requested format. * * @param string $type The document type to instantiate * @param array $attributes Array of attributes * * @return Document * * @since 4.0.0 */ public function createDocument(string $type = 'html', array $attributes = []): Document { $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); $ntype = null; $class = __NAMESPACE__ . '\\' . ucfirst($type) . 'Document'; if (!class_exists($class)) { $class = 'JDocument' . ucfirst($type); } if (!class_exists($class)) { $ntype = $type; $class = RawDocument::class; } // Inject this factory into the document unless one was provided if (!isset($attributes['factory'])) { $attributes['factory'] = $this; } /** @var Document $instance */ $instance = new $class($attributes); if (!\is_null($ntype)) { // Set the type to the Document type originally requested $instance->setType($ntype); } if ($instance instanceof CacheControllerFactoryAwareInterface) { $instance->setCacheControllerFactory($this->getCacheControllerFactory()); } return $instance; } /** * Creates a new renderer object. * * @param Document $document The Document instance to attach to the renderer * @param string $type The renderer type to instantiate * @param string $docType The document type the renderer is part of * * @return RendererInterface * * @since 4.0.0 */ public function createRenderer(Document $document, string $type, string $docType = ''): RendererInterface { $docType = $docType ? ucfirst($docType) : ucfirst($document->getType()); // Determine the path and class $class = __NAMESPACE__ . '\\Renderer\\' . $docType . '\\' . ucfirst($type) . 'Renderer'; if (!class_exists($class)) { $class = 'JDocumentRenderer' . $docType . ucfirst($type); } if (!class_exists($class)) { // "Legacy" class name structure $class = '\\JDocumentRenderer' . $type; if (!class_exists($class)) { throw new \RuntimeException(sprintf('Unable to load renderer class %s', $type), 500); } } $instance = new $class($document); if ($instance instanceof CacheControllerFactoryAwareInterface) { $instance->setCacheControllerFactory($this->getCacheControllerFactory()); } return $instance; } } PK1A#]����OO.src/Document/Renderer/Html/ModulesRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Helper\ModuleHelper; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML document renderer for a module position * * @since 3.5 */ class ModulesRenderer extends DocumentRenderer { /** * Renders multiple modules script and returns the results as a string * * @param string $position The position of the modules to render * @param array $params Associative array of values * @param string $content Module content * * @return string The output of the script * * @since 3.5 */ public function render($position, $params = [], $content = null) { $renderer = $this->_doc->loadRenderer('module'); $buffer = ''; $app = Factory::getApplication(); $user = Factory::getUser(); $frontediting = ($app->isClient('site') && $app->get('frontediting', 1) && !$user->guest); $menusEditing = ($app->get('frontediting', 1) == 2) && $user->authorise('core.edit', 'com_menus'); foreach (ModuleHelper::getModules($position) as $mod) { $moduleHtml = $renderer->render($mod, $params, $content); if ($frontediting && trim($moduleHtml) != '' && $user->authorise('module.edit.frontend', 'com_modules.module.' . $mod->id)) { $displayData = ['moduleHtml' => &$moduleHtml, 'module' => $mod, 'position' => $position, 'menusediting' => $menusEditing]; LayoutHelper::render('joomla.edit.frontediting_modules', $displayData); } $buffer .= $moduleHtml; } $app->triggerEvent('onAfterRenderModules', [&$buffer, &$params]); return $buffer; } } PK1A#]���� � .src/Document/Renderer/Html/MessageRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML document renderer for the system message queue * * @since 3.5 */ class MessageRenderer extends DocumentRenderer { /** * Renders the error stack and returns the results as a string * * @param string $name Not used. * @param array $params Associative array of values * @param string $content Not used. * * @return string The output of the script * * @since 3.5 */ public function render($name, $params = [], $content = null) { $msgList = $this->getData(); $displayData = [ 'msgList' => $msgList, 'name' => $name, 'params' => $params, 'content' => $content, ]; $app = Factory::getApplication(); $chromePath = JPATH_THEMES . '/' . $app->getTemplate() . '/html/message.php'; if (is_file($chromePath)) { include_once $chromePath; } if (\function_exists('renderMessage')) { @trigger_error( 'renderMessage() is deprecated. Override system message rendering with layouts instead.', E_USER_DEPRECATED ); return renderMessage($msgList); } return LayoutHelper::render('joomla.system.message', $displayData); } /** * Get and prepare system message data for output * * @return array An array contains system message * * @since 3.5 */ private function getData() { // Initialise variables. $lists = []; // Get the message queue $messages = Factory::getApplication()->getMessageQueue(); // Build the sorted message list if (\is_array($messages) && !empty($messages)) { foreach ($messages as $msg) { if (isset($msg['type']) && isset($msg['message'])) { $lists[$msg['type']][] = $msg['message']; } } } return $lists; } } PK1A#]D�[d*d*.src/Document/Renderer/Html/ScriptsRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\WebAsset\WebAssetItemInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JDocument head renderer * * @since 4.0.0 */ class ScriptsRenderer extends DocumentRenderer { /** * List of already rendered src * * @var array * * @since 4.0.0 */ private $renderedSrc = []; /** * Renders the document script tags and returns the results as a string * * @param string $head (unused) * @param array $params Associative array of values * @param string $content The script * * @return string The output of the script * * @since 4.0.0 */ public function render($head, $params = [], $content = null) { // Get line endings $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); $buffer = ''; $wam = $this->_doc->getWebAssetManager(); $assets = $wam->getAssets('script', true); // Get a list of inline assets and their relation with regular assets $inlineAssets = $wam->filterOutInlineAssets($assets); $inlineRelation = $wam->getInlineRelation($inlineAssets); // Merge with existing scripts, for rendering $assets = array_merge(array_values($assets), $this->_doc->_scripts); // Generate script file links foreach ($assets as $key => $item) { // Check whether we have an Asset instance, or old array with attributes $asset = $item instanceof WebAssetItemInterface ? $item : null; // Add src attribute for non Asset item if (!$asset) { $item['src'] = $key; } // Check for inline content "before" if ($asset && !empty($inlineRelation[$asset->getName()]['before'])) { foreach ($inlineRelation[$asset->getName()]['before'] as $itemBefore) { $buffer .= $this->renderInlineElement($itemBefore); // Remove this item from inline queue unset($inlineAssets[$itemBefore->getName()]); } } $buffer .= $this->renderElement($item); // Check for inline content "after" if ($asset && !empty($inlineRelation[$asset->getName()]['after'])) { foreach ($inlineRelation[$asset->getName()]['after'] as $itemBefore) { $buffer .= $this->renderInlineElement($itemBefore); // Remove this item from inline queue unset($inlineAssets[$itemBefore->getName()]); } } } // Generate script declarations for assets foreach ($inlineAssets as $item) { $buffer .= $this->renderInlineElement($item); } // Generate script declarations for old scripts foreach ($this->_doc->_script as $type => $contents) { // Test for B.C. in case someone still store script declarations as single string if (\is_string($contents)) { $contents = [$contents]; } foreach ($contents as $content) { $buffer .= $this->renderInlineElement( [ 'type' => $type, 'content' => $content, ] ); } } // Output the custom tags - array_unique makes sure that we don't output the same tags twice foreach (array_unique($this->_doc->_custom) as $custom) { $buffer .= $tab . $custom . $lnEnd; } return ltrim($buffer, $tab); } /** * Renders the element * * @param WebAssetItemInterface|array $item The element * * @return string The resulting string * * @since 4.0.0 */ private function renderElement($item): string { $buffer = ''; $asset = $item instanceof WebAssetItemInterface ? $item : null; $src = $asset ? $asset->getUri() : ($item['src'] ?? ''); // Make sure we have a src, and it not already rendered if (!$src || !empty($this->renderedSrc[$src]) || ($asset && $asset->getOption('webcomponent'))) { return ''; } $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); $mediaVersion = $this->_doc->getMediaVersion(); // Get the attributes and other options if ($asset) { $attribs = $asset->getAttributes(); $version = $asset->getVersion(); $conditional = $asset->getOption('conditional'); // Add an asset info for debugging if (JDEBUG) { $attribs['data-asset-name'] = $asset->getName(); if ($asset->getDependencies()) { $attribs['data-asset-dependencies'] = implode(',', $asset->getDependencies()); } if ($asset->getOption('deprecated')) { @trigger_error( sprintf('Web Asset script [%s] is deprecated. %s', $asset->getName(), $asset->getOption('deprecatedMsg', '')), E_USER_DEPRECATED ); } } } else { $attribs = $item; $version = isset($attribs['options']['version']) ? $attribs['options']['version'] : ''; $conditional = !empty($attribs['options']['conditional']) ? $attribs['options']['conditional'] : null; } // Add "nonce" attribute if exist if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { $attribs['nonce'] = $this->_doc->cspNonce; } // To prevent double rendering $this->renderedSrc[$src] = true; // Check if script uses media version. if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto')) { $src .= '?' . ($version === 'auto' ? $mediaVersion : $version); } $buffer .= $tab; // This is for IE conditional statements support. if (!\is_null($conditional)) { $buffer .= '<!--[if ' . $conditional . ']>'; } // Render the element with attributes $buffer .= '<script src="' . htmlspecialchars($src) . '"'; $buffer .= $this->renderAttributes($attribs); $buffer .= '></script>'; // This is for IE conditional statements support. if (!\is_null($conditional)) { $buffer .= '<![endif]-->'; } $buffer .= $lnEnd; return $buffer; } /** * Renders the inline element * * @param WebAssetItemInterface|array $item The element * * @return string The resulting string * * @since 4.0.0 */ private function renderInlineElement($item): string { $buffer = ''; $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); if ($item instanceof WebAssetItemInterface) { $attribs = $item->getAttributes(); $content = $item->getOption('content'); } else { $attribs = $item; $content = $item['content'] ?? ''; unset($attribs['content']); } // Do not produce empty elements if (!$content) { return ''; } // Add "nonce" attribute if exist if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { $attribs['nonce'] = $this->_doc->cspNonce; } $buffer .= $tab . '<script'; $buffer .= $this->renderAttributes($attribs); $buffer .= '>'; // This is for full XHTML support. if ($this->_doc->_mime !== 'text/html') { $buffer .= $tab . $tab . '//<![CDATA[' . $lnEnd; } $buffer .= $content; // See above note if ($this->_doc->_mime !== 'text/html') { $buffer .= $tab . $tab . '//]]>' . $lnEnd; } $buffer .= '</script>' . $lnEnd; return $buffer; } /** * Renders the element attributes * * @param array $attributes The element attributes * * @return string The attributes string * * @since 4.0.0 */ private function renderAttributes(array $attributes): string { $buffer = ''; $defaultJsMimes = ['text/javascript', 'application/javascript', 'text/x-javascript', 'application/x-javascript']; $html5NoValueAttributes = ['defer', 'async', 'nomodule']; foreach ($attributes as $attrib => $value) { // Don't add the 'options' attribute. This attribute is for internal use (version, conditional, etc). if ($attrib === 'options' || $attrib === 'src') { continue; } // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. if (\in_array($attrib, ['type', 'mime']) && $this->_doc->isHtml5() && \in_array($value, $defaultJsMimes)) { continue; } // B/C: If defer and async is false or empty don't render the attribute. Also skip if value is bool:false. if (\in_array($attrib, ['defer', 'async']) && !$value || $value === false) { continue; } // NoValue attribute, if it have bool:true $isNoValueAttrib = $value === true || \in_array($attrib, $html5NoValueAttributes); // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. if ($attrib === 'mime') { $attrib = 'type'; } elseif ($isNoValueAttrib) { // NoValue attribute in non HTML5 should contain a value, set it equal to attribute name. $value = $attrib; } // Add attribute to script tag output. $buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT, 'UTF-8'); if (!($this->_doc->isHtml5() && $isNoValueAttrib)) { // Json encode value if it's an array. $value = !is_scalar($value) ? json_encode($value) : $value; $buffer .= '="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"'; } } return $buffer; } } PK1A#]�ѩ��0src/Document/Renderer/Html/ComponentRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML document renderer for the component output * * @since 3.5 */ class ComponentRenderer extends DocumentRenderer { /** * Renders a component script and returns the results as a string * * @param string $component The name of the component to render * @param array $params Associative array of values * @param string $content Content script * * @return string The output of the script * * @since 3.5 */ public function render($component = null, $params = [], $content = null) { return $content; } } PK1A#]����� � -src/Document/Renderer/Html/ModuleRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Helper\ModuleHelper; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML document renderer for a single module * * @since 3.5 */ class ModuleRenderer extends DocumentRenderer { /** * Renders a module script and returns the results as a string * * @param string $module The name of the module to render * @param array $attribs Associative array of values * @param string $content If present, module information from the buffer will be used * * @return string The output of the script * * @since 3.5 */ public function render($module, $attribs = [], $content = null) { if (!\is_object($module)) { $title = $attribs['title'] ?? null; $module = ModuleHelper::getModule($module, $title); if (!\is_object($module)) { if (\is_null($content)) { return ''; } /** * If module isn't found in the database but data has been pushed in the buffer * we want to render it */ $tmp = $module; $module = new \stdClass(); $module->params = null; $module->module = $tmp; $module->id = 0; $module->user = 0; } } // Set the module content if (!\is_null($content)) { $module->content = $content; } // Get module parameters $params = new Registry($module->params); // Use parameters from template if (isset($attribs['params'])) { $template_params = new Registry(html_entity_decode($attribs['params'], ENT_COMPAT, 'UTF-8')); $params->merge($template_params); $module = clone $module; $module->params = (string) $params; } // Set cachemode parameter or use JModuleHelper::moduleCache from within the module instead $cachemode = $params->get('cachemode', 'static'); if ($params->get('cache', 0) == 1 && Factory::getApplication()->get('caching') >= 1 && $cachemode !== 'id' && $cachemode !== 'safeuri') { // Default to itemid creating method and workarounds on $cacheparams = new \stdClass(); $cacheparams->cachemode = $cachemode; $cacheparams->class = ModuleHelper::class; $cacheparams->method = 'renderModule'; $cacheparams->methodparams = [$module, $attribs]; $cacheparams->cachesuffix = $attribs['contentOnly'] ?? false; // It need to be done here because the cache controller does not keep reference to the module object $module->content = ModuleHelper::moduleCache($module, $params, $cacheparams); $module->contentRendered = true; return $module->content; } return ModuleHelper::renderModule($module, $attribs); } } PK1A#]�^���,src/Document/Renderer/Html/MetasRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Helper\TagsHelper; use Joomla\CMS\Uri\Uri; use Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JDocument metas renderer * * @since 4.0.0 */ class MetasRenderer extends DocumentRenderer { /** * Renders the document metas and returns the results as a string * * @param string $head (unused) * @param array $params Associative array of values * @param string $content The script * * @return string The output of the script * * @since 4.0.0 */ public function render($head, $params = [], $content = null) { // Convert the tagids to titles if (isset($this->_doc->_metaTags['name']['tags'])) { $tagsHelper = new TagsHelper(); $this->_doc->_metaTags['name']['tags'] = implode(', ', $tagsHelper->getTagNames($this->_doc->_metaTags['name']['tags'])); } /** @var \Joomla\CMS\Application\CMSApplication $app */ $app = Factory::getApplication(); $wa = $this->_doc->getWebAssetManager(); // Check for AttachBehavior and web components foreach ($wa->getAssets('script', true) as $asset) { if ($asset instanceof WebAssetAttachBehaviorInterface) { $asset->onAttachCallback($this->_doc); } } // Trigger the onBeforeCompileHead event $app->triggerEvent('onBeforeCompileHead'); // Add Script Options as inline asset $scriptOptions = $this->_doc->getScriptOptions(); if ($scriptOptions) { $prettyPrint = (JDEBUG && \defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : false); $jsonOptions = json_encode($scriptOptions, $prettyPrint); $jsonOptions = $jsonOptions ?: '{}'; $wa->addInlineScript( $jsonOptions, ['name' => 'joomla.script.options', 'position' => 'before'], ['type' => 'application/json', 'class' => 'joomla-script-options new'], ['core'] ); } // Lock the AssetManager $wa->lock(); // Get line endings $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); $buffer = ''; // Generate charset when using HTML5 (should happen first) if ($this->_doc->isHtml5()) { $buffer .= $tab . '<meta charset="' . $this->_doc->getCharset() . '">' . $lnEnd; } // Generate base tag (need to happen early) $base = $this->_doc->getBase(); if (!empty($base)) { $buffer .= $tab . '<base href="' . $base . '">' . $lnEnd; } $noFavicon = true; $searchFor = 'image/vnd.microsoft.icon'; array_map(function ($value) use (&$noFavicon, $searchFor) { if (isset($value['attribs']['type']) && $value['attribs']['type'] === $searchFor) { $noFavicon = false; } }, array_values((array)$this->_doc->_links)); if ($noFavicon) { $client = $app->isClient('administrator') === true ? 'administrator/' : 'site/'; $template = $app->getTemplate(true); // Try to find a favicon by checking the template and root folder $icon = '/favicon.ico'; $foldersToCheck = [ JPATH_BASE, JPATH_ROOT . '/media/templates/' . $client . $template->template, JPATH_BASE . '/templates/' . $template->template, ]; foreach ($foldersToCheck as $base => $dir) { if ( $template->parent !== '' && $base === 1 && !is_file(JPATH_ROOT . '/media/templates/' . $client . $template->template . $icon) ) { $dir = JPATH_ROOT . '/media/templates/' . $client . $template->parent; } if (is_file($dir . $icon)) { $urlBase = in_array($base, [0, 2]) ? Uri::base(true) : Uri::root(true); $base = in_array($base, [0, 2]) ? JPATH_BASE : JPATH_ROOT; $path = str_replace($base, '', $dir); $path = str_replace('\\', '/', $path); $this->_doc->addFavicon($urlBase . $path . $icon); break; } } } // Generate META tags (needs to happen as early as possible in the head) foreach ($this->_doc->_metaTags as $type => $tag) { foreach ($tag as $name => $contents) { if ($type === 'http-equiv' && !($this->_doc->isHtml5() && $name === 'content-type')) { $buffer .= $tab . '<meta http-equiv="' . $name . '" content="' . htmlspecialchars($contents, ENT_COMPAT, 'UTF-8') . '">' . $lnEnd; } elseif ($type !== 'http-equiv' && !empty($contents)) { $buffer .= $tab . '<meta ' . $type . '="' . $name . '" content="' . htmlspecialchars($contents, ENT_COMPAT, 'UTF-8') . '">' . $lnEnd; } } } // Don't add empty descriptions $documentDescription = $this->_doc->getDescription(); if ($documentDescription) { $buffer .= $tab . '<meta name="description" content="' . htmlspecialchars($documentDescription, ENT_COMPAT, 'UTF-8') . '">' . $lnEnd; } // Don't add empty generators $generator = $this->_doc->getGenerator(); if ($generator) { $buffer .= $tab . '<meta name="generator" content="' . htmlspecialchars($generator, ENT_COMPAT, 'UTF-8') . '">' . $lnEnd; } $buffer .= $tab . '<title>' . htmlspecialchars($this->_doc->getTitle(), ENT_COMPAT, 'UTF-8') . '</title>' . $lnEnd; // Generate link declarations foreach ($this->_doc->_links as $link => $linkAtrr) { $buffer .= $tab . '<link href="' . $link . '" ' . $linkAtrr['relType'] . '="' . $linkAtrr['relation'] . '"'; if (\is_array($linkAtrr['attribs'])) { if ($temp = ArrayHelper::toString($linkAtrr['attribs'])) { $buffer .= ' ' . $temp; } } $buffer .= '>' . $lnEnd; } return ltrim($buffer, $tab); } } PK1A#]�3����+src/Document/Renderer/Html/HeadRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * HTML document renderer for the document `<head>` element * * @since 3.5 */ class HeadRenderer extends DocumentRenderer { /** * Renders the document head and returns the results as a string * * @param string $head (unused) * @param array $params Associative array of values * @param string $content The script * * @return string The output of the script * * @since 3.5 */ public function render($head, $params = [], $content = null) { $buffer = ''; $buffer .= $this->_doc->loadRenderer('metas')->render($head, $params, $content); $buffer .= $this->_doc->loadRenderer('styles')->render($head, $params, $content); $buffer .= $this->_doc->loadRenderer('scripts')->render($head, $params, $content); return $buffer; } } PK1A#]��c~�(�(-src/Document/Renderer/Html/StylesRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Html; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\WebAsset\WebAssetItemInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JDocument styles renderer * * @since 4.0.0 */ class StylesRenderer extends DocumentRenderer { /** * List of already rendered src * * @var array * * @since 4.0.0 */ private $renderedSrc = []; /** * Renders the document stylesheets and style tags and returns the results as a string * * @param string $head (unused) * @param array $params Associative array of values * @param string $content The script * * @return string The output of the script * * @since 4.0.0 */ public function render($head, $params = [], $content = null) { $tab = $this->_doc->_getTab(); $buffer = ''; $wam = $this->_doc->getWebAssetManager(); $assets = $wam->getAssets('style', true); // Get a list of inline assets and their relation with regular assets $inlineAssets = $wam->filterOutInlineAssets($assets); $inlineRelation = $wam->getInlineRelation($inlineAssets); // Merge with existing styleSheets, for rendering $assets = array_merge(array_values($assets), $this->_doc->_styleSheets); // Generate stylesheet links foreach ($assets as $key => $item) { $asset = $item instanceof WebAssetItemInterface ? $item : null; // Add href attribute for non Asset item if (!$asset) { $item['href'] = $key; } // Check for inline content "before" if ($asset && !empty($inlineRelation[$asset->getName()]['before'])) { foreach ($inlineRelation[$asset->getName()]['before'] as $itemBefore) { $buffer .= $this->renderInlineElement($itemBefore); // Remove this item from inline queue unset($inlineAssets[$itemBefore->getName()]); } } $buffer .= $this->renderElement($item); // Check for inline content "after" if ($asset && !empty($inlineRelation[$asset->getName()]['after'])) { foreach ($inlineRelation[$asset->getName()]['after'] as $itemBefore) { $buffer .= $this->renderInlineElement($itemBefore); // Remove this item from inline queue unset($inlineAssets[$itemBefore->getName()]); } } } // Generate script declarations for assets foreach ($inlineAssets as $item) { $buffer .= $this->renderInlineElement($item); } // Generate stylesheet declarations foreach ($this->_doc->_style as $type => $contents) { // Test for B.C. in case someone still store stylesheet declarations as single string if (\is_string($contents)) { $contents = [$contents]; } foreach ($contents as $content) { $buffer .= $this->renderInlineElement( [ 'type' => $type, 'content' => $content, ] ); } } return ltrim($buffer, $tab); } /** * Renders the element * * @param WebAssetItemInterface|array $item The element * * @return string The resulting string * * @since 4.0.0 */ private function renderElement($item): string { $buffer = ''; $asset = $item instanceof WebAssetItemInterface ? $item : null; $src = $asset ? $asset->getUri() : ($item['href'] ?? ''); // Make sure we have a src, and it not already rendered if (!$src || !empty($this->renderedSrc[$src])) { return ''; } $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); $mediaVersion = $this->_doc->getMediaVersion(); // Get the attributes and other options if ($asset) { $attribs = $asset->getAttributes(); $version = $asset->getVersion(); $conditional = $asset->getOption('conditional'); // Add an asset info for debugging if (JDEBUG) { $attribs['data-asset-name'] = $asset->getName(); if ($asset->getDependencies()) { $attribs['data-asset-dependencies'] = implode(',', $asset->getDependencies()); } if ($asset->getOption('deprecated')) { @trigger_error( sprintf('Web Asset style [%s] is deprecated. %s', $asset->getName(), $asset->getOption('deprecatedMsg', '')), E_USER_DEPRECATED ); } } } else { $attribs = $item; $version = isset($attribs['options']['version']) ? $attribs['options']['version'] : ''; $conditional = !empty($attribs['options']['conditional']) ? $attribs['options']['conditional'] : null; } // Add "nonce" attribute if exist if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { $attribs['nonce'] = $this->_doc->cspNonce; } // To prevent double rendering $this->renderedSrc[$src] = true; // Check if script uses media version. if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto')) { $src .= '?' . ($version === 'auto' ? $mediaVersion : $version); } $buffer .= $tab; // This is for IE conditional statements support. if (!\is_null($conditional)) { $buffer .= '<!--[if ' . $conditional . ']>'; } $relation = isset($attribs['rel']) ? $attribs['rel'] : 'stylesheet'; if (isset($attribs['rel'])) { unset($attribs['rel']); } // Render the element with attributes $buffer .= '<link href="' . htmlspecialchars($src) . '" rel="' . $relation . '"'; $buffer .= $this->renderAttributes($attribs); $buffer .= ' />'; if ($relation === 'lazy-stylesheet') { $buffer .= '<noscript><link href="' . htmlspecialchars($src) . '" rel="stylesheet" /></noscript>'; } // This is for IE conditional statements support. if (!\is_null($conditional)) { $buffer .= '<![endif]-->'; } $buffer .= $lnEnd; return $buffer; } /** * Renders the inline element * * @param WebAssetItemInterface|array $item The element * * @return string The resulting string * * @since 4.0.0 */ private function renderInlineElement($item): string { $buffer = ''; $lnEnd = $this->_doc->_getLineEnd(); $tab = $this->_doc->_getTab(); if ($item instanceof WebAssetItemInterface) { $attribs = $item->getAttributes(); $content = $item->getOption('content'); } else { $attribs = $item; $content = $item['content'] ?? ''; unset($attribs['content']); } // Do not produce empty elements if (!$content) { return ''; } // Add "nonce" attribute if exist if ($this->_doc->cspNonce && !is_null($this->_doc->cspNonce)) { $attribs['nonce'] = $this->_doc->cspNonce; } $buffer .= $tab . '<style'; $buffer .= $this->renderAttributes($attribs); $buffer .= '>'; // This is for full XHTML support. if ($this->_doc->_mime !== 'text/html') { $buffer .= $tab . $tab . '/*<![CDATA[*/' . $lnEnd; } $buffer .= $content; // See above note if ($this->_doc->_mime !== 'text/html') { $buffer .= $tab . $tab . '/*]]>*/' . $lnEnd; } $buffer .= '</style>' . $lnEnd; return $buffer; } /** * Renders the element attributes * * @param array $attributes The element attributes * * @return string The attributes string * * @since 4.0.0 */ private function renderAttributes(array $attributes): string { $buffer = ''; $defaultCssMimes = ['text/css']; foreach ($attributes as $attrib => $value) { // Don't add the 'options' attribute. This attribute is for internal use (version, conditional, etc). if ($attrib === 'options' || $attrib === 'href') { continue; } // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. if (\in_array($attrib, ['type', 'mime']) && $this->_doc->isHtml5() && \in_array($value, $defaultCssMimes)) { continue; } // Skip the attribute if value is bool:false. if ($value === false) { continue; } // NoValue attribute, if it have bool:true $isNoValueAttrib = $value === true; // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C. if ($attrib === 'mime') { $attrib = 'type'; } elseif ($isNoValueAttrib) { // NoValue attribute in non HTML5 should contain a value, set it equal to attribute name. $value = $attrib; } // Add attribute to script tag output. $buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT, 'UTF-8'); if (!($this->_doc->isHtml5() && $isNoValueAttrib)) { // Json encode value if it's an array. $value = !is_scalar($value) ? json_encode($value) : $value; $buffer .= '="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"'; } } return $buffer; } } PK1A#]����%�%*src/Document/Renderer/Feed/RssRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Feed; use Joomla\CMS\Date\Date; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RssRenderer is a feed that implements RSS 2.0 Specification * * @link http://www.rssboard.org/rss-specification * @since 3.5 * * @property-read \Joomla\CMS\Document\FeedDocument $_doc Reference to the Document object that instantiated the renderer */ class RssRenderer extends DocumentRenderer { /** * Renderer mime type * * @var string * @since 3.5 */ protected $_mime = 'application/rss+xml'; /** * Render the feed. * * @param string $name The name of the element to render * @param array $params Array of values * @param string $content Override the output of the renderer * * @return string The output of the script * * @see DocumentRenderer::render() * @since 3.5 */ public function render($name = '', $params = null, $content = null) { $app = Factory::getApplication(); $tz = new \DateTimeZone($app->get('offset')); $data = $this->_doc; // If the last build date from the document isn't a Date object, create one if (!($data->lastBuildDate instanceof Date)) { // Gets and sets timezone offset from site configuration $data->lastBuildDate = Factory::getDate(); $data->lastBuildDate->setTimezone(new \DateTimeZone($app->get('offset'))); } $url = Uri::getInstance()->toString(['scheme', 'user', 'pass', 'host', 'port']); $syndicationURL = Route::_('&format=feed&type=rss'); $title = $data->getTitle(); if ($app->get('sitename_pagetitles', 0) == 1) { $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $data->getTitle()); } elseif ($app->get('sitename_pagetitles', 0) == 2) { $title = Text::sprintf('JPAGETITLE', $data->getTitle(), $app->get('sitename')); } $feed_title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $datalink = $data->getLink(); if (preg_match('/[\x80-\xFF]/', $datalink)) { $datalink = implode('/', array_map('rawurlencode', explode('/', $datalink))); } $feed = "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n"; $feed .= " <channel>\n"; $feed .= " <title>" . $feed_title . "</title>\n"; $feed .= " <description><![CDATA[" . $data->getDescription() . "]]></description>\n"; $feed .= " <link>" . str_replace(' ', '%20', $url . $datalink) . "</link>\n"; $feed .= " <lastBuildDate>" . htmlspecialchars($data->lastBuildDate->toRFC822(true), ENT_COMPAT, 'UTF-8') . "</lastBuildDate>\n"; $feed .= " <generator>" . $data->getGenerator() . "</generator>\n"; $feed .= " <atom:link rel=\"self\" type=\"application/rss+xml\" href=\"" . str_replace(' ', '%20', $url . $syndicationURL) . "\"/>\n"; if ($data->image != null) { $feed .= " <image>\n"; $feed .= " <url>" . $data->image->url . "</url>\n"; $feed .= " <title>" . htmlspecialchars($data->image->title, ENT_COMPAT, 'UTF-8') . "</title>\n"; $feed .= " <link>" . str_replace(' ', '%20', $data->image->link) . "</link>\n"; if ($data->image->width != '') { $feed .= " <width>" . $data->image->width . "</width>\n"; } if ($data->image->height != '') { $feed .= " <height>" . $data->image->height . "</height>\n"; } if ($data->image->description != '') { $feed .= " <description><![CDATA[" . $data->image->description . "]]></description>\n"; } $feed .= " </image>\n"; } if ($data->getLanguage() !== '') { $feed .= " <language>" . $data->getLanguage() . "</language>\n"; } if ($data->copyright != '') { $feed .= " <copyright>" . htmlspecialchars($data->copyright, ENT_COMPAT, 'UTF-8') . "</copyright>\n"; } if ($data->editorEmail != '') { $feed .= " <managingEditor>" . htmlspecialchars($data->editorEmail, ENT_COMPAT, 'UTF-8') . ' (' . htmlspecialchars($data->editor, ENT_COMPAT, 'UTF-8') . ")</managingEditor>\n"; } if ($data->webmaster != '') { $feed .= " <webMaster>" . htmlspecialchars($data->webmaster, ENT_COMPAT, 'UTF-8') . "</webMaster>\n"; } if ($data->pubDate != '') { $pubDate = Factory::getDate($data->pubDate); $pubDate->setTimezone($tz); $feed .= " <pubDate>" . htmlspecialchars($pubDate->toRFC822(true), ENT_COMPAT, 'UTF-8') . "</pubDate>\n"; } if (!empty($data->category)) { if (\is_array($data->category)) { foreach ($data->category as $cat) { $feed .= " <category>" . htmlspecialchars($cat, ENT_COMPAT, 'UTF-8') . "</category>\n"; } } else { $feed .= " <category>" . htmlspecialchars($data->category, ENT_COMPAT, 'UTF-8') . "</category>\n"; } } if ($data->docs != '') { $feed .= " <docs>" . htmlspecialchars($data->docs, ENT_COMPAT, 'UTF-8') . "</docs>\n"; } if ($data->ttl != '') { $feed .= " <ttl>" . htmlspecialchars($data->ttl, ENT_COMPAT, 'UTF-8') . "</ttl>\n"; } if ($data->rating != '') { $feed .= " <rating>" . htmlspecialchars($data->rating, ENT_COMPAT, 'UTF-8') . "</rating>\n"; } if ($data->skipHours != '') { $feed .= " <skipHours>" . htmlspecialchars($data->skipHours, ENT_COMPAT, 'UTF-8') . "</skipHours>\n"; } if ($data->skipDays != '') { $feed .= " <skipDays>" . htmlspecialchars($data->skipDays, ENT_COMPAT, 'UTF-8') . "</skipDays>\n"; } for ($i = 0, $count = \count($data->items); $i < $count; $i++) { $itemlink = $data->items[$i]->link; if (preg_match('/[\x80-\xFF]/', $itemlink)) { $itemlink = implode('/', array_map('rawurlencode', explode('/', $itemlink))); } if ((strpos($itemlink, 'http://') === false) && (strpos($itemlink, 'https://') === false)) { $itemlink = str_replace(' ', '%20', $url . $itemlink); } $feed .= " <item>\n"; $feed .= " <title>" . htmlspecialchars(strip_tags($data->items[$i]->title), ENT_COMPAT, 'UTF-8') . "</title>\n"; $feed .= " <link>" . str_replace(' ', '%20', $itemlink) . "</link>\n"; if (empty($data->items[$i]->guid)) { $feed .= " <guid isPermaLink=\"true\">" . str_replace(' ', '%20', $itemlink) . "</guid>\n"; } else { $feed .= " <guid isPermaLink=\"false\">" . htmlspecialchars($data->items[$i]->guid, ENT_COMPAT, 'UTF-8') . "</guid>\n"; } $feed .= " <description><![CDATA[" . $this->_relToAbs($data->items[$i]->description) . "]]></description>\n"; if ($data->items[$i]->authorEmail != '') { $feed .= ' <author>' . htmlspecialchars($data->items[$i]->authorEmail . ' (' . $data->items[$i]->author . ')', ENT_COMPAT, 'UTF-8') . "</author>\n"; } /* * @todo: On hold * if ($data->items[$i]->source!='') * { * $data.= " <source>" . htmlspecialchars($data->items[$i]->source, ENT_COMPAT, 'UTF-8') . "</source>\n"; * } */ if (empty($data->items[$i]->category) === false) { if (\is_array($data->items[$i]->category)) { foreach ($data->items[$i]->category as $cat) { $feed .= " <category>" . htmlspecialchars($cat, ENT_COMPAT, 'UTF-8') . "</category>\n"; } } else { $feed .= " <category>" . htmlspecialchars($data->items[$i]->category, ENT_COMPAT, 'UTF-8') . "</category>\n"; } } if ($data->items[$i]->comments != '') { $feed .= " <comments>" . htmlspecialchars($data->items[$i]->comments, ENT_COMPAT, 'UTF-8') . "</comments>\n"; } if ($data->items[$i]->date != '') { $itemDate = Factory::getDate($data->items[$i]->date); $itemDate->setTimezone($tz); $feed .= " <pubDate>" . htmlspecialchars($itemDate->toRFC822(true), ENT_COMPAT, 'UTF-8') . "</pubDate>\n"; } if ($data->items[$i]->enclosure != null) { $feed .= " <enclosure url=\""; $feed .= $data->items[$i]->enclosure->url; $feed .= "\" length=\""; $feed .= $data->items[$i]->enclosure->length; $feed .= "\" type=\""; $feed .= $data->items[$i]->enclosure->type; $feed .= "\"/>\n"; } $feed .= " </item>\n"; } $feed .= " </channel>\n"; $feed .= "</rss>\n"; return $feed; } } PK1A#]'�#+src/Document/Renderer/Feed/AtomRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document\Renderer\Feed; use Joomla\CMS\Document\DocumentRenderer; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Version; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * AtomRenderer is a feed that implements the atom specification * * Please note that just by using this class you won't automatically * produce valid atom files. For example, you have to specify either an editor * for the feed or an author for every single feed item. * * @link http://www.atomenabled.org/developers/syndication/atom-format-spec.php * @since 3.5 * * @property-read \Joomla\CMS\Document\FeedDocument $_doc Reference to the Document object that instantiated the renderer */ class AtomRenderer extends DocumentRenderer { /** * Document mime type * * @var string * @since 3.5 */ protected $_mime = 'application/atom+xml'; /** * Render the feed. * * @param string $name The name of the element to render * @param array $params Array of values * @param string $content Override the output of the renderer * * @return string The output of the script * * @see DocumentRenderer::render() * @since 3.5 */ public function render($name = '', $params = null, $content = null) { $app = Factory::getApplication(); // Gets and sets timezone offset from site configuration $tz = new \DateTimeZone($app->get('offset')); $now = Factory::getDate(); $now->setTimezone($tz); $data = $this->_doc; $url = Uri::getInstance()->toString(['scheme', 'user', 'pass', 'host', 'port']); $syndicationURL = Route::_('&format=feed&type=atom'); $title = $data->getTitle(); if ($app->get('sitename_pagetitles', 0) == 1) { $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $data->getTitle()); } elseif ($app->get('sitename_pagetitles', 0) == 2) { $title = Text::sprintf('JPAGETITLE', $data->getTitle(), $app->get('sitename')); } $feed_title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $feed = "<feed xmlns=\"http://www.w3.org/2005/Atom\""; if ($data->getLanguage() != '') { $feed .= " xml:lang=\"" . $data->getLanguage() . "\""; } $feed .= ">\n"; $feed .= " <title type=\"text\">" . $feed_title . "</title>\n"; $feed .= " <subtitle type=\"text\">" . htmlspecialchars($data->getDescription() ?? '', ENT_COMPAT, 'UTF-8') . "</subtitle>\n"; if (!empty($data->category)) { if (\is_array($data->category)) { foreach ($data->category as $cat) { $feed .= " <category term=\"" . htmlspecialchars($cat, ENT_COMPAT, 'UTF-8') . "\" />\n"; } } else { $feed .= " <category term=\"" . htmlspecialchars($data->category, ENT_COMPAT, 'UTF-8') . "\" />\n"; } } $feed .= " <link rel=\"alternate\" type=\"text/html\" href=\"" . $url . "\"/>\n"; $feed .= " <id>" . str_replace(' ', '%20', $data->getBase()) . "</id>\n"; $feed .= " <updated>" . htmlspecialchars($now->toISO8601(true), ENT_COMPAT, 'UTF-8') . "</updated>\n"; if ($data->editor != '') { $feed .= " <author>\n"; $feed .= " <name>" . $data->editor . "</name>\n"; if ($data->editorEmail != '') { $feed .= " <email>" . htmlspecialchars($data->editorEmail, ENT_COMPAT, 'UTF-8') . "</email>\n"; } $feed .= " </author>\n"; } $versionHtmlEscaped = ''; if ($app->get('MetaVersion', 0)) { $minorVersion = Version::MAJOR_VERSION . '.' . Version::MINOR_VERSION; $versionHtmlEscaped = ' version="' . htmlspecialchars($minorVersion, ENT_COMPAT, 'UTF-8') . '"'; } $feed .= " <generator uri=\"https://www.joomla.org\"" . $versionHtmlEscaped . ">" . $data->getGenerator() . "</generator>\n"; $feed .= " <link rel=\"self\" type=\"application/atom+xml\" href=\"" . str_replace(' ', '%20', $url . $syndicationURL) . "\"/>\n"; for ($i = 0, $count = \count($data->items); $i < $count; $i++) { $itemlink = $data->items[$i]->link; if (preg_match('/[\x80-\xFF]/', $itemlink)) { $itemlink = implode('/', array_map('rawurlencode', explode('/', $itemlink))); } $feed .= " <entry>\n"; $feed .= " <title>" . htmlspecialchars(strip_tags($data->items[$i]->title), ENT_COMPAT, 'UTF-8') . "</title>\n"; $feed .= " <link rel=\"alternate\" type=\"text/html\" href=\"" . $url . $itemlink . "\"/>\n"; if ($data->items[$i]->date == '') { $data->items[$i]->date = $now->toUnix(); } $itemDate = Factory::getDate($data->items[$i]->date); $itemDate->setTimezone($tz); $feed .= " <published>" . htmlspecialchars($itemDate->toISO8601(true), ENT_COMPAT, 'UTF-8') . "</published>\n"; $feed .= " <updated>" . htmlspecialchars($itemDate->toISO8601(true), ENT_COMPAT, 'UTF-8') . "</updated>\n"; if (empty($data->items[$i]->guid)) { $itemGuid = str_replace(' ', '%20', $url . $itemlink); } else { $itemGuid = htmlspecialchars($data->items[$i]->guid, ENT_COMPAT, 'UTF-8'); } $feed .= " <id>" . $itemGuid . "</id>\n"; if ($data->items[$i]->author != '') { $feed .= " <author>\n"; $feed .= " <name>" . htmlspecialchars($data->items[$i]->author, ENT_COMPAT, 'UTF-8') . "</name>\n"; if (!empty($data->items[$i]->authorEmail)) { $feed .= " <email>" . htmlspecialchars($data->items[$i]->authorEmail, ENT_COMPAT, 'UTF-8') . "</email>\n"; } $feed .= " </author>\n"; } if (!empty($data->items[$i]->description)) { $feed .= " <summary type=\"html\">" . htmlspecialchars($this->_relToAbs($data->items[$i]->description), ENT_COMPAT, 'UTF-8') . "</summary>\n"; $feed .= " <content type=\"html\">" . htmlspecialchars($this->_relToAbs($data->items[$i]->description), ENT_COMPAT, 'UTF-8') . "</content>\n"; } if (!empty($data->items[$i]->category)) { if (\is_array($data->items[$i]->category)) { foreach ($data->items[$i]->category as $cat) { $feed .= " <category term=\"" . htmlspecialchars($cat, ENT_COMPAT, 'UTF-8') . "\" />\n"; } } else { $feed .= " <category term=\"" . htmlspecialchars($data->items[$i]->category, ENT_COMPAT, 'UTF-8') . "\" />\n"; } } if ($data->items[$i]->enclosure != null) { $feed .= " <link rel=\"enclosure\" href=\"" . $data->items[$i]->enclosure->url . "\" type=\"" . $data->items[$i]->enclosure->type . "\" length=\"" . $data->items[$i]->enclosure->length . "\"/>\n"; } $feed .= " </entry>\n"; } $feed .= "</feed>\n"; return $feed; } } PK1A#]���]�t�tsrc/Document/Document.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\Application\AbstractWebApplication; use Joomla\CMS\Date\Date; use Joomla\CMS\Factory as CmsFactory; use Joomla\CMS\WebAsset\WebAssetManager; use Symfony\Component\WebLink\HttpHeaderSerializer; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Document class, provides an easy interface to parse and display a document * * @since 1.7.0 */ class Document { /** * Document title * * @var string * @since 1.7.0 */ public $title = ''; /** * Document description * * @var string * @since 1.7.0 */ public $description = ''; /** * Document full URL * * @var string * @since 1.7.0 */ public $link = ''; /** * Document base URL * * @var string * @since 1.7.0 */ public $base = ''; /** * Contains the document language setting * * @var string * @since 1.7.0 */ public $language = 'en-gb'; /** * Contains the document direction setting * * @var string * @since 1.7.0 */ public $direction = 'ltr'; /** * Document generator * * @var string * @since 1.7.0 */ public $_generator = 'Joomla! - Open Source Content Management'; /** * Document modified date * * @var string|Date * @since 1.7.0 */ public $_mdate = ''; /** * Tab string * * @var string * @since 1.7.0 */ public $_tab = "\11"; /** * Contains the line end string * * @var string * @since 1.7.0 */ public $_lineEnd = "\12"; /** * Contains the character encoding string * * @var string * @since 1.7.0 */ public $_charset = 'utf-8'; /** * Document mime type * * @var string * @since 1.7.0 */ public $_mime = ''; /** * Document namespace * * @var string * @since 1.7.0 */ public $_namespace = ''; /** * Document profile * * @var string * @since 1.7.0 */ public $_profile = ''; /** * Array of linked scripts * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager */ public $_scripts = []; /** * Array of scripts placed in the header * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager */ public $_script = []; /** * Array of scripts options * * @var array */ protected $scriptOptions = []; /** * Array of linked style sheets * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager */ public $_styleSheets = []; /** * Array of included style declarations * * @var array * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager */ public $_style = []; /** * Array of meta tags * * @var array * @since 1.7.0 */ public $_metaTags = []; /** * The rendering engine * * @var object * @since 1.7.0 */ public $_engine = null; /** * The document type * * @var string * @since 1.7.0 */ public $_type = null; /** * Array of buffered output * * @var mixed (depends on the renderer) * @since 1.7.0 */ public static $_buffer = null; /** * Document instances container. * * @var array * @since 1.7.3 */ protected static $instances = []; /** * Media version added to assets * * @var string * @since 3.2 */ protected $mediaVersion = null; /** * Factory for creating JDocument API objects * * @var FactoryInterface * @since 4.0.0 */ protected $factory; /** * Preload manager * * @var PreloadManagerInterface * @since 4.0.0 */ protected $preloadManager = null; /** * The supported preload types * * @var array * @since 4.0.0 */ protected $preloadTypes = ['preload', 'dns-prefetch', 'preconnect', 'prefetch', 'prerender']; /** * Web Asset instance * * @var WebAssetManager * @since 4.0.0 */ protected $webAssetManager = null; /** * Class constructor. * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { if (\array_key_exists('lineend', $options)) { $this->setLineEnd($options['lineend']); } if (\array_key_exists('charset', $options)) { $this->setCharset($options['charset']); } if (\array_key_exists('language', $options)) { $this->setLanguage($options['language']); } if (\array_key_exists('direction', $options)) { $this->setDirection($options['direction']); } if (\array_key_exists('tab', $options)) { $this->setTab($options['tab']); } if (\array_key_exists('link', $options)) { $this->setLink($options['link']); } if (\array_key_exists('base', $options)) { $this->setBase($options['base']); } if (\array_key_exists('mediaversion', $options)) { $this->setMediaVersion($options['mediaversion']); } if (\array_key_exists('factory', $options)) { $this->setFactory($options['factory']); } else { $this->setFactory(new Factory()); } if (\array_key_exists('preloadManager', $options)) { $this->setPreloadManager($options['preloadManager']); } else { $this->setPreloadManager(new PreloadManager()); } if (\array_key_exists('webAssetManager', $options)) { $this->setWebAssetManager($options['webAssetManager']); } else { $webAssetManager = new WebAssetManager(\Joomla\CMS\Factory::getContainer()->get('webassetregistry')); $this->setWebAssetManager($webAssetManager); } } /** * Returns the global Document object, only creating it * if it doesn't already exist. * * @param string $type The document type to instantiate * @param array $attributes Array of attributes * * @return static The document object. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the \Joomla\CMS\Document\FactoryInterface instead * Example: Factory::getApplication()->getDocument(); */ public static function getInstance($type = 'html', $attributes = []) { $signature = serialize([$type, $attributes]); if (empty(self::$instances[$signature])) { self::$instances[$signature] = CmsFactory::getContainer()->get(FactoryInterface::class)->createDocument($type, $attributes); } return self::$instances[$signature]; } /** * Set the factory instance * * @param FactoryInterface $factory The factory instance * * @return Document * * @since 4.0.0 */ public function setFactory(FactoryInterface $factory): self { $this->factory = $factory; return $this; } /** * Set the document type * * @param string $type Type document is to set to * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setType($type) { $this->_type = $type; return $this; } /** * Returns the document type * * @return string * * @since 1.7.0 */ public function getType() { return $this->_type; } /** * Get the contents of the document buffer * * @return mixed * * @since 1.7.0 */ public function getBuffer() { return self::$_buffer; } /** * Set the contents of the document buffer * * @param string $content The content to be set in the buffer. * @param array $options Array of optional elements. * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setBuffer($content, $options = []) { self::$_buffer = $content; return $this; } /** * Gets a meta tag. * * @param string $name Name of the meta HTML tag * @param string $attribute Attribute to use in the meta HTML tag * * @return string * * @since 1.7.0 */ public function getMetaData($name, $attribute = 'name') { // B/C old http_equiv parameter. if (!\is_string($attribute)) { $attribute = $attribute == true ? 'http-equiv' : 'name'; } if ($name === 'generator') { $result = $this->getGenerator(); } elseif ($name === 'description') { $result = $this->getDescription(); } else { $result = isset($this->_metaTags[$attribute]) && isset($this->_metaTags[$attribute][$name]) ? $this->_metaTags[$attribute][$name] : ''; } return $result; } /** * Sets or alters a meta tag. * * @param string $name Name of the meta HTML tag * @param mixed $content Value of the meta HTML tag as array or string * @param string $attribute Attribute to use in the meta HTML tag * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setMetaData($name, $content, $attribute = 'name') { // Pop the element off the end of array if target function expects a string or this http_equiv parameter. if (\is_array($content) && (\in_array($name, ['generator', 'description']) || !\is_string($attribute))) { $content = array_pop($content); } // B/C old http_equiv parameter. if (!\is_string($attribute)) { $attribute = $attribute == true ? 'http-equiv' : 'name'; } if ($name === 'generator') { $this->setGenerator($content); } elseif ($name === 'description') { $this->setDescription($content); } else { $this->_metaTags[$attribute][$name] = $content; } return $this; } /** * Adds a linked script to the page * * @param string $url URL to the linked script. * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9', 'preload' => array('preload')) * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1) * * @return Document instance of $this to allow chaining * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager * Example: $wa->registerAndUseScript(...); */ public function addScript($url, $options = [], $attribs = []) { // Default value for type. if (!isset($attribs['type']) && !isset($attribs['mime'])) { $attribs['type'] = 'text/javascript'; } $this->_scripts[$url] = isset($this->_scripts[$url]) ? array_replace($this->_scripts[$url], $attribs) : $attribs; $this->_scripts[$url]['options'] = isset($this->_scripts[$url]['options']) ? array_replace($this->_scripts[$url]['options'], $options) : $options; return $this; } /** * Adds a script to the page * * @param string $content Script * @param string $type Scripting mime (defaults to 'text/javascript') * * @return Document instance of $this to allow chaining * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager * Example: $wa->addInlineScript(...); */ public function addScriptDeclaration($content, $type = 'text/javascript') { $type = strtolower($type); if (empty($this->_script[$type])) { $this->_script[$type] = []; } $this->_script[$type][md5($content)] = $content; return $this; } /** * Add option for script * * @param string $key Name in Storage * @param mixed $options Scrip options as array or string * @param bool $merge Whether merge with existing (true) or replace (false) * * @return Document instance of $this to allow chaining * * @since 3.5 */ public function addScriptOptions($key, $options, $merge = true) { if (empty($this->scriptOptions[$key])) { $this->scriptOptions[$key] = []; } if ($merge && \is_array($options)) { $this->scriptOptions[$key] = array_replace_recursive($this->scriptOptions[$key], $options); } else { $this->scriptOptions[$key] = $options; } return $this; } /** * Get script(s) options * * @param string $key Name in Storage * * @return array Options for given $key, or all script options * * @since 3.5 */ public function getScriptOptions($key = null) { if ($key) { return (empty($this->scriptOptions[$key])) ? [] : $this->scriptOptions[$key]; } else { return $this->scriptOptions; } } /** * Adds a linked stylesheet to the page * * @param string $url URL to the linked style sheet * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9', 'preload' => array('preload')) * @param array $attribs Array of attributes. Example: array('id' => 'stylesheet', 'data-test' => 1) * * @return Document instance of $this to allow chaining * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager * Example: $wa->registerAndUseStyle(...); */ public function addStyleSheet($url, $options = [], $attribs = []) { // Default value for type. if (!isset($attribs['type']) && !isset($attribs['mime'])) { $attribs['type'] = 'text/css'; } $this->_styleSheets[$url] = isset($this->_styleSheets[$url]) ? array_replace($this->_styleSheets[$url], $attribs) : $attribs; if (isset($this->_styleSheets[$url]['options'])) { $this->_styleSheets[$url]['options'] = array_replace($this->_styleSheets[$url]['options'], $options); } else { $this->_styleSheets[$url]['options'] = $options; } return $this; } /** * Adds a stylesheet declaration to the page * * @param string $content Style declarations * @param string $type Type of stylesheet (defaults to 'text/css') * * @return Document instance of $this to allow chaining * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use WebAssetManager * Example: $wa->addInlineStyle(...); */ public function addStyleDeclaration($content, $type = 'text/css') { if ($content === null) { return $this; } $type = strtolower($type); if (empty($this->_style[$type])) { $this->_style[$type] = []; } $this->_style[$type][md5($content)] = $content; return $this; } /** * Sets the document charset * * @param string $type Charset encoding string * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setCharset($type = 'utf-8') { $this->_charset = $type; return $this; } /** * Returns the document charset encoding. * * @return string * * @since 1.7.0 */ public function getCharset() { return $this->_charset; } /** * Sets the global document language declaration. Default is English (en-gb). * * @param string $lang The language to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setLanguage($lang = 'en-gb') { $this->language = strtolower($lang); return $this; } /** * Returns the document language. * * @return string * * @since 1.7.0 */ public function getLanguage() { return $this->language; } /** * Sets the global document direction declaration. Default is left-to-right (ltr). * * @param string $dir The language direction to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setDirection($dir = 'ltr') { $this->direction = strtolower($dir); return $this; } /** * Returns the document direction declaration. * * @return string * * @since 1.7.0 */ public function getDirection() { return $this->direction; } /** * Sets the title of the document * * @param string $title The title to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setTitle($title) { $this->title = $title; return $this; } /** * Return the title of the document. * * @return string * * @since 1.7.0 */ public function getTitle() { return $this->title; } /** * Set the assets version * * @param string $mediaVersion Media version to use * * @return Document instance of $this to allow chaining * * @since 3.2 */ public function setMediaVersion($mediaVersion) { $this->mediaVersion = strtolower($mediaVersion); return $this; } /** * Return the media version * * @return string * * @since 3.2 */ public function getMediaVersion() { return $this->mediaVersion; } /** * Set the preload manager * * @param PreloadManagerInterface $preloadManager The preload manager service * * @return Document instance of $this to allow chaining * * @since 4.0.0 */ public function setPreloadManager(PreloadManagerInterface $preloadManager): self { $this->preloadManager = $preloadManager; return $this; } /** * Return the preload manager * * @return PreloadManagerInterface * * @since 4.0.0 */ public function getPreloadManager(): PreloadManagerInterface { return $this->preloadManager; } /** * Set WebAsset manager * * @param WebAssetManager $webAsset The WebAsset instance * * @return Document * * @since 4.0.0 */ public function setWebAssetManager(WebAssetManager $webAsset): self { $this->webAssetManager = $webAsset; return $this; } /** * Return WebAsset manager * * @return WebAssetManager * * @since 4.0.0 */ public function getWebAssetManager(): WebAssetManager { return $this->webAssetManager; } /** * Sets the base URI of the document * * @param string $base The base URI to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setBase($base) { $this->base = $base; return $this; } /** * Return the base URI of the document. * * @return string * * @since 1.7.0 */ public function getBase() { return $this->base; } /** * Sets the description of the document * * @param string $description The description to set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setDescription($description) { $this->description = $description; return $this; } /** * Return the description of the document. * * @return string * * @since 1.7.0 */ public function getDescription() { return $this->description; } /** * Sets the document link * * @param string $url A url * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setLink($url) { $this->link = $url; return $this; } /** * Returns the document base url * * @return string * * @since 1.7.0 */ public function getLink() { return $this->link; } /** * Sets the document generator * * @param string $generator The generator to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setGenerator($generator) { $this->_generator = $generator; return $this; } /** * Returns the document generator * * @return string * * @since 1.7.0 */ public function getGenerator() { return $this->_generator; } /** * Sets the document modified date * * @param string|Date $date The date to be set * * @return Document instance of $this to allow chaining * * @since 1.7.0 * @throws \InvalidArgumentException */ public function setModifiedDate($date) { if (!\is_string($date) && !($date instanceof Date)) { throw new \InvalidArgumentException( sprintf( 'The $date parameter of %1$s must be a string or a %2$s instance, a %3$s was given.', __METHOD__ . '()', 'Joomla\\CMS\\Date\\Date', \gettype($date) === 'object' ? (\get_class($date) . ' instance') : \gettype($date) ) ); } $this->_mdate = $date; return $this; } /** * Returns the document modified date * * @return string|Date * * @since 1.7.0 */ public function getModifiedDate() { return $this->_mdate; } /** * Sets the document MIME encoding that is sent to the browser. * * This usually will be text/html because most browsers cannot yet * accept the proper mime settings for XHTML: application/xhtml+xml * and to a lesser extent application/xml and text/xml. See the W3C note * ({@link https://www.w3.org/TR/xhtml-media-types/ * https://www.w3.org/TR/xhtml-media-types/}) for more details. * * @param string $type The document type to be sent * @param boolean $sync Should the type be synced with HTML? * * @return Document instance of $this to allow chaining * * @since 1.7.0 * * @link https://www.w3.org/TR/xhtml-media-types/ */ public function setMimeEncoding($type = 'text/html', $sync = true) { $this->_mime = strtolower($type); // Syncing with metadata if ($sync) { $this->setMetaData('content-type', $type . '; charset=' . $this->_charset, true); } return $this; } /** * Return the document MIME encoding that is sent to the browser. * * @return string * * @since 1.7.0 */ public function getMimeEncoding() { return $this->_mime; } /** * Sets the line end style to Windows, Mac, Unix or a custom string. * * @param string $style "win", "mac", "unix" or custom string. * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setLineEnd($style) { switch ($style) { case 'win': $this->_lineEnd = "\15\12"; break; case 'unix': $this->_lineEnd = "\12"; break; case 'mac': $this->_lineEnd = "\15"; break; default: $this->_lineEnd = $style; } return $this; } /** * Returns the lineEnd * * @return string * * @since 1.7.0 */ public function _getLineEnd() { return $this->_lineEnd; } /** * Sets the string used to indent HTML * * @param string $string String used to indent ("\11", "\t", ' ', etc.). * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function setTab($string) { $this->_tab = $string; return $this; } /** * Returns a string containing the unit for indenting HTML * * @return string * * @since 1.7.0 */ public function _getTab() { return $this->_tab; } /** * Load a renderer * * @param string $type The renderer type * * @return RendererInterface * * @since 1.7.0 * @throws \RuntimeException */ public function loadRenderer($type) { return $this->factory->createRenderer($this, $type); } /** * Parses the document and prepares the buffers * * @param array $params The array of parameters * * @return Document instance of $this to allow chaining * * @since 1.7.0 */ public function parse($params = []) { return $this; } /** * Outputs the document * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { $app = CmsFactory::getApplication(); if ($mdate = $this->getModifiedDate()) { if (!($mdate instanceof Date)) { $mdate = new Date($mdate); } $app->modifiedDate = $mdate; } $app->mimeType = $this->_mime; $app->charSet = $this->_charset; // Handle preloading for configured assets in web applications if ($app instanceof AbstractWebApplication) { $this->preloadAssets(); } return ''; } /** * Generate the Link header for assets configured for preloading * * @return void * * @since 4.0.0 */ protected function preloadAssets() { // Process stylesheets first foreach ($this->_styleSheets as $link => $properties) { if (empty($properties['options']['preload'])) { continue; } foreach ($properties['options']['preload'] as $preloadMethod) { // Make sure the preload method is supported, special case for `dns-prefetch` to convert it to the right method name if ($preloadMethod === 'dns-prefetch') { $this->getPreloadManager()->dnsPrefetch($link); } elseif (\in_array($preloadMethod, $this->preloadTypes)) { $this->getPreloadManager()->$preloadMethod($link); } else { throw new \InvalidArgumentException(sprintf('The "%s" method is not supported for preloading.', $preloadMethod), 500); } } } // Now process scripts foreach ($this->_scripts as $link => $properties) { if (empty($properties['options']['preload'])) { continue; } foreach ($properties['options']['preload'] as $preloadMethod) { // Make sure the preload method is supported, special case for `dns-prefetch` to convert it to the right method name if ($preloadMethod === 'dns-prefetch') { $this->getPreloadManager()->dnsPrefetch($link); } elseif (\in_array($preloadMethod, $this->preloadTypes)) { $this->getPreloadManager()->$preloadMethod($link); } else { throw new \InvalidArgumentException(sprintf('The "%s" method is not supported for preloading.', $preloadMethod), 500); } } } // Check if the manager's provider has links, if so add the Link header if ($links = $this->getPreloadManager()->getLinkProvider()->getLinks()) { CmsFactory::getApplication()->setHeader('Link', (new HttpHeaderSerializer())->serialize($links)); } } } PK1A#]<#��!src/Document/DocumentRenderer.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract class for a renderer * * @since 1.7.0 */ abstract class DocumentRenderer implements RendererInterface { /** * Reference to the Document object that instantiated the renderer * * @var Document * @since 1.7.0 */ protected $_doc = null; /** * Renderer mime type * * @var string * @since 1.7.0 */ protected $_mime = 'text/html'; /** * Class constructor * * @param Document $doc A reference to the Document object that instantiated the renderer * * @since 1.7.0 */ public function __construct(Document $doc) { $this->_doc = $doc; } /** * Return the content type of the renderer * * @return string The contentType * * @since 1.7.0 */ public function getContentType() { return $this->_mime; } /** * Convert links in a text from relative to absolute * * @param string $text The text processed * * @return string Text with converted links * * @since 1.7.0 */ protected function _relToAbs($text) { $base = Uri::base(); $text = preg_replace("/(href|src)=\"(?!http|ftp|https|mailto|data|\/\/)([^\"]*)\"/", "$1=\"$base\$2\"", $text); return $text; } } PK1A#]U��ee"src/Document/RendererInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a document renderer * * @since 4.0.0 */ interface RendererInterface { /** * Renders a script and returns the results as a string * * @param string $name The name of the element to render * @param array $params Array of values * @param string $content Override the output of the renderer * * @return string The output of the script * * @since 4.0.0 */ public function render($name, $params = null, $content = null); } PK1A#]HX=��src/Document/FeedDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Document\Feed\FeedImage; use Joomla\CMS\Document\Feed\FeedItem; use Joomla\CMS\Factory as CmsFactory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * FeedDocument class, provides an easy interface to parse and display any feed document * * @since 1.7.0 */ class FeedDocument extends Document { /** * Syndication URL feed element * * optional * * @var string * @since 1.7.0 */ public $syndicationURL = ''; /** * Image feed element * * optional * * @var FeedImage * @since 1.7.0 */ public $image = null; /** * Copyright feed element * * optional * * @var string * @since 1.7.0 */ public $copyright = ''; /** * Published date feed element * * optional * * @var string * @since 1.7.0 */ public $pubDate = ''; /** * Lastbuild date feed element * * @var \Joomla\CMS\Date\Date * @since 1.7.0 */ public $lastBuildDate; /** * Editor feed element * * optional * * @var string * @since 1.7.0 */ public $editor = ''; /** * Docs feed element * * @var string * @since 1.7.0 */ public $docs = ''; /** * Editor email feed element * * optional * * @var string * @since 1.7.0 */ public $editorEmail = ''; /** * Webmaster email feed element * * optional * * @var string * @since 1.7.0 */ public $webmaster = ''; /** * Category feed element * * optional * * @var string * @since 1.7.0 */ public $category = ''; /** * TTL feed attribute * * optional * * @var string * @since 1.7.0 */ public $ttl = ''; /** * Rating feed element * * optional * * @var string * @since 1.7.0 */ public $rating = ''; /** * Skiphours feed element * * optional * * @var string * @since 1.7.0 */ public $skipHours = ''; /** * Skipdays feed element * * optional * * @var string * @since 1.7.0 */ public $skipDays = ''; /** * The feed items collection * * @var FeedItem[] * @since 1.7.0 */ public $items = []; /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set document type $this->_type = 'feed'; // Gets and sets timezone offset from site configuration $this->lastBuildDate = CmsFactory::getDate(); $this->lastBuildDate->setTimezone(new \DateTimeZone(CmsFactory::getApplication()->get('offset', 'UTC'))); } /** * Render the document * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 * @throws \Exception * @todo Make this cacheable */ public function render($cache = false, $params = []) { // Get the feed type $type = CmsFactory::getApplication()->getInput()->get('type', 'rss'); // Instantiate feed renderer and set the mime encoding $renderer = $this->loadRenderer($type ?: 'rss'); if (!($renderer instanceof DocumentRenderer)) { throw new \Exception(Text::_('JGLOBAL_RESOURCE_NOT_FOUND'), 404); } $this->setMimeEncoding($renderer->getContentType()); // Output // Generate prolog $data = "<?xml version=\"1.0\" encoding=\"" . $this->_charset . "\"?>\n"; $data .= "<!-- generator=\"" . $this->getGenerator() . "\" -->\n"; // Generate stylesheet links foreach ($this->_styleSheets as $src => $attr) { $data .= "<?xml-stylesheet href=\"$src\" type=\"" . $attr['type'] . "\"?>\n"; } // Render the feed $data .= $renderer->render(); parent::render($cache, $params); return $data; } /** * Adds a FeedItem to the feed. * * @param FeedItem $item The feeditem to add to the feed. * * @return FeedDocument instance of $this to allow chaining * * @since 1.7.0 */ public function addItem(FeedItem $item) { $item->source = $this->link; $this->items[] = $item; return $this; } } PK1A#]t�Y�src/Document/PreloadManager.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Fig\Link\GenericLinkProvider; use Fig\Link\Link; use Psr\Link\EvolvableLinkProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Preload Manager * * @since 4.0.0 */ class PreloadManager implements PreloadManagerInterface { /** * The link provider * * @var EvolvableLinkProviderInterface * @since 4.0.0 */ protected $linkProvider; /** * PreloadManager constructor * * @param EvolvableLinkProviderInterface $linkProvider The link provider * * @since 4.0.0 */ public function __construct(EvolvableLinkProviderInterface $linkProvider = null) { $this->linkProvider = $linkProvider ?: new GenericLinkProvider(); } /** * Get the link provider * * @return EvolvableLinkProviderInterface * * @since 4.0.0 */ public function getLinkProvider(): EvolvableLinkProviderInterface { return $this->linkProvider; } /** * Set the link provider * * @param EvolvableLinkProviderInterface $linkProvider The link provider * * @return $this * * @since 4.0.0 */ public function setLinkProvider(EvolvableLinkProviderInterface $linkProvider) { $this->linkProvider = $linkProvider; return $this; } /** * Preloads a resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')") * * @return void * * @since 4.0.0 */ public function preload(string $uri, array $attributes = []) { $this->link($uri, 'preload', $attributes); } /** * Resolves a resource origin as early as possible. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function dnsPrefetch(string $uri, array $attributes = []) { $this->link($uri, 'dns-prefetch', $attributes); } /** * Initiates an early connection to a resource (DNS resolution, TCP handshake, TLS negotiation). * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function preconnect(string $uri, array $attributes = []) { $this->link($uri, 'preconnect', $attributes); } /** * Indicates to the client that it should prefetch this resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function prefetch(string $uri, array $attributes = []) { $this->link($uri, 'prefetch', $attributes); } /** * Indicates to the client that it should prerender this resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function prerender(string $uri, array $attributes = []) { $this->link($uri, 'prerender', $attributes); } /** * Adds a "Link" HTTP header. * * @param string $uri The relation URI * @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch") * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ private function link(string $uri, string $rel, array $attributes = []) { $link = new Link($rel, $uri); foreach ($attributes as $key => $value) { $link = $link->withAttribute($key, $value); } $this->setLinkProvider($this->getLinkProvider()->withLink($link)); } } PK1A#]vU�TT(src/Document/PreloadManagerInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Psr\Link\EvolvableLinkProviderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Preload Manager Interface * * @since 4.0.0 */ interface PreloadManagerInterface { /** * Get the link provider * * @return EvolvableLinkProviderInterface * * @since 4.0.0 */ public function getLinkProvider(): EvolvableLinkProviderInterface; /** * Set the link provider * * @param EvolvableLinkProviderInterface $linkProvider The link provider * * @return $this * * @since 4.0.0 */ public function setLinkProvider(EvolvableLinkProviderInterface $linkProvider); /** * Preloads a resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')") * * @return void * * @since 4.0.0 */ public function preload(string $uri, array $attributes = []); /** * Resolves a resource origin as early as possible. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function dnsPrefetch(string $uri, array $attributes = []); /** * Initiates an early connection to a resource (DNS resolution, TCP handshake, TLS negotiation). * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function preconnect(string $uri, array $attributes = []); /** * Indicates to the client that it should prefetch this resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function prefetch(string $uri, array $attributes = []); /** * Indicates to the client that it should prerender this resource. * * @param string $uri A public path * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") * * @return void * * @since 4.0.0 */ public function prerender(string $uri, array $attributes = []); } PK1A#]�}~���src/Document/ErrorDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; use Joomla\CMS\Factory as CmsFactory; use Joomla\CMS\Layout\LayoutHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * ErrorDocument class, provides an easy interface to parse and display an HTML based error page * * @since 1.7.0 */ class ErrorDocument extends HtmlDocument { /** * Flag if debug mode has been enabled * * @var boolean * @since 1.7.0 */ public $debug = false; /** * Error Object * * @var \Throwable * @since 1.7.0 */ public $error; /** * Error Object * * @var \Throwable * @since 1.7.0 */ protected $_error; /** * Class constructor * * @param array $options Associative array of attributes * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set document type $this->_type = 'error'; } /** * Set error object * * @param \Throwable $error Error object to set * * @return boolean True on success * * @since 1.7.0 */ public function setError($error) { if ($error instanceof \Throwable) { $this->_error = & $error; return true; } return false; } /** * Load a renderer * * @param string $type The renderer type * * @return RendererInterface * * @since 4.0.0 * @throws \RuntimeException */ public function loadRenderer($type) { // Need to force everything to go to the HTML renderers or we duplicate all the things return $this->factory->createRenderer($this, $type, 'html'); } /** * Render the document * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { // If no error object is set return null if (!isset($this->_error)) { return; } // Set the status header $status = $this->_error->getCode(); if ($status < 400 || $status > 599) { $status = 500; } $errorReporting = CmsFactory::getApplication()->get('error_reporting'); if ($errorReporting === "development" || $errorReporting === "maximum") { $status .= ' ' . str_replace("\n", ' ', $this->_error->getMessage()); } CmsFactory::getApplication()->setHeader('status', $status); // Set variables $this->debug = $params['debug'] ?? false; $this->error = $this->_error; $params['file'] = 'error.php'; return parent::render($cache, $params); } /** * Render the backtrace * * @return string The contents of the backtrace * * @since 1.7.0 */ public function renderBacktrace() { // If no error object is set return null if (!isset($this->_error)) { return; } // The back trace $backtrace = $this->_error->getTrace(); // Add the position of the actual file array_unshift($backtrace, ['file' => $this->_error->getFile(), 'line' => $this->_error->getLine(), 'function' => '']); return LayoutHelper::render('joomla.error.backtrace', ['backtrace' => $backtrace]); } } PK1A#]�K��%%!src/Document/FactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create Document objects * * @since 4.0.0 */ interface FactoryInterface { /** * Creates a new Document object for the requested format. * * @param string $type The document type to instantiate * @param array $attributes Array of attributes * * @return Document * * @since 4.0.0 */ public function createDocument(string $type = 'html', array $attributes = []): Document; /** * Creates a new renderer object. * * @param Document $document The Document instance to attach to the renderer * @param string $type The renderer type to instantiate * @param string $docType The document type the renderer is part of * * @return RendererInterface * * @since 4.0.0 */ public function createRenderer(Document $document, string $type, string $docType = ''): RendererInterface; } PK1A#]h��src/Document/RawDocument.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Document; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * RawDocument class, provides an easy interface to parse and display raw output * * @since 1.7.0 */ class RawDocument extends Document { /** * Class constructor * * @param array $options Associative array of options * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); // Set mime type $this->_mime = 'text/html'; // Set document type $this->_type = 'raw'; } /** * Render the document. * * @param boolean $cache If true, cache the output * @param array $params Associative array of attributes * * @return string The rendered data * * @since 1.7.0 */ public function render($cache = false, $params = []) { parent::render($cache, $params); return $this->getBuffer(); } } PK1A#]�#R�OO&src/Dispatcher/DispatcherInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform CMS Dispatcher Interface * * @since 4.0.0 */ interface DispatcherInterface { /** * Runs the dispatcher. * * @return void * * @since 4.0.0 */ public function dispatch(); } PK1A#]2+�F��,src/Dispatcher/LegacyComponentDispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a legacy Joomla Dispatcher * * Executes the single entry file of a legacy component. * * @since 4.0.0 */ class LegacyComponentDispatcher implements DispatcherInterface { /** * The application instance * * @var CMSApplication * @since 4.0.0 */ private $app; /** * Constructor for Dispatcher * * @param CMSApplication $app The application instance * * @since 4.0.0 */ public function __construct(CMSApplication $app) { $this->app = $app; } /** * Dispatch a controller task. Redirecting the user if appropriate. * * @return void * * @since 4.0.0 */ public function dispatch() { $path = JPATH_COMPONENT . '/' . substr($this->app->scope, 4) . '.php'; // If component file doesn't exist throw error if (!is_file($path)) { throw new \Exception(Text::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'), 404); } $lang = $this->app->getLanguage(); // Load common and local language files. $lang->load($this->app->scope, JPATH_BASE) || $lang->load($this->app->scope, JPATH_COMPONENT); // Execute the component $loader = static function ($path) { require_once $path; }; $loader($path); } } PK1A#]��PƘ�src/Dispatcher/Dispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Dispatcher * * @since 4.0.0 */ abstract class Dispatcher implements DispatcherInterface { /** * The application instance * * @var CMSApplicationInterface * @since 4.0.0 */ protected $app; /** * The input instance * * @var Input * @since 4.0.0 */ protected $input; /** * Constructor for Dispatcher * * @param CMSApplicationInterface $app The application instance * @param Input $input The input instance * * @since 4.0.0 */ public function __construct(CMSApplicationInterface $app, Input $input) { $this->app = $app; $this->input = $input; } /** * The application the dispatcher is working with. * * @return CMSApplicationInterface * * @since 4.0.0 */ protected function getApplication(): CMSApplicationInterface { return $this->app; } } PK1A#]ZV\z��-src/Dispatcher/ComponentDispatcherFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Namespace based implementation of the ComponentDispatcherFactoryInterface * * @since 4.0.0 */ class ComponentDispatcherFactory implements ComponentDispatcherFactoryInterface { /** * The extension namespace * * @var string * * @since 4.0.0 */ protected $namespace; /** * The MVC factory * * @var MVCFactoryInterface * * @since 4.0.0 */ private $mvcFactory; /** * ComponentDispatcherFactory constructor. * * @param string $namespace The namespace * @param MVCFactoryInterface $mvcFactory The MVC factory * * @since 4.0.0 */ public function __construct(string $namespace, MVCFactoryInterface $mvcFactory) { $this->namespace = $namespace; $this->mvcFactory = $mvcFactory; } /** * Creates a dispatcher. * * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function createDispatcher(CMSApplicationInterface $application, Input $input = null): DispatcherInterface { $name = ucfirst($application->getName()); $className = '\\' . trim($this->namespace, '\\') . '\\' . $name . '\\Dispatcher\\Dispatcher'; if (!class_exists($className)) { if ($application->isClient('api')) { $className = ApiDispatcher::class; } else { $className = ComponentDispatcher::class; } } return new $className($application, $input ?: $application->getInput(), $this->mvcFactory); } } PK1A#]�Ng�44 src/Dispatcher/ApiDispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * API Implementation for our dispatcher. It loads a component's administrator language files, and calls the API * Controller so that components that haven't implemented web services can add their own handling. * * @since 4.0.0 */ final class ApiDispatcher extends ComponentDispatcher { /** * Load the component's administrator language * * @since 4.0.0 * * @return void */ protected function loadLanguage() { // Load common and local language files. $this->app->getLanguage()->load($this->option, JPATH_BASE) || $this->app->getLanguage()->load($this->option, JPATH_ADMINISTRATOR) || $this->app->getLanguage()->load($this->option, JPATH_COMPONENT_ADMINISTRATOR); } /** * Dispatch a controller task. API Overrides to ensure there is no redirects. * * @return void * * @since 4.0.0 */ public function dispatch() { $task = $this->input->getCmd('task', 'display'); // Build controller config data $config = ['option' => $this->option]; // Set name of controller if it is passed in the request if ($this->input->exists('controller')) { $config['name'] = strtolower($this->input->get('controller')); } $controller = $this->input->get('controller'); $controller = $this->getController($controller, ucfirst($this->app->getName()), $config); $controller->execute($task); } } PK1A#]��[qq#src/Dispatcher/ModuleDispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Module Dispatcher * * Executes the single entry file of a module. * * @since 4.0.0 */ class ModuleDispatcher extends AbstractModuleDispatcher { /** * Dispatches the dispatcher. * * @return void * * @since 4.0.0 */ public function dispatch() { $path = JPATH_BASE . '/modules/' . $this->module->module . '/' . $this->module->module . '.php'; if (!is_file($path)) { return; } $this->loadLanguage(); // Execute the layout without the module context $loader = static function ($path, array $displayData) { // If $displayData doesn't exist in extracted data, unset the variable. if (!\array_key_exists('displayData', $displayData)) { extract($displayData); unset($displayData); } else { extract($displayData); } include $path; }; $loader($path, $this->getLayoutData()); } } PK1A#]J���~~+src/Dispatcher/AbstractModuleDispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Helper\ModuleHelper; use Joomla\Input\Input; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Module Dispatcher. * * @since 4.0.0 */ abstract class AbstractModuleDispatcher extends Dispatcher { /** * The module instance * * @var \stdClass * @since 4.0.0 */ protected $module; /** * Constructor for Dispatcher * * @param \stdClass $module The module * @param CMSApplicationInterface $app The application instance * @param Input $input The input instance * * @since 4.0.0 */ public function __construct(\stdClass $module, CMSApplicationInterface $app, Input $input) { parent::__construct($app, $input); $this->module = $module; } /** * Runs the dispatcher. * * @return void * * @since 4.0.0 */ public function dispatch() { $this->loadLanguage(); $displayData = $this->getLayoutData(); // Stop when display data is false if ($displayData === false) { return; } // Execute the layout without the module context $loader = static function (array $displayData) { // If $displayData doesn't exist in extracted data, unset the variable. if (!\array_key_exists('displayData', $displayData)) { extract($displayData); unset($displayData); } else { extract($displayData); } /** * Extracted variables * ----------------- * @var \stdClass $module * @var Registry $params */ require ModuleHelper::getLayoutPath($module->module, $params->get('layout', 'default')); }; $loader($displayData); } /** * Returns the layout data. This function can be overridden by subclasses to add more * attributes for the layout. * * If false is returned, then it means that the dispatch process should be stopped. * * @return array|false * * @since 4.0.0 */ protected function getLayoutData() { return [ 'module' => $this->module, 'app' => $this->app, 'input' => $this->input, 'params' => new Registry($this->module->params), 'template' => $this->app->getTemplate(), ]; } /** * Load the language. * * @return void * * @since 4.0.0 */ protected function loadLanguage() { $language = $this->app->getLanguage(); $coreLanguageDirectory = JPATH_BASE; $extensionLanguageDirectory = JPATH_BASE . '/modules/' . $this->module->module; $langPaths = $language->getPaths(); // Only load the module's language file if it hasn't been already if (!$langPaths || (!isset($langPaths[$coreLanguageDirectory]) && !isset($langPaths[$extensionLanguageDirectory]))) { // 1.5 or Core then 1.6 3PD $language->load($this->module->module, $coreLanguageDirectory) || $language->load($this->module->module, $extensionLanguageDirectory); } } } PK1A#]�Z5WW&src/Dispatcher/ComponentDispatcher.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Access\Exception\NotAllowed; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\BaseController; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for a Joomla Component Dispatcher * * Dispatchers are responsible for checking ACL of a component if appropriate and * choosing an appropriate controller (and if necessary, a task) and executing it. * * @since 4.0.0 */ class ComponentDispatcher extends Dispatcher { /** * The URL option for the component. * * @var string * @since 4.0.0 */ protected $option; /** * The MVC factory * * @var MVCFactoryInterface * * @since 4.0.0 */ protected $mvcFactory; /** * Constructor for ComponentDispatcher * * @param CMSApplicationInterface $app The application instance * @param Input $input The input instance * @param MVCFactoryInterface $mvcFactory The MVC factory instance * * @since 4.0.0 */ public function __construct(CMSApplicationInterface $app, Input $input, MVCFactoryInterface $mvcFactory) { parent::__construct($app, $input); $this->mvcFactory = $mvcFactory; // If option is not provided, detect it from dispatcher class name, ie ContentDispatcher if (empty($this->option)) { $this->option = ComponentHelper::getComponentName( $this, str_replace('com_', '', $input->get('option')) ); } $this->loadLanguage(); } /** * Load the language * * @return void * * @since 4.0.0 */ protected function loadLanguage() { // Load common and local language files. $this->app->getLanguage()->load($this->option, JPATH_BASE) || $this->app->getLanguage()->load($this->option, JPATH_COMPONENT); } /** * Method to check component access permission * * @return void * * @since 4.0.0 */ protected function checkAccess() { // Check the user has permission to access this component if in the backend if ($this->app->isClient('administrator') && !$this->app->getIdentity()->authorise('core.manage', $this->option)) { throw new NotAllowed($this->app->getLanguage()->_('JERROR_ALERTNOAUTHOR'), 403); } } /** * Dispatch a controller task. Redirecting the user if appropriate. * * @return void * * @since 4.0.0 */ public function dispatch() { // Check component access permission $this->checkAccess(); $command = $this->input->getCmd('task', 'display'); // Check for a controller.task command. if (strpos($command, '.') !== false) { // Explode the controller.task command. list($controller, $task) = explode('.', $command); $this->input->set('controller', $controller); $this->input->set('task', $task); } else { // Do we have a controller? $controller = $this->input->get('controller', 'display'); $task = $command; } // Build controller config data $config = ['option' => $this->option]; // Set name of controller if it is passed in the request if ($this->input->exists('controller')) { $config['name'] = strtolower($this->input->get('controller')); } // Execute the task for this component $controller = $this->getController($controller, ucfirst($this->app->getName()), $config); $controller->execute($task); $controller->redirect(); } /** * Get a controller from the component * * @param string $name Controller name * @param string $client Optional client (like Administrator, Site etc.) * @param array $config Optional controller config * * @return BaseController * * @since 4.0.0 */ public function getController(string $name, string $client = '', array $config = []): BaseController { // Set up the client $client = $client ?: ucfirst($this->app->getName()); // Get the controller instance $controller = $this->mvcFactory->createController( $name, $client, $config, $this->app, $this->input ); // Check if the controller could be created if (!$controller) { throw new \InvalidArgumentException(Text::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER_CLASS', $name)); } return $controller; } } PK1A#]�b��3src/Dispatcher/ModuleDispatcherFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Module dispatcher factory interface * * @since 4.0.0 */ interface ModuleDispatcherFactoryInterface { /** * Creates a dispatcher. * * @param \stdClass $module The module * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function createDispatcher(\stdClass $module, CMSApplicationInterface $application, Input $input = null): DispatcherInterface; } PK1A#]�m+��6src/Dispatcher/ComponentDispatcherFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Component dispatcher factory interface * * @since 4.0.0 */ interface ComponentDispatcherFactoryInterface { /** * Creates a dispatcher. * * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function createDispatcher(CMSApplicationInterface $application, Input $input = null): DispatcherInterface; } PK1A#]I5W���*src/Dispatcher/ModuleDispatcherFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Dispatcher; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\Input\Input; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Namespace based implementation of the ModuleDispatcherFactoryInterface * * @since 4.0.0 */ class ModuleDispatcherFactory implements ModuleDispatcherFactoryInterface { /** * The extension namespace * * @var string * * @since 4.0.0 */ private $namespace; /** * ModuleDispatcherFactory constructor. * * @param string $namespace The namespace * * @since 4.0.0 */ public function __construct(string $namespace) { $this->namespace = $namespace; } /** * Creates a dispatcher. * * @param \stdClass $module The module * @param CMSApplicationInterface $application The application * @param Input $input The input object, defaults to the one in the application * * @return DispatcherInterface * * @since 4.0.0 */ public function createDispatcher(\stdClass $module, CMSApplicationInterface $application, Input $input = null): DispatcherInterface { $name = 'Site'; if ($application->isClient('administrator')) { $name = 'Administrator'; } $className = '\\' . trim($this->namespace, '\\') . '\\' . $name . '\\Dispatcher\\Dispatcher'; if (!class_exists($className)) { $className = ModuleDispatcher::class; } return new $className($module, $application, $input ?: $application->getInput()); } } PK1A#]g>��src/String/PunycodeHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\String; use Algo26\IdnaConvert\Exception\AlreadyPunycodeException; use Algo26\IdnaConvert\ToIdn; use Algo26\IdnaConvert\ToUnicode; use Joomla\Uri\UriHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Platform String Punycode Class * * Class for handling UTF-8 URLs * Wraps the Punycode library * All functions assume the validity of utf-8 URLs. * * @since 3.1.2 */ abstract class PunycodeHelper { /** * Transforms a UTF-8 string to a Punycode string * * @param string $utfString The UTF-8 string to transform * * @return string The punycode string * * @since 3.1.2 */ public static function toPunycode($utfString) { try { $converted = (new ToIdn())->convert($utfString); } catch (AlreadyPunycodeException $e) { $converted = $utfString; } return $converted; } /** * Transforms a Punycode string to a UTF-8 string * * @param string $punycodeString The Punycode string to transform * * @return string The UF-8 URL * * @since 3.1.2 */ public static function fromPunycode($punycodeString) { return (new ToUnicode())->convert($punycodeString); } /** * Transforms a UTF-8 URL to a Punycode URL * * @param string $uri The UTF-8 URL to transform * * @return string The punycode URL * * @since 3.1.2 */ public static function urlToPunycode($uri) { $parsed = UriHelper::parse_url($uri); if (!isset($parsed['host']) || $parsed['host'] == '') { // If there is no host we do not need to convert it. return $uri; } $host = $parsed['host']; $hostExploded = explode('.', $host); $newhost = ''; foreach ($hostExploded as $hostex) { $hostex = static::toPunycode($hostex); $newhost .= $hostex . '.'; } $newhost = substr($newhost, 0, -1); $newuri = ''; if (!empty($parsed['scheme'])) { // Assume :// is required although it is not always. $newuri .= $parsed['scheme'] . '://'; } if (!empty($newhost)) { $newuri .= $newhost; } if (!empty($parsed['port'])) { $newuri .= ':' . $parsed['port']; } if (!empty($parsed['path'])) { $newuri .= $parsed['path']; } if (!empty($parsed['query'])) { $newuri .= '?' . $parsed['query']; } if (!empty($parsed['fragment'])) { $newuri .= '#' . $parsed['fragment']; } return $newuri; } /** * Transforms a Punycode URL to a UTF-8 URL * * @param string $uri The Punycode URL to transform * * @return string The UTF-8 URL * * @since 3.1.2 */ public static function urlToUTF8($uri) { if (empty($uri)) { return ''; } $parsed = UriHelper::parse_url($uri); if (!isset($parsed['host']) || $parsed['host'] == '') { // If there is no host we do not need to convert it. return $uri; } $host = $parsed['host']; $hostExploded = explode('.', $host); $newhost = ''; foreach ($hostExploded as $hostex) { $hostex = self::fromPunycode($hostex); $newhost .= $hostex . '.'; } $newhost = substr($newhost, 0, -1); $newuri = ''; if (!empty($parsed['scheme'])) { // Assume :// is required although it is not always. $newuri .= $parsed['scheme'] . '://'; } if (!empty($newhost)) { $newuri .= $newhost; } if (!empty($parsed['port'])) { $newuri .= ':' . $parsed['port']; } if (!empty($parsed['path'])) { $newuri .= $parsed['path']; } if (!empty($parsed['query'])) { $newuri .= '?' . $parsed['query']; } if (!empty($parsed['fragment'])) { $newuri .= '#' . $parsed['fragment']; } return $newuri; } /** * Transforms a UTF-8 email to a Punycode email * This assumes a valid email address * * @param string $email The UTF-8 email to transform * * @return string The punycode email * * @since 3.1.2 */ public static function emailToPunycode($email) { if ($email === null) { @trigger_error(sprintf('Passing null value is deprecated in %s and will throw an exception in 6.0.', __METHOD__), E_USER_DEPRECATED); return ''; } $explodedAddress = explode('@', $email); // Not addressing UTF-8 user names $newEmail = $explodedAddress[0]; if (!empty($explodedAddress[1])) { $domainExploded = explode('.', $explodedAddress[1]); $newdomain = ''; foreach ($domainExploded as $domainex) { $domainex = static::toPunycode($domainex); $newdomain .= $domainex . '.'; } $newdomain = substr($newdomain, 0, -1); $newEmail = $newEmail . '@' . $newdomain; } return $newEmail; } /** * Transforms a Punycode email to a UTF-8 email * This assumes a valid email address * * @param string $email The punycode email to transform * * @return string The punycode email * * @since 3.1.2 */ public static function emailToUTF8($email) { $explodedAddress = explode('@', $email); // Not addressing UTF-8 user names $newEmail = $explodedAddress[0]; if (!empty($explodedAddress[1])) { $domainExploded = explode('.', $explodedAddress[1]); $newdomain = ''; foreach ($domainExploded as $domainex) { $domainex = static::fromPunycode($domainex); $newdomain .= $domainex . '.'; } $newdomain = substr($newdomain, 0, -1); $newEmail = $newEmail . '@' . $newdomain; } return $newEmail; } } PK1A#]�I!���src/Router/Route.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; use Joomla\DI\Exception\KeyNotFoundException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Route handling class * * @since 1.7.0 */ class Route { /** * No change, use the protocol currently used. * * @since 3.9.7 */ public const TLS_IGNORE = 0; /** * Make URI secure using http over TLS (https). * * @since 3.9.7 */ public const TLS_FORCE = 1; /** * Make URI unsecure using plain http (http). * * @since 3.9.7 */ public const TLS_DISABLE = 2; /** * The route object so we don't have to keep fetching it. * * @var Router[] * @since 3.0.1 */ private static $_router = []; /** * Translates an internal Joomla URL to a humanly readable URL. This method builds links for the current active client. * * @param string $url Absolute or Relative URI to Joomla resource. * @param boolean $xhtml Replace & by & for XML compliance. * @param integer $tls Secure state for the resolved URI. Use Route::TLS_* constants * 0: (default) No change, use the protocol currently used in the request * 1: Make URI secure using global secure site URI. * 2: Make URI unsecure using the global unsecure site URI. * @param boolean $absolute Return an absolute URL * * @return string The translated humanly readable URL. * * @since 1.7.0 */ public static function _($url, $xhtml = true, $tls = self::TLS_IGNORE, $absolute = false) { try { /** * @deprecated 3.9 int conversion will be removed in 5.0 * Before 3.9.7 this method silently converted $tls to integer */ if (!is_int($tls)) { @trigger_error( __METHOD__ . '() called with incompatible variable type on parameter $tls.', E_USER_DEPRECATED ); $tls = (int) $tls; } /** * @deprecated 3.9 -1 as valid value will be removed in 5.0 * Before 3.9.7 this method accepted -1. */ if ($tls === -1) { $tls = self::TLS_DISABLE; } $app = Factory::getApplication(); $client = $app->getName(); return static::link($client, $url, $xhtml, $tls, $absolute); } catch (\RuntimeException $e) { /** * @deprecated 3.9 this method will not fail silently from 5.0 * Before 3.9.0 this method failed silently on router error. This B/C will be removed in Joomla 5.0 */ return null; } } /** * Translates an internal Joomla URL to a humanly readable URL. * NOTE: To build link for active client instead of a specific client, you can use <var>Route::_()</var> * * @param string $client The client name for which to build the link. * @param string $url Absolute or Relative URI to Joomla resource. * @param boolean $xhtml Replace & by & for XML compliance. * @param integer $tls Secure state for the resolved URI. Use Route::TLS_* constants * 0: (default) No change, use the protocol currently used in the request * 1: Make URI secure using global secure site URI. * 2: Make URI unsecure using the global unsecure site URI. * @param boolean $absolute Return an absolute URL * * @return string The translated humanly readable URL. * * @throws \RuntimeException * * @since 3.9.0 */ public static function link($client, $url, $xhtml = true, $tls = self::TLS_IGNORE, $absolute = false) { // If we cannot process this $url exit early. if (!\is_array($url) && (strpos($url, '&') !== 0) && (strpos($url, 'index.php') !== 0)) { return $url; } // Get the router instance, only attempt when a client name is given. if ($client && !isset(self::$_router[$client])) { try { self::$_router[$client] = Factory::getContainer()->get(ucfirst($client) . 'Router') ?: Factory::getApplication()::getRouter($client); } catch (KeyNotFoundException $e) { self::$_router[$client] = Factory::getApplication()::getRouter($client); } } // Make sure that we have our router if (!isset(self::$_router[$client])) { throw new \RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_ROUTER_LOAD', $client), 500); } // Build route. $uri = self::$_router[$client]->build($url); $scheme = ['path', 'query', 'fragment']; /* * Get the secure/unsecure URLs. * * If the first 5 characters of the BASE are 'https', then we are on an ssl connection over * https and need to set our secure URL to the current request URL, if not, and the scheme is * 'http', then we need to do a quick string manipulation to switch schemes. */ if ($tls === self::TLS_FORCE) { $uri->setScheme('https'); } elseif ($tls === self::TLS_DISABLE) { $uri->setScheme('http'); } // Set scheme if requested or if ($absolute || $tls > 0) { static $scheme_host_port; if (!\is_array($scheme_host_port)) { $uri2 = Uri::getInstance(); $scheme_host_port = [$uri2->getScheme(), $uri2->getHost(), $uri2->getPort()]; } if (is_null($uri->getScheme())) { $uri->setScheme($scheme_host_port[0]); } $uri->setHost($scheme_host_port[1]); $uri->setPort($scheme_host_port[2]); $scheme = array_merge($scheme, ['host', 'port', 'scheme']); } $url = $uri->toString($scheme); // Replace spaces. $url = preg_replace('/\s/u', '%20', $url); if ($xhtml) { $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); } return $url; } } PK1A#]A�[]]src/Router/ApiRouter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Router\Exception\RouteNotFoundException; use Joomla\CMS\Uri\Uri; use Joomla\Router\Route; use Joomla\Router\Router; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! API Router class * * @since 4.0.0 */ class ApiRouter extends Router { /** * The application object * * @var CMSApplicationInterface * @since 4.0.0 */ protected $app; /** * Constructor. * * @param CMSApplicationInterface $app The application object * @param array $maps An optional array of route maps * * @since 1.0 */ public function __construct(CMSApplicationInterface $app, array $maps = []) { $this->app = $app; parent::__construct($maps); } /** * Creates routes map for CRUD * * @param string $baseName The base name of the component. * @param string $controller The name of the controller that contains CRUD functions. * @param array $defaults An array of default values that are used when the URL is matched. * @param bool $publicGets Allow the public to make GET requests. * * @return void * * @since 4.0.0 */ public function createCRUDRoutes($baseName, $controller, $defaults = [], $publicGets = false) { $getDefaults = array_merge(['public' => $publicGets], $defaults); $routes = [ new Route(['GET'], $baseName, $controller . '.displayList', [], $getDefaults), new Route(['GET'], $baseName . '/:id', $controller . '.displayItem', ['id' => '(\d+)'], $getDefaults), new Route(['POST'], $baseName, $controller . '.add', [], $defaults), new Route(['PATCH'], $baseName . '/:id', $controller . '.edit', ['id' => '(\d+)'], $defaults), new Route(['DELETE'], $baseName . '/:id', $controller . '.delete', ['id' => '(\d+)'], $defaults), ]; $this->addRoutes($routes); } /** * Parse the given route and return the name of a controller mapped to the given route. * * @param string $method Request method to match. One of GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE or PATCH * * @return array An array containing the controller and the matched variables. * * @since 4.0.0 * @throws \InvalidArgumentException */ public function parseApiRoute($method = 'GET') { $method = strtoupper($method); $validMethods = ["GET", "POST", "PUT", "DELETE", "HEAD", "TRACE", "PATCH"]; if (!\in_array($method, $validMethods)) { throw new \InvalidArgumentException(sprintf('%s is not a valid HTTP method.', $method)); } // Get the path from the route and remove and leading or trailing slash. $routePath = $this->getRoutePath(); $query = Uri::getInstance()->getQuery(true); // Remove the public key as it is only supported coming from the route definition if (array_key_exists('public', $query)) { unset($query['public']); } // Iterate through all of the known routes looking for a match. foreach ($this->routes as $route) { if (\in_array($method, $route->getMethods())) { if (preg_match($route->getRegex(), ltrim($routePath, '/'), $matches)) { // If we have gotten this far then we have a positive match. $vars = $route->getDefaults(); foreach ($route->getRouteVariables() as $i => $var) { $vars[$var] = $matches[$i + 1]; } $controller = preg_split("/[.]+/", $route->getController()); /** @deprecated 4.3 will be removed in 5.0 * Query parameters will not be merged into route variables from 5.0 */ $vars = array_merge($vars, $query); return [ 'controller' => $controller[0], 'task' => $controller[1], 'vars' => $vars, ]; } } } throw new RouteNotFoundException(sprintf('Unable to handle request for route `%s`.', $routePath)); } /** * Get the path from the route and remove and leading or trailing slash. * * @return string * * @since 4.0.0 */ public function getRoutePath() { // Get the path from the route and remove and leading or trailing slash. $uri = Uri::getInstance(); $path = urldecode($uri->getPath()); /** * In some environments (e.g. CLI we can't form a valid base URL). In this case we catch the exception thrown * by URI and set an empty base URI for further work. * @todo: This should probably be handled better */ try { $baseUri = Uri::base(true); } catch (\RuntimeException $e) { $baseUri = ''; } // Remove the base URI path. $path = substr_replace($path, '', 0, \strlen($baseUri)); // Transform the route $path = $this->removeIndexPhpFromPath($path); return $path; } /** * Removes the index.php from the route's path. * * @param string $path The path * * @return string * * @since 4.0.0 */ private function removeIndexPhpFromPath(string $path): string { // Normalize the path $path = ltrim($path, '/'); // We can only remove index.php if it's present in the beginning of the route if (strpos($path, 'index.php') !== 0) { return $path; } // Edge case: the route is index.php without a trailing slash. Bad idea but we can still map it to a null route. if ($path === 'index.php') { return ''; } // Remove the "index.php/" part of the route and return the result. return substr($path, 10); } /** * Extract routes matching current route from all known routes. * * @return \Joomla\Router\Route[] * * @since 4.0.0 */ public function getMatchingRoutes() { $routePath = $this->getRoutePath(); // Extract routes matching $routePath from all known routes. return array_filter( $this->routes, function ($route) use ($routePath) { return preg_match($route->getRegex(), ltrim($routePath, '/')) === 1; } ); } } PK1A#]r����"src/Router/AdministratorRouter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to create and parse routes * * @since 1.5 */ class AdministratorRouter extends Router { /** * Function to convert a route to an internal URI. * * @param Uri &$uri The uri. * @param bool $setVars Set the parsed data in the internal * storage for current-request-URLs * * @return array * * @since 1.5 */ public function parse(&$uri, $setVars = false) { return []; } /** * Function to convert an internal URI to a route * * @param string $url The internal URL * * @return Uri The absolute search engine friendly URL * * @since 1.5 */ public function build($url) { // Create the URI object $uri = parent::build($url); // Get the path data $route = $uri->getPath(); // Add basepath to the uri $uri->setPath(Uri::root(true) . '/' . basename(JPATH_ADMINISTRATOR) . '/' . $route); return $uri; } } PK1A#]Z��> /src/Router/Exception/RouteNotFoundException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an error for a missing route * * @since 3.8.0 */ class RouteNotFoundException extends \InvalidArgumentException { /** * Constructor * * @param string $message The Exception message to throw. * @param integer $code The Exception code. * @param \Exception $previous The previous exception used for the exception chaining. * * @since 3.8.0 */ public function __construct($message = '', $code = 404, \Exception $previous = null) { if (empty($message)) { $message = 'URL was not found'; } parent::__construct($message, $code, $previous); } } PK1A#]�`����#src/Router/SiteRouterAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a Site Router Aware Class. * * @since 4.2.0 */ trait SiteRouterAwareTrait { /** * @var SiteRouter * @since 4.2.0 */ private $router; /** * Get the site router. * * @return SiteRouter * * @since 4.2.0 * * @throws \UnexpectedValueException May be thrown if the router has not been set. */ public function getSiteRouter(): SiteRouter { if ($this->router) { return $this->router; } throw new \UnexpectedValueException('SiteRouter not set in ' . __CLASS__); } /** * Set the router to use. * * @param SiteRouter $router The router to use. * * @return void * * @since 4.2.0 */ public function setSiteRouter(SiteRouter $router): void { $this->router = $router; } } PK1A#]e˄��'src/Router/SiteRouterAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for site router aware classes. * * @since 4.2.0 */ interface SiteRouterAwareInterface { /** * Set the router to use. * * @param SiteRouter $router The router to use. * * @return void * * @since 4.2.0 */ public function setSiteRouter(SiteRouter $router): void; } PK1A#]~�P�GGsrc/Router/SiteRouter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Component\Router\RouterInterface; use Joomla\CMS\Component\Router\RouterLegacy; use Joomla\CMS\Component\Router\RouterServiceInterface; use Joomla\CMS\Factory; use Joomla\CMS\Menu\AbstractMenu; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to create and parse routes for the site application * * @since 1.5 */ class SiteRouter extends Router { /** * Component-router objects * * @var array * * @since 3.3 */ protected $componentRouters = []; /** * @var CMSApplication * * @since 3.4 */ protected $app; /** * Current Menu-Object * * @var AbstractMenu * * @since 3.4 */ protected $menu; /** * Class constructor * * @param CMSApplication $app Application Object * @param AbstractMenu $menu Menu object * * @since 3.4 */ public function __construct(CMSApplication $app = null, AbstractMenu $menu = null) { $this->app = $app ?: Factory::getContainer()->get(SiteApplication::class); $this->menu = $menu ?: $this->app->getMenu(); // Add core rules if ((int) $this->app->get('force_ssl') === 2) { $this->attachParseRule([$this, 'parseCheckSSL'], self::PROCESS_BEFORE); } $this->attachParseRule([$this, 'parseInit'], self::PROCESS_BEFORE); $this->attachBuildRule([$this, 'buildInit'], self::PROCESS_BEFORE); $this->attachBuildRule([$this, 'buildComponentPreprocess'], self::PROCESS_BEFORE); if ($this->app->get('sef', 1)) { if ($this->app->get('sef_suffix')) { $this->attachParseRule([$this, 'parseFormat'], self::PROCESS_BEFORE); $this->attachBuildRule([$this, 'buildFormat'], self::PROCESS_AFTER); } $this->attachParseRule([$this, 'parseSefRoute'], self::PROCESS_DURING); $this->attachBuildRule([$this, 'buildSefRoute'], self::PROCESS_DURING); $this->attachParseRule([$this, 'parsePaginationData'], self::PROCESS_AFTER); $this->attachBuildRule([$this, 'buildPaginationData'], self::PROCESS_AFTER); if ($this->app->get('sef_rewrite')) { $this->attachBuildRule([$this, 'buildRewrite'], self::PROCESS_AFTER); } } $this->attachParseRule([$this, 'parseRawRoute'], self::PROCESS_DURING); $this->attachBuildRule([$this, 'buildBase'], self::PROCESS_AFTER); } /** * Force to SSL * * @param Router &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parseCheckSSL(&$router, &$uri) { if (strtolower($uri->getScheme()) !== 'https') { // Forward to https $uri->setScheme('https'); $this->app->redirect((string) $uri, 301); } } /** * Do some initial cleanup before parsing the URL * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parseInit(&$router, &$uri) { // Get the path // Decode URL to convert percent-encoding to unicode so that strings match when routing. $path = urldecode($uri->getPath()); /** * In some environments (e.g. CLI we can't form a valid base URL). In this case we catch the exception thrown * by URI and set an empty base URI for further work. * @todo: This should probably be handled better */ try { $baseUri = Uri::base(true); } catch (\RuntimeException $e) { $baseUri = ''; } // Remove the base URI path. $path = substr_replace($path, '', 0, \strlen($baseUri)); // Check to see if a request to a specific entry point has been made. if (preg_match("#.*?\.php#u", $path, $matches)) { // Get the current entry point path relative to the site path. $scriptPath = realpath($_SERVER['SCRIPT_FILENAME'] ?: str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED'])); $relativeScriptPath = str_replace('\\', '/', str_replace(JPATH_SITE, '', $scriptPath)); // If a php file has been found in the request path, check to see if it is a valid file. // Also verify that it represents the same file from the server variable for entry script. if (is_file(JPATH_SITE . $matches[0]) && ($matches[0] === $relativeScriptPath)) { // Remove the entry point segments from the request path for proper routing. $path = str_replace($matches[0], '', $path); } } // Set the route $uri->setPath(trim($path, '/')); } /** * Parse the format of the request * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parseFormat(&$router, &$uri) { $route = $uri->getPath(); // Identify format if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $suffix = pathinfo($route, PATHINFO_EXTENSION)) { $uri->setVar('format', $suffix); $route = str_replace('.' . $suffix, '', $route); $uri->setPath($route); } } /** * Convert a sef route to an internal URI * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parseSefRoute(&$router, &$uri) { $route = $uri->getPath(); // If the URL is empty, we handle this in the non-SEF parse URL if (empty($route)) { return; } // Parse the application route $segments = explode('/', $route); if (\count($segments) > 1 && $segments[0] === 'component') { $uri->setVar('option', 'com_' . $segments[1]); $uri->setVar('Itemid', null); $route = implode('/', \array_slice($segments, 2)); } else { // Get menu items. $items = $this->menu->getItems(['parent_id', 'access'], [1, null]); $lang_tag = $this->app->getLanguage()->getTag(); $found = null; foreach ($segments as $segment) { $matched = false; foreach ($items as $item) { if ( $item->alias == $segment && (!$this->app->getLanguageFilter() || ($item->language === '*' || $item->language === $lang_tag)) ) { $found = $item; $matched = true; $items = $item->getChildren(); break; } } if (!$matched) { break; } } // Menu links are not valid URLs. Find the first parent that isn't a menulink if ($found && $found->type === 'menulink') { while ($found->hasParent() && $found->type === 'menulink') { $found = $found->getParent(); } if ($found->type === 'menulink') { $found = null; } } if (!$found) { $found = $this->menu->getDefault($lang_tag); } else { $route = trim(substr($route, \strlen($found->route)), '/'); } if ($found) { if ($found->type === 'alias') { $newItem = $this->menu->getItem($found->getParams()->get('aliasoptions')); if ($newItem) { $found->query = array_merge($found->query, $newItem->query); $found->component = $newItem->component; } } $uri->setVar('Itemid', $found->id); $uri->setVar('option', $found->component); } } // Set the active menu item if ($uri->getVar('Itemid')) { $this->menu->setActive($uri->getVar('Itemid')); } // Parse the component route if (!empty($route) && $uri->getVar('option')) { $segments = explode('/', $route); if (\count($segments)) { // Handle component route $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $uri->getVar('option')); $crouter = $this->getComponentRouter($component); $uri->setQuery(array_merge($uri->getQuery(true), $crouter->parse($segments))); } $route = implode('/', $segments); } $uri->setPath($route); } /** * Convert a raw route to an internal URI * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parseRawRoute(&$router, &$uri) { if ($uri->getVar('Itemid')) { $item = $this->menu->getItem($uri->getVar('Itemid')); } else { $item = $this->menu->getDefault($this->app->getLanguage()->getTag()); } if ($item && $item->type === 'alias') { $newItem = $this->menu->getItem($item->getParams()->get('aliasoptions')); if ($newItem) { $item->query = array_merge($item->query, $newItem->query); $item->component = $newItem->component; } } if (\is_object($item)) { // Set the active menu item $this->menu->setActive($item->id); $uri->setVar('Itemid', $item->id); $uri->setQuery(array_merge($item->query, $uri->getQuery(true))); } } /** * Convert limits for pagination * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function parsePaginationData(&$router, &$uri) { // Process the pagination support $start = $uri->getVar('start'); if ($start !== null) { $uri->setVar('limitstart', $uri->getVar('start')); $uri->delVar('start'); } } /** * Do some initial processing for building a URL * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildInit(&$router, &$uri) { $itemid = $uri->getVar('Itemid'); // If no Itemid and option given, merge in the current requests data if (!$itemid && !$uri->getVar('option')) { $uri->setQuery(array_merge($this->getVars(), $uri->getQuery(true))); } // If Itemid is given, but no option, set the option from the menu item if ($itemid && !$uri->getVar('option')) { if ($item = $this->menu->getItem($itemid)) { $uri->setVar('option', $item->component); } } } /** * Run the component preprocess method * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildComponentPreprocess(&$router, &$uri) { // Get the query data $query = $uri->getQuery(true); if (!isset($query['option'])) { return; } $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']); $crouter = $this->getComponentRouter($component); $query = $crouter->preprocess($query); // Make sure any menu vars are used if no others are specified if ( isset($query['Itemid']) && (\count($query) === 2 || (\count($query) === 3 && isset($query['lang']))) ) { // Get the active menu item $item = $this->menu->getItem($query['Itemid']); if ($item !== null) { $query = array_merge($item->query, $query); } } $uri->setQuery($query); } /** * Build the SEF route * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildSefRoute(&$router, &$uri) { // Get the query data $query = $uri->getQuery(true); if (!isset($query['option'])) { return; } // Get Menu Item $item = empty($query['Itemid']) ? null : $this->menu->getItem($query['Itemid']); // Build the component route $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']); $crouter = $this->getComponentRouter($component); $parts = $crouter->build($query); $tmp = trim(implode('/', $parts)); // Build the application route if ($item !== null && $query['option'] === $item->component) { if (!$item->home) { $tmp = $tmp ? $item->route . '/' . $tmp : $item->route; } unset($query['Itemid']); } else { $tmp = 'component/' . substr($query['option'], 4) . '/' . $tmp; } // Get the route if ($tmp) { $uri->setPath($uri->getPath() . '/' . $tmp); } // Unset unneeded query information unset($query['option']); // Set query again in the URI $uri->setQuery($query); } /** * Convert limits for pagination * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildPaginationData(&$router, &$uri) { $limitstart = $uri->getVar('limitstart'); if ($limitstart !== null && $limitstart !== '') { $uri->setVar('start', (int) $limitstart); } $uri->delVar('limitstart'); } /** * Build the format of the request * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildFormat(&$router, &$uri) { $route = $uri->getPath(); // Identify format if (!(substr($route, -9) === 'index.php' || substr($route, -1) === '/') && $format = $uri->getVar('format', 'html')) { $route .= '.' . $format; $uri->setPath($route); $uri->delVar('format'); } } /** * Create a uri based on a full or partial URL string * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildRewrite(&$router, &$uri) { // Get the path data $route = $uri->getPath(); // Transform the route if ($route === 'index.php') { $route = ''; } else { $route = str_replace('index.php/', '', $route); } $uri->setPath($route); } /** * Add the basepath to the URI * * @param SiteRouter &$router Router object * @param Uri &$uri URI object to process * * @return void * * @since 4.0.0 */ public function buildBase(&$router, &$uri) { // Add frontend basepath to the uri $uri->setPath(Uri::root(true) . '/' . $uri->getPath()); } /** * Get component router * * @param string $component Name of the component including com_ prefix * * @return RouterInterface Component router * * @since 3.3 */ public function getComponentRouter($component) { if (!isset($this->componentRouters[$component])) { $componentInstance = $this->app->bootComponent($component); if ($componentInstance instanceof RouterServiceInterface) { $this->componentRouters[$component] = $componentInstance->createRouter($this->app, $this->menu); } if (!isset($this->componentRouters[$component])) { $this->componentRouters[$component] = new RouterLegacy(ucfirst(substr($component, 4))); } } return $this->componentRouters[$component]; } /** * Set a router for a component * * @param string $component Component name with com_ prefix * @param object $router Component router * * @return boolean True if the router was accepted, false if not * * @since 3.3 */ public function setComponentRouter($component, $router) { $reflection = new \ReflectionClass($router); if (\in_array('Joomla\\CMS\\Component\\Router\\RouterInterface', $reflection->getInterfaceNames())) { $this->componentRouters[$component] = $router; return true; } else { return false; } } } PK1A#]�y 11src/Router/Router.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Router; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Exception\RouteNotFoundException; use Joomla\CMS\Uri\Uri; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to create and parse routes * * @since 1.5 */ class Router { /** * Mask for the before process stage * * @var string * @since 3.4 */ public const PROCESS_BEFORE = 'preprocess'; /** * Mask for the during process stage * * @var string * @since 3.4 */ public const PROCESS_DURING = ''; /** * Mask for the after process stage * * @var string * @since 3.4 */ public const PROCESS_AFTER = 'postprocess'; /** * An array of variables * * @var array * @since 1.5 */ protected $vars = []; /** * An array of rules * * @var array * @since 1.5 */ protected $rules = [ 'buildpreprocess' => [], 'build' => [], 'buildpostprocess' => [], 'parsepreprocess' => [], 'parse' => [], 'parsepostprocess' => [], ]; /** * Caching of processed URIs * * @var array * @since 3.3 */ protected $cache = []; /** * Router instances container. * * @var Router[] * @since 1.7 */ protected static $instances = []; /** * Returns the global Router object, only creating it if it * doesn't already exist. * * @param string $client The name of the client * @param array $options An associative array of options * * @return Router A Router object. * * @since 1.5 * * @throws \RuntimeException * * @deprecated 4.0 will be removed in 6.0 * Inject the router or load it from the dependency injection container * Example: Factory::getContainer()->get(SiteRouter::class); */ public static function getInstance($client, $options = []) { if (empty(self::$instances[$client])) { // Create a Router object $classname = 'JRouter' . ucfirst($client); if (!class_exists($classname)) { throw new \RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_ROUTER_LOAD', $client), 500); } // Check for a possible service from the container otherwise manually instantiate the class if (Factory::getContainer()->has($classname)) { self::$instances[$client] = Factory::getContainer()->get($classname); } else { self::$instances[$client] = new $classname(); } } return self::$instances[$client]; } /** * Function to convert a route to an internal URI * * @param Uri &$uri The uri. * @param bool $setVars Set the parsed data in the internal * storage for current-request-URLs * * @return array * * @since 1.5 * @throws \Exception */ public function parse(&$uri, $setVars = false) { // Do the preprocess stage of the URL parse process $this->processParseRules($uri, self::PROCESS_BEFORE); // Do the main stage of the URL parse process $this->processParseRules($uri); // Do the postprocess stage of the URL parse process $this->processParseRules($uri, self::PROCESS_AFTER); // Check if all parts of the URL have been parsed. // Otherwise we have an invalid URL if (\strlen($uri->getPath()) > 0) { throw new RouteNotFoundException(Text::_('JERROR_PAGE_NOT_FOUND')); } if ($setVars) { $this->setVars($uri->getQuery(true)); return $this->getVars(); } return $uri->getQuery(true); } /** * Function to convert an internal URI to a route * * @param string|array|Uri $url The internal URL or an associative array * * @return Uri The absolute search engine friendly URL object * * @since 1.5 */ public function build($url) { $key = md5(serialize($url)); if (isset($this->cache[$key])) { return clone $this->cache[$key]; } if ($url instanceof Uri) { $uri = $url; } else { $uri = $this->createUri($url); } // Do the preprocess stage of the URL build process $this->processBuildRules($uri, self::PROCESS_BEFORE); // Do the main stage of the URL build process $this->processBuildRules($uri); // Do the postprocess stage of the URL build process $this->processBuildRules($uri, self::PROCESS_AFTER); $this->cache[$key] = clone $uri; return $uri; } /** * Set a router variable, creating it if it doesn't exist * * @param string $key The name of the variable * @param mixed $value The value of the variable * @param boolean $create If True, the variable will be created if it doesn't exist yet * * @return void * * @since 1.5 */ public function setVar($key, $value, $create = true) { if ($create || \array_key_exists($key, $this->vars)) { $this->vars[$key] = $value; } } /** * Set the router variable array * * @param array $vars An associative array with variables * @param boolean $merge If True, the array will be merged instead of overwritten * * @return void * * @since 1.5 */ public function setVars($vars = [], $merge = true) { if ($merge) { $this->vars = array_merge($this->vars, $vars); } else { $this->vars = $vars; } } /** * Get a router variable * * @param string $key The name of the variable * * @return mixed Value of the variable * * @since 1.5 */ public function getVar($key) { $result = null; if (isset($this->vars[$key])) { $result = $this->vars[$key]; } return $result; } /** * Get the router variable array * * @return array An associative array of router variables * * @since 1.5 */ public function getVars() { return $this->vars; } /** * Attach a build rule * * @param callable $callback The function to be called * @param string $stage The stage of the build process that * this should be added to. Possible values: * 'preprocess', '' for the main build process, * 'postprocess' * * @return void * * @since 1.5 */ public function attachBuildRule(callable $callback, $stage = self::PROCESS_DURING) { if (!\array_key_exists('build' . $stage, $this->rules)) { throw new \InvalidArgumentException(sprintf('The %s stage is not registered. (%s)', $stage, __METHOD__)); } $this->rules['build' . $stage][] = $callback; } /** * Attach a parse rule * * @param callable $callback The function to be called. * @param string $stage The stage of the parse process that * this should be added to. Possible values: * 'preprocess', '' for the main parse process, * 'postprocess' * * @return void * * @since 1.5 */ public function attachParseRule(callable $callback, $stage = self::PROCESS_DURING) { if (!\array_key_exists('parse' . $stage, $this->rules)) { throw new \InvalidArgumentException(sprintf('The %s stage is not registered. (%s)', $stage, __METHOD__)); } $this->rules['parse' . $stage][] = $callback; } /** * Remove a rule * * @param string $type Type of rule to remove (parse or build) * @param callable $rule The rule to be removed. * @param string $stage The stage of the parse process that * this should be added to. Possible values: * 'preprocess', '' for the main parse process, * 'postprocess' * * @return boolean Was a rule removed? * * @since 4.0.0 * @throws \InvalidArgumentException */ public function detachRule($type, $rule, $stage = self::PROCESS_DURING) { if (!\in_array($type, ['parse', 'build'])) { throw new \InvalidArgumentException(sprintf('The %s type is not supported. (%s)', $type, __METHOD__)); } if (!\array_key_exists($type . $stage, $this->rules)) { throw new \InvalidArgumentException(sprintf('The %s stage is not registered. (%s)', $stage, __METHOD__)); } foreach ($this->rules[$type . $stage] as $id => $r) { if ($r == $rule) { unset($this->rules[$type . $stage][$id]); return true; } } return false; } /** * Get all currently attached rules * * @return array All currently attached rules in an array * * @since 4.0.0 */ public function getRules() { return $this->rules; } /** * Process the parsed router variables based on custom defined rules * * @param \Joomla\CMS\Uri\Uri &$uri The URI to parse * @param string $stage The stage that should be processed. * Possible values: 'preprocess', 'postprocess' * and '' for the main parse stage * * @return void * * @since 3.2 */ protected function processParseRules(&$uri, $stage = self::PROCESS_DURING) { if (!\array_key_exists('parse' . $stage, $this->rules)) { throw new \InvalidArgumentException(sprintf('The %s stage is not registered. (%s)', $stage, __METHOD__)); } foreach ($this->rules['parse' . $stage] as $rule) { $rule($this, $uri); } } /** * Process the build uri query data based on custom defined rules * * @param \Joomla\CMS\Uri\Uri &$uri The URI * @param string $stage The stage that should be processed. * Possible values: 'preprocess', 'postprocess' * and '' for the main build stage * * @return void * * @since 3.2 */ protected function processBuildRules(&$uri, $stage = self::PROCESS_DURING) { if (!\array_key_exists('build' . $stage, $this->rules)) { throw new \InvalidArgumentException(sprintf('The %s stage is not registered. (%s)', $stage, __METHOD__)); } foreach ($this->rules['build' . $stage] as $rule) { \call_user_func_array($rule, [&$this, &$uri]); } } /** * Create a uri based on a full or partial URL string * * @param string $url The URI or an associative array * * @return Uri * * @since 3.2 */ protected function createUri($url) { if (!\is_array($url) && substr($url, 0, 1) !== '&') { return new Uri($url); } $uri = new Uri('index.php'); if (\is_string($url)) { $vars = []; if (strpos($url, '&') !== false) { $url = str_replace('&', '&', $url); } parse_str($url, $vars); } else { $vars = $url; } $vars = array_merge($this->getVars(), $vars); foreach ($vars as $key => $var) { if ($var == '') { unset($vars[$key]); } } $uri->setQuery($vars); return $uri; } } PK1A#]Z$Xc c !src/Cache/Storage/ApcuStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; use Joomla\CMS\Cache\CacheStorage; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * APCu cache storage handler * * @link https://www.php.net/manual/en/ref.apcu.php * @since 3.5 */ class ApcuStorage extends CacheStorage { /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group) { return apcu_exists($this->_getCacheId($id, $group)); } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 3.5 */ public function get($id, $group, $checkTime = true) { return apcu_fetch($this->_getCacheId($id, $group)); } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 3.5 */ public function getAll() { $allinfo = apcu_cache_info(); $keys = $allinfo['cache_list']; $secret = $this->_hash; $data = []; foreach ($keys as $key) { if (isset($key['info'])) { // The internal key name changed with APCu 4.0.7 from key to info $name = $key['info']; } elseif (isset($key['entry_name'])) { // Some APCu modules changed the internal key name from key to entry_name $name = $key['entry_name']; } else { // A fall back for the old internal key name $name = $key['key']; } $namearr = explode('-', $name); if ($namearr !== false && $namearr[0] == $secret && $namearr[1] === 'cache') { $group = $namearr[2]; if (!isset($data[$group])) { $item = new CacheStorageHelper($group); } else { $item = $data[$group]; } $item->updateSize($key['mem_size']); $data[$group] = $item; } } return $data; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 3.5 */ public function store($id, $group, $data) { return apcu_store($this->_getCacheId($id, $group), $data, $this->_lifetime); } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.5 */ public function remove($id, $group) { $cache_id = $this->_getCacheId($id, $group); // The apcu_delete function returns false if the ID does not exist if (apcu_exists($cache_id)) { return apcu_delete($cache_id); } return true; } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 3.5 */ public function clean($group, $mode = null) { $allinfo = apcu_cache_info(); $keys = $allinfo['cache_list']; $secret = $this->_hash; foreach ($keys as $key) { if (isset($key['info'])) { // The internal key name changed with APCu 4.0.7 from key to info $internalKey = $key['info']; } elseif (isset($key['entry_name'])) { // Some APCu modules changed the internal key name from key to entry_name $internalKey = $key['entry_name']; } else { // A fall back for the old internal key name $internalKey = $key['key']; } if (strpos($internalKey, $secret . '-cache-' . $group . '-') === 0 xor $mode !== 'group') { apcu_delete($internalKey); } } return true; } /** * Garbage collect expired cache data * * @return boolean * * @since 3.5 */ public function gc() { $allinfo = apcu_cache_info(); $keys = $allinfo['cache_list']; $secret = $this->_hash; foreach ($keys as $key) { if (isset($key['info'])) { // The internal key name changed with APCu 4.0.7 from key to info $internalKey = $key['info']; } elseif (isset($key['entry_name'])) { // Some APCu modules changed the internal key name from key to entry_name $internalKey = $key['entry_name']; } else { // A fall back for the old internal key name $internalKey = $key['key']; } if (strpos($internalKey, $secret . '-cache-')) { apcu_fetch($internalKey); } } return true; } /** * Test to see if the storage handler is available. * * @return boolean * * @since 3.5 */ public static function isSupported() { $supported = \extension_loaded('apcu') && ini_get('apc.enabled'); // If on the CLI interface, the `apc.enable_cli` option must also be enabled if ($supported && PHP_SAPI === 'cli') { $supported = ini_get('apc.enable_cli'); } return (bool) $supported; } /** * Lock cached item * * @param string $id The cache data ID * @param string $group The cache data group * @param integer $locktime Cached item max lock time * * @return mixed Boolean false if locking failed or an object containing properties lock and locklooped * * @since 3.5 */ public function lock($id, $group, $locktime) { $returning = new \stdClass(); $returning->locklooped = false; $looptime = $locktime * 10; $cache_id = $this->_getCacheId($id, $group) . '_lock'; $data_lock = apcu_add($cache_id, 1, $locktime); if ($data_lock === false) { $lock_counter = 0; // Loop until you find that the lock has been released. // That implies that data get from other thread has finished while ($data_lock === false) { if ($lock_counter > $looptime) { $returning->locked = false; $returning->locklooped = true; break; } usleep(100); $data_lock = apcu_add($cache_id, 1, $locktime); $lock_counter++; } } $returning->locked = $data_lock; return $returning; } /** * Unlock cached item * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.5 */ public function unlock($id, $group = null) { $cache_id = $this->_getCacheId($id, $group) . '_lock'; // The apcu_delete function returns false if the ID does not exist if (apcu_exists($cache_id)) { return apcu_delete($cache_id); } return true; } } PK1A#]�3��S�S!src/Cache/Storage/FileStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; use Joomla\CMS\Cache\CacheStorage; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\Filesystem\File; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * File cache storage handler * * @since 1.7.0 * @note For performance reasons this class does not use the Filesystem package's API */ class FileStorage extends CacheStorage { /** * Root path * * @var string * @since 1.7.0 */ protected $_root; /** * Locked resources * * @var array * @since 3.7.0 * */ protected $_locked_files = []; /** * Constructor * * @param array $options Optional parameters * * @since 1.7.0 */ public function __construct($options = []) { parent::__construct($options); $this->_root = $options['cachebase']; // Workaround for php 5.3 $locked_files = &$this->_locked_files; // Remove empty locked files at script shutdown. $clearAtShutdown = function () use (&$locked_files) { foreach ($locked_files as $path => $handle) { if (\is_resource($handle)) { @flock($handle, LOCK_UN); @fclose($handle); } // Delete only the existing file if it is empty. if (@filesize($path) === 0) { File::invalidateFileCache($path); @unlink($path); } unset($locked_files[$path]); } }; register_shutdown_function($clearAtShutdown); } /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group) { return $this->_checkExpire($id, $group); } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 */ public function get($id, $group, $checkTime = true) { $path = $this->_getFilePath($id, $group); $close = false; if ($checkTime == false || ($checkTime == true && $this->_checkExpire($id, $group) === true)) { if (file_exists($path)) { if (isset($this->_locked_files[$path])) { $_fileopen = $this->_locked_files[$path]; } else { $_fileopen = @fopen($path, 'rb'); // There is no lock, we have to close file after store data $close = true; } if ($_fileopen) { // On Windows system we can not use file_get_contents on the file locked by yourself $data = stream_get_contents($_fileopen); if ($close) { @fclose($_fileopen); } if ($data !== false) { // Remove the initial die() statement return str_replace('<?php die("Access Denied"); ?>#x#', '', $data); } } } } return false; } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 */ public function getAll() { $path = $this->_root; $folders = $this->_folders($path); $data = []; foreach ($folders as $folder) { $files = $this->_filesInFolder($path . '/' . $folder); $item = new CacheStorageHelper($folder); foreach ($files as $file) { $item->updateSize(filesize($path . '/' . $folder . '/' . $file)); } $data[$folder] = $item; } return $data; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 1.7.0 */ public function store($id, $group, $data) { $path = $this->_getFilePath($id, $group); $close = false; // Prepend a die string $data = '<?php die("Access Denied"); ?>#x#' . $data; if (isset($this->_locked_files[$path])) { $_fileopen = $this->_locked_files[$path]; // Because lock method uses flag c+b we have to truncate it manually @ftruncate($_fileopen, 0); } else { $_fileopen = @fopen($path, 'wb'); // There is no lock, we have to close file after store data $close = true; } if ($_fileopen) { $length = \strlen($data); $result = @fwrite($_fileopen, $data, $length); if ($close) { @fclose($_fileopen); } return $result === $length; } return false; } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function remove($id, $group) { $path = $this->_getFilePath($id, $group); File::invalidateFileCache($path); if (!@unlink($path)) { return false; } return true; } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 1.7.0 */ public function clean($group, $mode = null) { $return = true; $folder = $group; if (trim($folder) == '') { $mode = 'notgroup'; } switch ($mode) { case 'notgroup': $folders = $this->_folders($this->_root); for ($i = 0, $n = \count($folders); $i < $n; $i++) { if ($folders[$i] != $folder) { $return |= $this->_deleteFolder($this->_root . '/' . $folders[$i]); } } break; case 'group': default: if (is_dir($this->_root . '/' . $folder)) { $return = $this->_deleteFolder($this->_root . '/' . $folder); } break; } return (bool) $return; } /** * Garbage collect expired cache data * * @return boolean * * @since 1.7.0 */ public function gc() { $result = true; // Files older than lifeTime get deleted from cache $files = $this->_filesInFolder($this->_root, '', true, true, ['.svn', 'CVS', '.DS_Store', '__MACOSX', 'index.html']); foreach ($files as $file) { $time = @filemtime($file); if (($time + $this->_lifetime) < $this->_now || empty($time)) { File::invalidateFileCache($file); $result |= @unlink($file); } } return (bool) $result; } /** * Lock cached item * * @param string $id The cache data ID * @param string $group The cache data group * @param integer $locktime Cached item max lock time * * @return mixed Boolean false if locking failed or an object containing properties lock and locklooped * * @since 1.7.0 */ public function lock($id, $group, $locktime) { $returning = new \stdClass(); $returning->locklooped = false; $looptime = $locktime * 10; $path = $this->_getFilePath($id, $group); $_fileopen = @fopen($path, 'c+b'); if (!$_fileopen) { $returning->locked = false; return $returning; } $data_lock = (bool) @flock($_fileopen, LOCK_EX | LOCK_NB); if ($data_lock === false) { $lock_counter = 0; // Loop until you find that the lock has been released. // That implies that data get from other thread has finished while ($data_lock === false) { if ($lock_counter > $looptime) { break; } usleep(100); $data_lock = (bool) @flock($_fileopen, LOCK_EX | LOCK_NB); $lock_counter++; } $returning->locklooped = true; } if ($data_lock === true) { // Remember resource, flock release lock if you unset/close resource $this->_locked_files[$path] = $_fileopen; } $returning->locked = $data_lock; return $returning; } /** * Unlock cached item * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function unlock($id, $group = null) { $path = $this->_getFilePath($id, $group); if (isset($this->_locked_files[$path])) { $ret = (bool) @flock($this->_locked_files[$path], LOCK_UN); @fclose($this->_locked_files[$path]); unset($this->_locked_files[$path]); return $ret; } return true; } /** * Check if a cache object has expired * * Using @ error suppressor here because between if we did a file_exists() and then filemsize() there will * be a little time space when another process can delete the file and then you get PHP Warning * * @param string $id Cache ID to check * @param string $group The cache data group * * @return boolean True if the cache ID is valid * * @since 1.7.0 */ protected function _checkExpire($id, $group) { $path = $this->_getFilePath($id, $group); // Check prune period if (file_exists($path)) { $time = @filemtime($path); if (($time + $this->_lifetime) < $this->_now || empty($time)) { File::invalidateFileCache($path); @unlink($path); return false; } // If, right now, the file does not exist then return false if (@filesize($path) == 0) { return false; } return true; } return false; } /** * Get a cache file path from an ID/group pair * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean|string The path to the data object or boolean false if the cache directory does not exist * * @since 1.7.0 */ protected function _getFilePath($id, $group) { $name = $this->_getCacheId($id, $group); $dir = $this->_root . '/' . $group; // If the folder doesn't exist try to create it if (!is_dir($dir)) { // Make sure the index file is there $indexFile = $dir . '/index.html'; @mkdir($dir) && file_put_contents($indexFile, '<!DOCTYPE html><title></title>'); } // Make sure the folder exists if (!is_dir($dir)) { return false; } return $dir . '/' . $name . '.php'; } /** * Quickly delete a folder of files * * @param string $path The path to the folder to delete. * * @return boolean * * @since 1.7.0 */ protected function _deleteFolder($path) { // Sanity check if (!$path || !is_dir($path) || empty($this->_root)) { // Bad programmer! Bad, bad programmer! Log::add(__METHOD__ . ' ' . Text::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY'), Log::WARNING, 'jerror'); return false; } $path = $this->_cleanPath($path); // Check to make sure path is inside cache folder, we do not want to delete Joomla root! $pos = strpos($path, $this->_cleanPath($this->_root)); if ($pos === false || $pos > 0) { Log::add(__METHOD__ . ' ' . Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Remove all the files in folder if they exist; disable all filtering $files = $this->_filesInFolder($path, '.', false, true, [], []); if (!empty($files) && !\is_array($files)) { File::invalidateFileCache($files); if (@unlink($files) !== true) { return false; } } elseif (!empty($files) && \is_array($files)) { foreach ($files as $file) { $file = $this->_cleanPath($file); // In case of restricted permissions we delete it one way or the other as long as the owner is either the webserver or the ftp File::invalidateFileCache($file); if (@unlink($file) !== true) { Log::add(__METHOD__ . ' ' . Text::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', basename($file)), Log::WARNING, 'jerror'); return false; } } } // Remove sub-folders of folder; disable all filtering $folders = $this->_folders($path, '.', false, true, [], []); foreach ($folders as $folder) { if (is_link($folder)) { // Don't descend into linked directories, just delete the link. if (@unlink($folder) !== true) { return false; } } elseif ($this->_deleteFolder($folder) !== true) { return false; } } // In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp if (@rmdir($path)) { return true; } Log::add(Text::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE', $path), Log::WARNING, 'jerror'); return false; } /** * Function to strip additional / or \ in a path name * * @param string $path The path to clean * @param string $ds Directory separator (optional) * * @return string The cleaned path * * @since 1.7.0 */ protected function _cleanPath($path, $ds = DIRECTORY_SEPARATOR) { $path = trim($path); if (empty($path)) { return $this->_root; } // Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR $path = preg_replace('#[/\\\\]+#', $ds, $path); return $path; } /** * Utility function to quickly read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $fullpath True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludefilter Array of folder names to exclude * * @return array Files in the given folder. * * @since 1.7.0 */ protected function _filesInFolder( $path, $filter = '.', $recurse = false, $fullpath = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], $excludefilter = ['^\..*', '.*~'] ) { $arr = []; // Check to make sure the path valid and clean $path = $this->_cleanPath($path); // Is the path a folder? if (!is_dir($path)) { Log::add(__METHOD__ . ' ' . Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Read the source directory. if (!($handle = @opendir($path))) { return $arr; } if (\count($excludefilter)) { $excludefilter = '/(' . implode('|', $excludefilter) . ')/'; } else { $excludefilter = ''; } while (($file = readdir($handle)) !== false) { if (($file != '.') && ($file != '..') && (!\in_array($file, $exclude)) && (!$excludefilter || !preg_match($excludefilter, $file))) { $dir = $path . '/' . $file; $isDir = is_dir($dir); if ($isDir) { if ($recurse) { if (\is_int($recurse)) { $arr2 = $this->_filesInFolder($dir, $filter, $recurse - 1, $fullpath); } else { $arr2 = $this->_filesInFolder($dir, $filter, $recurse, $fullpath); } $arr = array_merge($arr, $arr2); } } else { if (preg_match("/$filter/", $file)) { if ($fullpath) { $arr[] = $path . '/' . $file; } else { $arr[] = $file; } } } } } closedir($handle); return $arr; } /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $fullpath True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludefilter Array with regular expressions matching folders which should not be shown in the result. * * @return array Folders in the given folder. * * @since 1.7.0 */ protected function _folders( $path, $filter = '.', $recurse = false, $fullpath = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], $excludefilter = ['^\..*'] ) { $arr = []; // Check to make sure the path valid and clean $path = $this->_cleanPath($path); // Is the path a folder? if (!is_dir($path)) { Log::add(__METHOD__ . ' ' . Text::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', __METHOD__, $path), Log::WARNING, 'jerror'); return false; } // Read the source directory if (!($handle = @opendir($path))) { return $arr; } if (\count($excludefilter)) { $excludefilter_string = '/(' . implode('|', $excludefilter) . ')/'; } else { $excludefilter_string = ''; } while (($file = readdir($handle)) !== false) { if ( ($file != '.') && ($file != '..') && (!\in_array($file, $exclude)) && (empty($excludefilter_string) || !preg_match($excludefilter_string, $file)) ) { $dir = $path . '/' . $file; $isDir = is_dir($dir); if ($isDir) { // Removes filtered directories if (preg_match("/$filter/", $file)) { if ($fullpath) { $arr[] = $dir; } else { $arr[] = $file; } } if ($recurse) { if (\is_int($recurse)) { $arr2 = $this->_folders($dir, $filter, $recurse - 1, $fullpath, $exclude, $excludefilter); } else { $arr2 = $this->_folders($dir, $filter, $recurse, $fullpath, $exclude, $excludefilter); } $arr = array_merge($arr, $arr2); } } } } closedir($handle); return $arr; } } PK1A#]� �Gk k "src/Cache/Storage/RedisStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; use Joomla\CMS\Cache\CacheStorage; use Joomla\CMS\Cache\Exception\CacheConnectingException; use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Redis cache storage handler for PECL * * @since 3.4 */ class RedisStorage extends CacheStorage { /** * Redis connection object * * @var \Redis * @since 3.4 */ protected static $_redis = null; /** * Persistent session flag * * @var boolean * @since 3.4 */ protected $_persistent = false; /** * Constructor * * @param array $options Optional parameters. * * @since 3.4 */ public function __construct($options = []) { parent::__construct($options); if (static::$_redis === null) { $this->getConnection(); } } /** * Create the Redis connection * * @return \Redis|boolean Redis connection object on success, boolean on failure * * @since 3.4 * @note As of 4.0 this method will throw a JCacheExceptionConnecting object on connection failure */ protected function getConnection() { if (static::isSupported() == false) { return false; } $app = Factory::getApplication(); $this->_persistent = $app->get('redis_persist', true); $server = [ 'host' => $app->get('redis_server_host', 'localhost'), 'port' => $app->get('redis_server_port', 6379), 'auth' => $app->get('redis_server_auth', null), 'db' => (int) $app->get('redis_server_db', null), ]; // If you are trying to connect to a socket file, ignore the supplied port if ($server['host'][0] === '/') { $server['port'] = 0; } static::$_redis = new \Redis(); try { if ($this->_persistent) { $connection = static::$_redis->pconnect($server['host'], $server['port']); } else { $connection = static::$_redis->connect($server['host'], $server['port']); } } catch (\RedisException $e) { $connection = false; Log::add($e->getMessage(), Log::DEBUG); } if ($connection == false) { static::$_redis = null; throw new CacheConnectingException('Redis connection failed', 500); } try { $auth = $server['auth'] ? static::$_redis->auth($server['auth']) : true; } catch (\RedisException $e) { $auth = false; Log::add($e->getMessage(), Log::DEBUG); } if ($auth === false) { static::$_redis = null; throw new CacheConnectingException('Redis authentication failed', 500); } $select = static::$_redis->select($server['db']); if ($select == false) { static::$_redis = null; throw new CacheConnectingException('Redis failed to select database', 500); } try { static::$_redis->ping(); } catch (\RedisException $e) { static::$_redis = null; throw new CacheConnectingException('Redis ping failed', 500); } return static::$_redis; } /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group) { if (static::isConnected() == false) { return false; } // Redis exists returns integer values lets convert that to boolean see: https://redis.io/commands/exists return (bool) static::$_redis->exists($this->_getCacheId($id, $group)); } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 3.4 */ public function get($id, $group, $checkTime = true) { if (static::isConnected() == false) { return false; } return static::$_redis->get($this->_getCacheId($id, $group)); } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 3.4 */ public function getAll() { if (static::isConnected() == false) { return false; } $allKeys = static::$_redis->keys('*'); $data = []; $secret = $this->_hash; if (!empty($allKeys)) { foreach ($allKeys as $key) { $namearr = explode('-', $key); if ($namearr !== false && $namearr[0] == $secret && $namearr[1] === 'cache') { $group = $namearr[2]; if (!isset($data[$group])) { $item = new CacheStorageHelper($group); } else { $item = $data[$group]; } $item->updateSize(\strlen($key) * 8); $data[$group] = $item; } } } return $data; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 3.4 */ public function store($id, $group, $data) { if (static::isConnected() == false) { return false; } static::$_redis->setex($this->_getCacheId($id, $group), $this->_lifetime, $data); return true; } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.4 */ public function remove($id, $group) { if (static::isConnected() == false) { return false; } return (bool) static::$_redis->del($this->_getCacheId($id, $group)); } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 3.4 */ public function clean($group, $mode = null) { if (static::isConnected() == false) { return false; } $allKeys = static::$_redis->keys('*'); if ($allKeys === false) { $allKeys = []; } $secret = $this->_hash; foreach ($allKeys as $key) { if (strpos($key, $secret . '-cache-' . $group . '-') === 0 && $mode === 'group') { static::$_redis->del($key); } if (strpos($key, $secret . '-cache-' . $group . '-') !== 0 && $mode !== 'group') { static::$_redis->del($key); } } return true; } /** * Test to see if the storage handler is available. * * @return boolean * * @since 3.4 */ public static function isSupported() { return class_exists('\\Redis'); } /** * Test to see if the Redis connection is available. * * @return boolean * * @since 3.4 */ public static function isConnected() { return static::$_redis instanceof \Redis; } } PK1A#]����%src/Cache/Storage/WincacheStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; use Joomla\CMS\Cache\CacheStorage; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * WinCache cache storage handler * * @link https://www.php.net/manual/en/book.wincache.php * @since 1.7.0 * @deprecated 4.3 will be removed in 6.0 * WinCache is abandoned and not supported from PHP 8 onwards * Will be removed without replacement */ class WincacheStorage extends CacheStorage { /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function contains($id, $group) { return wincache_ucache_exists($this->_getCacheId($id, $group)); } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function get($id, $group, $checkTime = true) { return wincache_ucache_get($this->_getCacheId($id, $group)); } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function getAll() { $allinfo = wincache_ucache_info(); $keys = $allinfo['ucache_entries']; $secret = $this->_hash; $data = []; foreach ($keys as $key) { $name = $key['key_name']; $namearr = explode('-', $name); if ($namearr !== false && $namearr[0] == $secret && $namearr[1] === 'cache') { $group = $namearr[2]; if (!isset($data[$group])) { $item = new CacheStorageHelper($group); } else { $item = $data[$group]; } if (isset($key['value_size'])) { $item->updateSize($key['value_size']); } else { // Dummy, WINCACHE version is too low. $item->updateSize(1); } $data[$group] = $item; } } return $data; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function store($id, $group, $data) { return wincache_ucache_set($this->_getCacheId($id, $group), $data, $this->_lifetime); } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function remove($id, $group) { return wincache_ucache_delete($this->_getCacheId($id, $group)); } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function clean($group, $mode = null) { $allinfo = wincache_ucache_info(); $keys = $allinfo['ucache_entries']; $secret = $this->_hash; foreach ($keys as $key) { if (strpos($key['key_name'], $secret . '-cache-' . $group . '-') === 0 xor $mode !== 'group') { wincache_ucache_delete($key['key_name']); } } return true; } /** * Garbage collect expired cache data * * @return boolean * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public function gc() { $allinfo = wincache_ucache_info(); $keys = $allinfo['ucache_entries']; $secret = $this->_hash; foreach ($keys as $key) { if (strpos($key['key_name'], $secret . '-cache-')) { wincache_ucache_get($key['key_name']); } } return true; } /** * Test to see if the storage handler is available. * * @return boolean * * @since 3.0.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ public static function isSupported() { return \extension_loaded('wincache') && \function_exists('wincache_ucache_get') && !strcmp(ini_get('wincache.ucenabled'), '1'); } } PK1A#] 'S�\.\.&src/Cache/Storage/MemcachedStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheStorage; use Joomla\CMS\Cache\Exception\CacheConnectingException; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Memcached cache storage handler * * @link https://www.php.net/manual/en/book.memcached.php * @since 3.0.0 */ class MemcachedStorage extends CacheStorage { /** * Memcached connection object * * @var \Memcached * @since 3.0.0 */ protected static $_db = null; /** * Payload compression level * * @var integer * @since 3.0.0 */ protected $_compress = 0; /** * Constructor * * @param array $options Optional parameters. * * @since 3.0.0 */ public function __construct($options = []) { parent::__construct($options); $this->_compress = Factory::getApplication()->get('memcached_compress', false) ? \Memcached::OPT_COMPRESSION : 0; if (static::$_db === null) { $this->getConnection(); } } /** * Create the Memcached connection * * @return void * * @since 3.0.0 * @throws \RuntimeException */ protected function getConnection() { if (!static::isSupported()) { throw new \RuntimeException('Memcached Extension is not available'); } $app = Factory::getApplication(); $host = $app->get('memcached_server_host', 'localhost'); $port = $app->get('memcached_server_port', 11211); // Create the memcached connection if ($app->get('memcached_persist', true)) { static::$_db = new \Memcached($this->_hash); $servers = static::$_db->getServerList(); if ($servers && ($servers[0]['host'] != $host || $servers[0]['port'] != $port)) { static::$_db->resetServerList(); $servers = []; } if (!$servers) { static::$_db->addServer($host, $port); } } else { static::$_db = new \Memcached(); static::$_db->addServer($host, $port); } static::$_db->setOption(\Memcached::OPT_COMPRESSION, $this->_compress); $stats = static::$_db->getStats(); $result = !empty($stats["$host:$port"]) && $stats["$host:$port"]['pid'] > 0; if (!$result) { // Null out the connection to inform the constructor it will need to attempt to connect if this class is instantiated again static::$_db = null; throw new CacheConnectingException('Could not connect to memcached server'); } } /** * Get a cache_id string from an id/group pair * * @param string $id The cache data id * @param string $group The cache data group * * @return string The cache_id string * * @since 1.7.0 */ protected function _getCacheId($id, $group) { $prefix = Cache::getPlatformPrefix(); $length = \strlen($prefix); $cache_id = parent::_getCacheId($id, $group); if ($length) { // Memcached use suffix instead of prefix $cache_id = substr($cache_id, $length) . strrev($prefix); } return $cache_id; } /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group) { static::$_db->get($this->_getCacheId($id, $group)); return static::$_db->getResultCode() !== \Memcached::RES_NOTFOUND; } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 3.0.0 */ public function get($id, $group, $checkTime = true) { return static::$_db->get($this->_getCacheId($id, $group)); } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 3.0.0 */ public function getAll() { $keys = static::$_db->get($this->_hash . '-index'); $secret = $this->_hash; $data = []; if (\is_array($keys)) { foreach ($keys as $key) { if (empty($key)) { continue; } $namearr = explode('-', $key->name); if ($namearr !== false && $namearr[0] == $secret && $namearr[1] === 'cache') { $group = $namearr[2]; if (!isset($data[$group])) { $item = new CacheStorageHelper($group); } else { $item = $data[$group]; } $item->updateSize($key->size); $data[$group] = $item; } } } return $data; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 3.0.0 */ public function store($id, $group, $data) { $cache_id = $this->_getCacheId($id, $group); if (!$this->lockindex()) { return false; } $index = static::$_db->get($this->_hash . '-index'); if (!\is_array($index)) { $index = []; } $tmparr = new \stdClass(); $tmparr->name = $cache_id; $tmparr->size = \strlen($data); $index[] = $tmparr; static::$_db->set($this->_hash . '-index', $index, 0); $this->unlockindex(); static::$_db->set($cache_id, $data, $this->_lifetime); return true; } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.0.0 */ public function remove($id, $group) { $cache_id = $this->_getCacheId($id, $group); if (!$this->lockindex()) { return false; } $index = static::$_db->get($this->_hash . '-index'); if (\is_array($index)) { foreach ($index as $key => $value) { if ($value->name == $cache_id) { unset($index[$key]); static::$_db->set($this->_hash . '-index', $index, 0); break; } } } $this->unlockindex(); return static::$_db->delete($cache_id); } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 3.0.0 */ public function clean($group, $mode = null) { if (!$this->lockindex()) { return false; } $index = static::$_db->get($this->_hash . '-index'); if (\is_array($index)) { $prefix = $this->_hash . '-cache-' . $group . '-'; foreach ($index as $key => $value) { if (strpos($value->name, $prefix) === 0 xor $mode !== 'group') { static::$_db->delete($value->name); unset($index[$key]); } } static::$_db->set($this->_hash . '-index', $index, 0); } $this->unlockindex(); return true; } /** * Flush all existing items in storage. * * @return boolean * * @since 3.6.3 */ public function flush() { if (!$this->lockindex()) { return false; } return static::$_db->flush(); } /** * Test to see if the storage handler is available. * * @return boolean * * @since 3.0.0 */ public static function isSupported() { /* * GAE and HHVM have both had instances where Memcached the class was defined but no extension was loaded. * If the class is there, we can assume support. */ return class_exists('Memcached'); } /** * Lock cached item * * @param string $id The cache data ID * @param string $group The cache data group * @param integer $locktime Cached item max lock time * * @return mixed Boolean false if locking failed or an object containing properties lock and locklooped * * @since 3.0.0 */ public function lock($id, $group, $locktime) { $returning = new \stdClass(); $returning->locklooped = false; $looptime = $locktime * 10; $cache_id = $this->_getCacheId($id, $group); $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime); if ($data_lock === false) { $lock_counter = 0; // Loop until you find that the lock has been released. // That implies that data get from other thread has finished. while ($data_lock === false) { if ($lock_counter > $looptime) { break; } usleep(100); $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime); $lock_counter++; } $returning->locklooped = true; } $returning->locked = $data_lock; return $returning; } /** * Unlock cached item * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.0.0 */ public function unlock($id, $group = null) { $cache_id = $this->_getCacheId($id, $group) . '_lock'; return static::$_db->delete($cache_id); } /** * Lock cache index * * @return boolean * * @since 3.0.0 */ protected function lockindex() { $looptime = 300; $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30); if ($data_lock === false) { $lock_counter = 0; // Loop until you find that the lock has been released. that implies that data get from other thread has finished while ($data_lock === false) { if ($lock_counter > $looptime) { return false; } usleep(100); $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30); $lock_counter++; } } return true; } /** * Unlock cache index * * @return boolean * * @since 3.0.0 */ protected function unlockindex() { return static::$_db->delete($this->_hash . '-index_lock'); } } PK1A#]��+��(src/Cache/Storage/CacheStorageHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Storage; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Cache storage helper functions. * * @since 1.7.0 */ class CacheStorageHelper { /** * Cache data group * * @var string * @since 1.7.0 */ public $group = ''; /** * Cached item size * * @var string * @since 1.7.0 */ public $size = 0; /** * Counter * * @var integer * @since 1.7.0 */ public $count = 0; /** * Constructor * * @param string $group The cache data group * * @since 1.7.0 */ public function __construct($group) { $this->group = $group; } /** * Increase cache items count. * * @param string $size Cached item size * * @return void * * @since 1.7.0 */ public function updateSize($size) { $this->size += $size; $this->count++; } } PK1A#]�T�%��src/Cache/CacheController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Public cache handler * * @since 1.7.0 * @note As of 4.0 this class will be abstract */ class CacheController { /** * Cache object * * @var Cache * @since 1.7.0 */ public $cache; /** * Array of options * * @var array * @since 1.7.0 */ public $options; /** * Constructor * * @param array $options Array of options * * @since 1.7.0 */ public function __construct($options) { $this->cache = new Cache($options); $this->options = &$this->cache->_options; // Overwrite default options with given options foreach ($options as $option => $value) { if (isset($options[$option])) { $this->options[$option] = $options[$option]; } } } /** * Magic method to proxy CacheController method calls to Cache * * @param string $name Name of the function * @param array $arguments Array of arguments for the function * * @return mixed * * @since 1.7.0 */ public function __call($name, $arguments) { return \call_user_func_array([$this->cache, $name], $arguments); } /** * Returns a reference to a cache adapter object, always creating it * * @param string $type The cache object type to instantiate; default is output. * @param array $options Array of options * * @return CacheController * * @since 1.7.0 * @throws \RuntimeException * * @deprecated 4.2 will be removed in 6.0 * Use the cache controller factory instead * Example: Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options); */ public static function getInstance($type = 'output', $options = []) { @trigger_error( sprintf( '%s() is deprecated. The cache controller should be fetched from the factory.', __METHOD__ ), E_USER_DEPRECATED ); try { return Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options); } catch (\RuntimeException $e) { $type = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $type)); $class = 'JCacheController' . ucfirst($type); if (!class_exists($class)) { // Search for the class file in the Cache include paths. $path = Path::find(self::addIncludePath(), strtolower($type) . '.php'); if ($path !== false) { \JLoader::register($class, $path); } // The class should now be loaded if (!class_exists($class)) { throw new \RuntimeException('Unable to load Cache Controller: ' . $type, 500); } // Only trigger a deprecation notice if the file and class are found @trigger_error( 'Support for including cache controllers using path lookup is deprecated and will be removed in 5.0.' . ' Use a custom cache controller factory instead.', E_USER_DEPRECATED ); } return new $class($options); } } /** * Add a directory where Cache should search for controllers. You may either pass a string or an array of directories. * * @param array|string $path A path to search. * * @return array An array with directory elements * * @since 1.7.0 * * @deprecated 4.2 will be removed in 6.0 * Use the cache controller factory instead * Example: Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options); */ public static function addIncludePath($path = '') { static $paths; if (!isset($paths)) { $paths = []; } if (!empty($path) && !\in_array($path, $paths)) { // Only trigger a deprecation notice when adding a lookup path @trigger_error( 'Support for including cache controllers using path lookup is deprecated and will be removed in 5.0.' . ' Use a custom cache controller factory instead.', E_USER_DEPRECATED ); array_unshift($paths, Path::clean($path)); } return $paths; } } PK1A#]�FTm2$2$src/Cache/CacheStorage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; use Joomla\CMS\Cache\Exception\UnsupportedCacheException; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract cache storage handler * * @since 1.7.0 * @note As of 4.0 this class will be abstract */ class CacheStorage { /** * The raw object name * * @var string * @since 1.7.0 */ protected $rawname; /** * Time that the cache storage handler was instantiated * * @var integer * @since 1.7.0 */ public $_now; /** * Cache lifetime * * @var integer * @since 1.7.0 */ public $_lifetime; /** * Flag if locking is enabled * * @var boolean * @since 1.7.0 */ public $_locking; /** * Language code * * @var string * @since 1.7.0 */ public $_language; /** * Application name * * @var string * @since 1.7.0 */ public $_application; /** * Object hash * * @var string * @since 1.7.0 */ public $_hash; /** * The threshold * * @var integer * @since 4.3.0 */ public $_threshold; /** * Constructor * * @param array $options Optional parameters * * @since 1.7.0 */ public function __construct($options = []) { $app = Factory::getApplication(); $this->_hash = md5($app->get('secret', '')); $this->_application = $options['application'] ?? md5(JPATH_CONFIGURATION); $this->_language = $options['language'] ?? 'en-GB'; $this->_locking = $options['locking'] ?? true; $this->_lifetime = ($options['lifetime'] ?? $app->get('cachetime')) * 60; $this->_now = $options['now'] ?? time(); // Set time threshold value. If the lifetime is not set, default to 60 (0 is BAD) // _threshold is now available ONLY as a legacy (it's deprecated). It's no longer used in the core. if (empty($this->_lifetime)) { $this->_threshold = $this->_now - 60; $this->_lifetime = 60; } else { $this->_threshold = $this->_now - $this->_lifetime; } } /** * Returns a cache storage handler object. * * @param string $handler The cache storage handler to instantiate * @param array $options Array of handler options * * @return CacheStorage * * @since 1.7.0 * @throws \UnexpectedValueException * @throws UnsupportedCacheException */ public static function getInstance($handler = null, $options = []) { static $now = null; if (!isset($handler)) { $handler = Factory::getApplication()->get('cache_handler'); if (empty($handler)) { throw new \UnexpectedValueException('Cache Storage Handler not set.'); } } if (\is_null($now)) { $now = time(); } $options['now'] = $now; // We can't cache this since options may change... $handler = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $handler)); /** @var CacheStorage $class */ $class = __NAMESPACE__ . '\\Storage\\' . ucfirst($handler) . 'Storage'; if (!class_exists($class)) { $class = 'JCacheStorage' . ucfirst($handler); } if (!class_exists($class)) { // Search for the class file in the JCacheStorage include paths. $path = Path::find(self::addIncludePath(), strtolower($handler) . '.php'); if ($path === false) { throw new UnsupportedCacheException(sprintf('Unable to load Cache Storage: %s', $handler)); } \JLoader::register($class, $path); // The class should now be loaded if (!class_exists($class)) { throw new UnsupportedCacheException(sprintf('Unable to load Cache Storage: %s', $handler)); } } // Validate the cache storage is supported on this platform if (!$class::isSupported()) { throw new UnsupportedCacheException(sprintf('The %s Cache Storage is not supported on this platform.', $handler)); } return new $class($options); } /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group) { return false; } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $checkTime True to verify cache time expiration threshold * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 */ public function get($id, $group, $checkTime = true) { return false; } /** * Get all cached data * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 */ public function getAll() { return false; } /** * Store the data to cache by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * @param string $data The data to store in cache * * @return boolean * * @since 1.7.0 */ public function store($id, $group, $data) { return true; } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function remove($id, $group) { return true; } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean * * @since 1.7.0 */ public function clean($group, $mode = null) { return true; } /** * Flush all existing items in storage. * * @return boolean * * @since 3.6.3 */ public function flush() { return true; } /** * Garbage collect expired cache data * * @return boolean * * @since 1.7.0 */ public function gc() { return true; } /** * Test to see if the storage handler is available. * * @return boolean * * @since 3.0.0 */ public static function isSupported() { return true; } /** * Lock cached item * * @param string $id The cache data ID * @param string $group The cache data group * @param integer $locktime Cached item max lock time * * @return mixed Boolean false if locking failed or an object containing properties lock and locklooped * * @since 1.7.0 */ public function lock($id, $group, $locktime) { return false; } /** * Unlock cached item * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function unlock($id, $group = null) { return false; } /** * Get a cache ID string from an ID/group pair * * @param string $id The cache data ID * @param string $group The cache data group * * @return string * * @since 1.7.0 */ protected function _getCacheId($id, $group) { $name = md5($this->_application . '-' . $id . '-' . $this->_language); $this->rawname = $this->_hash . '-' . $name; return Cache::getPlatformPrefix() . $this->_hash . '-cache-' . $group . '-' . $name; } /** * Add a directory where CacheStorage should search for handlers. You may either pass a string or an array of directories. * * @param array|string $path A path to search. * * @return array An array with directory elements * * @since 1.7.0 */ public static function addIncludePath($path = '') { static $paths; if (!isset($paths)) { $paths = []; } if (!empty($path) && !\in_array($path, $paths)) { array_unshift($paths, Path::clean($path)); } return $paths; } } PK1A#]��Y++0src/Cache/Exception/CacheConnectingException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an error connecting to the cache storage engine * * @since 3.6.3 */ class CacheConnectingException extends \RuntimeException implements CacheExceptionInterface { } PK1A#] PQ_��/src/Cache/Exception/CacheExceptionInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception interface defining a cache storage error * * @since 3.7.0 */ interface CacheExceptionInterface { } PK1A#]��h� 1src/Cache/Exception/UnsupportedCacheException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an unsupported cache storage object * * @since 3.6.3 */ class UnsupportedCacheException extends \RuntimeException implements CacheExceptionInterface { } PK1A#]T�Axx-src/Cache/CacheControllerFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create CacheController objects * * @since 4.0.0 */ interface CacheControllerFactoryInterface { /** * Method to get an instance of a cache controller. * * @param string $type The cache object type to instantiate * @param array $options Array of options * * @return CacheController * * @since 4.0.0 * @throws \RuntimeException */ public function createCacheController($type = 'output', $options = []): CacheController; } PK1A#]�Ve3��.src/Cache/CacheControllerFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a CacheControllerFactoryInterface Aware Class. * * @since 4.2.0 */ trait CacheControllerFactoryAwareTrait { /** * CacheControllerFactoryInterface * * @var CacheControllerFactoryInterface * * @since 4.2.0 */ private $cacheControllerFactory; /** * Get the CacheControllerFactoryInterface. * * @return CacheControllerFactoryInterface * * @since 4.2.0 */ protected function getCacheControllerFactory(): CacheControllerFactoryInterface { if ($this->cacheControllerFactory) { return $this->cacheControllerFactory; } @trigger_error( sprintf('A cache controller is needed in %s. An UnexpectedValueException will be thrown in 5.0.', __CLASS__), E_USER_DEPRECATED ); return Factory::getContainer()->get(CacheControllerFactoryInterface::class); } /** * Set the cache controller factory to use. * * @param CacheControllerFactoryInterface $cacheControllerFactory The cache controller factory to use. * * @return void * * @since 4.2.0 */ public function setCacheControllerFactory(CacheControllerFactoryInterface $cacheControllerFactory = null): void { $this->cacheControllerFactory = $cacheControllerFactory; } } PK1A#]qS�Y::2src/Cache/CacheControllerFactoryAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a cache controller factory. * * @since 4.2.0 */ interface CacheControllerFactoryAwareInterface { /** * Set the cache controller factory to use. * * @param CacheControllerFactoryInterface $factory The cache controller factory to use. * * @return void * * @since 4.2.0 */ public function setCacheControllerFactory(CacheControllerFactoryInterface $factory): void; } PK1A#]���aSaSsrc/Cache/Cache.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; use Joomla\Application\Web\WebClient; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Session\Session; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Cache base object * * @since 1.7.0 */ class Cache { /** * Storage handler * * @var CacheStorage[] * @since 1.7.0 */ public static $_handler = []; /** * Cache options * * @var array * @since 1.7.0 */ public $_options; /** * Constructor * * @param array $options Cache options * * @since 1.7.0 */ public function __construct($options) { $app = Factory::getApplication(); $this->_options = [ 'cachebase' => $app->get('cache_path', JPATH_CACHE), 'lifetime' => (int) $app->get('cachetime'), 'language' => $app->get('language', 'en-GB'), 'storage' => $app->get('cache_handler', ''), 'defaultgroup' => 'default', 'locking' => true, 'locktime' => 15, 'checkTime' => true, 'caching' => ($app->get('caching') >= 1), ]; // Overwrite default options with given options foreach ($options as $option => $value) { if (isset($options[$option]) && $options[$option] !== '') { $this->_options[$option] = $options[$option]; } } if (empty($this->_options['storage'])) { $this->setCaching(false); } } /** * Returns a reference to a cache adapter object, always creating it * * @param string $type The cache object type to instantiate * @param array $options The array of options * * @return CacheController * * @since 1.7.0 * * @deprecated 4.2 will be removed in 6.0 * Use the cache controller factory instead * Example: Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options); */ public static function getInstance($type = 'output', $options = []) { @trigger_error( sprintf( '%s() is deprecated. The cache controller should be fetched from the factory.', __METHOD__ ), E_USER_DEPRECATED ); return Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options); } /** * Get the storage handlers * * @return array * * @since 1.7.0 */ public static function getStores() { $handlers = []; // Get an iterator and loop through the driver classes. $iterator = new \DirectoryIterator(__DIR__ . '/Storage'); /** @type $file \DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() !== 'php' || $fileName === 'CacheStorageHelper.php') { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', __NAMESPACE__ . '\\Storage\\' . ucfirst(trim($fileName))); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } // Sweet! Our class exists, so now we just need to know if it passes its test method. if ($class::isSupported()) { // Connector names should not have file extensions. $handler = str_ireplace('Storage.php', '', $fileName); $handler = str_ireplace('.php', '', $handler); $handlers[] = strtolower($handler); } } return $handlers; } /** * Set caching enabled state * * @param boolean $enabled True to enable caching * * @return void * * @since 1.7.0 */ public function setCaching($enabled) { $this->_options['caching'] = $enabled; } /** * Get caching state * * @return boolean * * @since 1.7.0 */ public function getCaching() { return $this->_options['caching']; } /** * Set cache lifetime * * @param integer $lt Cache lifetime in minutes * * @return void * * @since 1.7.0 */ public function setLifeTime($lt) { $this->_options['lifetime'] = $lt; } /** * Check if the cache contains data stored by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 3.7.0 */ public function contains($id, $group = null) { if (!$this->getCaching()) { return false; } // Get the default group $group = $group ?: $this->_options['defaultgroup']; return $this->_getStorage()->contains($id, $group); } /** * Get cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return mixed Boolean false on failure or a cached data object * * @since 1.7.0 */ public function get($id, $group = null) { if (!$this->getCaching()) { return false; } // Get the default group $group = $group ?: $this->_options['defaultgroup']; return $this->_getStorage()->get($id, $group, $this->_options['checkTime']); } /** * Get a list of all cached data * * @return mixed Boolean false on failure or an object with a list of cache groups and data * * @since 1.7.0 */ public function getAll() { if (!$this->getCaching()) { return false; } return $this->_getStorage()->getAll(); } /** * Store the cached data by ID and group * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function store($data, $id, $group = null) { if (!$this->getCaching()) { return false; } // Get the default group $group = $group ?: $this->_options['defaultgroup']; // Get the storage and store the cached data return $this->_getStorage()->store($id, $group, $data); } /** * Remove a cached data entry by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function remove($id, $group = null) { // Get the default group $group = $group ?: $this->_options['defaultgroup']; try { return $this->_getStorage()->remove($id, $group); } catch (CacheExceptionInterface $e) { if (!$this->getCaching()) { return false; } throw $e; } } /** * Clean cache for a group given a mode. * * group mode : cleans all cache in the group * notgroup mode : cleans all cache not in the group * * @param string $group The cache data group * @param string $mode The mode for cleaning cache [group|notgroup] * * @return boolean True on success, false otherwise * * @since 1.7.0 */ public function clean($group = null, $mode = 'group') { // Get the default group $group = $group ?: $this->_options['defaultgroup']; try { return $this->_getStorage()->clean($group, $mode); } catch (CacheExceptionInterface $e) { if (!$this->getCaching()) { return false; } throw $e; } } /** * Garbage collect expired cache data * * @return boolean * * @since 1.7.0 */ public function gc() { try { return $this->_getStorage()->gc(); } catch (CacheExceptionInterface $e) { if (!$this->getCaching()) { return false; } throw $e; } } /** * Set lock flag on cached item * * @param string $id The cache data ID * @param string $group The cache data group * @param string $locktime The default locktime for locking the cache. * * @return \stdClass Object with properties of lock and locklooped * * @since 1.7.0 */ public function lock($id, $group = null, $locktime = null) { $returning = new \stdClass(); $returning->locklooped = false; if (!$this->getCaching()) { $returning->locked = false; return $returning; } // Get the default group $group = $group ?: $this->_options['defaultgroup']; // Get the default locktime $locktime = $locktime ?: $this->_options['locktime']; /* * Allow storage handlers to perform locking on their own * NOTE drivers with lock need also unlock or unlocking will fail because of false $id */ $handler = $this->_getStorage(); if ($this->_options['locking'] == true) { $locked = $handler->lock($id, $group, $locktime); if ($locked !== false) { return $locked; } } // Fallback $curentlifetime = $this->_options['lifetime']; // Set lifetime to locktime for storing in children $this->_options['lifetime'] = $locktime; $looptime = $locktime * 10; $id2 = $id . '_lock'; if ($this->_options['locking'] == true) { $data_lock = $handler->get($id2, $group, $this->_options['checkTime']); } else { $data_lock = false; $returning->locked = false; } if ($data_lock !== false) { $lock_counter = 0; // Loop until you find that the lock has been released. That implies that data get from other thread has finished while ($data_lock !== false) { if ($lock_counter > $looptime) { $returning->locked = false; $returning->locklooped = true; break; } usleep(100); $data_lock = $handler->get($id2, $group, $this->_options['checkTime']); $lock_counter++; } } if ($this->_options['locking'] == true) { $returning->locked = $handler->store($id2, $group, 1); } // Revert lifetime to previous one $this->_options['lifetime'] = $curentlifetime; return $returning; } /** * Unset lock flag on cached item * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 */ public function unlock($id, $group = null) { if (!$this->getCaching()) { return false; } // Get the default group $group = $group ?: $this->_options['defaultgroup']; // Allow handlers to perform unlocking on their own $handler = $this->_getStorage(); $unlocked = $handler->unlock($id, $group); if ($unlocked !== false) { return $unlocked; } // Fallback return $handler->remove($id . '_lock', $group); } /** * Get the cache storage handler * * @return CacheStorage * * @since 1.7.0 */ public function &_getStorage() { $hash = md5(serialize($this->_options)); if (isset(self::$_handler[$hash])) { return self::$_handler[$hash]; } self::$_handler[$hash] = CacheStorage::getInstance($this->_options['storage'], $this->_options); return self::$_handler[$hash]; } /** * Perform workarounds on retrieved cached data * * @param array $data Cached data * @param array $options Array of options * * @return string Body of cached data * * @since 1.7.0 */ public static function getWorkarounds($data, $options = []) { $app = Factory::getApplication(); $document = Factory::getDocument(); $body = null; // Get the document head out of the cache. if ( isset($options['mergehead']) && $options['mergehead'] == 1 && isset($data['head']) && !empty($data['head']) && method_exists($document, 'mergeHeadData') ) { $document->mergeHeadData($data['head']); } elseif (isset($data['head']) && method_exists($document, 'setHeadData')) { $document->setHeadData($data['head']); } // Get the document MIME encoding out of the cache if (isset($data['mime_encoding'])) { $document->setMimeEncoding($data['mime_encoding'], true); } // If the pathway buffer is set in the cache data, get it. if (isset($data['pathway']) && \is_array($data['pathway'])) { // Push the pathway data into the pathway object. $app->getPathway()->setPathway($data['pathway']); } // @todo check if the following is needed, seems like it should be in page cache // If a module buffer is set in the cache data, get it. if (isset($data['module']) && \is_array($data['module'])) { // Iterate through the module positions and push them into the document buffer. foreach ($data['module'] as $name => $contents) { $document->setBuffer($contents, 'module', $name); } } // Set cached headers. if (isset($data['headers']) && $data['headers']) { foreach ($data['headers'] as $header) { $app->setHeader($header['name'], $header['value']); } } // The following code searches for a token in the cached page and replaces it with the proper token. if (isset($data['body'])) { $token = Session::getFormToken(); $search = '#<input type="hidden" name="[0-9a-f]{32}" value="1">#'; $replacement = '<input type="hidden" name="' . $token . '" value="1">'; $data['body'] = preg_replace($search, $replacement, $data['body']); $body = $data['body']; } // Get the document body out of the cache. return $body; } /** * Create workarounds for data to be cached * * @param string $data Cached data * @param array $options Array of options * * @return array Data to be cached * * @since 1.7.0 */ public static function setWorkarounds($data, $options = []) { $loptions = [ 'nopathway' => 0, 'nohead' => 0, 'nomodules' => 0, 'modulemode' => 0, ]; if (isset($options['nopathway'])) { $loptions['nopathway'] = $options['nopathway']; } if (isset($options['nohead'])) { $loptions['nohead'] = $options['nohead']; } if (isset($options['nomodules'])) { $loptions['nomodules'] = $options['nomodules']; } if (isset($options['modulemode'])) { $loptions['modulemode'] = $options['modulemode']; } $app = Factory::getApplication(); $document = Factory::getDocument(); if ($loptions['nomodules'] != 1) { // Get the modules buffer before component execution. $buffer1 = $document->getBuffer(); if (!\is_array($buffer1)) { $buffer1 = []; } // Make sure the module buffer is an array. if (!isset($buffer1['module']) || !\is_array($buffer1['module'])) { $buffer1['module'] = []; } } // View body data $cached = ['body' => $data]; // Document head data if ($loptions['nohead'] != 1 && method_exists($document, 'getHeadData')) { if ($loptions['modulemode'] == 1) { $headNow = $document->getHeadData(); $unset = ['title', 'description', 'link', 'links', 'metaTags']; foreach ($unset as $key) { unset($headNow[$key]); } // Sanitize empty data foreach (\array_keys($headNow) as $key) { if (!isset($headNow[$key]) || $headNow[$key] === []) { unset($headNow[$key]); } } $cached['head'] = $headNow; } else { $cached['head'] = $document->getHeadData(); // Document MIME encoding $cached['mime_encoding'] = $document->getMimeEncoding(); } } // Pathway data if ($app->isClient('site') && $loptions['nopathway'] != 1) { $cached['pathway'] = $data['pathway'] ?? $app->getPathway()->getPathway(); } if ($loptions['nomodules'] != 1) { // @todo Check if the following is needed, seems like it should be in page cache // Get the module buffer after component execution. $buffer2 = $document->getBuffer(); if (!\is_array($buffer2)) { $buffer2 = []; } // Make sure the module buffer is an array. if (!isset($buffer2['module']) || !\is_array($buffer2['module'])) { $buffer2['module'] = []; } // Compare the second module buffer against the first buffer. $cached['module'] = array_diff_assoc($buffer2['module'], $buffer1['module']); } // Headers data if (isset($options['headers']) && $options['headers']) { $cached['headers'] = $app->getHeaders(); } return $cached; } /** * Create a safe ID for cached data from URL parameters * * @return string MD5 encoded cache ID * * @since 1.7.0 */ public static function makeId() { $app = Factory::getApplication(); $registeredurlparams = new \stdClass(); // Get url parameters set by plugins if (!empty($app->registeredurlparams)) { $registeredurlparams = $app->registeredurlparams; } // Platform defaults $defaulturlparams = [ 'format' => 'CMD', 'option' => 'CMD', 'view' => 'CMD', 'layout' => 'CMD', 'tpl' => 'CMD', 'id' => 'STRING', ]; // Use platform defaults if parameter doesn't already exist. foreach ($defaulturlparams as $param => $type) { if (!property_exists($registeredurlparams, $param)) { $registeredurlparams->$param = $type; } } $safeuriaddon = new \stdClass(); foreach ($registeredurlparams as $key => $value) { $safeuriaddon->$key = $app->getInput()->get($key, null, $value); } return md5(serialize($safeuriaddon)); } /** * Set a prefix cache key if device calls for separate caching * * @return string * * @since 3.5 */ public static function getPlatformPrefix() { // No prefix when Global Config is set to no platform specific prefix if (!Factory::getApplication()->get('cache_platformprefix', false)) { return ''; } $webclient = new WebClient(); if ($webclient->mobile) { return 'M-'; } return ''; } /** * Add a directory where Cache should search for handlers. You may either pass a string or an array of directories. * * @param array|string $path A path to search. * * @return array An array with directory elements * * @since 1.7.0 */ public static function addIncludePath($path = '') { static $paths; if (!isset($paths)) { $paths = []; } if (!empty($path) && !\in_array($path, $paths)) { array_unshift($paths, Path::clean($path)); } return $paths; } } PK1A#]���GG$src/Cache/CacheControllerFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating CacheController objects * * @since 4.0.0 */ class CacheControllerFactory implements CacheControllerFactoryInterface { /** * Method to get an instance of a cache controller. * * @param string $type The cache object type to instantiate * @param array $options Array of options * * @return CacheController * * @since 4.0.0 * @throws \RuntimeException */ public function createCacheController($type = 'output', $options = []): CacheController { if (!$type) { $type = 'output'; } $type = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $type)); $class = __NAMESPACE__ . '\\Controller\\' . ucfirst($type) . 'Controller'; // The class should now be loaded if (!class_exists($class)) { throw new \RuntimeException('Unable to load Cache Controller: ' . $type, 500); } return new $class($options); } } PK1A#]����+src/Cache/Controller/CallbackController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; use Joomla\CMS\Document\HtmlDocument; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Cache callback type object * * @since 1.7.0 */ class CallbackController extends CacheController { /** * Executes a cacheable callback if not found in cache else returns cached output and result * * @param callable $callback Callback or string shorthand for a callback * @param array $args Callback arguments * @param mixed $id Cache ID * @param boolean $wrkarounds True to use workarounds * @param array $woptions Workaround options * * @return mixed Result of the callback * * @since 1.7.0 */ public function get($callback, $args = [], $id = false, $wrkarounds = false, $woptions = []) { if (!\is_array($args)) { $referenceArgs = !empty($args) ? [&$args] : []; } else { $referenceArgs = &$args; } // Just execute the callback if caching is disabled. if (empty($this->options['caching'])) { return \call_user_func_array($callback, $referenceArgs); } if (!$id) { // Generate an ID $id = $this->_makeId($callback, $args); } $data = $this->cache->get($id); $locktest = (object) ['locked' => null, 'locklooped' => null]; if ($data === false) { $locktest = $this->cache->lock($id); // If locklooped is true try to get the cached data again; it could exist now. if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id); } } if ($data !== false) { if ($locktest->locked === true) { $this->cache->unlock($id); } $data = unserialize(trim($data)); if ($wrkarounds) { echo Cache::getWorkarounds( $data['output'], ['mergehead' => $woptions['mergehead'] ?? 0] ); } else { echo $data['output']; } return $data['result']; } if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return \call_user_func_array($callback, $referenceArgs); } $coptions = ['modulemode' => 0]; if (isset($woptions['modulemode']) && $woptions['modulemode'] == 1) { /** @var HtmlDocument $document */ $document = Factory::getDocument(); if (method_exists($document, 'getHeadData')) { $coptions['headerbefore'] = $document->getHeadData(); // Reset document head before rendering module. Module will cache only assets added by itself. $document->resetHeadData(); $document->getWebAssetManager()->reset(); $coptions['modulemode'] = 1; } } $coptions['nopathway'] = $woptions['nopathway'] ?? 1; $coptions['nohead'] = $woptions['nohead'] ?? 1; $coptions['nomodules'] = $woptions['nomodules'] ?? 1; ob_start(); ob_implicit_flush(false); $result = \call_user_func_array($callback, $referenceArgs); $output = ob_get_clean(); $data = ['result' => $result]; if ($wrkarounds) { $data['output'] = Cache::setWorkarounds($output, $coptions); } else { $data['output'] = $output; } // Restore document head data and merge module head data. if ($coptions['modulemode'] == 1) { $moduleHeadData = $document->getHeadData(); $document->resetHeadData(); $document->mergeHeadData($coptions['headerbefore']); $document->mergeHeadData($moduleHeadData); } // Store the cache data $this->cache->store(serialize($data), $id); if ($locktest->locked === true) { $this->cache->unlock($id); } echo $output; return $result; } /** * Store data to cache by ID and group * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use wrkarounds * * @return boolean True if cache stored * * @since 4.0.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { $locktest = $this->cache->lock($id, $group); if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } $result = $this->cache->store(serialize($data), $id, $group); if ($locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } /** * Generate a callback cache ID * * @param mixed $callback Callback to cache * @param array $args Arguments to the callback method to cache * * @return string MD5 Hash * * @since 1.7.0 */ protected function _makeId($callback, $args) { if (\is_array($callback) && \is_object($callback[0])) { $vars = get_object_vars($callback[0]); $vars[] = strtolower(\get_class($callback[0])); $callback[0] = $vars; } // A Closure can't be serialized, so to generate the ID we'll need to get its hash if ($callback instanceof \closure) { $hash = spl_object_hash($callback); return md5($hash . serialize([$args])); } return md5(serialize([$callback, $args])); } } PK1A#]6:���'src/Cache/Controller/ViewController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Cache view type object * * @since 1.7.0 */ class ViewController extends CacheController { /** * Get the cached view data * * @param object $view The view object to cache output for * @param string $method The method name of the view method to cache output for * @param mixed $id The cache data ID * @param boolean $wrkarounds True to enable workarounds. * * @return boolean True if the cache is hit (false else) * * @since 1.7.0 */ public function get($view, $method = 'display', $id = false, $wrkarounds = true) { // If an id is not given generate it from the request if (!$id) { $id = $this->_makeId($view, $method); } $data = $this->cache->get($id); $locktest = (object) ['locked' => null, 'locklooped' => null]; if ($data === false) { $locktest = $this->cache->lock($id); /* * If the loop is completed and returned true it means the lock has been set. * If looped is true try to get the cached data again; it could exist now. */ if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id); } // False means that locking is either turned off or maxtime has been exceeded. Execute the view. } if ($data !== false) { if ($locktest->locked === true) { $this->cache->unlock($id); } $data = unserialize(trim($data)); if ($wrkarounds) { echo Cache::getWorkarounds($data); } else { // No workarounds, so all data is stored in one piece echo $data; } return true; } // No hit so we have to execute the view if (!method_exists($view, $method)) { return false; } if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving $view->$method(); return false; } // Capture and echo output ob_start(); ob_implicit_flush(false); $view->$method(); $data = ob_get_clean(); echo $data; /* * For a view we have a special case. We need to cache not only the output from the view, but the state * of the document head after the view has been rendered. This will allow us to properly cache any attached * scripts or stylesheets or links or any other modifications that the view has made to the document object */ if ($wrkarounds) { $data = Cache::setWorkarounds($data); } // Store the cache data $this->cache->store(serialize($data), $id); if ($locktest->locked === true) { $this->cache->unlock($id); } return false; } /** * Store data to cache by ID and group * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use wrkarounds * * @return boolean True if cache stored * * @since 4.0.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { $locktest = $this->cache->lock($id, $group); if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } $result = $this->cache->store(serialize($data), $id, $group); if ($locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } /** * Generate a view cache ID. * * @param object $view The view object to cache output for * @param string $method The method name to cache for the view object * * @return string MD5 Hash * * @since 1.7.0 */ protected function _makeId($view, $method) { return md5(serialize([Cache::makeId(), \get_class($view), $method])); } } PK1A#]���� � )src/Cache/Controller/OutputController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; use Joomla\CMS\Cache\CacheController; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla Cache output type object * * @since 1.7.0 */ class OutputController extends CacheController { /** * Cache data ID * * @var string * @since 1.7.0 */ protected $_id; /** * Cache data group * * @var string * @since 1.7.0 */ protected $_group; /** * Get stored cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return mixed Boolean false on no result, cached object otherwise * * @since 1.7.0 */ public function get($id, $group = null) { $data = $this->cache->get($id, $group); if ($data === false) { $locktest = $this->cache->lock($id, $group); // If locklooped is true try to get the cached data again; it could exist now. if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id, $group); } if ($locktest->locked === true) { $this->cache->unlock($id, $group); } } // Check again because we might get it from second attempt if ($data !== false) { // Trim to fix unserialize errors $data = unserialize(trim($data)); } return $data; } /** * Store data to cache by ID and group * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use wrkarounds * * @return boolean True if cache stored * * @since 1.7.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { $locktest = $this->cache->lock($id, $group); if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } $result = $this->cache->store(serialize($data), $id, $group); if ($locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } } PK1A#]�<0��'src/Cache/Controller/PageController.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Cache page type object * * @since 1.7.0 */ class PageController extends CacheController { /** * ID property for the cache page object. * * @var integer * @since 1.7.0 */ protected $_id; /** * Cache group * * @var string * @since 1.7.0 */ protected $_group; /** * Cache lock test * * @var \stdClass * @since 1.7.0 */ protected $_locktest = null; /** * Get the cached page data * * @param boolean $id The cache data ID * @param string $group The cache data group * * @return mixed Boolean false on no result, cached object otherwise * * @since 1.7.0 */ public function get($id = false, $group = 'page') { // If an id is not given, generate it from the request if (!$id) { $id = $this->_makeId(); } // If the etag matches the page id ... set a no change header and exit : utilize browser cache if (!headers_sent() && isset($_SERVER['HTTP_IF_NONE_MATCH'])) { $etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); if ($etag == $id) { $browserCache = $this->options['browsercache'] ?? false; if ($browserCache) { $this->_noChange(); } } } // We got a cache hit... set the etag header and echo the page data $data = $this->cache->get($id, $group); $this->_locktest = (object) ['locked' => null, 'locklooped' => null]; if ($data === false) { $this->_locktest = $this->cache->lock($id, $group); // If locklooped is true try to get the cached data again; it could exist now. if ($this->_locktest->locked === true && $this->_locktest->locklooped === true) { $data = $this->cache->get($id, $group); } } if ($data !== false) { if ($this->_locktest->locked === true) { $this->cache->unlock($id, $group); } $data = unserialize(trim($data)); $data = Cache::getWorkarounds($data); $this->_setEtag($id); return $data; } // Set ID and group placeholders $this->_id = $id; $this->_group = $group; return false; } /** * Stop the cache buffer and store the cached data * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use workarounds * * @return boolean * * @since 1.7.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { if ($this->_locktest->locked === false && $this->_locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } // Get page data from the application object if (!$data) { $data = Factory::getApplication()->getBody(); // Only attempt to store if page data exists. if (!$data) { return false; } } // Get id and group and reset the placeholders if (!$id) { $id = $this->_id; } if (!$group) { $group = $this->_group; } if ($wrkarounds) { $data = Cache::setWorkarounds( $data, [ 'nopathway' => 1, 'nohead' => 1, 'nomodules' => 1, 'headers' => true, ] ); } $result = $this->cache->store(serialize($data), $id, $group); if ($this->_locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } /** * Generate a page cache id * * @return string MD5 Hash * * @since 1.7.0 * @todo Discuss whether this should be coupled to a data hash or a request hash ... perhaps hashed with a serialized request */ protected function _makeId() { return Cache::makeId(); } /** * There is no change in page data so send an unmodified header and die gracefully * * @return void * * @since 1.7.0 */ protected function _noChange() { $app = Factory::getApplication(); // Send not modified header and exit gracefully $app->setHeader('Status', 304, true); $app->sendHeaders(); $app->close(); } /** * Set the ETag header in the response * * @param string $etag The entity tag (etag) to set * * @return void * * @since 1.7.0 */ protected function _setEtag($etag) { Factory::getApplication()->setHeader('ETag', '"' . $etag . '"', true); } } PK1A#]H�����!src/Console/TasksStateCommand.phpnu�[���<?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Factory; use Joomla\Component\Scheduler\Administrator\Model\TaskModel; use Joomla\Component\Scheduler\Administrator\Table\TaskTable; use Joomla\Component\Scheduler\Administrator\Task\Task; use Joomla\Console\Application; use Joomla\Console\Command\AbstractCommand; use Joomla\Utilities\ArrayHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to change the state of tasks. * * @since 4.1.0 */ class TasksStateCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:state'; /** * The console application object * * @var Application * * @since 4.1.0 */ protected $application; /** * @var SymfonyStyle * * @since 4.1.0 */ private $ioStyle; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.1.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { Factory::getApplication()->getLanguage()->load('joomla', JPATH_ADMINISTRATOR); $this->configureIO($input, $output); $this->ioStyle->title('Change Task State'); $id = (string) $input->getOption('id'); $state = (string) $input->getOption('state'); // Try to validate and process ID, if passed if (\strlen($id)) { if (!Task::isValidId($id)) { $this->ioStyle->error('Invalid id passed!'); return 2; } $id = (is_numeric($id)) ? ($id + 0) : $id; } // Try to validate and process state, if passed if (\strlen($state)) { // If we get the logical state, we try to get the enumeration (but as a string) if (!is_numeric($state)) { $state = (string) ArrayHelper::arraySearch($state, Task::STATE_MAP); } if (!\strlen($state) || !Task::isValidState($state)) { $this->ioStyle->error('Invalid state passed!'); return 2; } } // If we didn't get ID as a flag, ask for it interactively while (!Task::isValidId($id)) { $id = $this->ioStyle->ask('Please specify the ID of the task'); } // If we didn't get state as a flag, ask for it interactively while ($state === false || !Task::isValidState($state)) { $state = (string) $this->ioStyle->ask('Should the state be "enable" (1), "disable" (0) or "trash" (-2)'); // Ensure we have the enumerated value (still as a string) $state = (Task::isValidState($state)) ? $state : ArrayHelper::arraySearch($state, Task::STATE_MAP); } // Finally, the enumerated state and id in their pure form $state = (int) $state; $id = (int) $id; /** @var ConsoleApplication $app */ $app = $this->getApplication(); /** @var TaskModel $taskModel */ $taskModel = $app->bootComponent('com_scheduler')->getMVCFactory()->createModel('Task', 'Administrator'); $task = $taskModel->getItem($id); // We couldn't fetch that task :( if (empty($task->id)) { $this->ioStyle->error("Task ID '{$id}' does not exist!"); return 1; } // If the item is checked-out we need a check in (currently not possible through the CLI) if ($taskModel->isCheckedOut($task)) { $this->ioStyle->error("Task ID '{$id}' is checked out!"); return 1; } /** @var TaskTable $table */ $table = $taskModel->getTable(); $action = Task::STATE_MAP[$state]; if (!$table->publish($id, $state)) { $this->ioStyle->error("Can't {$action} Task ID '{$id}'"); return 3; } $this->ioStyle->success("Task ID {$id} {$action}."); return 0; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $this->addOption('id', 'i', InputOption::VALUE_REQUIRED, 'The id of the task to change state.'); $this->addOption('state', 's', InputOption::VALUE_REQUIRED, 'The new state of the task, can be 1/enable, 0/disable, or -2/trash.'); $help = "<info>%command.name%</info> changes the state of a task. \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Enable, disable or trash a scheduled task'); $this->setHelp($help); } } PK1A#]2�� � !src/Console/CleanCacheCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for cleaning the system cache * * @since 4.0.0 */ class CleanCacheCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'cache:clean'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Cleaning System Cache'); $cache = $this->getApplication()->bootComponent('com_cache')->getMVCFactory(); /** @var \Joomla\Component\Cache\Administrator\Model\CacheModel $model */ $model = $cache->createModel('Cache', 'Administrator', ['ignore_request' => true]); if ($input->getArgument('expired')) { if (!$model->purge()) { $symfonyStyle->error('Expired Cache not cleaned'); return Command::FAILURE; } $symfonyStyle->success('Expired Cache cleaned'); return Command::SUCCESS; } if (!$model->clean()) { $symfonyStyle->error('Cache not cleaned'); return Command::FAILURE; } $symfonyStyle->success('Cache cleaned'); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will clear entries from the system cache \nUsage: <info>php %command.full_name%</info>"; $this->addArgument('expired', InputArgument::OPTIONAL, 'will clear expired entries from the system cache'); $this->setDescription('Clean cache entries'); $this->setHelp($help); } } PK1A#]UYMM)src/Console/ChangeUserPasswordCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to change a user's password * * @since 4.0.0 */ class ChangeUserPasswordCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:reset-password'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The password * * @var string * * @since 4.0.0 */ private $password; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Change Password'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return Command::FAILURE; } $user = User::getInstance($userId); $this->password = $this->getStringFromOption('password', 'Please enter a new password'); $user->password = UserHelper::hashPassword($this->password); if (!$user->save(true)) { $this->ioStyle->error($user->getError()); return Command::FAILURE; } $this->ioStyle->success("Password changed!"); return Command::SUCCESS; } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->cliInput->getOption($option); while (!$answer) { if ($option === 'password') { $answer = (string) $this->ioStyle->askHidden($question); } else { $answer = (string) $this->ioStyle->ask($question); } } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will change a user's password \nUsage: <info>php %command.full_name%</info>"; $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('password', null, InputOption::VALUE_OPTIONAL, 'password'); $this->setDescription("Change a user's password"); $this->setHelp($help); } } PK1A#]8H��%src/Console/RemoveOldFilesCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for removing files which should have been cleared during an update * * @since 4.0.0 */ class RemoveOldFilesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'update:joomla:remove-old-files'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $dryRun = $input->getOption('dry-run'); $symfonyStyle->title('Removing Unneeded Files & Folders' . ($dryRun ? ' - Dry Run' : '')); // We need the update script \JLoader::register('JoomlaInstallerScript', JPATH_ADMINISTRATOR . '/components/com_admin/script.php'); $status = (new \JoomlaInstallerScript())->deleteUnexistingFiles($dryRun, true); if ($output->isVeryVerbose() || $output->isDebug()) { foreach ($status['files_checked'] as $file) { $exists = in_array($file, array_values($status['files_exist'])); if ($exists) { $symfonyStyle->writeln('<error>File Checked & Exists</error> - ' . $file, OutputInterface::VERBOSITY_VERY_VERBOSE); } else { $symfonyStyle->writeln('<info>File Checked & Doesn\'t Exist</info> - ' . $file, OutputInterface::VERBOSITY_DEBUG); } } foreach ($status['folders_checked'] as $folder) { $exists = in_array($folder, array_values($status['folders_exist'])); if ($exists) { $symfonyStyle->writeln('<error>Folder Checked & Exists</error> - ' . $folder, OutputInterface::VERBOSITY_VERY_VERBOSE); } else { $symfonyStyle->writeln('<info>Folder Checked & Doesn\'t Exist</info> - ' . $folder, OutputInterface::VERBOSITY_DEBUG); } } } if ($dryRun === false) { foreach ($status['files_deleted'] as $file) { $symfonyStyle->writeln('<comment>File Deleted = ' . $file . '</comment>', OutputInterface::VERBOSITY_VERBOSE); } foreach ($status['files_errors'] as $error) { $symfonyStyle->error($error); } foreach ($status['folders_deleted'] as $folder) { $symfonyStyle->writeln('<comment>Folder Deleted = ' . $folder . '</comment>', OutputInterface::VERBOSITY_VERBOSE); } foreach ($status['folders_errors'] as $error) { $symfonyStyle->error($error); } } $symfonyStyle->success( sprintf( $dryRun ? '%s Files checked and %s would be deleted' : '%s Files checked and %s deleted', \count($status['files_checked']), ($dryRun ? \count($status['files_exist']) : \count($status['files_deleted'])) ) ); $symfonyStyle->success( sprintf( $dryRun ? '%s Folders checked and %s would be deleted' : '%s Folders checked and %s deleted', \count($status['folders_checked']), ($dryRun ? \count($status['folders_exist']) : \count($status['folders_deleted'])) ) ); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> removes old files which should have been deleted during a Joomla update \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Remove old system files'); $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Executes a dry run without deleting anything'); $this->setHelp($help); } } PK1A#]]wha[[ src/Console/TasksListCommand.phpnu�[���<?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\Component\Scheduler\Administrator\Scheduler\Scheduler; use Joomla\Console\Application; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to list scheduled tasks. * * @since 4.1.0 */ class TasksListCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:list'; /** * The console application object * * @var Application * @since 4.1.0 */ protected $application; /** * @var SymfonyStyle * @since 4.1.0 */ private $ioStyle; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.1.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { Factory::getApplication()->getLanguage()->load('joomla', JPATH_ADMINISTRATOR); $this->configureIO($input, $output); $this->ioStyle->title('List Scheduled Tasks'); $tasks = array_map( function (\stdClass $task): array { $enabled = $task->state === 1; $rule = json_decode($task->execution_rules); if ($rule->{'rule-type'} === 'manual') { $nextRun = 'Manual'; } else { $nextExec = Factory::getDate($task->next_execution, 'UTC'); $due = $enabled && $task->taskOption && Factory::getDate('now', 'UTC') > $nextExec; $nextRun = $due ? 'DUE!' : $nextExec->toRFC822(); } return [ 'id' => $task->id, 'title' => $task->title, 'type' => $task->safeTypeTitle, 'state' => $task->state === 1 ? 'Enabled' : ($task->state === 0 ? 'Disabled' : 'Trashed'), 'next_execution' => $nextRun, ]; }, $this->getTasks() ); $this->ioStyle->table(['ID', 'Title', 'Type', 'State', 'Next Run'], $tasks); return 0; } /** * Returns a stdClass object array of scheduled tasks. * * @return array * * @since 4.1.0 * @throws \RunTimeException */ private function getTasks(): array { $scheduler = new Scheduler(); return $scheduler->fetchTaskRecords( ['state' => '*'], ['ordering' => 'a.title', 'select' => 'a.id, a.title, a.type, a.state, a.next_execution, a.execution_rules'] ); } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $help = "<info>%command.name%</info> lists all scheduled tasks. \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('List all scheduled tasks'); $this->setHelp($help); } } PK1A#]W��� /src/Console/ExtensionDiscoverInstallCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Installer\Installer; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for discovering extensions * * @since 4.0.0 */ class ExtensionDiscoverInstallCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:discover:install'; /** * Stores the Input Object * * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('eid', null, InputOption::VALUE_REQUIRED, 'The ID of the extension to discover'); $help = "<info>%command.name%</info> is used to discover extensions \nYou can provide the following option to the command: \n --eid: The ID of the extension \n If you do not provide a ID all discovered extensions are installed. \nUsage: \n <info>php %command.full_name% --eid=<id_of_the_extension></info>"; $this->setDescription('Install discovered extensions'); $this->setHelp($help); } /** * Used for discovering extensions * * @param string $eid Id of the extension * * @return integer The count of installed extensions * * @throws \Exception * @since 4.0.0 */ public function processDiscover($eid): int { $jInstaller = new Installer(); $jInstaller->setDatabase($this->getDatabase()); $count = 0; if ($eid === -1) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName(['extension_id'])) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('state') . ' = -1'); $db->setQuery($query); $eidsToDiscover = $db->loadObjectList(); foreach ($eidsToDiscover as $eidToDiscover) { if (!$jInstaller->discover_install($eidToDiscover->extension_id)) { return -1; } $count++; } if (empty($eidsToDiscover)) { return 0; } } else { if ($jInstaller->discover_install($eid)) { return 1; } else { return -1; } } return $count; } /** * Used for finding the text for the note * * @param int $count Number of extensions to install * @param int $eid ID of the extension or -1 if no special * * @return string The text for the note * * @since 4.0.0 */ public function getNote(int $count, int $eid): string { if ($count < 0 && $eid >= 0) { return 'Unable to install the extension with ID ' . $eid; } elseif ($count < 0 && $eid < 0) { return 'Unable to install discovered extensions.'; } elseif ($count === 0) { return 'There are no pending discovered extensions for install. Perhaps you need to run extension:discover first?'; } elseif ($count === 1 && $eid > 0) { return 'Extension with ID ' . $eid . ' installed successfully.'; } elseif ($count === 1 && $eid < 0) { return $count . ' discovered extension has been installed.'; } elseif ($count > 1 && $eid < 0) { return $count . ' discovered extensions have been installed.'; } else { return 'The return value is not possible and has to be checked.'; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Install Discovered Extensions'); if ($eid = $this->cliInput->getOption('eid')) { $result = $this->processDiscover($eid); if ($result === -1) { $this->ioStyle->error($this->getNote($result, $eid)); return Command::FAILURE; } else { $this->ioStyle->success($this->getNote($result, $eid)); return Command::SUCCESS; } } else { $result = $this->processDiscover(-1); if ($result < 0) { $this->ioStyle->error($this->getNote($result, -1)); return Command::FAILURE; } elseif ($result === 0) { $this->ioStyle->note($this->getNote($result, -1)); return Command::SUCCESS; } else { $this->ioStyle->note($this->getNote($result, -1)); return Command::SUCCESS; } } } } PK1A#]�j� __&src/Console/ExtensionRemoveCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Extension; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for removing extensions * * @since 4.0.0 */ class ExtensionRemoveCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:remove'; /** * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Exit Code for extensions remove abort * @since 4.0.0 */ public const REMOVE_ABORT = 3; /** * Exit Code for extensions remove failure * @since 4.0.0 */ public const REMOVE_FAILED = 1; /** * Exit Code for invalid response * @since 4.0.0 */ public const REMOVE_INVALID_RESPONSE = 5; /** * Exit Code for invalid type * @since 4.0.0 */ public const REMOVE_INVALID_TYPE = 6; /** * Exit Code for extensions locked remove failure * @since 4.0.0 */ public const REMOVE_LOCKED = 4; /** * Exit Code for extensions not found * @since 4.0.0 */ public const REMOVE_NOT_FOUND = 2; /** * Exit Code for extensions remove success * @since 4.0.0 */ public const REMOVE_SUCCESSFUL = 0; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); $language = Factory::getLanguage(); $language->load('', JPATH_ADMINISTRATOR, null, false, false) || $language->load('', JPATH_ADMINISTRATOR, null, true); $language->load('com_installer', JPATH_ADMINISTRATOR, null, false, false) || $language->load('com_installer', JPATH_ADMINISTRATOR, null, true); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument( 'extensionId', InputArgument::REQUIRED, 'ID of extension to be removed (run extension:list command to check)' ); $help = "<info>%command.name%</info> is used to uninstall extensions. \nThe command requires one argument, the ID of the extension to uninstall. \nYou may find this ID by running the <info>extension:list</info> command. \nUsage: <info>php %command.full_name% <extension_id></info>"; $this->setDescription('Remove an extension'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Remove Extension'); $extensionId = $this->cliInput->getArgument('extensionId'); $response = $this->ioStyle->ask('Are you sure you want to remove this extension?', 'yes/no'); if (strtolower($response) === 'yes') { // Get an installer object for the extension type $installer = Installer::getInstance(); $row = new Extension($this->getDatabase()); if ((int) $extensionId === 0 || !$row->load($extensionId)) { $this->ioStyle->error("Extension with ID of $extensionId not found."); return self::REMOVE_NOT_FOUND; } // Do not allow to uninstall locked extensions. if ((int) $row->locked === 1) { $this->ioStyle->error(Text::sprintf('COM_INSTALLER_UNINSTALL_ERROR_LOCKED_EXTENSION', $row->name, $extensionId)); return self::REMOVE_LOCKED; } if ($row->type) { if (!$installer->uninstall($row->type, $extensionId)) { $this->ioStyle->error('Extension not removed.'); return self::REMOVE_FAILED; } $this->ioStyle->success('Extension removed!'); return self::REMOVE_SUCCESSFUL; } return self::REMOVE_INVALID_TYPE; } elseif (strtolower($response) === 'no') { $this->ioStyle->note('Extension not removed.'); return self::REMOVE_ABORT; } $this->ioStyle->warning('Invalid response'); return self::REMOVE_INVALID_RESPONSE; } } PK1A#]S���""(src/Console/ExtensionDiscoverCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for discovering extensions * * @since 4.0.0 */ class ExtensionDiscoverCommand extends AbstractCommand { /** * The default command name * * @var string * * @since 4.0.0 */ protected static $defaultName = 'extension:discover'; /** * Stores the Input Object * * @var InputInterface * * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * * @since 4.0.0 */ private $ioStyle; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to discover extensions \nUsage: \n <info>php %command.full_name%</info>"; $this->setDescription('Discover extensions'); $this->setHelp($help); } /** * Used for discovering extensions * * @return integer The count of discovered extensions * * @throws \Exception * * @since 4.0.0 */ public function processDiscover(): int { $app = $this->getApplication(); $mvcFactory = $app->bootComponent('com_installer')->getMVCFactory(); $model = $mvcFactory->createModel('Discover', 'Administrator'); return $model->discover(); } /** * Used for finding the text for the note * * @param int $count The count of installed Extensions * * @return string The text for the note * * @since 4.0.0 */ public function getNote(int $count): string { if ($count < 1) { return 'No extensions were discovered.'; } elseif ($count === 1) { return $count . ' extension has been discovered.'; } else { return $count . ' extensions have been discovered.'; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $count = $this->processDiscover(); $this->ioStyle->title('Discover Extensions'); $this->ioStyle->note($this->getNote($count)); return Command::SUCCESS; } } PK1A#]�9_J #src/Console/CheckUpdatesCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Updater\Updater; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for checking if there are pending extension updates * * @since 4.0.0 */ class CheckUpdatesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'update:extensions:check'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Fetching Extension Updates'); $this->getApplication()->getLanguage()->load('lib_joomla'); // Find all updates $ret = Updater::getInstance()->findUpdates(); if ($ret) { $symfonyStyle->note('There are available updates to apply'); $symfonyStyle->success('Check complete.'); } else { $symfonyStyle->success('There are no available updates'); } return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> command checks for pending extension updates \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Check for pending extension updates'); $this->setHelp($help); } } PK1A#]Ԗ +SSsrc/Console/TasksRunCommand.phpnu�[���<?php /** * Joomla! Content Management System. * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Component\Scheduler\Administrator\Scheduler\Scheduler; use Joomla\Component\Scheduler\Administrator\Task\Status; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to run scheduled tasks. * * @since 4.1.0 */ class TasksRunCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.1.0 */ protected static $defaultName = 'scheduler:run'; /** * @var SymfonyStyle * @since 4.1.0 */ private $ioStyle; /** * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code. * * @since 4.1.0 * @throws \RunTimeException * @throws InvalidArgumentException */ protected function doExecute(InputInterface $input, OutputInterface $output): int { /** * Not as a class constant because of some the autoload order doesn't let us * load the namespace when it's time to do that (why?) */ static $outTextMap = [ Status::OK => 'Task#%1$02d \'%2$s\' processed in %3$.2f seconds.', Status::WILL_RESUME => '<notice>Task#%1$02d \'%2$s\' ran for %3$.2f seconds, will resume next time.</notice>', Status::NO_RUN => '<warning>Task#%1$02d \'%2$s\' failed to run. Is it already running?</warning>', Status::NO_ROUTINE => '<error>Task#%1$02d \'%2$s\' is orphaned! Visit the backend to resolve.</error>', 'N/A' => '<error>Task#%1$02d \'%2$s\' exited with code %4$d in %3$.2f seconds.</error>', ]; $this->configureIo($input, $output); $this->ioStyle->title('Run Tasks'); $scheduler = new Scheduler(); $id = $input->getOption('id'); $all = $input->getOption('all'); if ($id) { $records[] = $scheduler->fetchTaskRecord($id); } else { $filters = $scheduler::TASK_QUEUE_FILTERS; $listConfig = $scheduler::TASK_QUEUE_LIST_CONFIG; $listConfig['limit'] = ($all ? null : 1); $records = $scheduler->fetchTaskRecords($filters, $listConfig); } if ($id && !$records[0]) { $this->ioStyle->writeln('<error>No matching task found!</error>'); return Status::NO_TASK; } elseif (!$records) { $this->ioStyle->writeln('<error>No tasks due!</error>'); return Status::NO_TASK; } $status = ['startTime' => microtime(true)]; $taskCount = \count($records); $exit = Status::OK; foreach ($records as $record) { $cStart = microtime(true); $task = $scheduler->runTask(['id' => $record->id, 'allowDisabled' => true, 'allowConcurrent' => true]); $exit = empty($task) ? Status::NO_RUN : $task->getContent()['status']; $duration = microtime(true) - $cStart; $key = (\array_key_exists($exit, $outTextMap)) ? $exit : 'N/A'; $this->ioStyle->writeln(sprintf($outTextMap[$key], $record->id, $record->title, $duration, $exit)); } $netTime = round(microtime(true) - $status['startTime'], 2); $this->ioStyle->newLine(); $this->ioStyle->writeln("<info>Finished running $taskCount tasks in $netTime seconds.</info>"); return $taskCount === 1 ? $exit : Status::OK; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.1.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.1.0 */ protected function configure(): void { $this->addOption('id', 'i', InputOption::VALUE_REQUIRED, 'The id of the task to run.'); $this->addOption('all', '', InputOption::VALUE_NONE, 'Run all due tasks. Note that this is overridden if --id is used.'); $help = "<info>%command.name%</info> run scheduled tasks. \nUsage: <info>php %command.full_name% [flags]</info>"; $this->setDescription('Run one or more scheduled tasks'); $this->setHelp($help); } } PK1A#]ת��UU)src/Console/CheckJoomlaUpdatesCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for checking if there are pending extension updates * * @since 4.0.0 */ class CheckJoomlaUpdatesCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'core:check-updates'; /** * Stores the Update Information * * @var UpdateModel * @since 4.0.0 */ private $updateInfo; /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will check for Joomla updates \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Check for Joomla updates'); $this->setHelp($help); } /** * Retrieves Update Information * * @return mixed * * @since 4.0.0 */ private function getUpdateInformationFromModel() { $app = $this->getApplication(); $updatemodel = $app->bootComponent('com_joomlaupdate')->getMVCFactory($app)->createModel('Update', 'Administrator'); $updatemodel->purge(); $updatemodel->refreshUpdates(true); return $updatemodel; } /** * Gets the Update Information * * @return mixed * * @since 4.0.0 */ public function getUpdateInfo() { if (!$this->updateInfo) { $this->setUpdateInfo(); } return $this->updateInfo; } /** * Sets the Update Information * * @param null $info stores update Information * * @return void * * @since 4.0.0 */ public function setUpdateInfo($info = null): void { if (!$info) { $this->updateInfo = $this->getUpdateInformationFromModel(); } else { $this->updateInfo = $info; } } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $model = $this->getUpdateInfo(); $data = $model->getUpdateInformation(); $symfonyStyle->title('Joomla! Updates'); if (!$data['hasUpdate']) { $symfonyStyle->success('You already have the latest Joomla version ' . $data['latest']); return Command::SUCCESS; } $symfonyStyle->note('New Joomla Version ' . $data['latest'] . ' is available.'); if (!isset($data['object']->downloadurl->_data)) { $symfonyStyle->warning('We cannot find an update URL'); } return Command::SUCCESS; } } PK1A#]�ڄ��!src/Console/DeleteUserCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Database\ParameterType; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for deleting a user * * @since 4.0.0 */ class DeleteUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:delete'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Delete User'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); $db = $this->getDatabase(); if (empty($userId)) { $this->ioStyle->error($this->username . ' does not exist!'); return Command::FAILURE; } if ($input->isInteractive() && !$this->ioStyle->confirm('Are you sure you want to delete this user?', false)) { $this->ioStyle->note('User not deleted'); return Command::SUCCESS; } $groups = UserHelper::getUserGroups($userId); $user = User::getInstance($userId); if ($user->block == 0) { foreach ($groups as $groupId) { if (Access::checkGroup($groupId, 'core.admin')) { $queryUser = $db->getQuery(true); $queryUser->select('COUNT(*)') ->from($db->quoteName('#__users', 'u')) ->leftJoin( $db->quoteName('#__user_usergroup_map', 'g'), '(' . $db->quoteName('u.id') . ' = ' . $db->quoteName('g.user_id') . ')' ) ->where($db->quoteName('g.group_id') . " = :groupId") ->where($db->quoteName('u.block') . " = 0") ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($queryUser); $activeSuperUser = $db->loadResult(); if ($activeSuperUser < 2) { $this->ioStyle->error("You can't delete the last active Super User"); return Command::FAILURE; } } } } // Trigger delete of user $result = $user->delete(); if (!$result) { $this->ioStyle->error("Can't remove " . $this->username . ' from usertable'); return Command::FAILURE; } $this->ioStyle->success('User ' . $this->username . ' deleted!'); return Command::SUCCESS; } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> deletes a user \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Delete a user'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->setHelp($help); } } PK1A#]c���G#G#'src/Console/GetConfigurationCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for displaying configuration options * * @since 4.0.0 */ class GetConfigurationCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'config:get'; /** * Stores the Input Object * @var Input * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Constant defining the Database option group * @var array * @since 4.0.0 */ public const DB_GROUP = [ 'name' => 'db', 'options' => [ 'dbtype', 'host', 'user', 'password', 'dbprefix', 'db', 'dbencryption', 'dbsslverifyservercert', 'dbsslkey', 'dbsslcert', 'dbsslca', 'dbsslcipher', ], ]; /** * Constant defining the Session option group * @var array * @since 4.0.0 */ public const SESSION_GROUP = [ 'name' => 'session', 'options' => [ 'session_handler', 'shared_session', 'session_metadata', ], ]; /** * Constant defining the Mail option group * @var array * @since 4.0.0 */ public const MAIL_GROUP = [ 'name' => 'mail', 'options' => [ 'mailonline', 'mailer', 'mailfrom', 'fromname', 'sendmail', 'smtpauth', 'smtpuser', 'smtppass', 'smtphost', 'smtpsecure', 'smtpport', ], ]; /** * Return code if configuration is get successfully * @since 4.0.0 */ public const CONFIG_GET_SUCCESSFUL = 0; /** * Return code if configuration group option is not found * @since 4.0.0 */ public const CONFIG_GET_GROUP_NOT_FOUND = 1; /** * Return code if configuration option is not found * @since 4.0.0 */ public const CONFIG_GET_OPTION_NOT_FOUND = 2; /** * Return code if the command has been invoked with wrong options * @since 4.0.0 */ public const CONFIG_GET_OPTION_FAILED = 3; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Displays logically grouped options * * @param string $group The group to be processed * * @return integer * * @since 4.0.0 */ public function processGroupOptions($group): int { $configs = $this->getApplication()->getConfig()->toArray(); $configs = $this->formatConfig($configs); $groups = $this->getGroups(); $foundGroup = false; foreach ($groups as $key => $value) { if ($value['name'] === $group) { $foundGroup = true; $options = []; foreach ($value['options'] as $option) { $options[] = [$option, $configs[$option]]; } $this->ioStyle->table(['Option', 'Value'], $options); } } if (!$foundGroup) { $this->ioStyle->error("Group *$group* not found"); return self::CONFIG_GET_GROUP_NOT_FOUND; } return self::CONFIG_GET_SUCCESSFUL; } /** * Gets the defined option groups * * @return array * * @since 4.0.0 */ public function getGroups() { return [ self::DB_GROUP, self::MAIL_GROUP, self::SESSION_GROUP, ]; } /** * Formats the configuration array into desired format * * @param array $configs Array of the configurations * * @return array * * @since 4.0.0 */ public function formatConfig(array $configs): array { $newConfig = []; foreach ($configs as $key => $config) { $config = $config === false ? "false" : $config; $config = $config === true ? "true" : $config; if (!in_array($key, ['cwd', 'execution'])) { $newConfig[$key] = $config; } } return $newConfig; } /** * Handles the command when a single option is requested * * @param string $option The option we want to get its value * * @return integer * * @since 4.0.0 */ public function processSingleOption($option): int { $configs = $this->getApplication()->getConfig()->toArray(); if (!array_key_exists($option, $configs)) { $this->ioStyle->error("Can't find option *$option* in configuration list"); return self::CONFIG_GET_OPTION_NOT_FOUND; } $value = $this->formatConfigValue($this->getApplication()->get($option)); $this->ioStyle->table(['Option', 'Value'], [[$option, $value]]); return self::CONFIG_GET_SUCCESSFUL; } /** * Formats the Configuration value * * @param mixed $value Value to be formatted * * @return string * * @since 4.0.0 */ protected function formatConfigValue($value): string { if ($value === false) { return 'false'; } elseif ($value === true) { return 'true'; } elseif ($value === null) { return 'Not Set'; } elseif (\is_array($value)) { return \json_encode($value); } elseif (\is_object($value)) { return \json_encode(\get_object_vars($value)); } else { return $value; } } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $groups = $this->getGroups(); foreach ($groups as $key => $group) { $groupNames[] = $group['name']; } $groupNames = implode(', ', $groupNames); $this->addArgument('option', null, 'Name of the option'); $this->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'Name of the option'); $help = "<info>%command.name%</info> displays the current value of a configuration option \nUsage: <info>php %command.full_name%</info> <option> \nGroup usage: <info>php %command.full_name%</info> --group <groupname> \nAvailable group names: $groupNames"; $this->setDescription('Display the current value of a configuration option'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $configs = $this->formatConfig($this->getApplication()->getConfig()->toArray()); $option = $this->cliInput->getArgument('option'); $group = $this->cliInput->getOption('group'); if ($group) { return $this->processGroupOptions($group); } if ($option) { return $this->processSingleOption($option); } if (!$option && !$group) { $options = []; array_walk( $configs, function ($value, $key) use (&$options) { $options[] = [$key, $this->formatConfigValue($value)]; } ); $this->ioStyle->title("Current options in Configuration"); $this->ioStyle->table(['Option', 'Value'], $options); return self::CONFIG_GET_SUCCESSFUL; } return self::CONFIG_GET_OPTION_NOT_FOUND; } } PK1A#]�g�=��%src/Console/AddUserToGroupCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to add a user to group * * @since 4.0.0 */ class AddUserToGroupCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:addtogroup'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Add User To Group'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = $this->getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return Command::FAILURE; } // Fetch user $user = User::getInstance($userId); $this->userGroups = $this->getGroups($user); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :userGroup'); foreach ($this->userGroups as $userGroup) { $query->bind(':userGroup', $userGroup); $db->setQuery($query); $result = $db->loadResult(); if (UserHelper::addUserToGroup($user->id, $userGroup)) { $this->ioStyle->success("Added '" . $user->username . "' to group '" . $result . "'!"); } else { $this->ioStyle->error("Can't add '" . $user->username . "' to group '" . $result . "'!"); return Command::FAILURE; } } return Command::SUCCESS; } /** * Method to get a value from option * * @param User $user a UserInstance * * @return array * * @since 4.0.0 */ protected function getGroups($user): array { $groups = $this->getApplication()->getConsoleInput()->getOption('group'); $db = $this->getDatabase(); $groupList = []; // Group names have been supplied as input arguments if ($groups) { $groups = explode(',', $groups); foreach ($groups as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } $userGroups = Access::getGroupsByUser($user->id, false); // Generate select list for user $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->whereNotIn($db->quoteName('id'), $userGroups) ->order($db->quoteName('id') . ' ASC'); $db->setQuery($query); $list = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $list ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . '= :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a user object * * @param string $username username * * @return object * * @since 4.0.0 */ protected function getUserId($username) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__users')) ->where($db->quoteName('username') . '= :username') ->bind(':username', $username); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> adds a user to a group \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Add a user to a group'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('group', null, InputOption::VALUE_OPTIONAL, 'group'); $this->setHelp($help); } } PK1A#]�q*� / /!src/Console/UpdateCoreCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Application\Cli\CliInput; use Joomla\CMS\Extension\ExtensionHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\InstallerHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for updating Joomla! core * * @since 4.0.0 */ class UpdateCoreCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'core:update'; /** * Stores the Input Object * @var CliInput * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Update Information * @var array * @since 4.0.0 */ public $updateInfo; /** * Update Model * @var array * @since 4.0.0 */ public $updateModel; /** * Progress Bar object * @var ProgressBar * @since 4.0.0 */ public $progressBar; /** * Return code for successful update * @since 4.0.0 */ public const UPDATE_SUCCESSFUL = 0; /** * Return code for failed update * @since 4.0.0 */ public const ERR_UPDATE_FAILED = 2; /** * Return code for failed checks * @since 4.0.0 */ public const ERR_CHECKS_FAILED = 1; /** * @var DatabaseInterface * @since 4.0.0 */ private $db; /** * UpdateCoreCommand constructor. * * @param DatabaseInterface $db Database Instance * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { $this->db = $db; parent::__construct(); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message%'); $this->progressBar = new ProgressBar($output, 8); $this->progressBar->setFormat('custom'); $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); $language = Factory::getLanguage(); $language->load('lib_joomla', JPATH_ADMINISTRATOR); $language->load('', JPATH_ADMINISTRATOR); $language->load('com_joomlaupdate', JPATH_ADMINISTRATOR); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 * @throws \Exception */ public function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Updating Joomla'); $this->progressBar->setMessage("Starting up ..."); $this->progressBar->start(); $model = $this->getUpdateModel(); // Make sure logging is working before continue try { Log::add('Test logging', Log::INFO, 'Update'); } catch (\Throwable $e) { $message = Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL', $e->getMessage()); $this->ioStyle->error($message); return self::ERR_UPDATE_FAILED; } Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', 0, 'CLI', \JVERSION), Log::INFO, 'Update'); $this->setUpdateInfo($model->getUpdateInformation()); $this->progressBar->advance(); $this->progressBar->setMessage('Running checks ...'); if (!$this->updateInfo['hasUpdate']) { $this->progressBar->finish(); $this->ioStyle->note('You already have the latest Joomla! version. ' . $this->updateInfo['latest']); return self::ERR_CHECKS_FAILED; } $this->progressBar->advance(); $this->progressBar->setMessage('Check Database Table Structure...'); $errors = $this->checkSchema(); if ($errors > 0) { $this->ioStyle->error('Database Table Structure not Up to Date'); $this->progressBar->finish(); $this->ioStyle->info('There were ' . $errors . ' errors'); return self::ERR_CHECKS_FAILED; } $this->progressBar->advance(); $this->progressBar->setMessage('Starting Joomla! update ...'); if ($this->updateJoomlaCore($model)) { $this->progressBar->finish(); if ($model->getErrors()) { $this->ioStyle->error('Update finished with errors. Please check logs for details.'); return self::ERR_UPDATE_FAILED; } $this->ioStyle->success('Joomla core updated successfully!'); return self::UPDATE_SUCCESSFUL; } $this->progressBar->finish(); $this->ioStyle->error('Update cannot be performed.'); return self::ERR_UPDATE_FAILED; } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to update Joomla \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Update Joomla'); $this->setHelp($help); } /** * Update Core Joomla * * @param mixed $updatemodel Update Model * * @return boolean success * * @since 4.0.0 */ private function updateJoomlaCore($updatemodel): bool { $updateInformation = $this->updateInfo; if (!empty($updateInformation['hasUpdate'])) { $this->progressBar->advance(); $this->progressBar->setMessage("Processing update package ..."); $package = $this->processUpdatePackage($updateInformation); $this->progressBar->advance(); $this->progressBar->setMessage("Finalizing update ..."); $result = $updatemodel->finaliseUpgrade(); if ($result) { $this->progressBar->advance(); $this->progressBar->setMessage("Cleaning up ..."); // Remove the administrator/cache/autoload_psr4.php file $autoloadFile = JPATH_CACHE . '/autoload_psr4.php'; if (file_exists($autoloadFile)) { File::delete($autoloadFile); } // Remove the xml if (file_exists(JPATH_BASE . '/joomla.xml')) { File::delete(JPATH_BASE . '/joomla.xml'); } InstallerHelper::cleanupInstall($package['file'], $package['extractdir']); $updatemodel->purge(); return true; } } return false; } /** * Sets the update Information * * @param array $data Stores the update information * * @since 4.0.0 * * @return void */ public function setUpdateInfo($data): void { $this->updateInfo = $data; } /** * Retrieves the Update model from com_joomlaupdate * * @return mixed * * @since 4.0.0 * * @throws \Exception */ public function getUpdateModel() { if (!isset($this->updateModel)) { $this->setUpdateModel(); } return $this->updateModel; } /** * Sets the Update Model * * @return void * * @since 4.0.0 */ public function setUpdateModel(): void { $app = $this->getApplication(); $updatemodel = $app->bootComponent('com_joomlaupdate')->getMVCFactory($app)->createModel('Update', 'Administrator'); if (is_bool($updatemodel)) { $this->updateModel = $updatemodel; return; } $updatemodel->purge(); $updatemodel->refreshUpdates(true); $this->updateModel = $updatemodel; } /** * Downloads and extracts the update Package * * @param array $updateInformation Stores the update information * * @return array | boolean * * @since 4.0.0 */ public function processUpdatePackage($updateInformation) { if (!$updateInformation['object']) { return false; } $this->progressBar->advance(); $this->progressBar->setMessage("Downloading update package ..."); $file = $this->downloadFile($updateInformation['object']->downloadurl->_data); $tmpPath = $this->getApplication()->get('tmp_path'); $updatePackage = $tmpPath . '/' . $file; $this->progressBar->advance(); $this->progressBar->setMessage("Extracting update package ..."); $package = $this->extractFile($updatePackage); $this->progressBar->advance(); $this->progressBar->setMessage("Copying files ..."); $this->copyFileTo($package['extractdir'], JPATH_BASE); return ['file' => $updatePackage, 'extractdir' => $package['extractdir']]; } /** * Downloads the Update file * * @param string $url URL to update file * * @return boolean | string * * @since 4.0.0 */ public function downloadFile($url) { $file = InstallerHelper::downloadPackage($url); if (!$file) { return false; } return $file; } /** * Extracts Update file * * @param string $file Full path to file location * * @return array | boolean * * @since 4.0.0 */ public function extractFile($file) { $package = InstallerHelper::unpack($file, true); return $package; } /** * Copy a file to a destination directory * * @param string $file Full path to file * @param string $dir Destination directory * * @return void * * @since 4.0.0 */ public function copyFileTo($file, $dir): void { Folder::copy($file, $dir, '', true); } /** * Check Database Table Structure * * @return integer the number of errors * * @since 4.4.0 */ public function checkSchema(): int { $app = $this->getApplication(); $app->getLanguage()->load('com_installer', JPATH_ADMINISTRATOR); $coreExtensionInfo = ExtensionHelper::getExtensionRecord('joomla', 'file'); $dbmodel = $app->bootComponent('com_installer')->getMVCFactory($app)->createModel('Database', 'Administrator'); // Ensure we only get information for core $dbmodel->setState('filter.extension_id', $coreExtensionInfo->extension_id); // We're filtering by a single extension which must always exist - so can safely access this through element 0 of the array $changeInformation = $dbmodel->getItems()[0]; foreach ($changeInformation['errorsMessage'] as $msg) { $this->ioStyle->info($msg); } return $changeInformation['errorsCount']; } } PK1A#]�QQ'src/Console/ExtensionInstallCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerHelper; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for installing extensions * * @since 4.0.0 */ class ExtensionInstallCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:install'; /** * Stores the Input Object * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Exit Code For installation failure * @since 4.0.0 */ public const INSTALLATION_FAILED = 1; /** * Exit Code For installation Success * @since 4.0.0 */ public const INSTALLATION_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path to the extension'); $this->addOption('url', null, InputOption::VALUE_REQUIRED, 'The url to the extension'); $help = "<info>%command.name%</info> is used to install extensions \nYou must provide one of the following options to the command: \n --path: The path on your local filesystem to the install package \n --url: The URL from where the install package should be downloaded \nUsage: \n <info>php %command.full_name% --path=<path_to_file></info> \n <info>php %command.full_name% --url=<url_to_file></info>"; $this->setDescription('Install an extension from a URL or from a path'); $this->setHelp($help); } /** * Used for installing extension from a path * * @param string $path Path to the extension zip file * * @return boolean * * @since 4.0.0 * * @throws \Exception */ public function processPathInstallation($path): bool { if (!file_exists($path)) { $this->ioStyle->warning('The file path specified does not exist.'); return false; } $tmpPath = $this->getApplication()->get('tmp_path'); $tmpPath = $tmpPath . '/' . basename($path); $package = InstallerHelper::unpack($path, true); if ($package['type'] === false) { return false; } $jInstaller = Installer::getInstance(); $result = $jInstaller->install($package['extractdir']); InstallerHelper::cleanupInstall($tmpPath, $package['extractdir']); return $result; } /** * Used for installing extension from a URL * * @param string $url URL to the extension zip file * * @return boolean * * @since 4.0.0 * * @throws \Exception */ public function processUrlInstallation($url): bool { $filename = InstallerHelper::downloadPackage($url); $tmpPath = $this->getApplication()->get('tmp_path'); $path = $tmpPath . '/' . basename($filename); $package = InstallerHelper::unpack($path, true); if ($package['type'] === false) { return false; } $jInstaller = new Installer(); $result = $jInstaller->install($package['extractdir']); InstallerHelper::cleanupInstall($path, $package['extractdir']); return $result; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @throws \Exception * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Install Extension'); if ($path = $this->cliInput->getOption('path')) { $result = $this->processPathInstallation($path); if (!$result) { $this->ioStyle->error('Unable to install extension'); return self::INSTALLATION_FAILED; } $this->ioStyle->success('Extension installed successfully.'); return self::INSTALLATION_SUCCESSFUL; } elseif ($url = $this->cliInput->getOption('url')) { $result = $this->processUrlInstallation($url); if (!$result) { $this->ioStyle->error('Unable to install extension'); return self::INSTALLATION_FAILED; } $this->ioStyle->success('Extension installed successfully.'); return self::INSTALLATION_SUCCESSFUL; } $this->ioStyle->error('Invalid argument supplied for command.'); return self::INSTALLATION_FAILED; } } PK1A#]%�l��� src/Console/SessionGcCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\DI\ContainerAwareInterface; use Joomla\DI\ContainerAwareTrait; use Joomla\Session\SessionInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for performing session garbage collection * * @since 4.0.0 */ class SessionGcCommand extends AbstractCommand implements ContainerAwareInterface { use ContainerAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'session:gc'; /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Running Session Garbage Collection'); $session = $this->getSessionService($input->getOption('application')); $gcResult = $session->gc(); // Destroy the session started for this process $session->destroy(); if ($gcResult === false) { $symfonyStyle->error('Garbage collection was not completed. Either the operation failed or it is not supported on your platform.'); return Command::FAILURE; } $symfonyStyle->success('Garbage collection completed.'); return Command::SUCCESS; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> runs PHP's garbage collection operation for session data \nUsage: <info>php %command.full_name%</info> \nThis command defaults to performing garbage collection for the frontend (site) application. \nTo run garbage collection for another application, you can specify it with the <info>--application</info> option. \nUsage: <info>php %command.full_name% --application=[APPLICATION]</info>"; $this->setDescription('Perform session garbage collection'); $this->addOption('application', 'app', InputOption::VALUE_OPTIONAL, 'The application to perform garbage collection for.', 'site'); $this->setHelp($help); } /** * Get the session service for the requested application. * * @param string $application The application session service to retrieve * * @return SessionInterface * * @since 4.0.0 */ private function getSessionService(string $application): SessionInterface { if (!$this->getContainer()->has("session.web.$application")) { throw new \InvalidArgumentException( sprintf( 'The `%s` application is not a valid option.', $application ) ); } return $this->getContainer()->get("session.web.$application"); } } PK1A#]���x=x="src/Console/FinderIndexCommand.phpnu�[���<?php /** * Joomla! CLI * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Language\LanguageAwareInterface; use Joomla\CMS\Language\LanguageAwareTrait; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Component\Finder\Administrator\Indexer\Indexer; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command Purges and rebuilds the index (search filters are preserved) * * @since 4.0.0 */ class FinderIndexCommand extends AbstractCommand implements LanguageAwareInterface { use LanguageAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'finder:index'; /** * Stores the Input Object * * @var InputInterface * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Database connector * * @var DatabaseInterface * @since 4.0.0 */ private $db; /** * Start time for the index process * * @var string * @since 2.5 */ private $time; /** * Start time for each batch * * @var string * @since 2.5 */ private $qtime; /** * Static filters information. * * @var array * @since 3.3 */ private $filters = []; /** * Pausing type or defined pause time in seconds. * One pausing type is implemented: 'division' for dynamic calculation of pauses * * Defaults to 'division' * * @var string|integer * @since 3.9.12 */ private $pause = 'division'; /** * The divisor of the division: batch-processing time / divisor. * This is used together with --pause=division in order to pause dynamically * in relation to the processing time * Defaults to 5 * * @var integer * @since 3.9.12 */ private $divisor = 5; /** * Minimum processing time in seconds, in order to apply a pause * Defaults to 1 * * @var integer * @since 3.9.12 */ private $minimumBatchProcessingTime = 1; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { $this->db = $db; parent::__construct(); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument('purge', InputArgument::OPTIONAL, 'Purge the index and rebuilds'); $this->addOption('minproctime', null, InputOption::VALUE_REQUIRED, 'Minimum processing time in seconds, in order to apply a pause', 1); $this->addOption('pause', null, InputOption::VALUE_REQUIRED, 'Pausing type or defined pause time in seconds', 'division'); $this->addOption('divisor', null, InputOption::VALUE_REQUIRED, 'The divisor of the division: batch-processing time / divisor', 5); $help = <<<'EOF' The <info>%command.name%</info> Purges and rebuilds the index (search filters are preserved). <info>php %command.full_name%</info> EOF; $this->setDescription('Purges and rebuild the index'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { // Initialize the time value. $this->time = microtime(true); $this->configureIO($input, $output); $this->ioStyle->writeln( [ '<info>Finder Indexer</>', '<info>==========================</>', '', ] ); if ($this->cliInput->getOption('minproctime')) { $this->minimumBatchProcessingTime = $this->cliInput->getOption('minproctime'); } if ($this->cliInput->getOption('pause')) { $this->pause = $this->cliInput->getOption('pause'); } if ($this->cliInput->getOption('divisor')) { $this->divisor = $this->cliInput->getOption('divisor'); } if ($this->cliInput->getArgument('purge')) { // Taxonomy ids will change following a purge/index, so save filter information first. $this->getFilters(); // Purge the index. $this->purge(); // Run the indexer. $this->index(); // Restore the filters again. $this->putFilters(); } else { $this->index(); } $this->ioStyle->newLine(1); // Total reporting. $this->ioStyle->writeln( [ '<info>' . Text::sprintf('FINDER_CLI_PROCESS_COMPLETE', round(microtime(true) - $this->time, 3)) . '</>', '<info>' . Text::sprintf('FINDER_CLI_PEAK_MEMORY_USAGE', number_format(memory_get_peak_usage(true))) . '</>', ] ); $this->ioStyle->newLine(1); return Command::SUCCESS; } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); try { $language = $this->getLanguage(); } catch (\UnexpectedValueException $e) { @trigger_error(sprintf('Language must be set in 6.0 in %s', __METHOD__), E_USER_DEPRECATED); $language = Factory::getLanguage(); } $language->load('', JPATH_ADMINISTRATOR, null, false, false) || $language->load('', JPATH_ADMINISTRATOR, null, true); $language->load('finder_cli', JPATH_SITE, null, false, false) || $language->load('finder_cli', JPATH_SITE, null, true); } /** * Save static filters. * * Since a purge/index cycle will cause all the taxonomy ids to change, * the static filters need to be updated with the new taxonomy ids. * The static filter information is saved prior to the purge/index * so that it can later be used to update the filters with new ids. * * @return void * * @since 4.0.0 */ private function getFilters(): void { $this->ioStyle->text(Text::_('FINDER_CLI_SAVE_FILTERS')); // Get the taxonomy ids used by the filters. $db = $this->db; $query = $db->getQuery(true); $query ->select('filter_id, title, data') ->from($db->quoteName('#__finder_filters')); $filters = $db->setQuery($query)->loadObjectList(); // Get the name of each taxonomy and the name of its parent. foreach ($filters as $filter) { // Skip empty filters. if ($filter->data === '') { continue; } // Get taxonomy records. $query = $db->getQuery(true); $query ->select('t.title, p.title AS parent') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->leftJoin($db->quoteName('#__finder_taxonomy') . ' AS p ON p.id = t.parent_id') ->where($db->quoteName('t.id') . ' IN (' . $filter->data . ')'); $taxonomies = $db->setQuery($query)->loadObjectList(); // Construct a temporary data structure to hold the filter information. foreach ($taxonomies as $taxonomy) { $this->filters[$filter->filter_id][] = [ 'filter' => $filter->title, 'title' => $taxonomy->title, 'parent' => $taxonomy->parent, ]; } } $this->ioStyle->text(Text::sprintf('FINDER_CLI_SAVE_FILTER_COMPLETED', count($filters))); } /** * Purge the index. * * @return void * * @since 3.3 */ private function purge() { $this->ioStyle->text(Text::_('FINDER_CLI_INDEX_PURGE')); // Load the model. $app = $this->getApplication(); $model = $app->bootComponent('com_finder')->getMVCFactory($app)->createModel('Index', 'Administrator'); // Attempt to purge the index. $return = $model->purge(); // If unsuccessful then stop. if (!$return) { $message = Text::_('FINDER_CLI_INDEX_PURGE_FAILED', $model->getError()); $this->ioStyle->error($message); exit(); } $this->ioStyle->text(Text::_('FINDER_CLI_INDEX_PURGE_SUCCESS')); } /** * Run the indexer. * * @return void * * @since 2.5 */ private function index() { // Disable caching. $app = $this->getApplication(); $app->set('caching', 0); $app->set('cache_handler', 'file'); // Reset the indexer state. Indexer::resetState(); // Import the plugins. PluginHelper::importPlugin('system'); PluginHelper::importPlugin('finder'); // Starting Indexer. $this->ioStyle->text(Text::_('FINDER_CLI_STARTING_INDEXER')); // Trigger the onStartIndex event. $app->triggerEvent('onStartIndex'); // Remove the script time limit. if (\function_exists('set_time_limit')) { set_time_limit(0); } // Get the indexer state. $state = Indexer::getState(); // Setting up plugins. $this->ioStyle->text(Text::_('FINDER_CLI_SETTING_UP_PLUGINS')); // Trigger the onBeforeIndex event. $app->triggerEvent('onBeforeIndex'); // Startup reporting. $this->ioStyle->text(Text::sprintf('FINDER_CLI_SETUP_ITEMS', $state->totalItems, round(microtime(true) - $this->time, 3))); // Get the number of batches. $t = (int) $state->totalItems; $c = (int) ceil($t / $state->batchSize); $c = $c === 0 ? 1 : $c; try { // Process the batches. for ($i = 0; $i < $c; $i++) { // Set the batch start time. $this->qtime = microtime(true); // Reset the batch offset. $state->batchOffset = 0; // Trigger the onBuildIndex event. Factory::getApplication()->triggerEvent('onBuildIndex'); // Batch reporting. $text = Text::sprintf('FINDER_CLI_BATCH_COMPLETE', $i + 1, $processingTime = round(microtime(true) - $this->qtime, 3)); $this->ioStyle->text($text); if ($this->pause !== 0) { // Pausing Section $skip = !($processingTime >= $this->minimumBatchProcessingTime); $pause = 0; if ($this->pause === 'division' && $this->divisor > 0) { if (!$skip) { $pause = round($processingTime / $this->divisor); } else { $pause = 1; } } elseif ($this->pause > 0) { $pause = $this->pause; } if ($pause > 0 && !$skip) { $this->ioStyle->text(Text::sprintf('FINDER_CLI_BATCH_PAUSING', $pause)); sleep($pause); $this->ioStyle->text(Text::_('FINDER_CLI_BATCH_CONTINUING')); } if ($skip) { $this->ioStyle->text( Text::sprintf( 'FINDER_CLI_SKIPPING_PAUSE_LOW_BATCH_PROCESSING_TIME', $processingTime, $this->minimumBatchProcessingTime ) ); } // End of Pausing Section } } } catch (\Exception $e) { // Display the error $this->ioStyle->error($e->getMessage()); // Reset the indexer state. Indexer::resetState(); // Close the app $app->close($e->getCode()); } // Reset the indexer state. Indexer::resetState(); } /** * Restore static filters. * * Using the saved filter information, update the filter records * with the new taxonomy ids. * * @return void * * @since 3.3 */ private function putFilters() { $this->ioStyle->text(Text::_('FINDER_CLI_RESTORE_FILTERS')); $db = $this->db; // Use the temporary filter information to update the filter taxonomy ids. foreach ($this->filters as $filter_id => $filter) { $tids = []; foreach ($filter as $element) { // Look for the old taxonomy in the new taxonomy table. $query = $db->getQuery(true); $query ->select('t.id') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->leftJoin($db->quoteName('#__finder_taxonomy') . ' AS p ON p.id = t.parent_id') ->where($db->quoteName('t.title') . ' = ' . $db->quote($element['title'])) ->where($db->quoteName('p.title') . ' = ' . $db->quote($element['parent'])); $taxonomy = $db->setQuery($query)->loadResult(); // If we found it then add it to the list. if ($taxonomy) { $tids[] = $taxonomy; } else { $text = Text::sprintf('FINDER_CLI_FILTER_RESTORE_WARNING', $element['parent'], $element['title'], $element['filter']); $this->ioStyle->text($text); } } // Construct a comma-separated string from the taxonomy ids. $taxonomyIds = empty($tids) ? '' : implode(',', $tids); // Update the filter with the new taxonomy ids. $query = $db->getQuery(true); $query ->update($db->quoteName('#__finder_filters')) ->set($db->quoteName('data') . ' = ' . $db->quote($taxonomyIds)) ->where($db->quoteName('filter_id') . ' = ' . (int) $filter_id); $db->setQuery($query)->execute(); } $this->ioStyle->text(Text::sprintf('FINDER_CLI_RESTORE_FILTER_COMPLETED', count($this->filters))); } } PK1A#]��vĆ�(src/Console/SessionMetadataGcCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Session\MetadataManager; use Joomla\Console\Command\AbstractCommand; use Joomla\Session\SessionInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for performing session metadata garbage collection * * @since 4.0.0 */ class SessionMetadataGcCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'session:metadata:gc'; /** * The session metadata manager. * * @var MetadataManager * @since 4.0.0 */ private $metadataManager; /** * The session object. * * @var SessionInterface * @since 4.0.0 */ private $session; /** * Instantiate the command. * * @param SessionInterface $session The session object. * @param MetadataManager $metadataManager The session metadata manager. * * @since 4.0.0 */ public function __construct(SessionInterface $session, MetadataManager $metadataManager) { $this->session = $session; $this->metadataManager = $metadataManager; parent::__construct(); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $symfonyStyle = new SymfonyStyle($input, $output); $symfonyStyle->title('Running Session Metadata Garbage Collection'); $sessionExpire = $this->session->getExpire(); $this->metadataManager->deletePriorTo(time() - $sessionExpire); $symfonyStyle->success('Metadata garbage collection completed.'); return Command::FAILURE; } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> runs the garbage collection operation for Joomla session metadata \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Perform session metadata garbage collection'); $this->setHelp($help); } } PK1A#]�H*ss.src/Console/Loader/WritableLoaderInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console\Loader; use Joomla\Console\Loader\LoaderInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a writable command loader. * * @since 4.0.0 */ interface WritableLoaderInterface extends LoaderInterface { /** * Adds a command to the loader. * * @param string $commandName The name of the command to load. * @param string $className The fully qualified class name of the command. * * @return void * * @since 4.0.0 */ public function add(string $commandName, string $className); } PK1A#]�� � .src/Console/Loader/WritableContainerLoader.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console\Loader; use Joomla\Console\Command\AbstractCommand; use Psr\Container\ContainerInterface; use Symfony\Component\Console\Exception\CommandNotFoundException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * PSR-11 compatible writable command loader. * * @since 4.0.0 */ final class WritableContainerLoader implements WritableLoaderInterface { /** * The service container. * * @var ContainerInterface * @since 4.0.0 */ private $container; /** * The command name to service ID map. * * @var string[] * @since 4.0.0 */ private $commandMap; /** * Constructor. * * @param ContainerInterface $container A container from which to load command services. * @param array $commandMap An array with command names as keys and service IDs as values. * * @since 4.0.0 */ public function __construct(ContainerInterface $container, array $commandMap) { $this->container = $container; $this->commandMap = $commandMap; } /** * Adds a command to the loader. * * @param string $commandName The name of the command to load. * @param string $className The fully qualified class name of the command. * * @return void * * @since 4.0.0 */ public function add(string $commandName, string $className) { $this->commandMap[$commandName] = $className; } /** * Loads a command. * * @param string $name The command to load. * * @return AbstractCommand * * @since 4.0.0 * @throws CommandNotFoundException */ public function get(string $name): AbstractCommand { if (!$this->has($name)) { throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); } return $this->container->get($this->commandMap[$name]); } /** * Get the names of the registered commands. * * @return string[] * * @since 4.0.0 */ public function getNames(): array { return array_keys($this->commandMap); } /** * Checks if a command exists. * * @param string $name The command to check. * * @return boolean * * @since 4.0.0 */ public function has($name): bool { return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); } } PK1A#]x�M�/�/'src/Console/SetConfigurationCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseDriver; use Joomla\Registry\Registry; use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command Setting Configuration options * * @since 4.0.0 */ class SetConfigurationCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'config:set'; /** * Stores the Input Object * @var Input * @since 4.0.0 */ private $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Options Array * @var array * @since 4.0.0 */ private $options; /** * Return code if configuration is set successfully * @since 4.0.0 */ public const CONFIG_SET_SUCCESSFUL = 0; /** * Return code if configuration set failed * @since 4.0.0 */ public const CONFIG_SET_FAILED = 1; /** * Return code if config validation failed * @since 4.0.0 */ public const CONFIG_VALIDATION_FAILED = 2; /** * Return code if options are wrong * @since 4.0.0 */ public const CONFIG_OPTIONS_WRONG = 3; /** * Return code if database validation failed * @since 4.0.0 */ public const DB_VALIDATION_FAILED = 4; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $language = Factory::getLanguage(); $language->load('', JPATH_INSTALLATION, null, false, false) || $language->load('', JPATH_INSTALLATION, null, true); $language->load('com_config', JPATH_ADMINISTRATOR, null, false, false) || $language->load('com_config', JPATH_ADMINISTRATOR, null, true); $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Collects options from user input * * @param array $options Options input by users * * @return boolean * * @since 4.0.0 */ private function retrieveOptionsFromInput(array $options): bool { $collected = []; foreach ($options as $option) { if (strpos($option, '=') === false) { $this->ioStyle->error('Options and values should be separated by "="'); return false; } list($option, $value) = explode('=', $option); $collected[$option] = $value; } $this->options = $collected; return true; } /** * Validates the options provided * * @return boolean * * @since 4.0.0 */ private function validateOptions(): bool { $config = $this->getInitialConfigurationOptions(); $configs = $config->toArray(); $valid = true; array_walk( $this->options, function ($value, $key) use ($configs, &$valid) { if (!array_key_exists($key, $configs)) { $this->ioStyle->error("Can't find option *$key* in configuration list"); $valid = false; } } ); return $valid; } /** * Sets the options array * * @param string $options Options string * * @since 4.0.0 * * @return void */ public function setOptions($options) { $this->options = explode(' ', $options); } /** * Collects the options array * * @return array|mixed * * @since 4.0.0 */ public function getOptions() { return $this->cliInput->getArgument('options'); } /** * Returns Default configuration Object * * @return Registry * * @since 4.0.0 */ public function getInitialConfigurationOptions(): Registry { return (new Registry(new \JConfig())); } /** * Save the configuration file * * @param array $options Options array * * @return boolean * * @since 4.0.0 */ public function saveConfiguration($options): bool { $app = $this->getApplication(); // Check db connection encryption properties $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator'); if (!$model->save($options)) { $this->ioStyle->error(Text::_('Failed to save properties')); return false; } return true; } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addArgument( 'options', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'All the options you want to set' ); $help = "<info>%command.name%</info> sets the value for a configuration option \nUsage: <info>php %command.full_name%</info> <option>=<value>"; $this->setDescription('Set a value for a configuration option'); $this->setHelp($help); } /** * Verifies database connection * * @param array $options Options array * * @return boolean|\Joomla\Database\DatabaseInterface * * @since 4.0.0 * @throws \Exception */ public function checkDb($options): bool { // Ensure a database type was selected. if (empty($options['dbtype'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_TYPE')); return false; } // Ensure that a hostname and user name were input. if (empty($options['host']) || empty($options['user'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_DB_DETAILS')); return false; } // Validate database table prefix. if (isset($options['dbprefix']) && !preg_match('#^[a-zA-Z]+[a-zA-Z0-9_]*$#', $options['dbprefix'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_PREFIX_MSG')); return false; } // Validate length of database table prefix. if (isset($options['dbprefix']) && strlen($options['dbprefix']) > 15) { $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_TOO_LONG'), 'warning'); return false; } // Validate length of database name. if (strlen($options['db']) > 64) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_TOO_LONG')); return false; } // Validate database name. if (in_array($options['dbtype'], ['pgsql', 'postgresql'], true) && !preg_match('#^[a-zA-Z_][0-9a-zA-Z_$]*$#', $options['db'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_POSTGRES')); return false; } if (in_array($options['dbtype'], ['mysql', 'mysqli']) && preg_match('#[\\\\\/]#', $options['db'])) { $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_MYSQL')); return false; } // Workaround for UPPERCASE table prefix for PostgreSQL if (in_array($options['dbtype'], ['pgsql', 'postgresql'])) { if (isset($options['dbprefix']) && strtolower($options['dbprefix']) !== $options['dbprefix']) { $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_LOWERCASE')); return false; } } $app = $this->getApplication(); // Check db connection encryption properties $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator'); if (!$model->validateDbConnection($options)) { $this->ioStyle->error(Text::_('Failed to validate the db connection encryption properties')); return false; } // Build the connection options array. $settings = [ 'driver' => $options['dbtype'], 'host' => $options['host'], 'user' => $options['user'], 'password' => $options['password'], 'database' => $options['db'], 'prefix' => $options['dbprefix'], ]; if ((int) $options['dbencryption'] !== 0) { $settings['ssl'] = [ 'enable' => true, 'verify_server_cert' => (bool) $options['dbsslverifyservercert'], ]; foreach (['cipher', 'ca', 'key', 'cert'] as $value) { $confVal = trim($options['dbssl' . $value]); if ($confVal !== '') { $settings['ssl'][$value] = $confVal; } } } // Get a database object. try { $db = DatabaseDriver::getInstance($settings); $db->getVersion(); } catch (\Exception $e) { $this->ioStyle->error( Text::sprintf( 'Cannot connect to database, verify that you specified the correct database details %s', $e->getMessage() ) ); return false; } if ((int) $options['dbencryption'] !== 0 && empty($db->getConnectionEncryption())) { if ($db->isConnectionEncryptionSupported()) { $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_CONN_NOT_ENCRYPT')); } else { $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_SRV_NOT_SUPPORTS')); } return false; } return true; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 * @throws \Exception */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $options = $this->getOptions(); if (!$this->retrieveOptionsFromInput($options)) { return self::CONFIG_OPTIONS_WRONG; } if (!$this->validateOptions()) { return self::CONFIG_VALIDATION_FAILED; } $initialOptions = $this->getInitialConfigurationOptions()->toArray(); $combinedOptions = $this->sanitizeOptions(array_merge($initialOptions, $this->options)); if (!$this->checkDb($combinedOptions)) { return self::DB_VALIDATION_FAILED; } if ($this->saveConfiguration($combinedOptions)) { $this->ioStyle->success('Configuration set'); return self::CONFIG_SET_SUCCESSFUL; } return self::CONFIG_SET_FAILED; } /** * Sanitize the options array for boolean * * @param array $options Options array * * @return array * * @since 4.0.0 */ public function sanitizeOptions(array $options): array { foreach ($options as $key => $value) { $value = $value === 'false' ? false : $value; $value = $value === 'true' ? true : $value; $options[$key] = $value; } return $options; } } PK1A#]��P�%%%src/Console/ExtensionsListCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for listing installed extensions * * @since 4.0.0 */ class ExtensionsListCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'extension:list'; /** * Stores the installed Extensions * @var array * @since 4.0.0 */ protected $extensions; /** * Stores the Input Object * @var InputInterface * @since 4.0.0 */ protected $cliInput; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ protected $ioStyle; /** * Instantiate the command. * * @param DatabaseInterface $db Database connector * * @since 4.0.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ protected function configureIO(InputInterface $input, OutputInterface $output): void { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $this->addOption('type', null, InputOption::VALUE_REQUIRED, 'Type of the extension'); $help = "<info>%command.name%</info> lists all installed extensions \nUsage: <info>php %command.full_name% <extension_id></info> \nYou may filter on the type of extension (component, module, plugin, etc.) using the <info>--type</info> option: \n <info>php %command.full_name% --type=<type></info>"; $this->setDescription('List installed extensions'); $this->setHelp($help); } /** * Retrieves all extensions * * @return mixed * * @since 4.0.0 */ public function getExtensions() { if (!$this->extensions) { $this->setExtensions(); } return $this->extensions; } /** * Retrieves the extension from the model and sets the class variable * * @param null $extensions Array of extensions * * @return void * * @since 4.0.0 */ public function setExtensions($extensions = null): void { if (!$extensions) { $this->extensions = $this->getAllExtensionsFromDB(); } else { $this->extensions = $extensions; } } /** * Retrieves extension list from DB * * @return array * * @since 4.0.0 */ private function getAllExtensionsFromDB(): array { $db = $this->getDatabase(); $query = $db->getQuery(true); $query->select('*') ->from('#__extensions'); $db->setQuery($query); $extensions = $db->loadAssocList('extension_id'); return $extensions; } /** * Transforms extension arrays into required form * * @param array $extensions Array of extensions * * @return array * * @since 4.0.0 */ protected function getExtensionsNameAndId($extensions): array { $extInfo = []; foreach ($extensions as $key => $extension) { $manifest = json_decode($extension['manifest_cache']); $extInfo[] = [ $extension['name'], $extension['extension_id'], $manifest ? $manifest->version : '--', $extension['type'], $extension['enabled'] == 1 ? 'Yes' : 'No', ]; } return $extInfo; } /** * Filters the extension type * * @param string $type Extension type * * @return array * * @since 4.0.0 */ private function filterExtensionsBasedOn($type): array { $extensions = []; foreach ($this->extensions as $key => $extension) { if ($extension['type'] == $type) { $extensions[] = $extension; } } return $extensions; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $extensions = $this->getExtensions(); $type = $this->cliInput->getOption('type'); if ($type) { $extensions = $this->filterExtensionsBasedOn($type); } if (empty($extensions)) { $this->ioStyle->error("Cannot find extensions of the type '$type' specified."); return Command::SUCCESS; } $extensions = $this->getExtensionsNameAndId($extensions); $this->ioStyle->title('Installed Extensions'); $this->ioStyle->table(['Name', 'Extension ID', 'Version', 'Type', 'Enabled'], $extensions); return Command::SUCCESS; } } PK1A#]�UѸ��,src/Console/ExtensionDiscoverListCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for list discovered extensions * * @since 4.0.0 */ class ExtensionDiscoverListCommand extends ExtensionsListCommand { /** * The default command name * * @var string * * @since 4.0.0 */ protected static $defaultName = 'extension:discover:list'; /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> is used to list all extensions that could be installed via discoverinstall \nUsage: \n <info>php %command.full_name%</info>"; $this->setDescription('List discovered extensions'); $this->setHelp($help); } /** * Filters the extension state * * @param array $extensions The Extensions * @param string $state The Extension state * * @return array * * @since 4.0.0 */ public function filterExtensionsBasedOnState($extensions, $state): array { $filteredExtensions = []; foreach ($extensions as $key => $extension) { if ($extension['state'] === $state) { $filteredExtensions[] = $extension; } } return $filteredExtensions; } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Discovered Extensions'); $extensions = $this->getExtensions(); $state = -1; $discovered_extensions = $this->filterExtensionsBasedOnState($extensions, $state); if (empty($discovered_extensions)) { $this->ioStyle->note("There are no pending discovered extensions to install. Perhaps you need to run extension:discover first?"); return Command::SUCCESS; } $discovered_extensions = $this->getExtensionsNameAndId($discovered_extensions); $this->ioStyle->table(['Name', 'Extension ID', 'Version', 'Type', 'Enabled'], $discovered_extensions); return Command::SUCCESS; } } PK1A#]�w�W``src/Console/SiteDownCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command wrapper for getting the site into offline mode * * @since 4.0.0 */ class SiteDownCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'site:down'; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Return code if site:down failed * @since 4.0.0 */ public const SITE_DOWN_FAILED = 1; /** * Return code if site:down was successful * @since 4.0.0 */ public const SITE_DOWN_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> puts the site into offline mode \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Put the site into offline mode'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Site Offline'); $returnCode = $this->getApplication()->getCommand(SetConfigurationCommand::getDefaultName())->execute( new ArrayInput(['options' => ['offline=true']]), $output ); if ($returnCode === 0) { $this->ioStyle->success("Website is now offline"); return self::SITE_DOWN_SUCCESSFUL; } return self::SITE_DOWN_FAILED; } } PK1A#]3VI��"�"*src/Console/RemoveUserFromGroupCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\Access\Access; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to remove a user from a group * * @since 4.0.0 */ class RemoveUserFromGroupCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:removefromgroup'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $username; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Remove User From Group'); $this->username = $this->getStringFromOption('username', 'Please enter a username'); $userId = UserHelper::getUserId($this->username); if (empty($userId)) { $this->ioStyle->error("The user " . $this->username . " does not exist!"); return 1; } $user = User::getInstance($userId); $this->userGroups = $this->getGroups($user); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :userGroup'); foreach ($this->userGroups as $userGroup) { $query->bind(':userGroup', $userGroup); $db->setQuery($query); $result = $db->loadResult(); if (Access::checkGroup($userGroup, 'core.admin')) { $queryUser = $db->getQuery(true); $queryUser->select('COUNT(*)') ->from($db->quoteName('#__users', 'u')) ->leftJoin( $db->quoteName('#__user_usergroup_map', 'g'), '(' . $db->quoteName('u.id') . ' = ' . $db->quoteName('g.user_id') . ')' ) ->where($db->quoteName('g.group_id') . " = :groupId") ->where($db->quoteName('u.block') . " = 0") ->bind(':groupId', $userGroup); $db->setQuery($queryUser); $activeSuperUser = $db->loadResult(); if ($activeSuperUser < 2) { $this->ioStyle->error("Can't remove user '" . $user->username . "' from group '" . $result . "'! " . $result . " needs at least one active user!"); return Command::FAILURE; } } if (\count(Access::getGroupsByUser($user->id, false)) < 2) { $this->ioStyle->error("Can't remove '" . $user->username . "' from group '" . $result . "'! Every user needs to be a member of at least one group"); return Command::FAILURE; } if (!UserHelper::removeUserFromGroup($user->id, $userGroup)) { $this->ioStyle->error("Can't remove '" . $user->username . "' from group '" . $result . "'!"); return Command::FAILURE; } $this->ioStyle->success("Removed '" . $user->username . "' from group '" . $result . "'!"); } return Command::SUCCESS; } /** * Method to get a value from option * * @param object $user user object * * @return array * * @since 4.0.0 */ protected function getGroups($user): array { $option = $this->getApplication()->getConsoleInput()->getOption('group'); $db = $this->getDatabase(); $userGroups = Access::getGroupsByUser($user->id, false); if (!$option) { $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->whereIn($db->quoteName('id'), $userGroups); $db->setQuery($query); $result = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $result ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); $groupList = []; foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } $groupList = []; $option = explode(',', $option); foreach ($option as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . '= :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ protected function getStringFromOption($option, $question): string { $answer = (string) $this->getApplication()->getConsoleInput()->getOption($option); while (!$answer) { $answer = (string) $this->ioStyle->ask($question); } return $answer; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> removes a user from a group \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Remove a user from a group'); $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('group', null, InputOption::VALUE_OPTIONAL, 'group'); $this->setHelp($help); } } PK1A#]e�v��!�!src/Console/AddUserCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\CMS\User\User; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Joomla\Filter\InputFilter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command for adding a user * * @since 4.0.0 */ class AddUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:add'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Stores the Input Object * @var object * @since 4.0.0 */ private $cliInput; /** * The username * * @var string * * @since 4.0.0 */ private $user; /** * The password * * @var string * * @since 4.0.0 */ private $password; /** * The name * * @var string * * @since 4.0.0 */ private $name; /** * The email address * * @var string * * @since 4.0.0 */ private $email; /** * The usergroups * * @var array * * @since 4.0.0 */ private $userGroups = []; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Add User'); $this->user = $this->getStringFromOption('username', 'Please enter a username'); $this->name = $this->getStringFromOption('name', 'Please enter a name (full name of user)'); $this->email = $this->getStringFromOption('email', 'Please enter an email address'); $this->password = $this->getStringFromOption('password', 'Please enter a password'); $this->userGroups = $this->getUserGroups(); if (\in_array("error", $this->userGroups)) { $this->ioStyle->error("'" . $this->userGroups[1] . "' user group doesn't exist!"); return Command::FAILURE; } // Get filter to remove invalid characters $filter = new InputFilter(); $user = [ 'username' => $filter->clean($this->user, 'USERNAME'), 'password' => $this->password, 'name' => $filter->clean($this->name, 'STRING'), 'email' => $this->email, 'groups' => $this->userGroups, ]; $userObj = User::getInstance(); $userObj->bind($user); if (!$userObj->save()) { switch ($userObj->getError()) { case "JLIB_DATABASE_ERROR_USERNAME_INUSE": $this->ioStyle->error("The username already exists!"); break; case "JLIB_DATABASE_ERROR_EMAIL_INUSE": $this->ioStyle->error("The email address already exists!"); break; case "JLIB_DATABASE_ERROR_VALID_MAIL": $this->ioStyle->error("The email address is invalid!"); break; } return 1; } $this->ioStyle->success("User created!"); return Command::SUCCESS; } /** * Method to get groupId by groupName * * @param string $groupName name of group * * @return integer * * @since 4.0.0 */ protected function getGroupId($groupName) { $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . ' = :groupName') ->bind(':groupName', $groupName); $db->setQuery($query); return $db->loadResult(); } /** * Method to get a value from option * * @param string $option set the option name * @param string $question set the question if user enters no value to option * * @return string * * @since 4.0.0 */ public function getStringFromOption($option, $question): string { $answer = (string) $this->cliInput->getOption($option); while (!$answer) { if ($option === 'password') { $answer = (string) $this->ioStyle->askHidden($question); } else { $answer = (string) $this->ioStyle->ask($question); } } return $answer; } /** * Method to get a value from option * * @return array * * @since 4.0.0 */ protected function getUserGroups(): array { $groups = $this->getApplication()->getConsoleInput()->getOption('usergroup'); $db = $this->getDatabase(); $groupList = []; // Group names have been supplied as input arguments if (!\is_null($groups) && $groups[0]) { $groups = explode(',', $groups); foreach ($groups as $group) { $groupId = $this->getGroupId($group); if (empty($groupId)) { $this->ioStyle->error("Invalid group name '" . $group . "'"); throw new InvalidOptionException("Invalid group name " . $group); } $groupList[] = $this->getGroupId($group); } return $groupList; } // Generate select list for user $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->order($db->quoteName('id') . 'ASC'); $db->setQuery($query); $list = $db->loadColumn(); $choice = new ChoiceQuestion( 'Please select a usergroup (separate multiple groups with a comma)', $list ); $choice->setMultiselect(true); $answer = (array) $this->ioStyle->askQuestion($choice); foreach ($answer as $group) { $groupList[] = $this->getGroupId($group); } return $groupList; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will add a user \nUsage: <info>php %command.full_name%</info>"; $this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'username'); $this->addOption('name', null, InputOption::VALUE_OPTIONAL, 'full name of user'); $this->addOption('password', null, InputOption::VALUE_OPTIONAL, 'password'); $this->addOption('email', null, InputOption::VALUE_OPTIONAL, 'email address'); $this->addOption('usergroup', null, InputOption::VALUE_OPTIONAL, 'usergroup (separate multiple groups with comma ",")'); $this->setDescription('Add a user'); $this->setHelp($help); } } PK1A#]4�v�qqsrc/Console/ListUserCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command to list existing users * * @since 4.0.0 */ class ListUserCommand extends AbstractCommand { use DatabaseAwareTrait; /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'user:list'; /** * SymfonyStyle Object * @var object * @since 4.0.0 */ private $ioStyle; /** * Command constructor. * * @param DatabaseInterface $db The database * * @since 4.2.0 */ public function __construct(DatabaseInterface $db) { parent::__construct(); $this->setDatabase($db); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $db = $this->getDatabase(); $this->configureIO($input, $output); $this->ioStyle->title('List Users'); $groupsQuery = $db->getQuery(true) ->select($db->quoteName(['title', 'id'])) ->from($db->quoteName('#__usergroups')); $groups = $db->setQuery($groupsQuery)->loadAssocList('id', 'title'); $query = $db->getQuery(true); $query->select($db->quoteName(['u.id', 'u.username', 'u.name', 'u.email', 'u.block'])) ->select($query->groupConcat($query->castAs('CHAR', $db->quoteName('g.group_id'))) . ' AS ' . $db->quoteName('groups')) ->innerJoin($db->quoteName('#__user_usergroup_map', 'g'), $db->quoteName('g.user_id') . ' = ' . $db->quoteName('u.id')) ->from($db->quoteName('#__users', 'u')) ->group($db->quoteName('u.id')); $db->setQuery($query); $users = []; foreach ($db->loadAssocList() as $user) { $user["groups"] = array_map( function ($groupId) use ($groups) { return $groups[$groupId]; }, explode(",", $user["groups"]) ); $user["groups"] = implode(", ", $user["groups"]); $users[] = $user; } $this->ioStyle->table(['ID', 'Username', 'Name', 'Email', 'Blocked', 'Groups'], $users); return Command::SUCCESS; } /** * Configure the IO. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return void * * @since 4.0.0 */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Configure the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> will list all users \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('List all users'); $this->setHelp($help); } } PK1A#]`�j�OOsrc/Console/SiteUpCommand.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Console; use Joomla\Console\Command\AbstractCommand; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Console command wrapper for getting the site into offline mode * * @since 4.0.0 */ class SiteUpCommand extends AbstractCommand { /** * The default command name * * @var string * @since 4.0.0 */ protected static $defaultName = 'site:up'; /** * SymfonyStyle Object * @var SymfonyStyle * @since 4.0.0 */ private $ioStyle; /** * Return code if site:up failed * @since 4.0.0 */ public const SITE_UP_FAILED = 1; /** * Return code if site:up was successful * @since 4.0.0 */ public const SITE_UP_SUCCESSFUL = 0; /** * Configures the IO * * @param InputInterface $input Console Input * @param OutputInterface $output Console Output * * @return void * * @since 4.0.0 * */ private function configureIO(InputInterface $input, OutputInterface $output) { $this->ioStyle = new SymfonyStyle($input, $output); } /** * Initialise the command. * * @return void * * @since 4.0.0 */ protected function configure(): void { $help = "<info>%command.name%</info> puts the site into online mode \nUsage: <info>php %command.full_name%</info>"; $this->setDescription('Put the site into online mode'); $this->setHelp($help); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 4.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $this->configureIO($input, $output); $this->ioStyle->title('Site Online'); $returnCode = $this->getApplication()->getCommand(SetConfigurationCommand::getDefaultName())->execute( new ArrayInput(['options' => ['offline=false']]), $output ); if ($returnCode === 0) { $this->ioStyle->success("Website is now online"); return self::SITE_UP_SUCCESSFUL; } return self::SITE_UP_FAILED; } } PK1A#]>,Y�� � src/Pathway/SitePathway.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Pathway; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\CMS\Language\Multilanguage; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to manage the site application pathway. * * @since 1.5 */ class SitePathway extends Pathway { /** * Class constructor. * * @param SiteApplication $app Application Object * * @since 1.5 */ public function __construct(SiteApplication $app = null) { $this->pathway = []; $app = $app ?: Factory::getContainer()->get(SiteApplication::class); $menu = $app->getMenu(); $lang = Factory::getLanguage(); if ($item = $menu->getActive()) { $menus = $menu->getMenu(); // Look for the home menu if (Multilanguage::isEnabled()) { $home = $menu->getDefault($lang->getTag()); } else { $home = $menu->getDefault(); } if (\is_object($home) && ($item->id != $home->id)) { foreach ($item->tree as $menupath) { $link = $menu->getItem($menupath); switch ($link->type) { case 'separator': case 'heading': $url = null; break; case 'url': if ((strpos($link->link, 'index.php?') === 0) && (strpos($link->link, 'Itemid=') === false)) { // If this is an internal Joomla link, ensure the Itemid is set. $url = $link->link . '&Itemid=' . $link->id; } else { $url = $link->link; } break; case 'alias': // If this is an alias use the item id stored in the parameters to make the link. $url = 'index.php?Itemid=' . $link->getParams()->get('aliasoptions'); break; default: $url = $link->link . '&Itemid=' . $link->id; break; } $this->addItem($menus[$menupath]->title, $url); } } } } } PK1A#]����src/Pathway/Pathway.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Pathway; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to maintain a pathway. * * The user's navigated path within the application. * * @since 1.5 */ class Pathway { /** * Array to hold the pathway item objects * * @var array * @since 4.0.0 */ protected $pathway = []; /** * Integer number of items in the pathway * * @var integer * @since 4.0.0 */ protected $count = 0; /** * Pathway instances container. * * @var Pathway[] * @since 1.7 */ protected static $instances = []; /** * Returns a Pathway object * * @param string $client The name of the client * * @return Pathway A Pathway object. * * @since 1.5 * @throws \RuntimeException * * @deprecated 4.3 will be removed in 6.0 * Get the instance from the application * Example: * $app->getPathway() */ public static function getInstance($client) { if (empty(self::$instances[$client])) { // Create a Pathway object $name = ucfirst($client) . 'Pathway'; if (!Factory::getContainer()->has($name)) { throw new \RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_PATHWAY_LOAD', $client), 500); } self::$instances[$client] = Factory::getContainer()->get($name); } return self::$instances[$client]; } /** * Return the Pathway items array * * @return array Array of pathway items * * @since 1.5 */ public function getPathway() { // Use array_values to reset the array keys numerically return array_values($this->pathway); } /** * Set the Pathway items array. * * @param array $pathway An array of pathway objects. * * @return array The previous pathway data. * * @since 1.5 */ public function setPathway($pathway) { $oldPathway = $this->pathway; // Set the new pathway. $this->pathway = array_values((array) $pathway); return array_values($oldPathway); } /** * Create and return an array of the pathway names. * * @return array Array of names of pathway items * * @since 1.5 */ public function getPathwayNames() { $names = []; // Build the names array using just the names of each pathway item foreach ($this->pathway as $item) { $names[] = $item->name; } // Use array_values to reset the array keys numerically return array_values($names); } /** * Create and add an item to the pathway. * * @param string $name The name of the item. * @param string $link The link to the item. * * @return boolean True on success * * @since 1.5 */ public function addItem($name, $link = '') { $ret = false; if ($this->pathway[] = $this->makeItem($name, $link)) { $ret = true; $this->count++; } return $ret; } /** * Set item name. * * @param integer $id The id of the item on which to set the name. * @param string $name The name to set. * * @return boolean True on success * * @since 1.5 */ public function setItemName($id, $name) { $ret = false; if (isset($this->pathway[$id])) { $this->pathway[$id]->name = $name; $ret = true; } return $ret; } /** * Create and return a new pathway object. * * @param string $name Name of the item * @param string $link Link to the item * * @return \stdClass Pathway item object * * @since 3.1 */ protected function makeItem($name, $link) { $item = new \stdClass(); $item->name = html_entity_decode($name, ENT_COMPAT, 'UTF-8'); $item->link = $link; return $item; } } PK1A#]�8�W##src/Changelog/Changelog.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Changelog; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Version; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Changelog class. * * @since 4.0.0 */ class Changelog extends CMSObject { /** * Update manifest `<element>` element * * @var string * @since 4.0.0 */ protected $element; /** * Update manifest `<type>` element * * @var string * @since 4.0.0 */ protected $type; /** * Update manifest `<version>` element * * @var string * @since 4.0.0 */ protected $version; /** * Update manifest `<security>` element * * @var array * @since 4.0.0 */ protected $security = []; /** * Update manifest `<fix>` element * * @var array * @since 4.0.0 */ protected $fix = []; /** * Update manifest `<language>` element * * @var array * @since 4.0.0 */ protected $language = []; /** * Update manifest `<addition>` element * * @var array * @since 4.0.0 */ protected $addition = []; /** * Update manifest `<change>` elements * * @var array * @since 4.0.0 */ protected $change = []; /** * Update manifest `<remove>` element * * @var array * @since 4.0.0 */ protected $remove = []; /** * Update manifest `<maintainer>` element * * @var array * @since 4.0.0 */ protected $note = []; /** * List of node items * * @var array * @since 4.0.0 */ private $items = []; /** * Resource handle for the XML Parser * * @var resource * @since 4.0.0 */ protected $xmlParser; /** * Element call stack * * @var array * @since 4.0.0 */ protected $stack = ['base']; /** * Object containing the current update data * * @var \stdClass * @since 4.0.0 */ protected $currentChangelog; /** * The version to match the changelog * * @var string * @since 4.0.0 */ private $matchVersion = ''; /** * Object containing the latest changelog data * * @var \stdClass * @since 4.0.0 */ protected $latest; /** * Gets the reference to the current direct parent * * @return string * * @since 4.0.0 */ protected function getStackLocation() { return implode('->', $this->stack); } /** * Get the last position in stack count * * @return string * * @since 4.0.0 */ protected function getLastTag() { return $this->stack[\count($this->stack) - 1]; } /** * Set the version to match. * * @param string $version The version to match * * @return void * * @since 4.0.0 */ public function setVersion(string $version) { $this->matchVersion = $version; } /** * XML Start Element callback * * @param object $parser Parser object * @param string $name Name of the tag found * @param array $attrs Attributes of the tag * * @return void * * @note This is public because it is called externally * @since 1.7.0 */ public function startElement($parser, $name, $attrs = []) { $this->stack[] = $name; $tag = $this->getStackLocation(); // Reset the data if (isset($this->$tag)) { $this->$tag->data = ''; } $name = strtolower($name); if (!isset($this->currentChangelog->$name)) { $this->currentChangelog->$name = new \stdClass(); } $this->currentChangelog->$name->data = ''; foreach ($attrs as $key => $data) { $key = strtolower($key); $this->currentChangelog->$name->$key = $data; } } /** * Callback for closing the element * * @param object $parser Parser object * @param string $name Name of element that was closed * * @return void * * @note This is public because it is called externally * @since 1.7.0 */ public function endElement($parser, $name) { array_pop($this->stack); switch ($name) { case 'SECURITY': case 'FIX': case 'LANGUAGE': case 'ADDITION': case 'CHANGE': case 'REMOVE': case 'NOTE': $name = strtolower($name); $this->currentChangelog->$name->data = $this->items; $this->items = []; break; case 'CHANGELOG': if (version_compare($this->currentChangelog->version->data, $this->matchVersion, '==') === true) { $this->latest = $this->currentChangelog; } // No version match, empty it $this->currentChangelog = new \stdClass(); break; case 'CHANGELOGS': // If the latest item is set then we transfer it to where we want to if (isset($this->latest)) { foreach (get_object_vars($this->latest) as $key => $val) { $this->$key = $val; } unset($this->latest); unset($this->currentChangelog); } elseif (isset($this->currentChangelog)) { // The update might be for an older version of j! unset($this->currentChangelog); } break; } } /** * Character Parser Function * * @param object $parser Parser object. * @param object $data The data. * * @return void * * @note This is public because its called externally. * @since 1.7.0 */ public function characterData($parser, $data) { $tag = $this->getLastTag(); switch ($tag) { case 'ITEM': $this->items[] = $data; break; case 'SECURITY': case 'FIX': case 'LANGUAGE': case 'ADDITION': case 'CHANGE': case 'REMOVE': case 'NOTE': break; default: // Throw the data for this item together $tag = strtolower($tag); if (isset($this->currentChangelog->$tag)) { $this->currentChangelog->$tag->data .= $data; } break; } } /** * Loads an XML file from a URL. * * @param string $url The URL. * * @return boolean True on success * * @since 4.0.0 */ public function loadFromXml($url) { $version = new Version(); $httpOption = new Registry(); $httpOption->set('userAgent', $version->getUserAgent('Joomla', true, false)); try { $http = HttpFactory::getHttp($httpOption); $response = $http->get($url); } catch (\RuntimeException $e) { $response = null; } if ($response === null || $response->code !== 200) { // @todo: Add a 'mark bad' setting here somehow Log::add(Text::sprintf('JLIB_UPDATER_ERROR_EXTENSION_OPEN_URL', $url), Log::WARNING, 'jerror'); return false; } $this->currentChangelog = new \stdClass(); $this->xmlParser = xml_parser_create(''); xml_set_object($this->xmlParser, $this); xml_set_element_handler($this->xmlParser, 'startElement', 'endElement'); xml_set_character_data_handler($this->xmlParser, 'characterData'); if (!xml_parse($this->xmlParser, $response->body)) { Log::add( sprintf( 'XML error: %s at line %d', xml_error_string(xml_get_error_code($this->xmlParser)), xml_get_current_line_number($this->xmlParser) ), Log::WARNING, 'updater' ); return false; } xml_parser_free($this->xmlParser); return true; } } PK1A#]���Kllsrc/Help/Help.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Help; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Version; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Help system class * * @since 1.5 */ class Help { /** * Create a URL for a given help key reference * * @param string $ref The name of the help screen (its key reference) * @param boolean $useComponent Use the help file in the component directory * @param string $override Use this URL instead of any other * @param string $component Name of component (or null for current component) * * @return string * * @since 1.5 */ public static function createUrl($ref, $useComponent = false, $override = null, $component = null) { $local = false; $app = Factory::getApplication(); if ($component === null) { $component = ApplicationHelper::getComponentName(); } // Determine the location of the help file. At this stage the URL // can contain substitution codes that will be replaced later. if ($override) { $url = $override; } else { // Get the global help URL. $url = $app->get('helpurl'); // Component help URL overrides user and global. if ($useComponent) { // Look for help URL in component parameters. $params = ComponentHelper::getParams($component); $url = $params->get('helpURL'); if ($url == '') { $local = true; $url = 'components/{component}/help/{language}/{keyref}'; } } // Set up a local help URL. if (!$url) { $local = true; $url = 'help/{language}/{keyref}'; } } // If the URL is local then make sure we have a valid file extension on the URL. if ($local) { if (!preg_match('#\.html$|\.xml$#i', $ref)) { $url .= '.html'; } } /* * Replace substitution codes in the URL. */ $lang = Factory::getLanguage(); $version = new Version(); $jver = explode('.', $version->getShortVersion()); $jlang = explode('-', $lang->getTag()); $debug = $lang->setDebug(false); $keyref = Text::_($ref); $lang->setDebug($debug); // Replace substitution codes in help URL. $search = [ // Application name (eg. 'Administrator') '{app}', // Component name (eg. 'com_content') '{component}', // Help screen key reference '{keyref}', // Full language code (eg. 'en-GB') '{language}', // Short language code (eg. 'en') '{langcode}', // Region code (eg. 'GB') '{langregion}', // Joomla major version number '{major}', // Joomla minor version number '{minor}', // Joomla maintenance version number '{maintenance}', ]; $replace = [ // {app} $app->getName(), // {component} $component, // {keyref} $keyref, // {language} $lang->getTag(), // {langcode} $jlang[0], // {langregion} $jlang[1], // {major} $jver[0], // {minor} $jver[1], // {maintenance} $jver[2], ]; // If the help file is local then check it exists. // If it doesn't then fallback to English. if ($local) { $try = str_replace($search, $replace, $url); if (!is_file(JPATH_BASE . '/' . $try)) { $replace[3] = 'en-GB'; $replace[4] = 'en'; $replace[5] = 'GB'; } } $url = str_replace($search, $replace, $url); return $url; } /** * Builds a list of the help sites which can be used in a select option. * * @param string $pathToXml Path to an XML file. * * @return array An array of arrays (text, value, selected). * * @since 1.5 */ public static function createSiteList($pathToXml) { $list = []; $xml = false; if (!empty($pathToXml)) { $xml = simplexml_load_file($pathToXml); } if (!$xml) { $option['text'] = 'English (GB) help.joomla.org'; $option['value'] = 'https://help.joomla.org'; $list[] = (object) $option; } else { $option = []; foreach ($xml->sites->site as $site) { $option['text'] = (string) $site; $option['value'] = (string) $site->attributes()->url; $list[] = (object) $option; } } return $list; } } PK1A#]�3�g��src/Layout/BaseLayout.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Layout; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for rendering a display layout * * @link https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout * @since 3.0 */ class BaseLayout implements LayoutInterface { /** * Options object * * @var Registry * @since 3.2 */ protected $options = null; /** * Data for the layout * * @var array * @since 3.5 */ protected $data = []; /** * Debug information messages * * @var array * @since 3.2 */ protected $debugMessages = []; /** * Set the options * * @param array|Registry $options Array / Registry object with the options to load * * @return BaseLayout Instance of $this to allow chaining. * * @since 3.2 */ public function setOptions($options = null) { // Received Registry if ($options instanceof Registry) { $this->options = $options; } elseif (\is_array($options)) { // Received array $this->options = new Registry($options); } else { $this->options = new Registry(); } return $this; } /** * Get the options * * @return Registry Object with the options * * @since 3.2 */ public function getOptions() { // Always return a Registry instance if (!($this->options instanceof Registry)) { $this->resetOptions(); } return $this->options; } /** * Function to empty all the options * * @return BaseLayout Instance of $this to allow chaining. * * @since 3.2 */ public function resetOptions() { return $this->setOptions(null); } /** * Method to escape output. * * @param string $output The output to escape. * * @return string The escaped output. * * @note the ENT_COMPAT flag was replaced by ENT_QUOTES in Joomla 4.0 to also escape single quotes * * @since 3.0 */ public function escape($output) { return $output === null ? '' : htmlspecialchars($output, ENT_QUOTES, 'UTF-8'); } /** * Get the debug messages array * * @return array * * @since 3.2 */ public function getDebugMessages() { return $this->debugMessages; } /** * Method to render the layout. * * @param array $displayData Array of properties available for use inside the layout file to build the displayed output * * @return string The necessary HTML to display the layout * * @since 3.0 */ public function render($displayData) { // Automatically merge any previously data set if $displayData is an array if (\is_array($displayData)) { $displayData = array_merge($this->data, $displayData); } return ''; } /** * Render the list of debug messages * * @return string Output text/HTML code * * @since 3.2 */ public function renderDebugMessages() { return implode("\n", $this->debugMessages); } /** * Add a debug message to the debug messages array * * @param string $message Message to save * * @return self * * @since 3.2 */ public function addDebugMessage($message) { $this->debugMessages[] = $message; return $this; } /** * Clear the debug messages array * * @return self * * @since 3.5 */ public function clearDebugMessages() { $this->debugMessages = []; return $this; } /** * Render a layout with debug info * * @param mixed $data Data passed to the layout * * @return string * * @since 3.5 */ public function debug($data = []) { $this->setDebug(true); $output = $this->render($data); $this->setDebug(false); return $output; } /** * Method to get the value from the data array * * @param string $key Key to search for in the data array * @param mixed $defaultValue Default value to return if the key is not set * * @return mixed Value from the data array | defaultValue if doesn't exist * * @since 3.5 */ public function get($key, $defaultValue = null) { return $this->data[$key] ?? $defaultValue; } /** * Get the data being rendered * * @return array * * @since 3.5 */ public function getData() { return $this->data; } /** * Check if debug mode is enabled * * @return boolean * * @since 3.5 */ public function isDebugEnabled() { return $this->getOptions()->get('debug', false) === true; } /** * Method to set a value in the data array. Example: $layout->set('items', $items); * * @param string $key Key for the data array * @param mixed $value Value to assign to the key * * @return self * * @since 3.5 */ public function set($key, $value) { $this->data[(string) $key] = $value; return $this; } /** * Set the data passed the layout * * @param array $data Array with the data for the layout * * @return self * * @since 3.5 */ public function setData(array $data) { $this->data = $data; return $this; } /** * Change the debug mode * * @param boolean $debug Enable / Disable debug * * @return self * * @since 3.5 */ public function setDebug($debug) { $this->options->set('debug', (bool) $debug); return $this; } } PK1A#]w��c� � src/Layout/LayoutHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Layout; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Helper to render a Layout object, storing a base path * * @link https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout * @since 3.1 */ class LayoutHelper { /** * A default base path that will be used if none is provided when calling the render method. * Note that FileLayout itself will defaults to JPATH_ROOT . '/layouts' if no basePath is supplied at all * * @var string * @since 3.1 */ public static $defaultBasePath = ''; /** * Method to render a layout with debug info * * @param string $layoutFile Dot separated path to the layout file, relative to base path * @param mixed $displayData Object which properties are used inside the layout file to build displayed output * @param string $basePath Base path to use when loading layout files * @param mixed $options Optional custom options to load. Registry or array format * * @return string * * @since 3.5 */ public static function debug($layoutFile, $displayData = null, $basePath = '', $options = null) { $basePath = empty($basePath) ? self::$defaultBasePath : $basePath; // Make sure we send null to FileLayout if no path set $basePath = empty($basePath) ? null : $basePath; $layout = new FileLayout($layoutFile, $basePath, $options); return $layout->debug($displayData); } /** * Method to render the layout. * * @param string $layoutFile Dot separated path to the layout file, relative to base path * @param mixed $displayData Object which properties are used inside the layout file to build displayed output * @param string $basePath Base path to use when loading layout files * @param mixed $options Optional custom options to load. Registry or array format * * @return string * * @since 3.1 */ public static function render($layoutFile, $displayData = null, $basePath = '', $options = null) { $basePath = empty($basePath) ? self::$defaultBasePath : $basePath; // Make sure we send null to FileLayout if no path set $basePath = empty($basePath) ? null : $basePath; $layout = new FileLayout($layoutFile, $basePath, $options); return $layout->render($displayData); } } PK1A#]�^��DDsrc/Layout/LayoutInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Layout; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to handle display layout * * @link https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout * @since 3.0 */ interface LayoutInterface { /** * Method to escape output. * * @param string $output The output to escape. * * @return string The escaped output. * * @since 3.0 */ public function escape($output); /** * Method to render the layout. * * @param array $displayData Array of properties available for use inside the layout file to build the displayed output * * @return string The rendered layout. * * @since 3.0 */ public function render($displayData); } PK1A#]���~AAsrc/Layout/FileLayout.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Layout; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\Path; use Joomla\CMS\Version; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for rendering a display layout * loaded from a layout file * * @link https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout * @since 3.0 */ class FileLayout extends BaseLayout { /** * Cached layout paths * * @var array * @since 3.5 */ protected static $cache = []; /** * Dot separated path to the layout file, relative to base path * * @var string * @since 3.0 */ protected $layoutId = ''; /** * Base path to use when loading layout files * * @var string * @since 3.0 */ protected $basePath = null; /** * Full path to actual layout files, after possible template override check * * @var string * @since 3.0.3 */ protected $fullPath = null; /** * Paths to search for layouts * * @var array * @since 3.2 */ protected $includePaths = []; /** * Method to instantiate the file-based layout. * * @param string $layoutId Dot separated path to the layout file, relative to base path * @param string $basePath Base path to use when loading layout files * @param mixed $options Optional custom options to load. Registry or array format [@since 3.2] * * @since 3.0 */ public function __construct($layoutId, $basePath = null, $options = null) { // Initialise / Load options $this->setOptions($options); // Main properties $this->setLayoutId($layoutId); $this->basePath = $basePath; // Init Environment $this->setComponent($this->options->get('component', 'auto')); $this->setClient($this->options->get('client', 'auto')); } /** * Method to render the layout. * * @param array $displayData Array of properties available for use inside the layout file to build the displayed output * * @return string The necessary HTML to display the layout * * @since 3.0 */ public function render($displayData = []) { $this->clearDebugMessages(); // Inherit base output from parent class $layoutOutput = ''; // Automatically merge any previously data set if $displayData is an array if (\is_array($displayData)) { $displayData = array_merge($this->data, $displayData); } // Check possible overrides, and build the full path to layout file $path = $this->getPath(); if ($this->isDebugEnabled()) { echo '<pre>' . $this->renderDebugMessages() . '</pre>'; } // Nothing to show if (empty($path)) { return $layoutOutput; } ob_start(); include $path; $layoutOutput .= ob_get_contents(); ob_end_clean(); return $layoutOutput; } /** * Method to finds the full real file path, checking possible overrides * * @return string The full path to the layout file * * @since 3.0 */ protected function getPath() { $layoutId = $this->getLayoutId(); $includePaths = $this->getIncludePaths(); $suffixes = $this->getSuffixes(); $this->addDebugMessage('<strong>Layout:</strong> ' . $this->layoutId); if (!$layoutId) { $this->addDebugMessage('<strong>There is no active layout</strong>'); return; } if (!$includePaths) { $this->addDebugMessage('<strong>There are no folders to search for layouts:</strong> ' . $layoutId); return; } $hash = md5( json_encode( [ 'paths' => $includePaths, 'suffixes' => $suffixes, ] ) ); if (!empty(static::$cache[$layoutId][$hash])) { $this->addDebugMessage('<strong>Cached path:</strong> ' . static::$cache[$layoutId][$hash]); return static::$cache[$layoutId][$hash]; } $this->addDebugMessage('<strong>Include Paths:</strong> ' . print_r($includePaths, true)); // Search for suffixed versions. Example: tags.j31.php if ($suffixes) { $this->addDebugMessage('<strong>Suffixes:</strong> ' . print_r($suffixes, true)); foreach ($suffixes as $suffix) { $rawPath = str_replace('.', '/', $this->layoutId) . '.' . $suffix . '.php'; $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath); if ($foundLayout = Path::find($this->includePaths, $rawPath)) { $this->addDebugMessage('<strong>Found layout:</strong> ' . $this->fullPath); static::$cache[$layoutId][$hash] = $foundLayout; return static::$cache[$layoutId][$hash]; } } } // Standard version $rawPath = str_replace('.', '/', $this->layoutId) . '.php'; $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath); $foundLayout = Path::find($this->includePaths, $rawPath); if (!$foundLayout) { $this->addDebugMessage('<strong>Unable to find layout: </strong> ' . $layoutId); return; } $this->addDebugMessage('<strong>Found layout:</strong> ' . $foundLayout); static::$cache[$layoutId][$hash] = $foundLayout; return static::$cache[$layoutId][$hash]; } /** * Add one path to include in layout search. Proxy of addIncludePaths() * * @param string|string[] $path The path to search for layouts * * @return self * * @since 3.2 */ public function addIncludePath($path) { $this->addIncludePaths($path); return $this; } /** * Add one or more paths to include in layout search * * @param string|string[] $paths The path or array of paths to search for layouts * * @return self * * @since 3.2 */ public function addIncludePaths($paths) { if (empty($paths)) { return $this; } $includePaths = $this->getIncludePaths(); if (\is_array($paths)) { $includePaths = array_unique(array_merge($paths, $includePaths)); } else { array_unshift($includePaths, $paths); } $this->setIncludePaths($includePaths); return $this; } /** * Clear the include paths * * @return self * * @since 3.5 */ public function clearIncludePaths() { $this->includePaths = []; return $this; } /** * Get the active include paths * * @return array * * @since 3.5 */ public function getIncludePaths() { if (empty($this->includePaths)) { $this->includePaths = $this->getDefaultIncludePaths(); } return $this->includePaths; } /** * Get the active layout id * * @return string * * @since 3.5 */ public function getLayoutId() { return $this->layoutId; } /** * Get the active suffixes * * @return array * * @since 3.5 */ public function getSuffixes() { return $this->getOptions()->get('suffixes', []); } /** * Load the automatically generated language suffixes. * Example: array('es-ES', 'es', 'ltr') * * @return self * * @since 3.5 */ public function loadLanguageSuffixes() { $lang = Factory::getLanguage(); $langTag = $lang->getTag(); $langParts = explode('-', $langTag); $suffixes = [$langTag, $langParts[0]]; $suffixes[] = $lang->isRtl() ? 'rtl' : 'ltr'; $this->setSuffixes($suffixes); return $this; } /** * Load the automatically generated version suffixes. * Example: array('j311', 'j31', 'j3') * * @return self * * @since 3.5 */ public function loadVersionSuffixes() { $cmsVersion = new Version(); // Example j311 $fullVersion = 'j' . str_replace('.', '', $cmsVersion->getShortVersion()); // Create suffixes like array('j311', 'j31', 'j3') $suffixes = [ $fullVersion, substr($fullVersion, 0, 3), substr($fullVersion, 0, 2), ]; $this->setSuffixes(array_unique($suffixes)); return $this; } /** * Remove one path from the layout search * * @param string $path The path to remove from the layout search * * @return self * * @since 3.2 */ public function removeIncludePath($path) { $this->removeIncludePaths($path); return $this; } /** * Remove one or more paths to exclude in layout search * * @param string $paths The path or array of paths to remove for the layout search * * @return self * * @since 3.2 */ public function removeIncludePaths($paths) { if (!empty($paths)) { $paths = (array) $paths; $this->includePaths = array_diff($this->includePaths, $paths); } return $this; } /** * Validate that the active component is valid * * @param string $option URL Option of the component. Example: com_content * * @return boolean * * @since 3.2 */ protected function validComponent($option = null) { // By default we will validate the active component $component = $option ?? $this->options->get('component', null); // Valid option format if (!empty($component) && substr_count($component, 'com_')) { // Latest check: component exists and is enabled return ComponentHelper::isEnabled($component); } return false; } /** * Change the component for the search paths for layouts * * @param string $option URL Option of the component. Example: com_content * * @return void * * @since 3.2 */ public function setComponent($option) { $component = null; switch ((string) $option) { case 'none': $component = null; break; case 'auto': $component = ApplicationHelper::getComponentName(); break; default: $component = $option; break; } // Extra checks if (!$this->validComponent($component)) { $component = null; } $this->options->set('component', $component); // Refresh include paths $this->clearIncludePaths(); } /** * Function to initialise the application client * * @param mixed $client Frontend: 'site' or 0 | Backend: 'admin' or 1 * * @return void * * @since 3.2 */ public function setClient($client) { // Force string conversion to avoid unexpected states switch ((string) $client) { case 'site': case '0': $client = 0; break; case 'admin': case '1': $client = 1; break; default: $client = (int) Factory::getApplication()->isClient('administrator'); break; } $this->options->set('client', $client); // Refresh include paths $this->clearIncludePaths(); } /** * Set the active layout id * * @param string $layoutId Layout identifier * * @return self * * @since 3.5 */ public function setLayoutId($layoutId) { $this->layoutId = $layoutId; $this->fullPath = null; return $this; } /** * Get the default array of include paths * * @return array * * @since 3.5 */ public function getDefaultIncludePaths() { // Get the template $app = Factory::getApplication(); $templateName = $this->options->get('template'); if ($templateName) { // Check template name in the options $template = (object) [ 'template' => $templateName, 'parent' => '', ]; } elseif ($app->isClient('site') || $app->isClient('administrator')) { // Try to get a default template $template = $app->getTemplate(true); } else { // Template not found $template = false; } // Reset includePaths $paths = []; // (1 - highest priority) Received a custom high priority path if ($this->basePath !== null) { $paths[] = rtrim($this->basePath, DIRECTORY_SEPARATOR); } // Component layouts & overrides if exist $component = $this->options->get('component', null); if (!empty($component)) { if ($template) { // (2) Component template overrides path $paths[] = JPATH_THEMES . '/' . $template->template . '/html/layouts/' . $component; if (!empty($template->parent)) { // (2.a) Component template overrides path for an inherited template using the parent $paths[] = JPATH_THEMES . '/' . $template->parent . '/html/layouts/' . $component; } } // (3) Component path if ($this->options->get('client') == 0) { $paths[] = JPATH_SITE . '/components/' . $component . '/layouts'; } else { $paths[] = JPATH_ADMINISTRATOR . '/components/' . $component . '/layouts'; } } if ($template) { // (4) Standard Joomla! layouts overridden $paths[] = JPATH_THEMES . '/' . $template->template . '/html/layouts'; if (!empty($template->parent)) { // (4.a) Component template overrides path for an inherited template using the parent $paths[] = JPATH_THEMES . '/' . $template->parent . '/html/layouts'; } } // (5 - lower priority) Frontend base layouts $paths[] = JPATH_ROOT . '/layouts'; return $paths; } /** * Set the include paths to search for layouts * * @param array $paths Array with paths to search in * * @return self * * @since 3.5 */ public function setIncludePaths($paths) { $this->includePaths = (array) $paths; return $this; } /** * Set suffixes to search layouts * * @param array $suffixes Array of suffixes to utilise * * @return self * * @since 3.5 */ public function setSuffixes(array $suffixes) { $this->options->set('suffixes', $suffixes); return $this; } /** * Render a layout with the same include paths & options * * @param string $layoutId The identifier for the sublayout to be searched in a subfolder with the name of the current layout * @param mixed $displayData Data to be rendered * * @return string The necessary HTML to display the layout * * @since 3.2 */ public function sublayout($layoutId, $displayData) { // Sublayouts are searched in a subfolder with the name of the current layout if (!empty($this->layoutId)) { $layoutId = $this->layoutId . '.' . $layoutId; } $sublayout = new static($layoutId, $this->basePath, $this->options); $sublayout->includePaths = $this->includePaths; return $sublayout->render($displayData); } } PK1A#]t�#z��src/Profiler/Profiler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Profiler; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class to assist in the process of benchmarking the execution * of sections of code to understand where time is being spent. * * @since 1.7.0 */ class Profiler { /** * @var integer The start time. * @since 3.0.0 */ protected $start = 0; /** * @var string The prefix to use in the output * @since 3.0.0 */ protected $prefix = ''; /** * @var array The buffer of profiling messages. * @since 3.0.0 */ protected $buffer = null; /** * @var array The profiling messages. * @since 3.0.0 */ protected $marks = null; /** * @var float The previous time marker * @since 3.0.0 */ protected $previousTime = 0.0; /** * @var float The previous memory marker * @since 3.0.0 */ protected $previousMem = 0.0; /** * @var array JProfiler instances container. * @since 1.7.3 */ protected static $instances = []; /** * Constructor * * @param string $prefix Prefix for mark messages * * @since 1.7.0 */ public function __construct($prefix = '') { $this->start = microtime(1); $this->prefix = $prefix; $this->marks = []; $this->buffer = []; } /** * Returns the global Profiler object, only creating it * if it doesn't already exist. * * @param string $prefix Prefix used to distinguish profiler objects. * * @return Profiler The Profiler object. * * @since 1.7.0 */ public static function getInstance($prefix = '') { if (empty(self::$instances[$prefix])) { self::$instances[$prefix] = new static($prefix); } return self::$instances[$prefix]; } /** * Output a time mark * * @param string $label A label for the time mark * * @return string * * @since 1.7.0 */ public function mark($label) { $current = microtime(1) - $this->start; $currentMem = memory_get_usage() / 1048576; $m = (object) [ 'prefix' => $this->prefix, 'time' => ($current - $this->previousTime) * 1000, 'totalTime' => ($current * 1000), 'memory' => $currentMem - $this->previousMem, 'totalMemory' => $currentMem, 'label' => $label, ]; $this->marks[] = $m; $mark = sprintf( '%s %.3f seconds (%.3f); %0.2f MB (%0.3f) - %s', $m->prefix, $m->totalTime / 1000, $m->time / 1000, $m->totalMemory, $m->memory, $m->label ); $this->buffer[] = $mark; $this->previousTime = $current; $this->previousMem = $currentMem; return $mark; } /** * Get all profiler marks. * * Returns an array of all marks created since the Profiler object * was instantiated. Marks are objects as per {@link JProfiler::mark()}. * * @return array Array of profiler marks * * @since 1.7.0 */ public function getMarks() { return $this->marks; } /** * Get all profiler mark buffers. * * Returns an array of all mark buffers created since the Profiler object * was instantiated. Marks are strings as per {@link Profiler::mark()}. * * @return array Array of profiler marks * * @since 1.7.0 */ public function getBuffer() { return $this->buffer; } /** * Sets the start time. * * @param double $startTime Unix timestamp in microseconds for setting the Profiler start time. * @param int $startMem Memory amount in bytes for setting the Profiler start memory. * * @return $this For chaining * * @since 3.0.0 */ public function setStart($startTime = 0.0, $startMem = 0) { $this->start = (float) $startTime; $this->previousMem = (int) $startMem / 1048576; return $this; } } PK1A#];���"src/Tag/TaggableTableInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tag; use Joomla\CMS\Helper\TagsHelper; use Joomla\CMS\Table\TableInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for a taggable Table class * * @since 3.10.0 */ interface TaggableTableInterface extends TableInterface { /** * Get the type alias for the tags mapping table * * The type alias generally is the internal component name with the * content type. Ex.: com_content.article * * @return string The alias as described above * * @since 4.0.0 */ public function getTypeAlias(); /** * Get the tags helper * * @return ?TagsHelper The tags helper object * * @since 4.0.0 */ public function getTagsHelper(): ?TagsHelper; /** * Set the tags helper * * @param TagsHelper $tagsHelper The tags helper object * * @return void * * @since 4.0.0 */ public function setTagsHelper(TagsHelper $tagsHelper): void; /** * Clears a set tags helper * * @return void * * @since 4.0.0 */ public function clearTagsHelper(): void; } PK1A#]�s�--src/Tag/TagServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tag; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access to component specific tagging information. * * @since 4.0.0 */ interface TagServiceInterface { /** * Adds Count Items for Tag Manager. * * @param \stdClass[] $items The content objects * @param string $extension The name of the active view. * * @return void * * @since 4.0.0 * @throws \Exception */ public function countTagItems(array $items, string $extension); } PK1A#]cy��!src/Tag/TagApiSerializerTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tag; use Joomla\CMS\Router\Route; use Joomla\CMS\Serializer\JoomlaSerializer; use Joomla\CMS\Uri\Uri; use Tobscure\JsonApi\Collection; use Tobscure\JsonApi\Relationship; use Tobscure\JsonApi\Resource; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for implementing tags in an API Serializer * * @since 4.0.0 */ trait TagApiSerializerTrait { /** * Build tags relationship * * @param \stdClass $model Item model * * @return Relationship * * @since 4.0.0 */ public function tags($model) { $resources = []; $serializer = new JoomlaSerializer('tags'); foreach ($model->tags as $id => $tagName) { $resources[] = (new Resource($id, $serializer)) ->addLink('self', Route::link('site', Uri::root() . 'api/index.php/v1/tags/' . $id)); } $collection = new Collection($resources, $serializer); return new Relationship($collection); } } PK1A#]�͖���src/Tag/TagServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tag; use Joomla\CMS\Helper\ContentHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for component tags service. * * @since 4.0.0 */ trait TagServiceTrait { /** * Adds Count Items for Tag Manager. * * @param \stdClass[] $items The content objects * @param string $extension The name of the active view. * * @return void * * @since 4.0.0 */ public function countTagItems(array $items, string $extension) { $parts = explode('.', $extension); $section = \count($parts) > 1 ? $parts[1] : null; $config = (object) [ 'related_tbl' => $this->getTableNameForSection($section), 'state_col' => $this->getStateColumnForSection($section), 'group_col' => 'tag_id', 'extension' => $extension, 'relation_type' => 'tag_assigments', ]; ContentHelper::countRelations($items, $config); } /** * Returns the table for the count items functions for the given section. * * @param string $section The section * * @return string|null * * @since 4.0.0 */ protected function getTableNameForSection(string $section = null) { return null; } /** * Returns the state column for the count items functions for the given section. * * @param string $section The section * * @return string|null * * @since 4.0.0 */ protected function getStateColumnForSection(string $section = null) { return 'state'; } } PK1A#]�H�d��src/Tag/TaggableTableTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Tag; use Joomla\CMS\Helper\TagsHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a Taggable Table Class. * * @since 3.10.0 */ trait TaggableTableTrait { /** * The tags helper property * * @var TagsHelper * @since 4.0.0 * @note The tags helper property is set to public for backwards compatibility for Joomla 4.0. It will be made a * protected property in Joomla 5.0 */ public $tagsHelper; /** * Get the tags helper * * @return TagsHelper The tags helper object * * @since 4.0.0 */ public function getTagsHelper(): ?TagsHelper { return $this->tagsHelper; } /** * Set the tags helper * * @param TagsHelper $tagsHelper The tags helper to be set * * @return void * * @since 4.0.0 */ public function setTagsHelper(TagsHelper $tagsHelper): void { $this->tagsHelper = $tagsHelper; } /** * Clears the tags helper * * @return void * * @since 4.0.0 */ public function clearTagsHelper(): void { $this->tagsHelper = null; } } PK1A#]�l�v$v$src/Menu/SiteMenu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\CMS\Language\Language; use Joomla\CMS\Language\Multilanguage; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseDriver; use Joomla\Database\Exception\ExecutionFailureException; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Menu class * * @since 1.5 */ class SiteMenu extends AbstractMenu implements CacheControllerFactoryAwareInterface { use CacheControllerFactoryAwareTrait; /** * Application object * * @var CMSApplication * @since 3.5 */ protected $app; /** * Database driver * * @var DatabaseDriver * @since 3.5 */ protected $db; /** * Language object * * @var Language * @since 3.5 */ protected $language; /** * Class constructor * * @param array $options An array of configuration options. * * @since 1.5 */ public function __construct($options = []) { // Extract the internal dependencies before calling the parent constructor since it calls $this->load() $this->app = isset($options['app']) && $options['app'] instanceof CMSApplication ? $options['app'] : Factory::getApplication(); $this->language = isset($options['language']) && $options['language'] instanceof Language ? $options['language'] : Factory::getLanguage(); if (!isset($options['db']) || !($options['db'] instanceof DatabaseDriver)) { @trigger_error(sprintf('Database will be mandatory in 5.0.'), E_USER_DEPRECATED); $options['db'] = Factory::getContainer()->get(DatabaseDriver::class); } $this->db = $options['db']; parent::__construct($options); } /** * Loads the entire menu table into memory. * * @return boolean True on success, false on failure * * @since 1.5 */ public function load() { $loader = function () { $currentDate = Factory::getDate()->toSql(); $query = $this->db->getQuery(true) ->select( $this->db->quoteName( [ 'm.id', 'm.menutype', 'm.title', 'm.alias', 'm.note', 'm.link', 'm.type', 'm.level', 'm.language', 'm.browserNav', 'm.access', 'm.params', 'm.home', 'm.img', 'm.template_style_id', 'm.component_id', 'm.parent_id', ] ) ) ->select( $this->db->quoteName( [ 'm.path', 'e.element', ], [ 'route', 'component', ] ) ) ->from($this->db->quoteName('#__menu', 'm')) ->join( 'LEFT', $this->db->quoteName('#__extensions', 'e'), $this->db->quoteName('m.component_id') . ' = ' . $this->db->quoteName('e.extension_id') ) ->where( [ $this->db->quoteName('m.published') . ' = 1', $this->db->quoteName('m.parent_id') . ' > 0', $this->db->quoteName('m.client_id') . ' = 0', ] ) ->extendWhere( 'AND', [ $this->db->quoteName('m.publish_up') . ' IS NULL', $this->db->quoteName('m.publish_up') . ' <= :currentDate1', ], 'OR' ) ->bind(':currentDate1', $currentDate) ->extendWhere( 'AND', [ $this->db->quoteName('m.publish_down') . ' IS NULL', $this->db->quoteName('m.publish_down') . ' >= :currentDate2', ], 'OR' ) ->bind(':currentDate2', $currentDate) ->order($this->db->quoteName('m.lft')); $items = []; $iterator = $this->db->setQuery($query)->getIterator(); foreach ($iterator as $item) { $items[$item->id] = new MenuItem((array) $item); } return $items; }; try { /** @var CallbackController $cache */ $cache = $this->getCacheControllerFactory()->createCacheController('callback', ['defaultgroup' => 'com_menus']); $this->items = $cache->get($loader, [], md5(\get_class($this)), false); } catch (CacheExceptionInterface $e) { try { $this->items = $loader(); } catch (ExecutionFailureException $databaseException) { $this->app->enqueueMessage(Text::sprintf('JERROR_LOADING_MENUS', $databaseException->getMessage()), 'warning'); return false; } } catch (ExecutionFailureException $e) { $this->app->enqueueMessage(Text::sprintf('JERROR_LOADING_MENUS', $e->getMessage()), 'warning'); return false; } foreach ($this->items as &$item) { // Get parent information. $parent_tree = []; if (isset($this->items[$item->parent_id])) { $item->setParent($this->items[$item->parent_id]); $parent_tree = $this->items[$item->parent_id]->tree; } // Create tree. $parent_tree[] = $item->id; $item->tree = $parent_tree; // Create the query array. $url = str_replace('index.php?', '', $item->link); $url = str_replace('&', '&', $url); parse_str($url, $item->query); } return true; } /** * Gets menu items by attribute * * @param string $attributes The field name * @param string $values The value of the field * @param boolean $firstonly If true, only returns the first item found * * @return MenuItem|MenuItem[] An array of menu item objects or a single object if the $firstonly parameter is true * * @since 1.6 */ public function getItems($attributes, $values, $firstonly = false) { $attributes = (array) $attributes; $values = (array) $values; if ($this->app->isClient('site')) { // Filter by language if not set if (($key = array_search('language', $attributes)) === false) { if (Multilanguage::isEnabled()) { $attributes[] = 'language'; $values[] = [Factory::getLanguage()->getTag(), '*']; } } elseif ($values[$key] === null) { unset($attributes[$key], $values[$key]); } // Filter by access level if not set if (($key = array_search('access', $attributes)) === false) { $attributes[] = 'access'; $values[] = $this->user->getAuthorisedViewLevels(); } elseif ($values[$key] === null) { unset($attributes[$key], $values[$key]); } } // Reset arrays or we get a notice if some values were unset $attributes = array_values($attributes); $values = array_values($values); return parent::getItems($attributes, $values, $firstonly); } /** * Get menu item by id * * @param string $language The language code. * * @return MenuItem|null The item object or null when not found for given language * * @since 1.6 */ public function getDefault($language = '*') { // Get menu items first to ensure defaults have been populated $items = $this->getMenu(); if (\array_key_exists($language, $this->default) && $this->app->isClient('site') && $this->app->getLanguageFilter()) { return $items[$this->default[$language]]; } if (\array_key_exists('*', $this->default)) { return $items[$this->default['*']]; } } } PK1A#]n\wRdd"src/Menu/AdministratorMenuItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Object representing an administrator menu item * * @since 4.0.0 */ class AdministratorMenuItem extends MenuItem { /** * The target attribute of the link * * @var string|null * @since 4.0.0 */ public $target; /** * The icon image of the menu item * * @var string|null * @since 4.0.0 */ public $icon; /** * The icon image of the link * * @var string|null * @since 4.0.0 */ public $iconImage; } PK1A#]��)�)src/Menu/AbstractMenu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\User\User; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Menu class * * @since 1.5 */ abstract class AbstractMenu { /** * Array to hold the menu items * * @var MenuItem[] * * @since 4.0.0 */ protected $items = []; /** * Identifier of the default menu item. Key of the array is the language. * * @var integer[] * * @since 4.0.0 */ protected $default = []; /** * Identifier of the active menu item * * @var integer * * @since 4.0.0 */ protected $active = 0; /** * Menu instances container. * * @var AbstractMenu[] * * @since 1.7 * * @deprecated 4.3 will be removed in 6.0 * Use the MenuFactoryInterface from the container instead * Example: Factory::getContainer()->get(MenuFactoryInterface::class)->createMenu($client, $options) */ public static $instances = []; /** * User object to check access levels for * * @var User * * @since 3.9.26 */ protected $storedUser; /** * Flag for checking if the menu items have been loaded * * @var boolean * * @since 4.0.0 */ private $itemsLoaded = false; /** * Class constructor * * @param array $options An array of configuration options. * * @since 1.5 */ public function __construct($options = []) { /** * It is preferred NOT to inject and store the user when constructing the menu object, * at least for the Menu object used by Joomla. * The menu object can be built very early in the request, from an onAfterInitialise event * but the user can be updated later (by the Remember me plugin for instance). As the stored * user object is not updated, the menu will render incorrectly, not complying with * menu items access levels. * * @see https://github.com/joomla/joomla-cms/issues/11541 */ $this->storedUser = isset($options['user']) && $options['user'] instanceof User ? $options['user'] : null; } /** * Returns a Menu object * * @param string $client The name of the client * @param array $options An associative array of options * * @return AbstractMenu A menu object. * * @since 1.5 * * @throws \Exception * * @deprecated 4.3 will be removed in 6.0 * Use the MenuFactoryInterface from the container instead * Example: Factory::getContainer()->get(MenuFactoryInterface::class)->createMenu($client, $options) */ public static function getInstance($client, $options = []) { if (!$client) { throw new \Exception(Text::sprintf('JLIB_APPLICATION_ERROR_MENU_LOAD', $client), 500); } if (empty(self::$instances[$client])) { self::$instances[$client] = Factory::getContainer()->get(MenuFactoryInterface::class)->createMenu($client, $options); } return self::$instances[$client]; } /** * Setter for the current user used to build menu. * * @param User $user The new user to set. * * @return void * * @since 3.9.26 */ public function setUser($user) { $this->storedUser = $user; } /** * Get menu item by id * * @param integer $id The item id * * @return MenuItem|null The item object if the ID exists or null if not found * * @since 1.5 */ public function getItem($id) { $result = null; if (isset($this->getMenu()[$id])) { $result = &$this->getMenu()[$id]; } return $result; } /** * Set the default item by id and language code. * * @param integer $id The menu item id. * @param string $language The language code (since 1.6). * * @return boolean True if a menu item with the given ID exists * * @since 1.5 */ public function setDefault($id, $language = '*') { if (isset($this->getMenu()[$id])) { $this->default[$language] = $id; return true; } return false; } /** * Get the default item by language code. * * @param string $language The language code, default value of * means all. * * @return MenuItem|null The item object or null when not found for given language * * @since 1.5 */ public function getDefault($language = '*') { // Get menu items first to ensure defaults have been populated $items = $this->getMenu(); if (\array_key_exists($language, $this->default)) { return $items[$this->default[$language]]; } if (\array_key_exists('*', $this->default)) { return $items[$this->default['*']]; } return null; } /** * Set the default item by id * * @param integer $id The item id * * @return MenuItem|null The menu item representing the given ID if present or null otherwise * * @since 1.5 */ public function setActive($id) { if (isset($this->getMenu()[$id])) { $this->active = $id; return $this->getMenu()[$id]; } return null; } /** * Get menu item by id. * * @return MenuItem|null The item object if an active menu item has been set or null * * @since 1.5 */ public function getActive() { if ($this->active) { return $this->getMenu()[$this->active]; } return null; } /** * Gets menu items by attribute * * @param mixed $attributes The field name(s). * @param mixed $values The value(s) of the field. If an array, need to match field names * each attribute may have multiple values to lookup for. * @param boolean $firstonly If true, only returns the first item found * * @return MenuItem|MenuItem[] An array of menu item objects or a single object if the $firstonly parameter is true * * @since 1.5 */ public function getItems($attributes, $values, $firstonly = false) { $items = []; $attributes = (array) $attributes; $values = (array) $values; $count = \count($attributes); foreach ($this->getMenu() as $item) { if (!\is_object($item)) { continue; } $test = true; for ($i = 0; $i < $count; $i++) { if (\is_array($values[$i])) { if (!\in_array($item->{$attributes[$i]}, $values[$i])) { $test = false; break; } } else { if ($item->{$attributes[$i]} != $values[$i]) { $test = false; break; } } } if ($test) { if ($firstonly) { return $item; } $items[] = $item; } } return $items; } /** * Gets the parameter object for a certain menu item * * @param integer $id The item id * * @return Registry * * @since 1.5 */ public function getParams($id) { if ($menu = $this->getItem($id)) { return $menu->getParams(); } return new Registry(); } /** * Getter for the menu array * * @return MenuItem[] * * @since 1.5 */ public function getMenu() { if (!$this->itemsLoaded) { $this->load(); foreach ($this->items as $item) { if ($item->home) { $this->default[trim($item->language)] = $item->id; } } $this->itemsLoaded = true; } return $this->items; } /** * Method to check Menu object authorization against an access control object and optionally an access extension object * * @param integer $id The menu id * * @return boolean * * @since 1.5 */ public function authorise($id) { $menu = $this->getItem($id); if ($menu) { $access = (int) $menu->access; // If the access level is public we don't need to load the user session if ($access === 1) { return true; } return \in_array($access, $this->user->getAuthorisedViewLevels(), true); } return true; } /** * Loads the menu items * * @return array * * @since 1.5 */ abstract public function load(); /** * Internal getter for the user. Returns the injected * one if any, or the current one if none. * * @return User * * @since 3.9.26 */ protected function getUser() { return empty($this->storedUser) ? Factory::getUser() : $this->storedUser; } /** * Magic getter for the user object. Returns the injected * one if any, or the current one if none. * * Using a magic getter to preserve B/C when we stopped storing the user object upon construction of the menu object. * As the user property is not initialized anymore, this getter ensures any class extending * this one can still use $instance->user and get a proper value. * * @param string $propName Name of the missing or protected property. * * @return User|null * * @since 3.9.26 */ public function __get($propName) { if ($propName === 'user') { return empty($this->storedUser) ? Factory::getUser() : $this->storedUser; } return null; } } PK1A#]?&*O66!src/Menu/MenuFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create Menu objects * * @since 4.0.0 */ interface MenuFactoryInterface { /** * Creates a new Menu object for the requested format. * * @param string $client The name of the client * @param array $options An associative array of options * * @return AbstractMenu * * @since 4.0.0 */ public function createMenu(string $client, array $options = []): AbstractMenu; } PK1A#]7B�F��src/Menu/MenuFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface; use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait; use Joomla\CMS\Language\Text; use Joomla\Database\DatabaseAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating Menu objects * * @since 4.0.0 */ class MenuFactory implements MenuFactoryInterface { use CacheControllerFactoryAwareTrait; use DatabaseAwareTrait; /** * Creates a new Menu object for the requested format. * * @param string $client The name of the client * @param array $options An associative array of options * * @return AbstractMenu * * @since 4.0.0 * @throws \InvalidArgumentException */ public function createMenu(string $client, array $options = []): AbstractMenu { // Create a Menu object $classname = __NAMESPACE__ . '\\' . ucfirst(strtolower($client)) . 'Menu'; if (!class_exists($classname)) { throw new \InvalidArgumentException(Text::sprintf('JLIB_APPLICATION_ERROR_MENU_LOAD', $client), 500); } if (!array_key_exists('db', $options)) { $options['db'] = $this->getDatabase(); } $instance = new $classname($options); if ($instance instanceof CacheControllerFactoryAwareInterface) { $instance->setCacheControllerFactory($this->getCacheControllerFactory()); } return $instance; } } PK1A#]@���AAsrc/Menu/MenuItem.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; use Joomla\CMS\Tree\NodeInterface; use Joomla\CMS\Tree\NodeTrait; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Object representing a menu item * * @since 3.7.0 */ #[\AllowDynamicProperties] class MenuItem implements NodeInterface { use NodeTrait; /** * Primary key * * @var integer * @since 3.7.0 */ public $id; /** * The type of menu this item belongs to * * @var integer * @since 3.7.0 */ public $menutype; /** * The display title of the menu item * * @var string * @since 3.7.0 */ public $title; /** * The SEF alias of the menu item * * @var string * @since 3.7.0 */ public $alias; /** * A note associated with the menu item * * @var string * @since 3.7.0 */ public $note; /** * The computed path of the menu item based on the alias field, this is populated from the `path` field in the `#__menu` table * * @var string * @since 3.7.0 */ public $route; /** * The actual link the menu item refers to * * @var string * @since 3.7.0 */ public $link; /** * The type of link * * @var string * @since 3.7.0 */ public $type; /** * The relative level in the tree * * @var integer * @since 3.7.0 */ public $level; /** * The assigned language for this item * * @var string * @since 3.7.0 */ public $language; /** * The click behaviour of the link * * @var integer * @since 3.7.0 */ public $browserNav; /** * The access level required to view the menu item * * @var integer * @since 3.7.0 */ public $access; /** * The menu item parameters * * @var string|Registry * @since 3.7.0 * @note This field is protected to require reading this field to proxy through the getter to convert the params to a Registry instance */ protected $params; /** * Indicates if this menu item is the home or default page * * @var integer * @since 3.7.0 */ public $home; /** * The image of the menu item * * @var string * @since 3.7.0 */ public $img; /** * The optional template style applied to this menu item * * @var integer * @since 3.7.0 */ public $template_style_id; /** * The extension ID of the component this menu item is for * * @var integer * @since 3.7.0 */ public $component_id; /** * The parent menu item in the menu tree * * @var integer * @since 3.7.0 */ public $parent_id; /** * The name of the component this menu item is for * * @var string * @since 3.7.0 */ public $component; /** * The tree of parent menu items * * @var array * @since 3.7.0 */ public $tree = []; /** * An array of the query string values for this item * * @var array * @since 3.7.0 */ public $query = []; /** * Class constructor * * @param array $data The menu item data to load * * @since 3.7.0 */ public function __construct($data = []) { foreach ((array) $data as $key => $value) { $this->$key = $value; } } /** * Returns the menu item parameters * * @return Registry * * @since 3.7.0 */ public function getParams() { if (!($this->params instanceof Registry)) { try { $this->params = new Registry($this->params); } catch (\RuntimeException $e) { /* * Joomla shipped with a broken sample json string for 4 years which caused fatals with new * error checks. So for now we catch the exception here - but one day we should remove it and require * valid JSON. */ $this->params = new Registry(); } } return $this->params; } /** * Sets the menu item parameters * * @param Registry|string $params The data to be stored as the parameters * * @return void * * @since 3.7.0 */ public function setParams($params) { $this->params = $params; } } PK1A#]�*�ZZsrc/Menu/AdministratorMenu.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Menu; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Menu class. * * @since 1.5 */ class AdministratorMenu extends AbstractMenu { /** * Loads the menu items * * @return array * * @since 4.0.0 */ public function load() { return []; } } PK1A#],J�src/Image/ImageFilter.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to manipulate an image. * * @since 1.7.3 */ abstract class ImageFilter { /** * @var resource The image resource handle. * @since 2.5.0 */ protected $handle; /** * Class constructor. * * @param resource $handle The image resource on which to apply the filter. * * @since 1.7.3 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function __construct($handle) { // Verify that image filter support for PHP is available. if (!\function_exists('imagefilter')) { throw new \RuntimeException('The imagefilter function for PHP is not available.'); } /** * Make sure the file handle is valid. * @todo: Remove check for resource when we only support PHP 8 */ if ( !((\is_object($handle) && get_class($handle) == 'GdImage') || (\is_resource($handle) && get_resource_type($handle) == 'gd')) ) { throw new \InvalidArgumentException('The image handle is invalid for the image filter.'); } $this->handle = $handle; } /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ abstract public function execute(array $options = []); } PK1A#]��^AA#src/Image/Filter/Backgroundfill.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class fill background with color; * * @since 3.4 */ class Backgroundfill extends ImageFilter { /** * Method to apply a background color to an image resource. * * @param array $options An array of options for the filter. * color Background matte color * * @return void * * @since 3.4 * @throws \InvalidArgumentException */ public function execute(array $options = []) { // Validate that the color value exists and is an integer. if (!isset($options['color'])) { throw new \InvalidArgumentException('No color value was given. Expected string or array.'); } $colorCode = $options['color'] ?? null; // Get resource dimensions $width = imagesx($this->handle); $height = imagesy($this->handle); // Sanitize color $rgba = $this->sanitizeColor($colorCode); // Enforce alpha on source image if (imageistruecolor($this->handle)) { imagealphablending($this->handle, false); imagesavealpha($this->handle, true); } // Create background $bg = imagecreatetruecolor($width, $height); imagesavealpha($bg, empty($rgba['alpha'])); // Allocate background color. $color = imagecolorallocatealpha($bg, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Fill background imagefill($bg, 0, 0, $color); // Apply image over background imagecopy($bg, $this->handle, 0, 0, 0, 0, $width, $height); // Move flattened result onto current handle. // If handle was palette-based, it'll stay like that. imagecopy($this->handle, $bg, 0, 0, 0, 0, $width, $height); // Free up memory imagedestroy($bg); } /** * Method to sanitize color values and/or convert to an array * * @param mixed $input Associative array of colors and alpha, or hex RGBA string when alpha FF is opaque. Defaults to black and opaque alpha. * * @return array Associative array of red, green, blue and alpha * * @since 3.4 * * @note '#FF0000FF' returns an array with alpha of 0 (opaque) */ protected function sanitizeColor($input) { // Construct default values $colors = ['red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0]; // Make sure all values are in if (\is_array($input)) { $colors = array_merge($colors, $input); } elseif (\is_string($input)) { // Convert RGBA 6-9 char string $hex = ltrim($input, '#'); $hexValues = [ 'red' => substr($hex, 0, 2), 'green' => substr($hex, 2, 2), 'blue' => substr($hex, 4, 2), 'alpha' => substr($hex, 6, 2), ]; $colors = array_map('hexdec', $hexValues); // Convert Alpha to 0..127 when provided if (\strlen($hex) > 6) { $colors['alpha'] = floor((255 - $colors['alpha']) / 2); } } else { // Cannot sanitize such type return $colors; } // Make sure each value is within the allowed range foreach ($colors as &$value) { $value = max(0, min(255, (int) $value)); } $colors['alpha'] = min(127, $colors['alpha']); return $colors; } } PK1A#]�QV���src/Image/Filter/Smooth.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class adjust the smoothness of an image. * * @since 2.5.0 */ class Smooth extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 * @throws \InvalidArgumentException */ public function execute(array $options = []) { // Validate that the smoothing value exists and is an integer. if (!isset($options[IMG_FILTER_SMOOTH]) || !\is_int($options[IMG_FILTER_SMOOTH])) { throw new \InvalidArgumentException('No valid smoothing value was given. Expected integer.'); } // Perform the smoothing filter. imagefilter($this->handle, IMG_FILTER_SMOOTH, $options[IMG_FILTER_SMOOTH]); } } PK1A#]��$��src/Image/Filter/Edgedetect.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class to add an edge detect effect to an image. * * @since 2.5.0 */ class Edgedetect extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ public function execute(array $options = []) { // Perform the edge detection filter. imagefilter($this->handle, IMG_FILTER_EDGEDETECT); } } PK1A#]-�)���src/Image/Filter/Brightness.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class adjust the brightness of an image. * * @since 2.5.0 */ class Brightness extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 * @throws \InvalidArgumentException */ public function execute(array $options = []) { // Validate that the brightness value exists and is an integer. if (!isset($options[IMG_FILTER_BRIGHTNESS]) || !\is_int($options[IMG_FILTER_BRIGHTNESS])) { throw new \InvalidArgumentException('No valid brightness value was given. Expected integer.'); } // Perform the brightness filter. imagefilter($this->handle, IMG_FILTER_BRIGHTNESS, $options[IMG_FILTER_BRIGHTNESS]); } } PK1A#]�'ttsrc/Image/Filter/Sketchy.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class to make an image appear "sketchy". * * @since 2.5.0 */ class Sketchy extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ public function execute(array $options = []) { // Perform the sketchy filter. imagefilter($this->handle, IMG_FILTER_MEAN_REMOVAL); } } PK1A#]�Bvvsrc/Image/Filter/Grayscale.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class to transform an image to grayscale. * * @since 2.5.0 */ class Grayscale extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ public function execute(array $options = []) { // Perform the grayscale filter. imagefilter($this->handle, IMG_FILTER_GRAYSCALE); } } PK1A#]�Ƈ�]]src/Image/Filter/Emboss.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class to emboss an image. * * @since 2.5.0 */ class Emboss extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ public function execute(array $options = []) { // Perform the emboss filter. imagefilter($this->handle, IMG_FILTER_EMBOSS); } } PK1A#]Ő����src/Image/Filter/Contrast.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class adjust the contrast of an image. * * @since 2.5.0 */ class Contrast extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 * @throws \InvalidArgumentException */ public function execute(array $options = []) { // Validate that the contrast value exists and is an integer. if (!isset($options[IMG_FILTER_CONTRAST]) || !\is_int($options[IMG_FILTER_CONTRAST])) { throw new \InvalidArgumentException('No valid contrast value was given. Expected integer.'); } // Perform the contrast filter. imagefilter($this->handle, IMG_FILTER_CONTRAST, $options[IMG_FILTER_CONTRAST]); } } PK1A#]*���mmsrc/Image/Filter/Negate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Filter; use Joomla\CMS\Image\ImageFilter; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Image Filter class to negate the colors of an image. * * @since 2.5.0 */ class Negate extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 2.5.0 */ public function execute(array $options = []) { // Perform the negative filter. imagefilter($this->handle, IMG_FILTER_NEGATE); } } PK1A#]���Z0src/Image/Exception/UnparsableImageException.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception thrown when an image has no known properties. * * @since 4.0.0 */ class UnparsableImageException extends \RuntimeException { } PK1A#]��ð��src/Image/Image.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Image; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to manipulate an image. * * @since 1.7.3 */ class Image { /** * @const integer * @since 2.5.0 */ public const SCALE_FILL = 1; /** * @const integer * @since 2.5.0 */ public const SCALE_INSIDE = 2; /** * @const integer * @since 2.5.0 */ public const SCALE_OUTSIDE = 3; /** * @const integer * @since 2.5.0 */ public const CROP = 4; /** * @const integer * @since 2.5.0 */ public const CROP_RESIZE = 5; /** * @const integer * @since 2.5.0 */ public const SCALE_FIT = 6; /** * @const string * @since 3.4.2 */ public const ORIENTATION_LANDSCAPE = 'landscape'; /** * @const string * @since 3.4.2 */ public const ORIENTATION_PORTRAIT = 'portrait'; /** * @const string * @since 3.4.2 */ public const ORIENTATION_SQUARE = 'square'; /** * @var resource The image resource handle. * @since 2.5.0 */ protected $handle; /** * @var string The source image path. * @since 2.5.0 */ protected $path = null; /** * @var array Whether or not different image formats are supported. * @since 2.5.0 */ protected static $formats = []; /** * @var boolean Flag if an image should use the best quality available. Disable for improved performance. * @since 3.7.0 */ protected $generateBestQuality = true; /** * Class constructor. * * @param mixed $source Either a file path for a source image or a GD resource handler for an image. * * @since 1.7.3 * @throws \RuntimeException */ public function __construct($source = null) { // Verify that GD support for PHP is available. if (!\extension_loaded('gd')) { // @codeCoverageIgnoreStart throw new \RuntimeException('The GD extension for PHP is not available.'); // @codeCoverageIgnoreEnd } // Determine which image types are supported by GD, but only once. if (empty(static::$formats)) { $info = gd_info(); static::$formats[IMAGETYPE_JPEG] = $info['JPEG Support']; static::$formats[IMAGETYPE_PNG] = $info['PNG Support']; static::$formats[IMAGETYPE_GIF] = $info['GIF Read Support']; static::$formats[IMAGETYPE_WEBP] = $info['WebP Support']; } /** * If the source input is a resource, set it as the image handle. * @todo: Remove check for resource when we only support PHP 8 */ if ( $source && (\is_object($source) && get_class($source) == 'GdImage') || (\is_resource($source) && get_resource_type($source) == 'gd') ) { $this->handle = $source; } elseif (!empty($source) && \is_string($source)) { // If the source input is not empty, assume it is a path and populate the image handle. $this->loadFile($source); } } /** * Get the image resource handle * * @return resource * * @since 3.8.0 * @throws \LogicException if an image has not been loaded into the instance */ public function getHandle() { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } return $this->handle; } /** * Method to return a properties object for an image given a filesystem path. * * The result object has values for image width, height, type, attributes, mime type, bits, and channels. * * @param string $path The filesystem path to the image for which to get properties. * * @return \stdClass * * @since 2.5.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public static function getImageFileProperties($path) { // Make sure the file exists. if (!is_file($path)) { throw new \InvalidArgumentException('The image file does not exist.'); } // Get the image file information. $info = getimagesize($path); if (!$info) { throw new Exception\UnparsableImageException('Unable to get properties for the image.'); } // Build the response object. return (object) [ 'width' => $info[0], 'height' => $info[1], 'type' => $info[2], 'attributes' => $info[3], 'bits' => $info['bits'] ?? null, 'channels' => $info['channels'] ?? null, 'mime' => $info['mime'], 'filesize' => filesize($path), 'orientation' => self::getOrientationString((int) $info[0], (int) $info[1]), ]; } /** * Method to detect whether an image's orientation is landscape, portrait or square. * * The orientation will be returned as a string. * * @return mixed Orientation string or null. * * @since 3.4.2 */ public function getOrientation() { if ($this->isLoaded()) { return self::getOrientationString($this->getWidth(), $this->getHeight()); } return null; } /** * Compare width and height integers to determine image orientation. * * @param integer $width The width value to use for calculation * @param integer $height The height value to use for calculation * * @return string Orientation string * * @since 3.4.2 */ private static function getOrientationString(int $width, int $height): string { switch (true) { case ($width > $height): return self::ORIENTATION_LANDSCAPE; case ($width < $height): return self::ORIENTATION_PORTRAIT; default: return self::ORIENTATION_SQUARE; } } /** * Method to generate thumbnails from the current image. It allows creation by resizing or cropping the original image. * * @param mixed $thumbSizes String or array of strings. Example: $thumbSizes = array('150x75','250x150'); * @param integer $creationMethod 1-3 resize $scaleMethod | 4 create cropping | 5 resize then crop * * @return array * * @since 2.5.0 * @throws \LogicException * @throws \InvalidArgumentException */ public function generateThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // Accept a single thumbsize string as parameter if (!\is_array($thumbSizes)) { $thumbSizes = [$thumbSizes]; } // Process thumbs $generated = []; if (!empty($thumbSizes)) { foreach ($thumbSizes as $thumbSize) { // Desired thumbnail size $size = explode('x', strtolower($thumbSize)); if (\count($size) != 2) { throw new \InvalidArgumentException('Invalid thumb size received: ' . $thumbSize); } $thumbWidth = $size[0]; $thumbHeight = $size[1]; switch ($creationMethod) { case self::CROP: $thumb = $this->crop($thumbWidth, $thumbHeight, null, null, true); break; case self::CROP_RESIZE: $thumb = $this->cropResize($thumbWidth, $thumbHeight, true); break; default: $thumb = $this->resize($thumbWidth, $thumbHeight, true, $creationMethod); break; } // Store the thumb in the results array $generated[] = $thumb; } } return $generated; } /** * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing or cropping the original image. * * @param mixed $thumbSizes string or array of strings. Example: $thumbSizes = ['150x75','250x150']; * @param integer $creationMethod 1-3 resize $scaleMethod | 4 create cropping * @param string $thumbsFolder destination thumbs folder. null generates a thumbs folder in the image folder * @param boolean $useOriginalName Shall we use the original image name? Defaults is false, {filename}_{width}x{height}.{ext} * * @return array * * @since 4.3.0 * @throws \LogicException * @throws \InvalidArgumentException */ public function createThumbnails($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null, $useOriginalName = false) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // No thumbFolder set -> we will create a thumbs folder in the current image folder if (\is_null($thumbsFolder)) { $thumbsFolder = \dirname($this->getPath()) . '/thumbs'; } // Check destination if (!is_dir($thumbsFolder) && (!is_dir(\dirname($thumbsFolder)) || !@mkdir($thumbsFolder))) { throw new \InvalidArgumentException('Folder does not exist and cannot be created: ' . $thumbsFolder); } // Process thumbs $thumbsCreated = []; if ($thumbs = $this->generateThumbs($thumbSizes, $creationMethod)) { // Parent image properties $imgProperties = static::getImageFileProperties($this->getPath()); // Get image filename and extension. $pathInfo = pathinfo($this->getPath()); $filename = $pathInfo['filename']; $fileExtension = $pathInfo['extension'] ?? ''; foreach ($thumbs as $thumb) { // Get thumb properties $thumbWidth = $thumb->getWidth(); $thumbHeight = $thumb->getHeight(); if ($useOriginalName) { // Generate thumb name $thumbFileName = $filename . '.' . $fileExtension; } else { // Generate thumb name $thumbFileName = $filename . '_' . $thumbWidth . 'x' . $thumbHeight . '.' . $fileExtension; } // Save thumb file to disk $thumbFileName = $thumbsFolder . '/' . $thumbFileName; if ($thumb->toFile($thumbFileName, $imgProperties->type)) { // Return Image object with thumb path to ease further manipulation $thumb->path = $thumbFileName; $thumbsCreated[] = $thumb; } } } return $thumbsCreated; } /** * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing or cropping the original image. * * @param mixed $thumbSizes string or array of strings. Example: $thumbSizes = ['150x75','250x150']; * @param integer $creationMethod 1-3 resize $scaleMethod | 4 create cropping * @param string $thumbsFolder destination thumbs folder. null generates a thumbs folder in the image folder * * @return array * * @since 2.5.0 * @throws \LogicException * @throws \InvalidArgumentException * * @deprecated 4.0 will be removed in 6.0 * Use \Joomla\CMS\Image\createThumbnails instead */ public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null) { return $this->createThumbnails($thumbSizes, $creationMethod, $thumbsFolder, false); } /** * Method to crop the current image. * * @param mixed $width The width of the image section to crop in pixels or a percentage. * @param mixed $height The height of the image section to crop in pixels or a percentage. * @param integer $left The number of pixels from the left to start cropping. * @param integer $top The number of pixels from the top to start cropping. * @param boolean $createNew If true the current image will be cloned, cropped and returned; else * the current image will be cropped and returned. * * @return Image * * @since 2.5.0 * @throws \LogicException */ public function crop($width, $height, $left = null, $top = null, $createNew = true) { // Sanitize width. $width = $this->sanitizeWidth($width, $height); // Sanitize height. $height = $this->sanitizeHeight($height, $width); // Autocrop offsets if (\is_null($left)) { $left = round(($this->getWidth() - $width) / 2); } if (\is_null($top)) { $top = round(($this->getHeight() - $height) / 2); } // Sanitize left. $left = $this->sanitizeOffset($left); // Sanitize top. $top = $this->sanitizeOffset($top); // Create the new truecolor image handle. $handle = imagecreatetruecolor($width, $height); // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); if ($this->isTransparent()) { // Get the transparent color values for the current image. $rgba = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle())); $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Set the transparent color values for the new image. imagecolortransparent($handle, $color); imagefill($handle, 0, 0, $color); } if (!$this->generateBestQuality) { imagecopyresized($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height); } else { imagecopyresampled($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height); } // If we are cropping to a new image, create a new Image object. if ($createNew) { return new static($handle); } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to apply a filter to the image by type. Two examples are: grayscale and sketchy. * * @param string $type The name of the image filter to apply. * @param array $options An array of options for the filter. * * @return Image * * @since 2.5.0 * @see \Joomla\CMS\Image\Filter * @throws \LogicException */ public function filter($type, array $options = []) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // Get the image filter instance. $filter = $this->getFilterInstance($type); // Execute the image filter. $filter->execute($options); return $this; } /** * Method to get the height of the image in pixels. * * @return integer * * @since 2.5.0 * @throws \LogicException */ public function getHeight() { return imagesy($this->getHandle()); } /** * Method to get the width of the image in pixels. * * @return integer * * @since 2.5.0 * @throws \LogicException */ public function getWidth() { return imagesx($this->getHandle()); } /** * Method to return the path * * @return string * * @since 2.5.0 */ public function getPath() { return $this->path; } /** * Method to determine whether or not an image has been loaded into the object. * * @return boolean * * @since 2.5.0 */ public function isLoaded() { /** * Make sure the resource handle is valid. * @todo: Remove check for resource when we only support PHP 8 */ if ( !((\is_object($this->handle) && get_class($this->handle) == 'GdImage') || (\is_resource($this->handle) && get_resource_type($this->handle) == 'gd')) ) { return false; } return true; } /** * Method to determine whether or not the image has transparency. * * @return boolean * * @since 2.5.0 * @throws \LogicException */ public function isTransparent() { return imagecolortransparent($this->getHandle()) >= 0; } /** * Method to load a file into the Image object as the resource. * * @param string $path The filesystem path to load as an image. * * @return void * * @since 2.5.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function loadFile($path) { // Destroy the current image handle if it exists $this->destroy(); // Make sure the file exists. if (!is_file($path)) { throw new \InvalidArgumentException('The image file does not exist.'); } // Get the image properties. $properties = static::getImageFileProperties($path); // Attempt to load the image based on the MIME-Type switch ($properties->mime) { case 'image/gif': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_GIF])) { throw new \RuntimeException('Attempting to load an image of unsupported type GIF.'); } // Attempt to create the image handle. $handle = imagecreatefromgif($path); $type = 'GIF'; break; case 'image/jpeg': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_JPEG])) { throw new \RuntimeException('Attempting to load an image of unsupported type JPG.'); } // Attempt to create the image handle. $handle = imagecreatefromjpeg($path); $type = 'JPEG'; break; case 'image/png': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_PNG])) { throw new \RuntimeException('Attempting to load an image of unsupported type PNG.'); } // Attempt to create the image handle. $handle = imagecreatefrompng($path); $type = 'PNG'; break; case 'image/webp': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_WEBP])) { throw new \RuntimeException('Attempting to load an image of unsupported type WebP.'); } // Attempt to create the image handle. $handle = imagecreatefromwebp($path); $type = 'WebP'; break; default: throw new \InvalidArgumentException('Attempting to load an image of unsupported type ' . $properties->mime); } /** * Check if handle has been created successfully * @todo: Remove check for resource when we only support PHP 8 */ if (!(\is_object($handle) || \is_resource($handle))) { throw new \RuntimeException('Unable to process ' . $type . ' image.'); } $this->handle = $handle; // Set the filesystem path to the source image. $this->path = $path; } /** * Method to resize the current image. * * @param mixed $width The width of the resized image in pixels or a percentage. * @param mixed $height The height of the resized image in pixels or a percentage. * @param boolean $createNew If true the current image will be cloned, resized and returned; else * the current image will be resized and returned. * @param integer $scaleMethod Which method to use for scaling * * @return Image * * @since 2.5.0 * @throws \LogicException */ public function resize($width, $height, $createNew = true, $scaleMethod = self::SCALE_INSIDE) { // Sanitize width. $width = $this->sanitizeWidth($width, $height); // Sanitize height. $height = $this->sanitizeHeight($height, $width); // Prepare the dimensions for the resize operation. $dimensions = $this->prepareDimensions($width, $height, $scaleMethod); // Instantiate offset. $offset = new \stdClass(); $offset->x = $offset->y = 0; // Center image if needed and create the new truecolor image handle. if ($scaleMethod == self::SCALE_FIT) { // Get the offsets $offset->x = round(($width - $dimensions->width) / 2); $offset->y = round(($height - $dimensions->height) / 2); $handle = imagecreatetruecolor($width, $height); // Make image transparent, otherwise canvas outside initial image would default to black if (!$this->isTransparent()) { $transparency = imagecolorallocatealpha($this->getHandle(), 0, 0, 0, 127); imagecolortransparent($this->getHandle(), $transparency); } } else { $handle = imagecreatetruecolor($dimensions->width, $dimensions->height); } // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); if ($this->isTransparent()) { // Get the transparent color values for the current image. $rgba = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle())); $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Set the transparent color values for the new image. imagecolortransparent($handle, $color); imagefill($handle, 0, 0, $color); } if (!$this->generateBestQuality) { imagecopyresized( $handle, $this->getHandle(), $offset->x, $offset->y, 0, 0, $dimensions->width, $dimensions->height, $this->getWidth(), $this->getHeight() ); } else { // Use resampling for better quality imagecopyresampled( $handle, $this->getHandle(), $offset->x, $offset->y, 0, 0, $dimensions->width, $dimensions->height, $this->getWidth(), $this->getHeight() ); } // If we are resizing to a new image, create a new Image object. if ($createNew) { return new static($handle); } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to crop an image after resizing it to maintain * proportions without having to do all the set up work. * * @param integer $width The desired width of the image in pixels or a percentage. * @param integer $height The desired height of the image in pixels or a percentage. * @param boolean $createNew If true the current image will be cloned, resized, cropped and returned. * * @return Image * * @since 2.5.0 */ public function cropResize($width, $height, $createNew = true) { $width = $this->sanitizeWidth($width, $height); $height = $this->sanitizeHeight($height, $width); $resizewidth = $width; $resizeheight = $height; if (($this->getWidth() / $width) < ($this->getHeight() / $height)) { $resizeheight = 0; } else { $resizewidth = 0; } return $this->resize($resizewidth, $resizeheight, $createNew)->crop($width, $height, null, null, false); } /** * Method to rotate the current image. * * @param mixed $angle The angle of rotation for the image * @param integer $background The background color to use when areas are added due to rotation * @param boolean $createNew If true the current image will be cloned, rotated and returned; else * the current image will be rotated and returned. * * @return Image * * @since 2.5.0 * @throws \LogicException */ public function rotate($angle, $background = -1, $createNew = true) { // Sanitize input $angle = (float) $angle; // Create the new truecolor image handle. $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight()); // Make background transparent if no external background color is provided. if ($background == -1) { // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); $background = imagecolorallocatealpha($handle, 0, 0, 0, 127); } // Copy the image imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight()); // Rotate the image $handle = imagerotate($handle, $angle, $background); // If we are resizing to a new image, create a new Image object. if ($createNew) { return new static($handle); } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to flip the current image. * * @param integer $mode The flip mode for flipping the image {@link http://php.net/imageflip#refsect1-function.imageflip-parameters} * @param boolean $createNew If true the current image will be cloned, flipped and returned; else * the current image will be flipped and returned. * * @return Image * * @since 3.4.2 * @throws \LogicException */ public function flip($mode, $createNew = true) { // Create the new truecolor image handle. $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight()); // Copy the image imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight()); // Flip the image if (!imageflip($handle, $mode)) { throw new \LogicException('Unable to flip the image.'); } // If we are resizing to a new image, create a new Image object. if ($createNew) { // @codeCoverageIgnoreStart return new static($handle); // @codeCoverageIgnoreEnd } // Free the memory from the current handle $this->destroy(); // Swap out the current handle for the new image handle. $this->handle = $handle; return $this; } /** * Watermark the image * * @param Image $watermark The Image object containing the watermark graphic * @param integer $transparency The transparency to use for the watermark graphic * @param integer $bottomMargin The margin from the bottom of this image * @param integer $rightMargin The margin from the right side of this image * * @return Image * * @since 3.8.0 * @link https://secure.php.net/manual/en/image.examples-watermark.php */ public function watermark(Image $watermark, $transparency = 50, $bottomMargin = 0, $rightMargin = 0) { imagecopymerge( $this->getHandle(), $watermark->getHandle(), $this->getWidth() - $watermark->getWidth() - $rightMargin, $this->getHeight() - $watermark->getHeight() - $bottomMargin, 0, 0, $watermark->getWidth(), $watermark->getHeight(), $transparency ); return $this; } /** * Method to write the current image out to a file or output directly. * * @param mixed $path The filesystem path to save the image. * When null, the raw image stream will be outputted directly. * @param integer $type The image type to save the file as. * @param array $options The image type options to use in saving the file. * For PNG and JPEG formats use `quality` key to set compression level (0..9 and 0..100) * * @return boolean * * @link http://www.php.net/manual/image.constants.php * @since 2.5.0 * @throws \LogicException */ public function toFile($path, $type = IMAGETYPE_JPEG, array $options = []) { switch ($type) { case IMAGETYPE_GIF: return imagegif($this->getHandle(), $path); case IMAGETYPE_PNG: return imagepng($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 0); case IMAGETYPE_WEBP: return imagewebp($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 100); } // Case IMAGETYPE_JPEG & default return imagejpeg($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 100); } /** * Method to get an image filter instance of a specified type. * * @param string $type The image filter type to get. * * @return ImageFilter * * @since 2.5.0 * @throws \RuntimeException */ protected function getFilterInstance($type) { // Sanitize the filter type. $type = strtolower(preg_replace('#[^A-Z0-9_]#i', '', $type)); // Verify that the filter type exists. $className = 'JImageFilter' . ucfirst($type); if (!class_exists($className)) { $className = __NAMESPACE__ . '\\Filter\\' . ucfirst($type); if (!class_exists($className)) { throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not available.'); } } // Instantiate the filter object. $instance = new $className($this->getHandle()); // Verify that the filter type is valid. if (!($instance instanceof ImageFilter)) { throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not valid.'); } return $instance; } /** * Method to get the new dimensions for a resized image. * * @param integer $width The width of the resized image in pixels. * @param integer $height The height of the resized image in pixels. * @param integer $scaleMethod The method to use for scaling * * @return \stdClass * * @since 2.5.0 * @throws \InvalidArgumentException If width, height or both given as zero */ protected function prepareDimensions($width, $height, $scaleMethod) { // Instantiate variables. $dimensions = new \stdClass(); switch ($scaleMethod) { case self::SCALE_FILL: $dimensions->width = (int) round($width); $dimensions->height = (int) round($height); break; case self::SCALE_INSIDE: case self::SCALE_OUTSIDE: case self::SCALE_FIT: $rx = ($width > 0) ? ($this->getWidth() / $width) : 0; $ry = ($height > 0) ? ($this->getHeight() / $height) : 0; if ($scaleMethod != self::SCALE_OUTSIDE) { $ratio = max($rx, $ry); } else { $ratio = min($rx, $ry); } $dimensions->width = (int) round($this->getWidth() / $ratio); $dimensions->height = (int) round($this->getHeight() / $ratio); break; default: throw new \InvalidArgumentException('Invalid scale method.'); } return $dimensions; } /** * Method to sanitize a height value. * * @param mixed $height The input height value to sanitize. * @param mixed $width The input width value for reference. * * @return integer * * @since 2.5.0 */ protected function sanitizeHeight($height, $width) { // If no height was given we will assume it is a square and use the width. $height = ($height === null) ? $width : $height; // If we were given a percentage, calculate the integer value. if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $height)) { $height = (int) round($this->getHeight() * (float) str_replace('%', '', $height) / 100); } else { // Else do some rounding so we come out with a sane integer value. $height = (int) round((float) $height); } return $height; } /** * Method to sanitize an offset value like left or top. * * @param mixed $offset An offset value. * * @return integer * * @since 2.5.0 */ protected function sanitizeOffset($offset) { return (int) round((float) $offset); } /** * Method to sanitize a width value. * * @param mixed $width The input width value to sanitize. * @param mixed $height The input height value for reference. * * @return integer * * @since 2.5.0 */ protected function sanitizeWidth($width, $height) { // If no width was given we will assume it is a square and use the height. $width = ($width === null) ? $height : $width; // If we were given a percentage, calculate the integer value. if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $width)) { $width = (int) round($this->getWidth() * (float) str_replace('%', '', $width) / 100); } else { // Else do some rounding so we come out with a sane integer value. $width = (int) round((float) $width); } return $width; } /** * Method to destroy an image handle and * free the memory associated with the handle * * @return boolean True on success, false on failure or if no image is loaded * * @since 2.5.0 */ public function destroy() { if ($this->isLoaded()) { return imagedestroy($this->getHandle()); } return false; } /** * Method to call the destroy() method one last time * to free any memory when the object is unset * * @see Image::destroy() * @since 2.5.0 */ public function __destruct() { $this->destroy(); } /** * Method for set option of generate thumbnail method * * @param boolean $quality True for best quality. False for best speed. * * @return void * * @since 3.7.0 */ public function setThumbnailGenerate($quality = true) { $this->generateBestQuality = (bool) $quality; } } PK1A#]i�a���src/Captcha/Captcha.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Captcha; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Field\CaptchaField; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\DispatcherAwareInterface; use Joomla\Event\DispatcherAwareTrait; use Joomla\Event\DispatcherInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Captcha base object * * @abstract * @since 2.5 */ class Captcha implements DispatcherAwareInterface { use DispatcherAwareTrait; /** * Captcha Plugin object * * @var CMSPlugin * @since 2.5 */ private $captcha; /** * Captcha Plugin name * * @var string * @since 2.5 */ private $name; /** * Array of instances of this class. * * @var Captcha[] * @since 2.5 */ private static $instances = []; /** * Class constructor. * * @param string $captcha The plugin to use. * @param array $options Associative array of options. * * @since 2.5 * @throws \RuntimeException */ public function __construct($captcha, $options) { $this->name = $captcha; if (!empty($options['dispatcher']) && $options['dispatcher'] instanceof DispatcherInterface) { $this->setDispatcher($options['dispatcher']); } else { $this->setDispatcher(Factory::getApplication()->getDispatcher()); } $this->_load($options); } /** * Returns the global Captcha object, only creating it * if it doesn't already exist. * * @param string $captcha The plugin to use. * @param array $options Associative array of options. * * @return Captcha|null Instance of this class. * * @since 2.5 * @throws \RuntimeException */ public static function getInstance($captcha, array $options = []) { $signature = md5(serialize([$captcha, $options])); if (empty(self::$instances[$signature])) { self::$instances[$signature] = new Captcha($captcha, $options); } return self::$instances[$signature]; } /** * Fire the onInit event to initialise the captcha plugin. * * @param string $id The id of the field. * * @return boolean True on success * * @since 2.5 * @throws \RuntimeException */ public function initialise($id) { $arg = ['id' => $id]; $this->update('onInit', $arg); return true; } /** * Get the HTML for the captcha. * * @param string $name The control name. * @param string $id The id for the control. * @param string $class Value for the HTML class attribute * * @return string The return value of the function "onDisplay" of the selected Plugin. * * @since 2.5 * @throws \RuntimeException */ public function display($name, $id, $class = '') { // Check if captcha is already loaded. if ($this->captcha === null) { return ''; } // Initialise the Captcha. if (!$this->initialise($id)) { return ''; } $arg = [ 'name' => $name, 'id' => $id ?: $name, 'class' => $class, ]; $result = $this->update('onDisplay', $arg); return $result; } /** * Checks if the answer is correct. * * @param string $code The answer. * * @return bool Whether the provided answer was correct * * @since 2.5 * @throws \RuntimeException */ public function checkAnswer($code) { // Check if captcha is already loaded if ($this->captcha === null) { return false; } $arg = ['code' => $code]; $result = $this->update('onCheckAnswer', $arg); return $result; } /** * Method to react on the setup of a captcha field. Gives the possibility * to change the field and/or the XML element for the field. * * @param CaptchaField $field Captcha field instance * @param \SimpleXMLElement $element XML form definition * * @return void */ public function setupField(CaptchaField $field, \SimpleXMLElement $element) { if ($this->captcha === null) { return; } $arg = ['field' => $field, 'element' => $element]; return $this->update('onSetupField', $arg); } /** * Method to call the captcha callback if it exist. * * @param string $name Callback name * @param array &$args Arguments * * @return mixed * * @since 4.0.0 */ private function update($name, &$args) { if (method_exists($this->captcha, $name)) { return call_user_func_array([$this->captcha, $name], array_values($args)); } return null; } /** * Load the Captcha plugin. * * @param array $options Associative array of options. * * @return void * * @since 2.5 * @throws \RuntimeException */ private function _load(array $options = []) { // Build the path to the needed captcha plugin $name = InputFilter::getInstance()->clean($this->name, 'cmd'); // Boot the captcha plugin $this->captcha = Factory::getApplication()->bootPlugin($name, 'captcha'); // Check if the captcha can be loaded if (!$this->captcha) { throw new \RuntimeException(Text::sprintf('JLIB_CAPTCHA_ERROR_PLUGIN_NOT_FOUND', $name)); } } } PK1A#]�$`!!2src/Captcha/Google/HttpBridgePostRequestMethod.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Captcha\Google; use Joomla\CMS\Http\HttpFactory; use Joomla\Http\Exception\InvalidResponseCodeException; use Joomla\Http\Http; use ReCaptcha\RequestMethod; use ReCaptcha\RequestParameters; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Bridges the Joomla! HTTP API to the Google Recaptcha RequestMethod interface for a POST request. * * @since 3.9.0 */ final class HttpBridgePostRequestMethod implements RequestMethod { /** * URL to which requests are sent. * * @var string * @since 3.9.0 */ public const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; /** * The HTTP adapter * * @var Http * @since 3.9.0 */ private $http; /** * Class constructor. * * @param Http|null $http The HTTP adapter * * @since 3.9.0 */ public function __construct(Http $http = null) { $this->http = $http ?: HttpFactory::getHttp(); } /** * Submit the request with the specified parameters. * * @param RequestParameters $params Request parameters * * @return string Body of the reCAPTCHA response * * @since 3.9.0 */ public function submit(RequestParameters $params) { try { $response = $this->http->post(self::SITE_VERIFY_URL, $params->toArray()); return (string) $response->getBody(); } catch (InvalidResponseCodeException $exception) { return ''; } } } PK1A#]a.o��#src/Access/Exception/NotAllowed.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining a not allowed access * * @since 3.6.3 */ class NotAllowed extends \RuntimeException { } PK1A#]I|�?��-src/Access/Exception/AuthenticationFailed.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an authentication failed event * * @since 4.0.0 */ class AuthenticationFailed extends \RuntimeException { } PK1A#]ڇ-�V�V�src/Access/Access.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\Log\Log; use Joomla\CMS\Profiler\Profiler; use Joomla\CMS\Table\Asset; use Joomla\CMS\User\User; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class that handles all access authorisation routines. * * @since 1.7.0 */ class Access { /** * Array of view levels * * @var array * @since 1.7.0 */ protected static $viewLevels = []; /** * Array of rules for the asset * * @var array * @since 1.7.0 */ protected static $assetRules = []; /** * Array of identities for asset rules * * @var array * @since 1.7.0 */ protected static $assetRulesIdentities = []; /** * Array of the permission parent ID mappings * * @var array * @since 1.7.0 */ protected static $assetPermissionsParentIdMapping = []; /** * Array of asset types that have been preloaded * * @var array * @since 1.7.0 */ protected static $preloadedAssetTypes = []; /** * Array of loaded user identities * * @var array * @since 1.7.0 */ protected static $identities = []; /** * Array of user groups. * * @var array * @since 1.7.0 */ protected static $userGroups = []; /** * Array of user group paths. * * @var array * @since 1.7.0 */ protected static $userGroupPaths = []; /** * Array of cached groups by user. * * @var array * @since 1.7.0 */ protected static $groupsByUser = []; /** * Array of preloaded asset names and ids (key is the asset id). * * @var array * @since 3.7.0 */ protected static $preloadedAssets = []; /** * The root asset id. * * @var integer * @since 3.7.0 */ protected static $rootAssetId = null; /** * Method for clearing static caches. * * @return void * * @since 1.7.3 */ public static function clearStatics() { self::$viewLevels = []; self::$assetRules = []; self::$assetRulesIdentities = []; self::$assetPermissionsParentIdMapping = []; self::$preloadedAssetTypes = []; self::$identities = []; self::$userGroups = []; self::$userGroupPaths = []; self::$groupsByUser = []; self::$preloadedAssets = []; self::$rootAssetId = null; } /** * Method to check if a user is authorised to perform an action, optionally on an asset. * * @param integer $userId Id of the user for which to check authorisation. * @param string $action The name of the action to authorise. * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $preload Indicates whether preloading should be used. * * @return boolean|null True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ public static function check($userId, $action, $assetKey = null, $preload = true) { // Sanitise inputs. $userId = (int) $userId; $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action))); if (!isset(self::$identities[$userId])) { // Get all groups against which the user is mapped. self::$identities[$userId] = self::getGroupsByUser($userId); array_unshift(self::$identities[$userId], $userId * -1); } return self::getAssetRules($assetKey, true, true, $preload)->allow($action, self::$identities[$userId]); } /** * Method to preload the Rules object for the given asset type. * * @param integer|string|array $assetTypes The type or name of the asset (e.g. 'com_content.article', 'com_menus.menu.2'). * Also accepts the asset id. An array of asset type or a special * 'components' string to load all component assets. * @param boolean $reload Set to true to reload from database. * * @return boolean True on success. * * @since 1.6 * @note This method will return void in 4.0. */ public static function preload($assetTypes = 'components', $reload = false) { // If sent an asset id, we first get the asset type for that asset id. if (is_numeric($assetTypes)) { $assetTypes = self::getAssetType($assetTypes); } // Check for default case: $isDefault = \is_string($assetTypes) && \in_array($assetTypes, ['components', 'component']); // Preload the rules for all of the components. if ($isDefault) { self::preloadComponents(); return true; } // If we get to this point, this is a regular asset type and we'll proceed with the preloading process. if (!\is_array($assetTypes)) { $assetTypes = (array) $assetTypes; } foreach ($assetTypes as $assetType) { self::preloadPermissions($assetType, $reload); } return true; } /** * Method to recursively retrieve the list of parent Asset IDs * for a particular Asset. * * @param string $assetType The asset type, or the asset name, or the extension of the asset * (e.g. 'com_content.article', 'com_menus.menu.2', 'com_contact'). * @param integer $assetId The numeric asset id. * * @return array List of ancestor ids (includes original $assetId). * * @since 1.6 */ protected static function getAssetAncestors($assetType, $assetId) { // Get the extension name from the $assetType provided $extensionName = self::getExtensionNameFromAsset($assetType); // Holds the list of ancestors for the Asset ID: $ancestors = []; // Add in our starting Asset ID: $ancestors[] = (int) $assetId; // Initialize the variable we'll use in the loop: $id = (int) $assetId; while ($id !== 0) { if (isset(self::$assetPermissionsParentIdMapping[$extensionName][$id])) { $id = (int) self::$assetPermissionsParentIdMapping[$extensionName][$id]->parent_id; if ($id !== 0) { $ancestors[] = $id; } } else { // Add additional case to break out of the while loop automatically in // the case that the ID is non-existent in our mapping variable above. break; } } return $ancestors; } /** * Method to retrieve the Asset Rule strings for this particular * Asset Type and stores them for later usage in getAssetRules(). * Stores 2 arrays: one where the list has the Asset ID as the key * and a second one where the Asset Name is the key. * * @param string $assetType The asset type, or the asset name, or the extension of the asset * (e.g. 'com_content.article', 'com_menus.menu.2', 'com_contact'). * @param boolean $reload Reload the preloaded assets. * * @return void * * @since 1.6 */ protected static function preloadPermissions($assetType, $reload = false) { // Get the extension name from the $assetType provided $extensionName = self::getExtensionNameFromAsset($assetType); // If asset is a component, make sure that all the component assets are preloaded. if ((isset(self::$preloadedAssetTypes[$extensionName]) || isset(self::$preloadedAssetTypes[$assetType])) && !$reload) { return; } !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::preloadPermissions (' . $extensionName . ')'); // Get the database connection object. $db = Factory::getDbo(); $assetKey = $extensionName . '.%'; // Get a fresh query object. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'name', 'rules', 'parent_id'])) ->from($db->quoteName('#__assets')) ->where( [ $db->quoteName('name') . ' LIKE :asset', $db->quoteName('name') . ' = :extension', $db->quoteName('parent_id') . ' = 0', ], 'OR' ) ->bind(':extension', $extensionName) ->bind(':asset', $assetKey); // Get the permission map for all assets in the asset extension. $assets = $db->setQuery($query)->loadObjectList(); self::$assetPermissionsParentIdMapping[$extensionName] = []; foreach ($assets as $asset) { self::$assetPermissionsParentIdMapping[$extensionName][$asset->id] = $asset; self::$preloadedAssets[$asset->id] = $asset->name; } // Mark asset type and it's extension name as preloaded. self::$preloadedAssetTypes[$assetType] = true; self::$preloadedAssetTypes[$extensionName] = true; !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::preloadPermissions (' . $extensionName . ')'); } /** * Method to preload the Rules objects for all components. * * Note: This will only get the base permissions for the component. * e.g. it will get 'com_content', but not 'com_content.article.1' or * any more specific asset type rules. * * @return array Array of component names that were preloaded. * * @since 1.6 */ protected static function preloadComponents() { // If the components already been preloaded do nothing. if (isset(self::$preloadedAssetTypes['components'])) { return []; } !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::preloadComponents (all components)'); // Add root to asset names list. $components = ['root.1']; // Add enabled components to asset names list. foreach (ComponentHelper::getComponents() as $component) { if ($component->enabled) { $components[] = $component->option; } } // Get the database connection object. $db = Factory::getDbo(); // Get the asset info for all assets in asset names list. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'name', 'rules', 'parent_id'])) ->from($db->quoteName('#__assets')) ->whereIn($db->quoteName('name'), $components, ParameterType::STRING); // Get the Name Permission Map List $assets = $db->setQuery($query)->loadObjectList(); $rootAsset = null; // First add the root asset and save it to preload memory and mark it as preloaded. foreach ($assets as &$asset) { if ((int) $asset->parent_id === 0) { $rootAsset = $asset; self::$rootAssetId = $asset->id; self::$preloadedAssetTypes[$asset->name] = true; self::$preloadedAssets[$asset->id] = $asset->name; self::$assetPermissionsParentIdMapping[$asset->name][$asset->id] = $asset; unset($asset); break; } } // Now create save the components asset tree to preload memory. foreach ($assets as $asset) { if (!isset(self::$assetPermissionsParentIdMapping[$asset->name])) { self::$assetPermissionsParentIdMapping[$asset->name] = [$rootAsset->id => $rootAsset, $asset->id => $asset]; self::$preloadedAssets[$asset->id] = $asset->name; } } // Mark all components asset type as preloaded. self::$preloadedAssetTypes['components'] = true; !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::preloadComponents (all components)'); return $components; } /** * Method to check if a group is authorised to perform an action, optionally on an asset. * * @param integer $groupId The path to the group for which to check authorisation. * @param string $action The name of the action to authorise. * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $preload Indicates whether preloading should be used. * * @return boolean True if authorised. * * @since 1.7.0 */ public static function checkGroup($groupId, $action, $assetKey = null, $preload = true) { // Sanitize input. $groupId = (int) $groupId; $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action))); return self::getAssetRules($assetKey, true, true, $preload)->allow($action, self::getGroupPath($groupId)); } /** * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree * (including the leaf group id). * * @param mixed $groupId An integer or array of integers representing the identities to check. * * @return mixed True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ protected static function getGroupPath($groupId) { // Load all the groups to improve performance on intensive groups checks $groups = UserGroupsHelper::getInstance()->getAll(); if (!isset($groups[$groupId])) { return []; } return $groups[$groupId]->path; } /** * Method to return the Rules object for an asset. The returned object can optionally hold * only the rules explicitly set for the asset or the summation of all inherited rules from * parent assets and explicit rules. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $recursive True to return the rules object with inherited rules. * @param boolean $recursiveParentAsset True to calculate the rule also based on inherited component/extension rules. * @param boolean $preload Indicates whether preloading should be used. * * @return Rules Rules object for the asset. * * @since 1.7.0 * @note The non preloading code will be removed in 4.0. All asset rules should use asset preloading. */ public static function getAssetRules($assetKey, $recursive = false, $recursiveParentAsset = true, $preload = true) { // Auto preloads the components assets and root asset (if chosen). if ($preload) { self::preload('components'); } // When asset key is null fallback to root asset. $assetKey = self::cleanAssetKey($assetKey); // Auto preloads assets for the asset type (if chosen). if ($preload) { self::preload(self::getAssetType($assetKey)); } // Get the asset id and name. $assetId = self::getAssetId($assetKey); // If asset rules already cached em memory return it (only in full recursive mode). if ($recursive && $recursiveParentAsset && $assetId && isset(self::$assetRules[$assetId])) { return self::$assetRules[$assetId]; } // Get the asset name and the extension name. $assetName = self::getAssetName($assetKey); $extensionName = self::getExtensionNameFromAsset($assetName); // If asset id does not exist fallback to extension asset, then root asset. if (!$assetId) { if ($extensionName && $assetName !== $extensionName) { Log::add('No asset found for ' . $assetName . ', falling back to ' . $extensionName, Log::WARNING, 'assets'); return self::getAssetRules($extensionName, $recursive, $recursiveParentAsset, $preload); } if (self::$rootAssetId !== null && $assetName !== self::$preloadedAssets[self::$rootAssetId]) { Log::add('No asset found for ' . $assetName . ', falling back to ' . self::$preloadedAssets[self::$rootAssetId], Log::WARNING, 'assets'); return self::getAssetRules(self::$preloadedAssets[self::$rootAssetId], $recursive, $recursiveParentAsset, $preload); } } // Almost all calls can take advantage of preloading. if ($assetId && isset(self::$preloadedAssets[$assetId])) { !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); // Collects permissions for each asset $collected = []; // If not in any recursive mode. We only want the asset rules. if (!$recursive && !$recursiveParentAsset) { $collected = [self::$assetPermissionsParentIdMapping[$extensionName][$assetId]->rules]; } else { // If there is any type of recursive mode. $ancestors = array_reverse(self::getAssetAncestors($extensionName, $assetId)); foreach ($ancestors as $id) { // There are no rules for this ancestor if (!isset(self::$assetPermissionsParentIdMapping[$extensionName][$id])) { continue; } // If full recursive mode, but not recursive parent mode, do not add the extension asset rules. if ($recursive && !$recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name === $extensionName) { continue; } // If not full recursive mode, but recursive parent mode, do not add other recursion rules. if ( !$recursive && $recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name !== $extensionName && (int) self::$assetPermissionsParentIdMapping[$extensionName][$id]->id !== $assetId ) { continue; } // If empty asset to not add to rules. if (self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules === '{}') { continue; } $collected[] = self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules; } } /** * Hashing the collected rules allows us to store * only one instance of the Rules object for * Assets that have the same exact permissions... * it's a great way to save some memory. */ $hash = md5(implode(',', $collected)); if (!isset(self::$assetRulesIdentities[$hash])) { $rules = new Rules(); $rules->mergeCollection($collected); self::$assetRulesIdentities[$hash] = $rules; } // Save asset rules to memory cache(only in full recursive mode). if ($recursive && $recursiveParentAsset) { self::$assetRules[$assetId] = self::$assetRulesIdentities[$hash]; } !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); return self::$assetRulesIdentities[$hash]; } // Non preloading code. Use old slower method, slower. Only used in rare cases (if any) or without preloading chosen. Log::add('Asset ' . $assetKey . ' permissions fetch without preloading (slower method).', Log::INFO, 'assets'); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (assetKey:' . $assetKey . ')'); // There's no need to process it with the recursive method for the Root Asset ID. if ((int) $assetKey === 1) { $recursive = false; } // Get the database connection object. $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->getQuery(true) ->select($db->quoteName($recursive ? 'b.rules' : 'a.rules', 'rules')) ->from($db->quoteName('#__assets', 'a')); // If the asset identifier is numeric assume it is a primary key, else lookup by name. if (is_numeric($assetKey)) { $query->where($db->quoteName('a.id') . ' = :asset', 'OR') ->bind(':asset', $assetKey, ParameterType::INTEGER); } else { $query->where($db->quoteName('a.name') . ' = :asset', 'OR') ->bind(':asset', $assetKey); } if ($recursiveParentAsset && ($extensionName !== $assetKey || is_numeric($assetKey))) { $query->where($db->quoteName('a.name') . ' = :extension') ->bind(':extension', $extensionName); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->where($db->quoteName('a.parent_id') . ' = 0') ->join( 'LEFT', $db->quoteName('#__assets', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ) ->order($db->quoteName('b.lft')); } // Execute the query and load the rules from the result. $result = $db->setQuery($query)->loadColumn(); // Get the root even if the asset is not found and in recursive mode if (empty($result)) { $rootId = (new Asset($db))->getRootId(); $query->clear() ->select($db->quoteName('rules')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = :rootId') ->bind(':rootId', $rootId, ParameterType::INTEGER); $result = $db->setQuery($query)->loadColumn(); } // Instantiate and return the Rules object for the asset rules. $rules = new Rules(); $rules->mergeCollection($result); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules <strong>Slower</strong> (assetKey:' . $assetKey . ')'); return $rules; } /** * Method to clean the asset key to make sure we always have something. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * * @return integer|string Asset id or asset name. * * @since 3.7.0 */ protected static function cleanAssetKey($assetKey = null) { // If it's a valid asset key, clean it and return it. if ($assetKey) { return strtolower(preg_replace('#[\s\-]+#', '.', trim($assetKey))); } // Return root asset id if already preloaded. if (self::$rootAssetId !== null) { return self::$rootAssetId; } // No preload. Return root asset id from Assets. $assets = new Asset(Factory::getDbo()); return $assets->getRootId(); } /** * Method to get the asset id from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return integer The asset id. * * @since 3.7.0 */ protected static function getAssetId($assetKey) { static $loaded = []; // If the asset is already an id return it. if (is_numeric($assetKey)) { return (int) $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$preloadedAssets[self::$rootAssetId]) { $loaded[$assetKey] = self::$rootAssetId; } else { $preloadedAssetsByName = array_flip(self::$preloadedAssets); // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. if (isset($preloadedAssetsByName[$assetKey])) { $loaded[$assetKey] = $preloadedAssetsByName[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table. $table = new Asset(Factory::getDbo()); $table->load(['name' => $assetKey]); $loaded[$assetKey] = $table->id ?? 0; } } } return (int) $loaded[$assetKey]; } /** * Method to get the asset name from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset name (ex: com_content.article.8). * * @since 3.7.0 */ protected static function getAssetName($assetKey) { static $loaded = []; // If the asset is already a string return it. if (!is_numeric($assetKey)) { return $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$rootAssetId) { $loaded[$assetKey] = self::$preloadedAssets[self::$rootAssetId]; } elseif (isset(self::$preloadedAssets[$assetKey])) { // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. $loaded[$assetKey] = self::$preloadedAssets[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table fetch it from table. $table = new Asset(Factory::getDbo()); $table->load($assetKey); $loaded[$assetKey] = $table->name; } } return $loaded[$assetKey]; } /** * Method to get the extension name from the asset name. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The extension name (ex: com_content). * * @since 1.6 */ public static function getExtensionNameFromAsset($assetKey) { static $loaded = []; if (!isset($loaded[$assetKey])) { $assetName = self::getAssetName($assetKey); $firstDot = strpos($assetName, '.'); if ($assetName !== 'root.1' && $firstDot !== false) { $assetName = substr($assetName, 0, $firstDot); } $loaded[$assetKey] = $assetName; } return $loaded[$assetKey]; } /** * Method to get the asset type from the asset name. * * For top level components this returns "components": * 'com_content' returns 'components' * * For other types: * 'com_content.article.1' returns 'com_content.article' * 'com_content.category.1' returns 'com_content.category' * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset type (ex: com_content.article). * * @since 1.6 */ public static function getAssetType($assetKey) { // If the asset is already a string return it. $assetName = self::getAssetName($assetKey); $lastDot = strrpos($assetName, '.'); if ($assetName !== 'root.1' && $lastDot !== false) { return substr($assetName, 0, $lastDot); } return 'components'; } /** * Method to return the title of a user group * * @param integer $groupId Id of the group for which to get the title of. * * @return string The title of the group * * @since 3.5 */ public static function getGroupTitle($groupId) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Fetch the group title from the database $db = Factory::getDbo(); $query = $db->getQuery(true); $query->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); return $db->loadResult(); } /** * Method to return a list of user groups mapped to a user. The returned list can optionally hold * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited * by the user. * * @param integer $userId Id of the user for which to get the list of groups. * @param boolean $recursive True to include inherited user groups. * * @return array List of user group ids to which the user is mapped. * * @since 1.7.0 */ public static function getGroupsByUser($userId, $recursive = true) { // Cast as integer until method is typehinted. $userId = (int) $userId; // Creates a simple unique string for each parameter combination: $storeId = $userId . ':' . (int) $recursive; if (!isset(self::$groupsByUser[$storeId])) { // @todo: Uncouple this from ComponentHelper and allow for a configuration setting or value injection. $guestUsergroup = (int) ComponentHelper::getParams('com_users')->get('guest_usergroup', 1); // Guest user (if only the actually assigned group is requested) if (empty($userId) && !$recursive) { $result = [$guestUsergroup]; } else { // Registered user and guest if all groups are requested $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->getQuery(true) ->select($db->quoteName($recursive ? 'b.id' : 'a.id')); if (empty($userId)) { $query->from($db->quoteName('#__usergroups', 'a')) ->where($db->quoteName('a.id') . ' = :guest') ->bind(':guest', $guestUsergroup, ParameterType::INTEGER); } else { $query->from($db->quoteName('#__user_usergroup_map', 'map')) ->where($db->quoteName('map.user_id') . ' = :userId') ->join('LEFT', $db->quoteName('#__usergroups', 'a'), $db->quoteName('a.id') . ' = ' . $db->quoteName('map.group_id')) ->bind(':userId', $userId, ParameterType::INTEGER); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->join( 'LEFT', $db->quoteName('#__usergroups', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ); } // Execute the query and load the rules from the result. $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL or duplicate values, just in case $result = ArrayHelper::toInteger($result); if (empty($result)) { $result = [1]; } else { $result = array_unique($result); } } self::$groupsByUser[$storeId] = $result; } return self::$groupsByUser[$storeId]; } /** * Method to return a list of user Ids contained in a Group * * @param integer $groupId The group Id * @param boolean $recursive Recursively include all child groups (optional) * * @return array * * @since 1.7.0 * @todo This method should move somewhere else */ public static function getUsersByGroup($groupId, $recursive = false) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Get a database object. $db = Factory::getDbo(); $test = $recursive ? ' >= ' : ' = '; // First find the users contained in the group $query = $db->getQuery(true) ->select('DISTINCT(' . $db->quoteName('user_id') . ')') ->from($db->quoteName('#__usergroups', 'ug1')) ->join( 'INNER', $db->quoteName('#__usergroups', 'ug2'), $db->quoteName('ug2.lft') . $test . $db->quoteName('ug1.lft') . ' AND ' . $db->quoteName('ug1.rgt') . $test . $db->quoteName('ug2.rgt') ) ->join('INNER', $db->quoteName('#__user_usergroup_map', 'm'), $db->quoteName('ug2.id') . ' = ' . $db->quoteName('m.group_id')) ->where($db->quoteName('ug1.id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL values, just in case $result = ArrayHelper::toInteger($result); return $result; } /** * Method to return a list of view levels for which the user is authorised. * * @param integer $userId Id of the user for which to get the list of authorised view levels. * * @return array List of view levels for which the user is authorised. * * @since 1.7.0 */ public static function getAuthorisedViewLevels($userId) { // Only load the view levels once. if (empty(self::$viewLevels)) { // Get a database object. $db = Factory::getDbo(); // Build the base query. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'rules'])) ->from($db->quoteName('#__viewlevels')); // Set the query for execution. $db->setQuery($query); // Build the view levels array. foreach ($db->loadAssocList() as $level) { self::$viewLevels[$level['id']] = (array) json_decode($level['rules']); } } // Initialise the authorised array. $authorised = [1]; // Check for the recovery mode setting and return early. $user = User::getInstance($userId); $root_user = Factory::getApplication()->get('root_user'); if (($user->username && $user->username == $root_user) || (is_numeric($root_user) && $user->id > 0 && $user->id == $root_user)) { // Find the super user levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if ($id > 0 && self::checkGroup($id, 'core.admin')) { $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } // Get all groups that the user is mapped to recursively. $groups = self::getGroupsByUser($userId); // Find the authorised levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if (($id < 0) && (($id * -1) == $userId)) { $authorised[] = $level; break; } elseif (($id >= 0) && \in_array($id, $groups)) { // Check to see if the group is mapped to the level. $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } /** * Method to return a list of actions from a file for which permissions can be set. * * @param string $file The path to the XML file. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/") { if (!is_file($file) || !is_readable($file)) { // If unable to find the file return false. return false; } else { // Else return the actions from the xml. $xml = simplexml_load_file($file); return self::getActionsFromData($xml, $xpath); } } /** * Method to return a list of actions from a string or from an xml for which permissions can be set. * * @param string|\SimpleXMLElement $data The XML string or an XML element. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/") { // If the data to load isn't already an XML element or string return false. if ((!($data instanceof \SimpleXMLElement)) && (!\is_string($data))) { return false; } // Attempt to load the XML if a string. if (\is_string($data)) { try { $data = new \SimpleXMLElement($data); } catch (\Exception $e) { return false; } // Make sure the XML loaded correctly. if (!$data) { return false; } } // Initialise the actions array $actions = []; // Get the elements from the xpath $elements = $data->xpath($xpath . 'action[@name][@title]'); // If there some elements, analyse them if (!empty($elements)) { foreach ($elements as $element) { // Add the action to the actions array $action = [ 'name' => (string) $element['name'], 'title' => (string) $element['title'], ]; if (isset($element['description'])) { $action['description'] = (string) $element['description']; } $actions[] = (object) $action; } } // Finally return the actions array return $actions; } } PK1A#]iR1v��src/Access/Rule.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Rule class. * * @since 2.5.0 */ class Rule { /** * A named array * * @var array * @since 1.7.0 */ protected $data = []; /** * Constructor. * * The input array must be in the form: array(-42 => true, 3 => true, 4 => false) * or an equivalent JSON encoded string. * * @param mixed $identities A JSON format string (probably from the database) or a named array. * * @since 1.7.0 */ public function __construct($identities) { // Convert string input to an array. if (\is_string($identities)) { $identities = json_decode($identities, true); } $this->mergeIdentities($identities); } /** * Get the data for the action. * * @return array A named array * * @since 1.7.0 */ public function getData() { return $this->data; } /** * Merges the identities * * @param mixed $identities An integer or array of integers representing the identities to check. * * @return void * * @since 1.7.0 */ public function mergeIdentities($identities) { if ($identities instanceof Rule) { $identities = $identities->getData(); } if (\is_array($identities)) { foreach ($identities as $identity => $allow) { $this->mergeIdentity($identity, $allow); } } } /** * Merges the values for an identity. * * @param integer $identity The identity. * @param boolean $allow The value for the identity (true == allow, false == deny). * * @return void * * @since 1.7.0 */ public function mergeIdentity($identity, $allow) { $identity = (int) $identity; $allow = (int) ((bool) $allow); // Check that the identity exists. if (isset($this->data[$identity])) { // Explicit deny always wins a merge. if ($this->data[$identity] !== 0) { $this->data[$identity] = $allow; } } else { $this->data[$identity] = $allow; } } /** * Checks that this action can be performed by an identity. * * The identity is an integer where +ve represents a user group, * and -ve represents a user. * * @param mixed $identities An integer or array of integers representing the identities to check. * * @return mixed True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ public function allow($identities) { // Implicit deny by default. $result = null; // Check that the inputs are valid. if (!empty($identities)) { if (!\is_array($identities)) { $identities = [$identities]; } foreach ($identities as $identity) { // Technically the identity just needs to be unique. $identity = (int) $identity; // Check if the identity is known. if (isset($this->data[$identity])) { $result = (bool) $this->data[$identity]; // An explicit deny wins. if ($result === false) { break; } } } } return $result; } /** * Convert this object into a JSON encoded string. * * @return string JSON encoded string * * @since 1.7.0 */ public function __toString() { return json_encode($this->data); } } PK1A#]�G�0PPsrc/Access/Rules.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access rules class. * * @since 2.5.0 */ class Rules { /** * A named array. * * @var array * @since 1.7.0 */ protected $data = []; /** * Constructor. * * The input array must be in the form: array('action' => array(-42 => true, 3 => true, 4 => false)) * or an equivalent JSON encoded string, or an object where properties are arrays. * * @param mixed $input A JSON format string (probably from the database) or a nested array. * * @since 1.7.0 */ public function __construct($input = '') { // Convert in input to an array. if (\is_string($input)) { $input = json_decode($input, true); } elseif (\is_object($input)) { $input = (array) $input; } if (\is_array($input)) { // Top level keys represent the actions. foreach ($input as $action => $identities) { $this->mergeAction($action, $identities); } } } /** * Get the data for the action. * * @return array A named array of Rule objects. * * @since 1.7.0 */ public function getData() { return $this->data; } /** * Method to merge a collection of Rules. * * @param mixed $input Rule or array of Rules * * @return void * * @since 1.7.0 */ public function mergeCollection($input) { // Check if the input is an array. if (\is_array($input)) { foreach ($input as $actions) { $this->merge($actions); } } } /** * Method to merge actions with this object. * * @param mixed $actions Rule object, an array of actions or a JSON string array of actions. * * @return void * * @since 1.7.0 */ public function merge($actions) { if (\is_string($actions)) { $actions = json_decode($actions, true); } if (\is_array($actions)) { foreach ($actions as $action => $identities) { $this->mergeAction($action, $identities); } } elseif ($actions instanceof Rules) { $data = $actions->getData(); foreach ($data as $name => $identities) { $this->mergeAction($name, $identities); } } } /** * Merges an array of identities for an action. * * @param string $action The name of the action. * @param array $identities An array of identities * * @return void * * @since 1.7.0 */ public function mergeAction($action, $identities) { if (isset($this->data[$action])) { // If exists, merge the action. $this->data[$action]->mergeIdentities($identities); } else { // If new, add the action. $this->data[$action] = new Rule($identities); } } /** * Checks that an action can be performed by an identity. * * The identity is an integer where +ve represents a user group, * and -ve represents a user. * * @param string $action The name of the action. * @param mixed $identity An integer representing the identity, or an array of identities * * @return mixed Object or null if there is no information about the action. * * @since 1.7.0 */ public function allow($action, $identity) { // Check we have information about this action. if (isset($this->data[$action])) { return $this->data[$action]->allow($identity); } } /** * Get the allowed actions for an identity. * * @param mixed $identity An integer representing the identity or an array of identities * * @return CMSObject Allowed actions for the identity or identities * * @since 1.7.0 */ public function getAllowed($identity) { // Sweep for the allowed actions. $allowed = new CMSObject(); foreach ($this->data as $name => &$action) { if ($action->allow($identity)) { $allowed->set($name, true); } } return $allowed; } /** * Magic method to convert the object to JSON string representation. * * @return string JSON representation of the actions array * * @since 1.7.0 */ public function __toString() { $temp = []; foreach ($this->data as $name => $rule) { if ($data = $rule->getData()) { $temp[$name] = $data; } } return json_encode($temp, JSON_FORCE_OBJECT); } } PK1A#]l�`��src/Encrypt/Randval.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Generates cryptographically-secure random values. * * @since 4.0.0 */ class Randval implements RandValInterface { /** * Returns a cryptographically secure random value. * * This method allows us to quickly address any future issues if we ever find problems with PHP's random_bytes() on * some weird host (you can't be too careful when releasing mass-distributed software). * * @param integer $bytes How many bytes to return * * @return string */ public function generate($bytes = 32) { return random_bytes($bytes); } } PK1A#]�<K��src/Encrypt/Totp.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ namespace Joomla\CMS\Encrypt; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * This class provides an RFC6238-compliant Time-based One Time Passwords, * compatible with Google Authenticator (with PassCodeLength = 6 and TimePeriod = 30). * * @since 4.0.0 */ class Totp { /** * Passcode length * * @var integer */ private $_passCodeLength = 6; /** * Pin modulo * * @var integer */ private $_pinModulo; /** * The length of the secret in bytes. * RFC 4226: "The length of the shared secret MUST be at least 128 bits. This document RECOMMENDs a shared secret length of 160 bits." * The original value was 10 bytes (80 bits) this value has been increased to 20 (160 bits) with Joomla! 3.9.25 * * @var integer */ private $_secretLength = 20; /** * Timestep * * @var integer */ private $_timeStep = 30; /** * Base32 * * @var integer */ private $_base32 = null; /** * Initialises an RFC6238-compatible TOTP generator. Please note that this * class does not implement the constraint in the last paragraph of §5.2 * of RFC6238. It's up to you to ensure that the same user/device does not * retry validation within the same Time Step. * * @param int $timeStep The Time Step (in seconds). Use 30 to be compatible with Google Authenticator. * @param int $passCodeLength The generated passcode length. Default: 6 digits. * @param int $secretLength The length of the secret key. Default: 10 bytes (80 bits). * @param Object $base32 The base32 en/decrypter */ public function __construct($timeStep = 30, $passCodeLength = 6, $secretLength = 10, $base32 = null) { $this->_timeStep = $timeStep; $this->_passCodeLength = $passCodeLength; $this->_secretLength = $secretLength; $this->_pinModulo = pow(10, $this->_passCodeLength); if (\is_null($base32)) { $this->_base32 = new Base32(); } else { $this->_base32 = $base32; } } /** * Get the time period based on the $time timestamp and the Time Step * defined. If $time is skipped or set to null the current timestamp will * be used. * * @param int|null $time Timestamp * * @return integer The time period since the UNIX Epoch */ public function getPeriod($time = null) { if (\is_null($time)) { $time = time(); } $period = floor($time / $this->_timeStep); return $period; } /** * Check is the given passcode $code is a valid TOTP generated using secret * key $secret * * @param string $secret The Base32-encoded secret key * @param string $code The passcode to check * * @return boolean True if the code is valid */ public function checkCode($secret, $code) { $time = $this->getPeriod(); for ($i = -1; $i <= 1; $i++) { if ($this->getCode($secret, ($time + $i) * $this->_timeStep) == $code) { return true; } } return false; } /** * Gets the TOTP passcode for a given secret key $secret and a given UNIX * timestamp $time * * @param string $secret The Base32-encoded secret key * @param int $time UNIX timestamp * * @return string */ public function getCode($secret, $time = null) { $period = $this->getPeriod($time); $secret = $this->_base32->decode($secret); $time = pack("N", $period); $time = str_pad($time, 8, \chr(0), STR_PAD_LEFT); $hash = hash_hmac('sha1', $time, $secret, true); $offset = \ord(substr($hash, -1)); $offset = $offset & 0xF; $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF; $pinValue = str_pad($truncatedHash % $this->_pinModulo, $this->_passCodeLength, "0", STR_PAD_LEFT); return $pinValue; } /** * Extracts a part of a hash as an integer * * @param string $bytes The hash * @param string $start The char to start from (0 = first char) * * @return string */ protected function hashToInt($bytes, $start) { $input = substr($bytes, $start, \strlen($bytes) - $start); $val2 = unpack("N", substr($input, 0, 4)); return $val2[1]; } /** * Returns a QR code URL for easy setup of TOTP apps like Google Authenticator * * @param string $user User * @param string $hostname Hostname * @param string $secret Secret string * * @return string */ public function getUrl($user, $hostname, $secret) { $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret); $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl="; $encoderURL = $encoder . urlencode($url); return $encoderURL; } /** * Generates a (semi-)random Secret Key for TOTP generation * * @return string * * @note Since 3.9.25 we use the secure method "random_bytes" over the original insecure "rand" function. * The random_bytes function has been backported to outdated PHP versions by the core shipped library paragonie/random_compat */ public function generateSecret() { $secret = random_bytes($this->_secretLength); return $this->_base32->encode($secret); } } PK1A#]q�!11src/Encrypt/Base32.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base32 encryption class * * @since 1.0 */ class Base32 { /** * CSRFC3548 * * The character set as defined by RFC3548 * @link http://www.ietf.org/rfc/rfc3548.txt */ public const CSRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; /** * str2bin * * Converts any ascii string to a binary string * * @param string $str The string you want to convert * * @return string String of 0's and 1's */ private function str2bin($str) { $chrs = unpack('C*', $str); return vsprintf(str_repeat('%08b', \count($chrs)), $chrs); } /** * bin2str * * Converts a binary string to an ascii string * * @param string $str The string of 0's and 1's you want to convert * * @return string The ascii output * * @throws \Exception */ private function bin2str($str) { if (\strlen($str) % 8 > 0) { throw new \Exception('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new \Exception('Only 0\'s and 1\'s are permitted'); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map('bindec', $chrs[0]); // I'm just being slack here array_unshift($chrs, 'C*'); return \call_user_func_array('pack', $chrs); } /** * fromBin * * Converts a correct binary string to base32 * * @param string $str The string of 0's and 1's you want to convert * * @return string String encoded as base32 * * @throws \Exception */ private function fromBin($str) { if (\strlen($str) % 8 > 0) { throw new \Exception('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new \Exception('Only 0\'s and 1\'s are permitted'); } // Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out $str = preg_replace('/(.{5})/', '000$1', $str); // We need a string divisible by 5 $length = \strlen($str); $rbits = $length & 7; if ($rbits > 0) { // Excessive bits need to be padded $ebits = substr($str, $length - $rbits); $str = substr($str, 0, $length - $rbits); $str .= "000$ebits" . str_repeat('0', 5 - \strlen($ebits)); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map([$this, '_mapcharset'], $chrs[0]); return implode('', $chrs); } /** * toBin * * Accepts a base32 string and returns an ascii binary string * * @param string $str The base32 string to convert * * @return string Ascii binary string * * @throws \Exception */ private function toBin($str) { if (!preg_match('/^[' . self::CSRFC3548 . ']+$/', $str)) { throw new \Exception('Must match character set'); } // Convert the base32 string back to a binary string $str = implode('', array_map([$this, '_mapbin'], str_split($str))); // Remove the extra 0's we added $str = preg_replace('/000(.{5})/', '$1', $str); // Unpad if necessary $length = \strlen($str); $rbits = $length & 7; if ($rbits > 0) { $str = substr($str, 0, $length - $rbits); } return $str; } /** * fromString * * Convert any string to a base32 string * This should be binary safe... * * @param string $str The string to convert * * @return string The converted base32 string */ public function encode($str) { return $this->fromBin($this->str2bin($str)); } /** * toString * * Convert any base32 string to a normal sctring * This should be binary safe... * * @param string $str The base32 string to convert * * @return string The normal string */ public function decode($str) { $str = strtoupper($str); return $this->bin2str($this->toBin($str)); } /** * _mapcharset * * Used with array_map to map the bits from a binary string * directly into a base32 character set * * @param string $str The string of 0's and 1's you want to convert * * @return string Resulting base32 character * * @access private */ private function _mapcharset($str) { // Huh! $x = self::CSRFC3548; return $x[bindec($str)]; } /** * _mapbin * * Used with array_map to map the characters from a base32 * character set directly into a binary string * * @param string $chr The character to map * * @return string String of 0's and 1's * * @access private */ private function _mapbin($chr) { return sprintf('%08b', strpos(self::CSRFC3548, $chr)); } } PK1A#]`�{f��src/Encrypt/AES/OpenSSL.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt\AES; use Joomla\CMS\Encrypt\Randval; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * OpenSSL encryption class * * @since 4.0.0 */ class OpenSSL extends AbstractAES implements AesInterface { /** * The OpenSSL options for encryption / decryption * * @var integer */ protected $openSSLOptions = 0; /** * The encryption method to use * * @var string */ protected $method = 'aes-128-cbc'; /** * Constructor for this class */ public function __construct() { $this->openSSLOptions = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; } /** * Sets the AES encryption mode. * * WARNING: The strength is deprecated as it has a different effect in MCrypt and OpenSSL. MCrypt was abandoned in * 2003 before the Rijndael-128 algorithm was officially the Advanced Encryption Standard (AES). MCrypt also offered * Rijndael-192 and Rijndael-256 algorithms with different block sizes. These are NOT used in AES. OpenSSL, however, * implements AES correctly. It always uses a 128-bit (16 byte) block. The 192 and 256 bit strengths refer to the * key size, not the block size. Therefore using different strengths in MCrypt and OpenSSL will result in different * and incompatible ciphertexts. * * TL;DR: Always use $strength = 128! * * @param string $mode Choose between CBC (recommended) or ECB * @param int $strength Bit strength of the key (128, 192 or 256 bits). DEPRECATED. READ NOTES ABOVE. * * @return void */ public function setEncryptionMode($mode = 'cbc', $strength = 128) { static $availableAlgorithms = null; static $defaultAlgo = 'aes-128-cbc'; if (!\is_array($availableAlgorithms)) { $availableAlgorithms = openssl_get_cipher_methods(); foreach ( ['aes-256-cbc', 'aes-256-ecb', 'aes-192-cbc', 'aes-192-ecb', 'aes-128-cbc', 'aes-128-ecb', ] as $algo ) { if (\in_array($algo, $availableAlgorithms)) { $defaultAlgo = $algo; break; } } } $strength = (int) $strength; $mode = strtolower($mode); if (!\in_array($strength, [128, 192, 256])) { $strength = 256; } if (!\in_array($mode, ['cbc', 'ebc'])) { $mode = 'cbc'; } $algo = 'aes-' . $strength . '-' . $mode; if (!\in_array($algo, $availableAlgorithms)) { $algo = $defaultAlgo; } $this->method = $algo; } /** * Encrypts a string. Returns the raw binary ciphertext. * * WARNING: The plaintext is zero-padded to the algorithm's block size. You are advised to store the size of the * plaintext and trim the string to that length upon decryption. * * @param string $plainText The plaintext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * @param null|string $iv The initialization vector (for CBC mode algorithms) * * @return string The raw encrypted binary string. */ public function encrypt($plainText, $key, $iv = null) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = $this->resizeKey($iv, $iv_size); if (empty($iv)) { $randVal = new Randval(); $iv = $randVal->generate($iv_size); } $plainText .= $this->getZeroPadding($plainText, $iv_size); $cipherText = openssl_encrypt($plainText, $this->method, $key, $this->openSSLOptions, $iv); $cipherText = $iv . $cipherText; return $cipherText; } /** * Decrypts a string. Returns the raw binary plaintext. * * $ciphertext MUST start with the IV followed by the ciphertext, even for EBC data (the first block of data is * dropped in EBC mode since there is no concept of IV in EBC). * * WARNING: The returned plaintext is zero-padded to the algorithm's block size during encryption. You are advised * to trim the string to the original plaintext's length upon decryption. While rtrim($decrypted, "\0") sounds * appealing it's NOT the correct approach for binary data (zero bytes may actually be part of your plaintext, not * just padding!). * * @param string $cipherText The ciphertext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * * @return string The raw unencrypted binary string. */ public function decrypt($cipherText, $key) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = substr($cipherText, 0, $iv_size); $cipherText = substr($cipherText, $iv_size); $plainText = openssl_decrypt($cipherText, $this->method, $key, $this->openSSLOptions, $iv); // Remove the zero padding return rtrim($plainText, "\0"); } /** * Is this adapter supported? * * @return boolean */ public function isSupported() { if (!\function_exists('openssl_get_cipher_methods')) { return false; } if (!\function_exists('openssl_random_pseudo_bytes')) { return false; } if (!\function_exists('openssl_cipher_iv_length')) { return false; } if (!\function_exists('openssl_encrypt')) { return false; } if (!\function_exists('openssl_decrypt')) { return false; } if (!\function_exists('hash')) { return false; } if (!\function_exists('hash_algos')) { return false; } $algorithms = openssl_get_cipher_methods(); if (!\in_array('aes-128-cbc', $algorithms)) { return false; } $algorithms = hash_algos(); if (!\in_array('sha256', $algorithms)) { return false; } return true; } /** * Returns the encryption block size in bytes * * @return integer */ public function getBlockSize() { return openssl_cipher_iv_length($this->method); } } PK1A#]R���W W src/Encrypt/AES/AesInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt\AES; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface for AES encryption adapters * * @since 4.0.0 */ interface AesInterface { /** * Sets the AES encryption mode. * * WARNING: The strength is deprecated as it has a different effect in MCrypt and OpenSSL. MCrypt was abandoned in * 2003 before the Rijndael-128 algorithm was officially the Advanced Encryption Standard (AES). MCrypt also offered * Rijndael-192 and Rijndael-256 algorithms with different block sizes. These are NOT used in AES. OpenSSL, however, * implements AES correctly. It always uses a 128-bit (16 byte) block. The 192 and 256 bit strengths refer to the * key size, not the block size. Therefore using different strengths in MCrypt and OpenSSL will result in different * and incompatible ciphertexts. * * TL;DR: Always use $strength = 128! * * @param string $mode Choose between CBC (recommended) or ECB * @param int $strength Bit strength of the key (128, 192 or 256 bits). DEPRECATED. READ NOTES ABOVE. * * @return mixed */ public function setEncryptionMode($mode = 'cbc', $strength = 128); /** * Encrypts a string. Returns the raw binary ciphertext. * * WARNING: The plaintext is zero-padded to the algorithm's block size. You are advised to store the size of the * plaintext and trim the string to that length upon decryption. * * @param string $plainText The plaintext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * @param null|string $iv The initialization vector (for CBC mode algorithms) * * @return string The raw encrypted binary string. */ public function encrypt($plainText, $key, $iv = null); /** * Decrypts a string. Returns the raw binary plaintext. * * $ciphertext MUST start with the IV followed by the ciphertext, even for EBC data (the first block of data is * dropped in EBC mode since there is no concept of IV in EBC). * * WARNING: The returned plaintext is zero-padded to the algorithm's block size during encryption. You are advised * to trim the string to the original plaintext's length upon decryption. While rtrim($decrypted, "\0") sounds * appealing it's NOT the correct approach for binary data (zero bytes may actually be part of your plaintext, not * just padding!). * * @param string $cipherText The ciphertext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * * @return string The raw unencrypted binary string. */ public function decrypt($cipherText, $key); /** * Returns the encryption block size in bytes * * @return integer */ public function getBlockSize(); /** * Is this adapter supported? * * @return boolean */ public function isSupported(); } PK1A#]�ꠒ��src/Encrypt/AES/AbstractAES.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt\AES; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Abstract AES encryption class * * @since 4.0.0 */ abstract class AbstractAES { /** * Trims or zero-pads a key / IV * * @param string $key The key or IV to treat * @param int $size The block size of the currently used algorithm * * @return null|string Null if $key is null, treated string of $size byte length otherwise */ public function resizeKey($key, $size) { if (empty($key)) { return null; } $keyLength = \strlen($key); if (\function_exists('mb_strlen')) { $keyLength = mb_strlen($key, 'ASCII'); } if ($keyLength == $size) { return $key; } if ($keyLength > $size) { if (\function_exists('mb_substr')) { return mb_substr($key, 0, $size, 'ASCII'); } return substr($key, 0, $size); } return $key . str_repeat("\0", ($size - $keyLength)); } /** * Returns null bytes to append to the string so that it's zero padded to the specified block size * * @param string $string The binary string which will be zero padded * @param int $blockSize The block size * * @return string The zero bytes to append to the string to zero pad it to $blockSize */ protected function getZeroPadding($string, $blockSize) { $stringSize = \strlen($string); if (\function_exists('mb_strlen')) { $stringSize = mb_strlen($string, 'ASCII'); } if ($stringSize == $blockSize) { return ''; } if ($stringSize < $blockSize) { return str_repeat("\0", $blockSize - $stringSize); } $paddingBytes = $stringSize % $blockSize; return str_repeat("\0", $blockSize - $paddingBytes); } } PK1A#]SSrsrc/Encrypt/AES/Mcrypt.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt\AES; use Joomla\CMS\Encrypt\Randval; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Mcrypt implementation * * @since 4.0.0 * * @deprecated 4.3 will be removed in 6.0 * Will be removed without replacement */ class Mcrypt extends AbstractAES implements AesInterface { /** * Cypher Type * * @var string */ protected $cipherType = MCRYPT_RIJNDAEL_128; /** * Cypher Mode * * @var string */ protected $cipherMode = MCRYPT_MODE_CBC; /** * Set the encryption mode * * @param string $mode Encryption Mode * @param integer $strength Encryption Strength * * @return void */ public function setEncryptionMode($mode = 'cbc', $strength = 128) { switch ((int) $strength) { default: case '128': $this->cipherType = MCRYPT_RIJNDAEL_128; break; case '192': $this->cipherType = MCRYPT_RIJNDAEL_192; break; case '256': $this->cipherType = MCRYPT_RIJNDAEL_256; break; } switch (strtolower($mode)) { case 'ecb': $this->cipherMode = MCRYPT_MODE_ECB; break; default: case 'cbc': $this->cipherMode = MCRYPT_MODE_CBC; break; } } /** * Encrypt the data * * @param string $plainText Plaintext data * @param string $key Encryption key * @param string $iv IV for the encryption * * @return string Encrypted data */ public function encrypt($plainText, $key, $iv = null) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = $this->resizeKey($iv, $iv_size); if (empty($iv)) { $randVal = new Randval(); $iv = $randVal->generate($iv_size); } $cipherText = mcrypt_encrypt($this->cipherType, $key, $plainText, $this->cipherMode, $iv); $cipherText = $iv . $cipherText; return $cipherText; } /** * Decrypt encrypted data * * @param string $cipherText Encrypted data * @param string $key Encryptionkey * * @return string Plaintext data */ public function decrypt($cipherText, $key) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = substr($cipherText, 0, $iv_size); $cipherText = substr($cipherText, $iv_size); $plainText = mcrypt_decrypt($this->cipherType, $key, $cipherText, $this->cipherMode, $iv); return $plainText; } /** * Is this adapter supported? * * @return boolean */ public function isSupported() { if (!\function_exists('mcrypt_get_key_size')) { return false; } if (!\function_exists('mcrypt_get_iv_size')) { return false; } if (!\function_exists('mcrypt_create_iv')) { return false; } if (!\function_exists('mcrypt_encrypt')) { return false; } if (!\function_exists('mcrypt_decrypt')) { return false; } if (!\function_exists('mcrypt_list_algorithms')) { return false; } if (!\function_exists('hash')) { return false; } if (!\function_exists('hash_algos')) { return false; } $algorigthms = mcrypt_list_algorithms(); if (!\in_array('rijndael-128', $algorigthms)) { return false; } if (!\in_array('rijndael-192', $algorigthms)) { return false; } if (!\in_array('rijndael-256', $algorigthms)) { return false; } $algorigthms = hash_algos(); if (!\in_array('sha256', $algorigthms)) { return false; } return true; } /** * Get the block size * * @return integer */ public function getBlockSize() { return mcrypt_get_iv_size($this->cipherType, $this->cipherMode); } } PK1A#]�E�PP src/Encrypt/RandValInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Encrypt; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface RandValInterface * * @since 4.0.0 */ interface RandValInterface { /** * * Returns a cryptographically secure random value. * * @return string * */ public function generate(); } PK1A#]����"�"src/Encrypt/Aes.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ namespace Joomla\CMS\Encrypt; use Joomla\CMS\Encrypt\AES\AesInterface; use Joomla\CMS\Encrypt\AES\Mcrypt; use Joomla\CMS\Encrypt\AES\OpenSSL; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * A simple implementation of AES-128, AES-192 and AES-256 encryption using the * high performance mcrypt library. * * @since 1.0 */ class Aes { /** * The cipher key. * * @var string */ protected $key = ''; /** * The AES encryption adapter in use. * * @var AesInterface */ protected $adapter; /** * Initialise the AES encryption object. * * Note: If the key is not 16 bytes this class will do a stupid key expansion for legacy reasons (produce the * SHA-256 of the key string and throw away half of it). * * @param string $key The encryption key (password). It can be a raw key (16 bytes) or a passphrase. * @param int $strength Bit strength (128, 192 or 256) – ALWAYS USE 128 BITS. THIS PARAMETER IS DEPRECATED. * @param string $mode Encryption mode. Can be ebc or cbc. We recommend using cbc. * @param string $priority Priority which adapter we should try first * * @deprecated 4.3 $strength will be removed in 6.0 */ public function __construct($key, $strength = 128, $mode = 'cbc', $priority = 'openssl') { if ($priority === 'openssl') { $this->adapter = new OpenSSL(); if (!$this->adapter->isSupported()) { $this->adapter = new Mcrypt(); } } else { $this->adapter = new Mcrypt(); if (!$this->adapter->isSupported()) { $this->adapter = new OpenSSL(); } } $this->adapter->setEncryptionMode($mode, $strength); $this->setPassword($key, true); } /** * Sets the password for this instance. * * WARNING: Do not use the legacy mode, it's insecure * * @param string $password The password (either user-provided password or binary encryption key) to use * @param bool $legacyMode True to use the legacy key expansion. We recommend against using it. * * @since 4.0.0 * @return void */ public function setPassword($password, $legacyMode = false) { $this->key = $password; $passLength = \strlen($password); if (\function_exists('mb_strlen')) { $passLength = mb_strlen($password, 'ASCII'); } // Legacy mode was doing something stupid, requiring a key of 32 bytes. DO NOT USE LEGACY MODE! if ($legacyMode && ($passLength != 32)) { // Legacy mode: use the sha256 of the password $this->key = hash('sha256', $password, true); // We have to trim or zero pad the password (we end up throwing half of it away in Rijndael-128 / AES...) $this->key = $this->adapter->resizeKey($this->key, $this->adapter->getBlockSize()); } } /** * Encrypts a string using AES * * @param string $stringToEncrypt The plaintext to encrypt * @param bool $base64encoded Should I Base64-encode the result? * * @return string The cryptotext. Please note that the first 16 bytes of * the raw string is the IV (initialisation vector) which * is necessary for decoding the string. */ public function encryptString($stringToEncrypt, $base64encoded = true) { $blockSize = $this->adapter->getBlockSize(); $randVal = new Randval(); $iv = $randVal->generate($blockSize); $key = $this->getExpandedKey($blockSize, $iv); $cipherText = $this->adapter->encrypt($stringToEncrypt, $key, $iv); // Optionally pass the result through Base64 encoding if ($base64encoded) { $cipherText = base64_encode($cipherText); } // Return the result return $cipherText; } /** * Decrypts a ciphertext into a plaintext string using AES * * @param string $stringToDecrypt The ciphertext to decrypt. The first 16 bytes of the raw string must contain * the IV (initialisation vector). * @param bool $base64encoded Should I Base64-decode the data before decryption? * * @return string The plain text string */ public function decryptString($stringToDecrypt, $base64encoded = true) { if ($base64encoded) { $stringToDecrypt = base64_decode($stringToDecrypt); } // Extract IV $iv_size = $this->adapter->getBlockSize(); $iv = substr($stringToDecrypt, 0, $iv_size); $key = $this->getExpandedKey($iv_size, $iv); // Decrypt the data $plainText = $this->adapter->decrypt($stringToDecrypt, $key); return $plainText; } /** * Is AES encryption supported by this PHP installation? * * @return boolean */ public static function isSupported() { $adapter = new OpenSSL(); if (!$adapter->isSupported()) { $adapter = new Mcrypt(); if (!$adapter->isSupported()) { return false; } } if (!\function_exists('base64_encode')) { return false; } if (!\function_exists('base64_decode')) { return false; } if (!\function_exists('hash_algos')) { return false; } $algorithms = hash_algos(); if (!\in_array('sha256', $algorithms)) { return false; } return true; } /** * Get the expanded key * * @param integer $blockSize Blocksize to process * @param string $iv IV * * @return string */ public function getExpandedKey($blockSize, $iv) { $key = $this->key; $passLength = \strlen($key); if (\function_exists('mb_strlen')) { $passLength = mb_strlen($key, 'ASCII'); } if ($passLength != $blockSize) { $iterations = 1000; $salt = $this->adapter->resizeKey($iv, 16); $key = hash_pbkdf2('sha256', $this->key, $salt, $iterations, $blockSize, true); } return $key; } } if (!\function_exists('hash_pbkdf2')) { /** * Shim for missing hash_pbkdf2 * * @param string $algo Algorithm to use * @param string $password Plaintext password * @param string $salt Salt for the hash * @param integer $count Number of iterations * @param integer $length Length * @param boolean $rawOutput Raw output * * @return string Hashed string */ function hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $rawOutput = false) { if (!\in_array(strtolower($algo), hash_algos())) { trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING); } if (!is_numeric($count)) { trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . \gettype($count) . ' given', E_USER_WARNING); } if (!is_numeric($length)) { trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . \gettype($length) . ' given', E_USER_WARNING); } if ($count <= 0) { trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING); } if ($length < 0) { trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 0: ' . $length, E_USER_WARNING); } $output = ''; $block_count = $length ? ceil($length / \strlen(hash($algo, '', $rawOutput))) : 1; for ($i = 1; $i <= $block_count; $i++) { $last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true); for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = hash_hmac($algo, $last, $password, true)); } $output .= $xorsum; } if (!$rawOutput) { $output = bin2hex($output); } return $length ? substr($output, 0, $length) : $output; } } PK1A#]�x*���src/UCM/UCMBase.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\UCM; use Joomla\CMS\Factory; use Joomla\CMS\Helper\ContentHelper; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for implementing UCM * * @since 3.1 */ class UCMBase implements UCM { /** * The UCM type object * * @var UCMType * @since 3.1 */ protected $type; /** * The alias for the content table * * @var string * @since 3.1 */ protected $alias; /** * Instantiate the UCMBase. * * @param string $alias The alias string * @param UCMType $type The type object * * @since 3.1 */ public function __construct($alias = null, UCMType $type = null) { // Setup dependencies. $input = Factory::getApplication()->getInput(); $this->alias = $alias ?: $input->get('option') . '.' . $input->get('view'); $this->type = $type ?: $this->getType(); } /** * Store data to the appropriate table * * @param array $data Data to be stored * @param TableInterface $table Table Object * @param string $primaryKey The primary key name * * @return boolean True on success * * @since 3.1 * @throws \Exception */ protected function store($data, TableInterface $table = null, $primaryKey = null) { if (!$table) { $table = Table::getInstance('Ucm'); } $ucmId = $data['ucm_id'] ?? null; $primaryKey = $primaryKey ?: $ucmId; if (isset($primaryKey)) { $table->load($primaryKey); } try { $table->bind($data); } catch (\RuntimeException $e) { throw new \Exception($e->getMessage(), 500, $e); } try { $table->store(); } catch (\RuntimeException $e) { throw new \Exception($e->getMessage(), 500, $e); } return true; } /** * Get the UCM Content type. * * @return UCMType The UCM content type * * @since 3.1 */ public function getType() { if (!$this->type) { $this->type = new UCMType($this->alias); } return $this->type; } /** * Method to map the base ucm fields * * @param array $original Data array * @param UCMType $type UCM Content Type * * @return array Data array of UCM mappings * * @since 3.1 */ public function mapBase($original, UCMType $type = null) { $type = $type ?: $this->type; $data = [ 'ucm_type_id' => $type->id, 'ucm_item_id' => $original[$type->primary_key], 'ucm_language_id' => ContentHelper::getLanguageId($original['language']), ]; return $data; } } PK1A#]�{�֜�src/UCM/UCMType.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\UCM; use Joomla\Application\AbstractApplication; use Joomla\CMS\Factory; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * UCM Class for handling content types * * @property-read string $core_content_id * @property-read string $core_type_alias * @property-read string $core_title * @property-read string $core_alias * @property-read string $core_body * @property-read string $core_state * * @property-read string $core_checked_out_time * @property-read string $core_checked_out_user_id * @property-read string $core_access * @property-read string $core_params * @property-read string $core_featured * @property-read string $core_metadata * @property-read string $core_created_user_id * @property-read string $core_created_by_alias * @property-read string $core_created_time * @property-read string $core_modified_user_id * @property-read string $core_modified_time * @property-read string $core_language * @property-read string $core_publish_up * @property-read string $core_publish_down * @property-read string $core_content_item_id * @property-read string $asset_id * @property-read string $core_images * @property-read string $core_urls * @property-read string $core_hits * @property-read string $core_version * @property-read string $core_ordering * @property-read string $core_metakey * @property-read string $core_metadesc * @property-read string $core_catid * @property-read string $core_typeid * * @since 3.1 */ class UCMType implements UCM { /** * The UCM Type * * @var UCMType * @since 3.1 */ public $type; /** * The Database object * * @var DatabaseDriver * @since 3.1 */ protected $db; /** * The alias for the content type * * @var string * @since 3.1 */ protected $alias; /** * Class constructor * * @param string $alias The alias for the item * @param DatabaseDriver $database The database object * @param AbstractApplication $application The application object * * @since 3.1 */ public function __construct($alias = null, DatabaseDriver $database = null, AbstractApplication $application = null) { $this->db = $database ?: Factory::getDbo(); $app = $application ?: Factory::getApplication(); // Make the best guess we can in the absence of information. $this->alias = $alias ?: $app->getInput()->get('option') . '.' . $app->getInput()->get('view'); $this->type = $this->getTypeByAlias($this->alias); } /** * Get the Content Type * * @param integer $pk The primary key of the alias type * * @return object The UCM Type data * * @since 3.1 */ public function getType($pk = null) { if (!$pk) { return $this->getTypeByAlias($this->alias); } $query = $this->db->getQuery(true) ->select($this->db->quoteName('ct') . '.*') ->from($this->db->quoteName('#__content_types', 'ct')) ->where($this->db->quoteName('ct.type_id') . ' = :pk') ->bind(':pk', $pk, ParameterType::INTEGER); $this->db->setQuery($query); return $this->db->loadObject(); } /** * Get the Content Type from the alias * * @param string $typeAlias The alias for the type * * @return object The UCM Type data * * @since 3.2 */ public function getTypeByAlias($typeAlias = null) { $query = $this->db->getQuery(true) ->select($this->db->quoteName('ct') . '.*') ->from($this->db->quoteName('#__content_types', 'ct')) ->where($this->db->quoteName('ct.type_alias') . ' = :alias') ->bind(':alias', $typeAlias); $this->db->setQuery($query); return $this->db->loadObject(); } /** * Get the Content Type from the table class name * * @param string $tableName The table for the type * * @return mixed The UCM Type data if found, false if no match is found * * @since 3.2 */ public function getTypeByTable($tableName) { $query = $this->db->getQuery(true) ->select($this->db->quoteName('ct') . '.*') ->from($this->db->quoteName('#__content_types', 'ct')); $this->db->setQuery($query); $types = $this->db->loadObjectList(); foreach ($types as $type) { $tableFromType = json_decode($type->table); $tableNameFromType = $tableFromType->special->prefix . $tableFromType->special->type; if ($tableNameFromType === $tableName) { return $type; } } return false; } /** * Retrieves the UCM type ID * * @param string $alias The string of the type alias * * @return mixed The ID of the requested type or false if type is not found * * @since 3.1 */ public function getTypeId($alias = null) { if (!$alias) { $alias = $this->alias; } $query = $this->db->getQuery(true) ->select($this->db->quoteName('ct.type_id')) ->from($this->db->quoteName('#__content_types', 'ct')) ->where($this->db->quoteName('ct.type_alias') . ' = :alias') ->bind(':alias', $alias); $this->db->setQuery($query); $id = $this->db->loadResult(); if (!$id) { return false; } return $id; } /** * Method to expand the field mapping * * @param boolean $assoc True to return an associative array. * * @return mixed Array or object with field mappings. Defaults to object. * * @since 3.2 */ public function fieldmapExpand($assoc = false) { if (!empty($this->type->field_mappings)) { return $this->fieldmap = json_decode($this->type->field_mappings, $assoc); } else { return false; } } /** * Magic method to get the name of the field mapped to a ucm field (core_something). * * @param string $ucmField The name of the field in JTableCorecontent * * @return string The name mapped to the $ucmField for a given content type * * @since 3.2 */ public function __get($ucmField) { if (!isset($this->fieldmap)) { $this->fieldmapExpand(false); } return $this->fieldmap->common->$ucmField ?? null; } } PK1A#]��]_src/UCM/UCMContent.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\UCM; use Joomla\CMS\Factory; use Joomla\CMS\Helper\ContentHelper; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; use Joomla\Database\ParameterType; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Base class for implementing UCM * * @since 3.1 */ class UCMContent extends UCMBase { /** * The related table object * * @var Table * @since 3.1 */ protected $table; /** * The UCM data array * * @var array[] * @since 3.1 */ public $ucmData; /** * Instantiate UCMContent. * * @param TableInterface $table The table object * @param string $alias The type alias * @param UCMType $type The type object * * @since 3.1 */ public function __construct(TableInterface $table = null, $alias = null, UCMType $type = null) { parent::__construct($alias, $type); if ($table) { $this->table = $table; } else { $tableObject = json_decode($this->type->type->table); $this->table = Table::getInstance($tableObject->special->type, $tableObject->special->prefix, $tableObject->special->config); } } /** * Method to save the data * * @param array $original The original data to be saved * @param UCMType $type The UCM Type object * * @return boolean true * * @since 3.1 */ public function save($original = null, UCMType $type = null) { $type = $type ?: $this->type; $ucmData = $original ? $this->mapData($original, $type) : $this->ucmData; // Store the Common fields $this->store($ucmData['common']); // Store the special fields if (isset($ucmData['special'])) { $table = $this->table; $this->store($ucmData['special'], $table, ''); } return true; } /** * Delete content from the Core Content table * * @param mixed $pk Array or comma-separated string of ids to delete * @param UCMType $type The content type object * * @return boolean True if success * * @since 3.1 */ public function delete($pk, UCMType $type = null) { $db = Factory::getDbo(); $type = $type ?: $this->type; if (!\is_array($pk)) { $pk = explode(',', $pk); } $query = $db->getQuery(true) ->delete($db->quoteName('#__ucm_content')) ->where($db->quoteName('core_type_id') . ' = :typeId') ->whereIn($db->quoteName('core_content_item_id'), $pk) ->bind(':typeId', $type->type_id, ParameterType::INTEGER); $db->setQuery($query); $db->execute(); return true; } /** * Map the original content to the Core Content fields * * @param array $original The original data array * @param UCMType $type Type object for this data * * @return array[] $ucmData The mapped UCM data * * @since 3.1 */ public function mapData($original, UCMType $type = null) { $contentType = $type ?: $this->type; $fields = json_decode($contentType->type->field_mappings); $ucmData = []; $common = \is_object($fields->common) ? $fields->common : $fields->common[0]; foreach ($common as $i => $field) { if ($field && $field !== 'null' && \array_key_exists($field, $original)) { $ucmData['common'][$i] = $original[$field]; } } if (\array_key_exists('special', $ucmData)) { $special = \is_object($fields->special) ? $fields->special : $fields->special[0]; foreach ($special as $i => $field) { if ($field && $field !== 'null' && \array_key_exists($field, $original)) { $ucmData['special'][$i] = $original[$field]; } } } $ucmData['common']['core_type_alias'] = $contentType->type->type_alias; $ucmData['common']['core_type_id'] = $contentType->type->type_id; if (isset($ucmData['special'])) { $ucmData['special']['ucm_id'] = $ucmData['common']['ucm_id']; } $this->ucmData = $ucmData; return $this->ucmData; } /** * Store data to the appropriate table * * @param array $data Data to be stored * @param TableInterface $table Table Object * @param boolean $primaryKey Flag that is true for data that are using #__ucm_content as their primary table * * @return boolean true on success * * @since 3.1 */ protected function store($data, TableInterface $table = null, $primaryKey = null) { $table = $table ?: Table::getInstance('Corecontent'); $typeId = $this->getType()->type->type_id; $primaryKey = $primaryKey ?: $this->getPrimaryKey($typeId, $data['core_content_item_id']); if (!$primaryKey) { // Store the core UCM mappings $baseData = []; $baseData['ucm_type_id'] = $typeId; $baseData['ucm_item_id'] = $data['core_content_item_id']; $baseData['ucm_language_id'] = ContentHelper::getLanguageId($data['core_language']); if (parent::store($baseData)) { $primaryKey = $this->getPrimaryKey($typeId, $data['core_content_item_id']); } } return parent::store($data, $table, $primaryKey); } /** * Get the value of the primary key from #__ucm_base * * @param integer $typeId The ID for the type * @param integer $contentItemId Value of the primary key in the legacy or secondary table * * @return integer The integer of the primary key * * @since 3.1 */ public function getPrimaryKey($typeId, $contentItemId) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('ucm_id')) ->from($db->quoteName('#__ucm_base')) ->where( [ $db->quoteName('ucm_item_id') . ' = :itemId', $db->quoteName('ucm_type_id') . ' = :typeId', ] ) ->bind(':itemId', $contentItemId, ParameterType::INTEGER) ->bind(':typeId', $typeId, ParameterType::INTEGER); $db->setQuery($query); return $db->loadResult(); } } PK1A#]�(���src/UCM/UCM.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\UCM; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to handle UCM * * @since 3.1 */ interface UCM { } PK1A#]�l��66src/Button/TransitionButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Button; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The PublishedButton class. * * @since 4.0.0 */ class TransitionButton extends ActionButton { /** * The layout path to render. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.button.transition-button'; /** * ActionButton constructor. * * @param array $options The options for all buttons in this group. * * @since 4.0.0 */ public function __construct(array $options = []) { parent::__construct($options); $this->unknownState['icon'] = 'shuffle'; $this->unknownState['title'] = $options['title'] ?? Text::_('JLIB_HTML_UNKNOWN_STATE'); $this->unknownState['tip_content'] = $options['tip_content'] ?? $this->unknownState['title']; } /** * Render action button by item value. * * @param integer|null $value Current value of this item. * @param integer|null $row The row number of this item. * @param array $options The options to override group options. * * @return string Rendered HTML. * * @since 4.0.0 */ public function render(?int $value = null, ?int $row = null, array $options = []): string { $default = $this->unknownState; $options['tip_title'] = $options['tip_title'] ?? ($options['title'] ?? $default['title']); return parent::render($value, $row, $options); } } PK1A#]���qqsrc/Button/FeaturedButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Button; use Joomla\CMS\Date\Date; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The FeaturedButton class. * * @since 4.0.0 */ class FeaturedButton extends ActionButton { /** * Configure this object. * * @return void * * @since 4.0.0 */ protected function preprocess() { $this->addState( 0, 'featured', 'icon-unfeatured', Text::_('JGLOBAL_TOGGLE_FEATURED'), ['tip_title' => Text::_('JUNFEATURED')] ); $this->addState( 1, 'unfeatured', 'icon-color-featured icon-star', Text::_('JGLOBAL_TOGGLE_FEATURED'), ['tip_title' => Text::_('JFEATURED')] ); } /** * Render action button by item value. * * @param integer|null $value Current value of this item. * @param integer|null $row The row number of this item. * @param array $options The options to override group options. * @param string|Date $featuredUp The date which item featured up. * @param string|Date $featuredDown The date which item featured down. * * @return string Rendered HTML. * * @since 4.0.0 */ public function render(?int $value = null, ?int $row = null, array $options = [], $featuredUp = null, $featuredDown = null): string { if ($featuredUp || $featuredDown) { $bakState = $this->getState($value); $default = $this->getState($value) ?? $this->unknownState; $nowDate = Factory::getDate()->toUnix(); $tz = Factory::getUser()->getTimezone(); if (!is_null($featuredUp)) { $featuredUp = Factory::getDate($featuredUp, 'UTC')->setTimezone($tz); } if (!is_null($featuredDown)) { $featuredDown = Factory::getDate($featuredDown, 'UTC')->setTimezone($tz); } // Add tips and special titles // Create special titles for featured items if ($value === 1) { // Create tip text, only we have featured up or down settings $tips = []; if ($featuredUp) { $tips[] = Text::sprintf('JLIB_HTML_FEATURED_STARTED', HTMLHelper::_('date', $featuredUp, Text::_('DATE_FORMAT_LC5'), 'UTC')); } if ($featuredDown) { $tips[] = Text::sprintf('JLIB_HTML_FEATURED_FINISHED', HTMLHelper::_('date', $featuredDown, Text::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br>', $tips); $default['title'] = $tip; $options['tip_title'] = Text::_('JLIB_HTML_FEATURED_ITEM'); if ($featuredUp && $nowDate < $featuredUp->toUnix()) { $options['tip_title'] = Text::_('JLIB_HTML_FEATURED_PENDING_ITEM'); $default['icon'] = 'pending'; } if ($featuredDown && $nowDate > $featuredDown->toUnix()) { $options['tip_title'] = Text::_('JLIB_HTML_FEATURED_EXPIRED_ITEM'); $default['icon'] = 'expired'; } } $this->states[$value] = $default; $html = parent::render($value, $row, $options); $this->states[$value] = $bakState; return $html; } return parent::render($value, $row, $options); } } PK1A#]��2VVsrc/Button/ActionButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Button; use Joomla\CMS\Language\Text; use Joomla\CMS\Layout\FileLayout; use Joomla\CMS\Layout\LayoutHelper; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The TaskButton class. * * @since 4.0.0 */ class ActionButton { /** * The button states profiles. * * @var array * * @since 4.0.0 */ protected $states = []; /** * Default options for unknown state. * * @var array * * @since 4.0.0 */ protected $unknownState = [ 'value' => null, 'task' => '', 'icon' => 'question', 'title' => 'Unknown state', 'options' => [ 'disabled' => false, 'only_icon' => false, 'tip' => true, 'tip_title' => '', 'task_prefix' => '', 'checkbox_name' => 'cb', ], ]; /** * Options of this button set. * * @var Registry * * @since 4.0.0 */ protected $options; /** * The layout path to render. * * @var string * * @since 4.0.0 */ protected $layout = 'joomla.button.action-button'; /** * ActionButton constructor. * * @param array $options The options for all buttons in this group. * * @since 4.0.0 */ public function __construct(array $options = []) { $this->options = new Registry($options); // Replace some dynamic values $this->unknownState['title'] = Text::_('JLIB_HTML_UNKNOWN_STATE'); $this->preprocess(); } /** * Configure this object. * * @return void * * @since 4.0.0 */ protected function preprocess() { // Implement this method. } /** * Add a state profile. * * @param integer $value The value of this state. * @param string $task The task you want to execute after click this button. * @param string $icon The icon to display for user. * @param string $title Title text will show if we enable tooltips. * @param array $options The button options, will override group options. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function addState(int $value, string $task, string $icon = 'ok', string $title = '', array $options = []): self { // Force type to prevent null data $this->states[$value] = [ 'value' => $value, 'task' => $task, 'icon' => $icon, 'title' => $title, 'options' => $options, ]; return $this; } /** * Get state profile by value name. * * @param integer $value The value name we want to get. * * @return array|null Return state profile or NULL. * * @since 4.0.0 */ public function getState(int $value): ?array { return $this->states[$value] ?? null; } /** * Remove a state by value name. * * @param integer $value Remove state by this value. * * @return static Return to support chaining. * * @since 4.0.0 */ public function removeState(int $value): self { if (isset($this->states[$value])) { unset($this->states[$value]); } return $this; } /** * Render action button by item value. * * @param integer|null $value Current value of this item. * @param integer|null $row The row number of this item. * @param array $options The options to override group options. * * @return string Rendered HTML. * * @since 4.0.0 * * @throws \InvalidArgumentException */ public function render(?int $value = null, ?int $row = null, array $options = []): string { $data = $this->getState($value) ?? $this->unknownState; $data = ArrayHelper::mergeRecursive( $this->unknownState, $data, [ 'options' => $this->options->toArray(), ], [ 'options' => $options, ] ); $data['row'] = $row; $data['icon'] = $this->fetchIconClass($data['icon']); return LayoutHelper::render($this->layout, $data); } /** * Render to string. * * @return string * * @since 4.0.0 */ public function __toString(): string { try { return $this->render(); } catch (\Throwable $e) { return (string) $e; } } /** * Method to get property layout. * * @return string * * @since 4.0.0 */ public function getLayout(): string { return $this->layout; } /** * Method to set property template. * * @param string $layout The layout path. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function setLayout(string $layout): self { $this->layout = $layout; return $this; } /** * Method to get property options. * * @return array * * @since 4.0.0 */ public function getOptions(): array { return (array) $this->options->toArray(); } /** * Method to set property options. * * @param array $options The options of this button group. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function setOptions(array $options): self { $this->options = new Registry($options); return $this; } /** * Get an option value. * * @param string $name The option name. * @param mixed $default Default value if not exists. * * @return mixed Return option value or default value. * * @since 4.0.0 */ public function getOption(string $name, $default = null) { return $this->options->get($name, $default); } /** * Set option value. * * @param string $name The option name. * @param mixed $value The option value. * * @return static Return self to support chaining. * * @since 4.0.0 */ public function setOption(string $name, $value): self { $this->options->set($name, $value); return $this; } /** * Method to get the CSS class name for an icon identifier. * * Can be redefined in the final class. * * @param string $identifier Icon identification string. * * @return string CSS class name. * * @since 4.0.0 */ public function fetchIconClass(string $identifier): string { // It's an ugly hack, but this allows templates to define the icon classes for the toolbar $layout = new FileLayout('joomla.button.iconclass'); return $layout->render(['icon' => $identifier]); } } PK1A#]�OT��src/Button/PublishedButton.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Button; use Joomla\CMS\Date\Date; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * The PublishedButton class. * * @since 4.0.0 */ class PublishedButton extends ActionButton { /** * Configure this object. * * @return void * * @since 4.0.0 */ protected function preprocess() { $this->addState(1, 'unpublish', 'publish', Text::_('JLIB_HTML_UNPUBLISH_ITEM'), ['tip_title' => Text::_('JPUBLISHED')]); $this->addState(0, 'publish', 'unpublish', Text::_('JLIB_HTML_PUBLISH_ITEM'), ['tip_title' => Text::_('JUNPUBLISHED')]); $this->addState(2, 'unpublish', 'archive', Text::_('JLIB_HTML_UNPUBLISH_ITEM'), ['tip_title' => Text::_('JARCHIVED')]); $this->addState(-2, 'publish', 'trash', Text::_('JLIB_HTML_PUBLISH_ITEM'), ['tip_title' => Text::_('JTRASHED')]); } /** * Render action button by item value. * * @param integer|null $value Current value of this item. * @param integer|null $row The row number of this item. * @param array $options The options to override group options. * @param string|Date $publishUp The date which item publish up. * @param string|Date $publishDown The date which item publish down. * * @return string Rendered HTML. * * @since 4.0.0 */ public function render(?int $value = null, ?int $row = null, array $options = [], $publishUp = null, $publishDown = null): string { if ($publishUp || $publishDown) { $bakState = $this->getState($value); $default = $this->getState($value) ?? $this->unknownState; $nullDate = Factory::getDbo()->getNullDate(); $nowDate = Factory::getDate()->toUnix(); $tz = Factory::getUser()->getTimezone(); $publishUp = ($publishUp !== null && $publishUp !== $nullDate) ? Factory::getDate($publishUp, 'UTC')->setTimezone($tz) : false; $publishDown = ($publishDown !== null && $publishDown !== $nullDate) ? Factory::getDate($publishDown, 'UTC')->setTimezone($tz) : false; // Add tips and special titles // Create special titles for published items if ($value === 1) { // Create tip text, only we have publish up or down settings $tips = []; if ($publishUp) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_START', HTMLHelper::_('date', $publishUp, Text::_('DATE_FORMAT_LC5'), 'UTC')); $tips[] = Text::_('JLIB_HTML_PUBLISHED_UNPUBLISH'); } if ($publishDown) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_FINISHED', HTMLHelper::_('date', $publishDown, Text::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br>', $tips); $default['title'] = $tip; $options['tip_title'] = Text::_('JLIB_HTML_PUBLISHED_ITEM'); if ($publishUp && $nowDate < $publishUp->toUnix()) { $options['tip_title'] = Text::_('JLIB_HTML_PUBLISHED_PENDING_ITEM'); $default['icon'] = 'pending'; } if ($publishDown && $nowDate > $publishDown->toUnix()) { $options['tip_title'] = Text::_('JLIB_HTML_PUBLISHED_EXPIRED_ITEM'); $default['icon'] = 'expired'; } if (array_key_exists('category_published', $options)) { $categoryPublished = $options['category_published']; if ($categoryPublished === 0) { $options['tip_title'] = Text::_('JLIB_HTML_ITEM_PUBLISHED_BUT_CATEGORY_UNPUBLISHED'); $default['icon'] = 'expired'; } if ($categoryPublished === -2) { $options['tip_title'] = Text::_('JLIB_HTML_ITEM_PUBLISHED_BUT_CATEGORY_TRASHED'); $default['icon'] = 'expired'; } } } $this->states[$value] = $default; $html = parent::render($value, $row, $options); $this->states[$value] = $bakState; return $html; } return parent::render($value, $row, $options); } } PK1A#]g�j� src/User/CurrentUserTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; use Joomla\CMS\Factory; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for classes which require a user to work with. * * @since 4.2.0 */ trait CurrentUserTrait { /** * The current user object. * * @var User * @since 4.2.0 */ private $currentUser; /** * Returns the current user, if none is set the identity of the global app * is returned. This will change in 6.0 and an empty user will be returned. * * @return User * * @since 4.2.0 */ protected function getCurrentUser(): User { if (!$this->currentUser) { @trigger_error( sprintf('User must be set in %s. This will not be caught anymore in 6.0', __METHOD__), E_USER_DEPRECATED ); $this->currentUser = Factory::getApplication()->getIdentity() ?: Factory::getUser(); } return $this->currentUser; } /** * Sets the current user. * * @param User $currentUser The current user object * * @return void * * @since 4.2.0 */ public function setCurrentUser(User $currentUser): void { $this->currentUser = $currentUser; } } PK1A#]0Tljj"src/User/UserFactoryAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a UserFactoryInterface Aware Class. * * @since 4.4.0 */ trait UserFactoryAwareTrait { /** * UserFactoryInterface * * @var UserFactoryInterface * @since 4.4.0 */ private $userFactory; /** * Get the UserFactoryInterface. * * @return UserFactoryInterface * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the UserFactory has not been set. */ protected function getUserFactory(): UserFactoryInterface { if ($this->userFactory) { return $this->userFactory; } throw new \UnexpectedValueException('UserFactory not set in ' . __CLASS__); } /** * Set the user factory to use. * * @param UserFactoryInterface $userFactory The user factory to use. * * @return void * * @since 4.4.0 */ public function setUserFactory(UserFactoryInterface $userFactory): void { $this->userFactory = $userFactory; } } PK1A#]"T��a�asrc/User/User.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; use Joomla\CMS\Access\Access; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Table; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * User class. Handles all application interaction with a user * * @since 1.7.0 */ class User extends CMSObject { /** * A cached switch for if this user has root access rights. * * @var boolean * @since 1.7.0 */ protected $isRoot = null; /** * Unique id * * @var integer * @since 1.7.0 */ public $id = null; /** * The user's real name (or nickname) * * @var string * @since 1.7.0 */ public $name = null; /** * The login name * * @var string * @since 1.7.0 */ public $username = null; /** * The email * * @var string * @since 1.7.0 */ public $email = null; /** * MD5 encrypted password * * @var string * @since 1.7.0 */ public $password = null; /** * Clear password, only available when a new password is set for a user * * @var string * @since 1.7.0 */ public $password_clear = ''; /** * Block status * * @var integer * @since 1.7.0 */ public $block = null; /** * Should this user receive system email * * @var integer * @since 1.7.0 */ public $sendEmail = null; /** * Date the user was registered * * @var string * @since 1.7.0 */ public $registerDate = null; /** * Date of last visit * * @var string * @since 1.7.0 */ public $lastvisitDate = null; /** * Activation hash * * @var string * @since 1.7.0 */ public $activation = null; /** * User parameters * * @var Registry * @since 1.7.0 */ public $params = null; /** * Associative array of user names => group ids * * @var array * @since 1.7.0 */ public $groups = []; /** * Guest status * * @var integer * @since 1.7.0 */ public $guest = null; /** * Last Reset Time * * @var string * @since 3.0.1 */ public $lastResetTime = null; /** * Count since last Reset Time * * @var integer * @since 3.0.1 */ public $resetCount = null; /** * Flag to require the user's password be reset * * @var integer * @since 3.2 */ public $requireReset = null; /** * User parameters * * @var Registry * @since 1.7.0 */ protected $_params = null; /** * Authorised access groups * * @var array * @since 1.7.0 */ protected $_authGroups = null; /** * Authorised access levels * * @var array * @since 1.7.0 */ protected $_authLevels = null; /** * Authorised access actions * * @var array * @since 1.7.0 */ protected $_authActions = null; /** * Error message * * @var string * @since 1.7.0 */ protected $_errorMsg = null; /** * @var array User instances container. * @since 1.7.3 */ protected static $instances = []; /** * The access level id * * @var integer * @since 4.3.0 * * @deprecated 4.4.0 will be removed in 6.0 as this property is not used anymore */ public $aid = null; /** * Constructor activating the default information of the language * * @param integer $identifier The primary key of the user to load (optional). * * @since 1.7.0 */ public function __construct($identifier = 0) { // Create the user parameters object $this->_params = new Registry(); // Load the user if it exists if (!empty($identifier)) { $this->load($identifier); } else { // Initialise $this->id = 0; $this->sendEmail = 0; $this->aid = 0; $this->guest = 1; } } /** * Returns the global User object, only creating it if it doesn't already exist. * * @param integer $identifier The primary key of the user to load (optional). * * @return User The User object. * * @since 1.7.0 * @deprecated 4.3 will be removed in 6.0 * Load the user service from the dependency injection container or via $app->getIdentity() * Example: Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id) */ public static function getInstance($identifier = 0) { @trigger_error( sprintf( '%1$s() is deprecated. Load the user from the dependency injection container or via %2$s::getApplication()->getIdentity().', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); // Find the user id if (!is_numeric($identifier)) { return Factory::getContainer()->get(UserFactoryInterface::class)->loadUserByUsername($identifier); } else { $id = $identifier; } // If the $id is zero, just return an empty User. // Note: don't cache this user because it'll have a new ID on save! if ($id === 0) { return Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id); } // Check if the user ID is already cached. if (empty(self::$instances[$id])) { self::$instances[$id] = Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id); } return self::$instances[$id]; } /** * Method to get a parameter value * * @param string $key Parameter key * @param mixed $default Parameter default value * * @return mixed The value or the default if it did not exist * * @since 1.7.0 */ public function getParam($key, $default = null) { return $this->_params->get($key, $default); } /** * Method to set a parameter * * @param string $key Parameter key * @param mixed $value Parameter value * * @return mixed Set parameter value * * @since 1.7.0 */ public function setParam($key, $value) { return $this->_params->set($key, $value); } /** * Method to set a default parameter if it does not exist * * @param string $key Parameter key * @param mixed $value Parameter value * * @return mixed Set parameter value * * @since 1.7.0 */ public function defParam($key, $value) { return $this->_params->def($key, $value); } /** * Method to check User object authorisation against an access control * object and optionally an access extension object * * @param string $action The name of the action to check for permission. * @param string $assetname The name of the asset on which to perform the action. * * @return boolean True if authorised * * @since 1.7.0 */ public function authorise($action, $assetname = null) { // Make sure we only check for core.admin once during the run. if ($this->isRoot === null) { $this->isRoot = false; // Check for the configuration file failsafe. $rootUser = Factory::getApplication()->get('root_user'); // The root_user variable can be a numeric user ID or a username. if (is_numeric($rootUser) && $this->id > 0 && $this->id == $rootUser) { $this->isRoot = true; } elseif ($this->username && $this->username == $rootUser) { $this->isRoot = true; } elseif ($this->id > 0) { // Get all groups against which the user is mapped. $identities = $this->getAuthorisedGroups(); array_unshift($identities, $this->id * -1); if (Access::getAssetRules(1)->allow('core.admin', $identities)) { $this->isRoot = true; return true; } } } return $this->isRoot ? true : (bool) Access::check($this->id, $action, $assetname); } /** * Method to return a list of all categories that a user has permission for a given action * * @param string $component The component from which to retrieve the categories * @param string $action The name of the section within the component from which to retrieve the actions. * * @return array List of categories that this group can do this action to (empty array if none). Categories must be published. * * @since 1.7.0 */ public function getAuthorisedCategories($component, $action) { // Brute force method: get all published category rows for the component and check each one // @todo: Modify the way permissions are stored in the db to allow for faster implementation and better scaling $db = Factory::getDbo(); $subQuery = $db->getQuery(true) ->select($db->quoteName(['id', 'asset_id'])) ->from($db->quoteName('#__categories')) ->where( [ $db->quoteName('extension') . ' = :component', $db->quoteName('published') . ' = 1', ] ); $query = $db->getQuery(true) ->select($db->quoteName(['c.id', 'a.name'])) ->from('(' . $subQuery . ') AS ' . $db->quoteName('c')) ->join('INNER', $db->quoteName('#__assets', 'a'), $db->quoteName('c.asset_id') . ' = ' . $db->quoteName('a.id')) ->bind(':component', $component); $db->setQuery($query); $allCategories = $db->loadObjectList('id'); $allowedCategories = []; foreach ($allCategories as $category) { if ($this->authorise($action, $category->name)) { $allowedCategories[] = (int) $category->id; } } return $allowedCategories; } /** * Gets an array of the authorised access levels for the user * * @return array * * @since 1.7.0 */ public function getAuthorisedViewLevels() { if ($this->_authLevels === null) { $this->_authLevels = []; } if (empty($this->_authLevels)) { $this->_authLevels = Access::getAuthorisedViewLevels($this->id); } return $this->_authLevels; } /** * Gets an array of the authorised user groups * * @return array * * @since 1.7.0 */ public function getAuthorisedGroups() { if ($this->_authGroups === null) { $this->_authGroups = []; } if (empty($this->_authGroups)) { $this->_authGroups = Access::getGroupsByUser($this->id); } return $this->_authGroups; } /** * Clears the access rights cache of this user * * @return void * * @since 3.4.0 */ public function clearAccessRights() { $this->_authLevels = null; $this->_authGroups = null; $this->isRoot = null; Access::clearStatics(); } /** * Pass through method to the table for setting the last visit date * * @param integer $timestamp The timestamp, defaults to 'now'. * * @return boolean True on success. * * @since 1.7.0 */ public function setLastVisit($timestamp = null) { // Create the user table object /** @var \Joomla\CMS\Table\User $table */ $table = $this->getTable(); $table->load($this->id); return $table->setLastVisit($timestamp); } /** * Method to get the user timezone. * * If the user didn't set a timezone, it will return the server timezone * * @return \DateTimeZone * * @since 3.7.0 */ public function getTimezone() { $timezone = $this->getParam('timezone', Factory::getApplication()->get('offset', 'GMT')); return new \DateTimeZone($timezone); } /** * Method to get the user parameters * * @param object $params The user parameters object * * @return void * * @since 1.7.0 */ public function setParameters($params) { $this->_params = $params; } /** * Method to get the user table object * * This function uses a static variable to store the table name of the user table to * instantiate. You can call this function statically to set the table name if * needed. * * @param string $type The user table name to be used * @param string $prefix The user table prefix to be used * * @return Table The user table object * * @note At 4.0 this method will no longer be static * @since 1.7.0 */ public static function getTable($type = null, $prefix = 'JTable') { static $tabletype; // Set the default tabletype; if (!isset($tabletype)) { $tabletype['name'] = 'user'; $tabletype['prefix'] = 'JTable'; } // Set a custom table type is defined if (isset($type)) { $tabletype['name'] = $type; $tabletype['prefix'] = $prefix; } // Create the user table object return Table::getInstance($tabletype['name'], $tabletype['prefix']); } /** * Method to bind an associative array of data to a user object * * @param array &$array The associative array to bind to the object * * @return boolean True on success * * @since 1.7.0 */ public function bind(&$array) { // Let's check to see if the user is new or not if (empty($this->id)) { // Check the password and create the crypted password if (empty($array['password'])) { $array['password'] = UserHelper::genRandomPassword(32); $array['password2'] = $array['password']; } // Not all controllers check the password, although they should. // Hence this code is required: if (isset($array['password2']) && $array['password'] != $array['password2']) { Factory::getApplication()->enqueueMessage(Text::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH'), 'error'); return false; } $this->password_clear = ArrayHelper::getValue($array, 'password', '', 'string'); $array['password'] = UserHelper::hashPassword($array['password']); // Set the registration timestamp $this->set('registerDate', Factory::getDate()->toSql()); } else { // Updating an existing user if (!empty($array['password'])) { if ($array['password'] != $array['password2']) { $this->setError(Text::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH')); return false; } $this->password_clear = ArrayHelper::getValue($array, 'password', '', 'string'); // Check if the user is reusing the current password if required to reset their password if ($this->requireReset == 1 && UserHelper::verifyPassword($this->password_clear, $this->password)) { $this->setError(Text::_('JLIB_USER_ERROR_CANNOT_REUSE_PASSWORD')); return false; } $array['password'] = UserHelper::hashPassword($array['password']); // Reset the change password flag $array['requireReset'] = 0; } else { $array['password'] = $this->password; } // Prevent updating internal fields unset($array['registerDate']); unset($array['lastvisitDate']); unset($array['lastResetTime']); unset($array['resetCount']); } if (\array_key_exists('params', $array)) { $this->_params->loadArray($array['params']); if (\is_array($array['params'])) { $params = (string) $this->_params; } else { $params = $array['params']; } $this->params = $params; } // Bind the array if (!$this->setProperties($array)) { $this->setError(Text::_('JLIB_USER_ERROR_BIND_ARRAY')); return false; } // Make sure its an integer $this->id = (int) $this->id; return true; } /** * Method to save the User object to the database * * @param boolean $updateOnly Save the object only if not a new user * Currently only used in the user reset password method. * * @return boolean True on success * * @since 1.7.0 * @throws \RuntimeException */ public function save($updateOnly = false) { // Create the user table object $table = $this->getTable(); $this->params = (string) $this->_params; $table->bind($this->getProperties()); // Allow an exception to be thrown. try { // Check and store the object. if (!$table->check()) { $this->setError($table->getError()); return false; } // If user is made a Super Admin group and user is NOT a Super Admin // @todo ACL - this needs to be acl checked $my = Factory::getUser(); // Are we creating a new user $isNew = empty($this->id); // If we aren't allowed to create new users return if ($isNew && $updateOnly) { return true; } // Get the old user $oldUser = new User($this->id); // Access Checks // The only mandatory check is that only Super Admins can operate on other Super Admin accounts. // To add additional business rules, use a user plugin and throw an Exception with onUserBeforeSave. // Check if I am a Super Admin $iAmSuperAdmin = $my->authorise('core.admin'); $iAmRehashingSuperadmin = false; if (($my->id == 0 && !$isNew) && $this->id == $oldUser->id && $oldUser->authorise('core.admin') && $oldUser->password != $this->password) { $iAmRehashingSuperadmin = true; } // Check if we are using a CLI application $isCli = false; if (Factory::getApplication()->isCli()) { $isCli = true; } // We are only worried about edits to this account if I am not a Super Admin. if ($iAmSuperAdmin != true && $iAmRehashingSuperadmin != true && $isCli != true) { // I am not a Super Admin, and this one is, so fail. if (!$isNew && Access::check($this->id, 'core.admin')) { throw new \RuntimeException('User not Super Administrator'); } if ($this->groups != null) { // I am not a Super Admin and I'm trying to make one. foreach ($this->groups as $groupId) { if (Access::checkGroup($groupId, 'core.admin')) { throw new \RuntimeException('User not Super Administrator'); } } } } // Unset the activation token, if the mail address changes - that affects both, activation and PW resets if ($this->email !== $oldUser->email && $this->id !== 0 && !empty($this->activation) && !$this->block) { $table->activation = ''; } // Fire the onUserBeforeSave event. PluginHelper::importPlugin('user'); $result = Factory::getApplication()->triggerEvent('onUserBeforeSave', [$oldUser->getProperties(), $isNew, $this->getProperties()]); if (\in_array(false, $result, true)) { // Plugin will have to raise its own error or throw an exception. return false; } // Store the user data in the database $result = $table->store(); // Set the id for the User object in case we created a new user. if (empty($this->id)) { $this->id = $table->get('id'); } if ($my->id == $table->id) { $registry = new Registry($table->params); $my->setParameters($registry); } // Fire the onUserAfterSave event Factory::getApplication()->triggerEvent('onUserAfterSave', [$this->getProperties(), $isNew, $result, $this->getError()]); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } return $result; } /** * Method to delete the User object from the database * * @return boolean True on success * * @since 1.7.0 */ public function delete() { PluginHelper::importPlugin('user'); // Trigger the onUserBeforeDelete event Factory::getApplication()->triggerEvent('onUserBeforeDelete', [$this->getProperties()]); // Create the user table object $table = $this->getTable(); if (!$result = $table->delete($this->id)) { $this->setError($table->getError()); } // Trigger the onUserAfterDelete event Factory::getApplication()->triggerEvent('onUserAfterDelete', [$this->getProperties(), $result, $this->getError()]); return $result; } /** * Method to load a User object by user id number * * @param mixed $id The user id of the user to load * * @return boolean True on success * * @since 1.7.0 */ public function load($id) { // Create the user table object $table = $this->getTable(); // Load the UserModel object based on the user id or throw a warning. if (!$table->load($id)) { // Reset to guest user $this->guest = 1; Log::add(Text::sprintf('JLIB_USER_ERROR_UNABLE_TO_LOAD_USER', $id), Log::WARNING, 'jerror'); return false; } /* * Set the user parameters using the default XML file. We might want to * extend this in the future to allow for the ability to have custom * user parameters, but for right now we'll leave it how it is. */ if ($table->params) { $this->_params->loadString($table->params); } // Assuming all is well at this point let's bind the data $this->setProperties($table->getProperties()); // The user is no longer a guest if ($this->id != 0) { $this->guest = 0; } else { $this->guest = 1; } return true; } /** * Method to allow serialize the object with minimal properties. * * @return array The names of the properties to include in serialization. * * @since 3.6.0 */ public function __sleep() { return ['id']; } /** * Method to recover the full object on unserialize. * * @return void * * @since 3.6.0 */ public function __wakeup() { // Initialise some variables $this->_params = new Registry(); // Load the user if it exists if (!empty($this->id) && $this->load($this->id)) { // Push user into cached instances. self::$instances[$this->id] = $this; } else { // Initialise $this->id = 0; $this->sendEmail = 0; $this->aid = 0; $this->guest = 1; } } } PK1A#]^k6��src/User/UserFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; use Joomla\Database\DatabaseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating User objects * * @since 4.0.0 */ class UserFactory implements UserFactoryInterface { /** * The database. * * @var DatabaseInterface */ private $db; /** * UserFactory constructor. * * @param DatabaseInterface $db The database */ public function __construct(DatabaseInterface $db) { $this->db = $db; } /** * Method to get an instance of a user for the given id. * * @param int $id The id * * @return User * * @since 4.0.0 */ public function loadUserById(int $id): User { return new User($id); } /** * Method to get an instance of a user for the given username. * * @param string $username The username * * @return User * * @since 4.0.0 */ public function loadUserByUsername(string $username): User { // Initialise some variables $query = $this->db->getQuery(true) ->select($this->db->quoteName('id')) ->from($this->db->quoteName('#__users')) ->where($this->db->quoteName('username') . ' = :username') ->bind(':username', $username) ->setLimit(1); $this->db->setQuery($query); return $this->loadUserById((int) $this->db->loadResult()); } } PK1A#]� Ҥ��&src/User/UserFactoryAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a user factory. * * @since 4.4.0 */ interface UserFactoryAwareInterface { /** * Set the user factory to use. * * @param UserFactoryInterface $factory The user factory to use. * * @return void * * @since 4.4.0 */ public function setUserFactory(UserFactoryInterface $factory): void; } PK1A#]ц�6R6Rsrc/User/UserHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; use Joomla\Authentication\Password\Argon2idHandler; use Joomla\Authentication\Password\Argon2iHandler; use Joomla\Authentication\Password\BCryptHandler; use Joomla\CMS\Access\Access; use Joomla\CMS\Authentication\Password\ChainedHandler; use Joomla\CMS\Authentication\Password\CheckIfRehashNeededHandlerInterface; use Joomla\CMS\Authentication\Password\MD5Handler; use Joomla\CMS\Authentication\Password\PHPassHandler; use Joomla\CMS\Crypt\Crypt; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Object\CMSObject; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Session\SessionManager; use Joomla\CMS\Uri\Uri; use Joomla\Database\Exception\ExecutionFailureException; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Authorisation helper class, provides static methods to perform various tasks relevant * to the Joomla user and authorisation classes * * This class has influences and some method logic from the Horde Auth package * * @since 1.7.0 */ abstract class UserHelper { /** * Constant defining the Argon2i password algorithm for use with password hashes * * Note: PHP's native `PASSWORD_ARGON2I` constant is not used as PHP may be compiled without this constant * * @var string * @since 4.0.0 */ public const HASH_ARGON2I = 'argon2i'; /** * B/C constant `PASSWORD_ARGON2I` for PHP < 7.4 (using integer) * * Note: PHP's native `PASSWORD_ARGON2I` constant is not used as PHP may be compiled without this constant * * @var integer * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Use UserHelper::HASH_ARGON2I instead */ public const HASH_ARGON2I_BC = 2; /** * Constant defining the Argon2id password algorithm for use with password hashes * * Note: PHP's native `PASSWORD_ARGON2ID` constant is not used as PHP may be compiled without this constant * * @var string * @since 4.0.0 */ public const HASH_ARGON2ID = 'argon2id'; /** * B/C constant `PASSWORD_ARGON2ID` for PHP < 7.4 (using integer) * * Note: PHP's native `PASSWORD_ARGON2ID` constant is not used as PHP may be compiled without this constant * * @var integer * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Use UserHelper::HASH_ARGON2ID instead */ public const HASH_ARGON2ID_BC = 3; /** * Constant defining the BCrypt password algorithm for use with password hashes * * @var string * @since 4.0.0 */ public const HASH_BCRYPT = '2y'; /** * B/C constant `PASSWORD_BCRYPT` for PHP < 7.4 (using integer) * * @var integer * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Use UserHelper::HASH_BCRYPT instead */ public const HASH_BCRYPT_BC = 1; /** * Constant defining the MD5 password algorithm for use with password hashes * * @var string * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Support for MD5 hashed passwords will be removed use any of the other hashing methods */ public const HASH_MD5 = 'md5'; /** * Constant defining the PHPass password algorithm for use with password hashes * * @var string * @since 4.0.0 * * @deprecated 4.0 will be removed in 6.0 * Support for PHPass hashed passwords will be removed use any of the other hashing methods */ public const HASH_PHPASS = 'phpass'; /** * Mapping array for the algorithm handler * * @var array * @since 4.0.0 */ public const HASH_ALGORITHMS = [ self::HASH_ARGON2I => Argon2iHandler::class, self::HASH_ARGON2I_BC => Argon2iHandler::class, self::HASH_ARGON2ID => Argon2idHandler::class, self::HASH_ARGON2ID_BC => Argon2idHandler::class, self::HASH_BCRYPT => BCryptHandler::class, self::HASH_BCRYPT_BC => BCryptHandler::class, self::HASH_MD5 => MD5Handler::class, self::HASH_PHPASS => PHPassHandler::class, ]; /** * Method to add a user to a group. * * @param integer $userId The id of the user. * @param integer $groupId The id of the group. * * @return boolean True on success * * @since 1.7.0 * @throws \RuntimeException */ public static function addUserToGroup($userId, $groupId) { // Cast as integer until method is typehinted. $userId = (int) $userId; $groupId = (int) $groupId; // Get the user object. $user = new User($userId); // Add the user to the group if necessary. if (!\in_array($groupId, $user->groups)) { // Check whether the group exists. $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); // If the group does not exist, return an exception. if ($db->loadResult() === null) { throw new \RuntimeException('Access Usergroup Invalid'); } // Add the group data to the user object. $user->groups[$groupId] = $groupId; // Reindex the array for prepared statements binding $user->groups = array_values($user->groups); // Store the user object. $user->save(); } // Set the group data for any preloaded user objects. $temp = User::getInstance($userId); $temp->groups = $user->groups; if (Factory::getSession()->getId()) { // Set the group data for the user object in the session. $temp = Factory::getUser(); if ($temp->id == $userId) { $temp->groups = $user->groups; } } return true; } /** * Method to get a list of groups a user is in. * * @param integer $userId The id of the user. * * @return array List of groups * * @since 1.7.0 */ public static function getUserGroups($userId) { // Get the user object. $user = User::getInstance((int) $userId); return $user->groups ?? []; } /** * Method to remove a user from a group. * * @param integer $userId The id of the user. * @param integer $groupId The id of the group. * * @return boolean True on success * * @since 1.7.0 */ public static function removeUserFromGroup($userId, $groupId) { // Get the user object. $user = User::getInstance((int) $userId); // Remove the user from the group if necessary. $key = array_search($groupId, $user->groups); if ($key !== false) { unset($user->groups[$key]); $user->groups = array_values($user->groups); // Store the user object. $user->save(); } // Set the group data for any preloaded user objects. $temp = Factory::getUser((int) $userId); $temp->groups = $user->groups; // Set the group data for the user object in the session. $temp = Factory::getUser(); if ($temp->id == $userId) { $temp->groups = $user->groups; } return true; } /** * Method to set the groups for a user. * * @param integer $userId The id of the user. * @param array $groups An array of group ids to put the user in. * * @return boolean True on success * * @since 1.7.0 */ public static function setUserGroups($userId, $groups) { // Get the user object. $user = User::getInstance((int) $userId); // Set the group ids. $groups = ArrayHelper::toInteger($groups); $user->groups = $groups; // Get the titles for the user groups. $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName(['id', 'title'])) ->from($db->quoteName('#__usergroups')) ->whereIn($db->quoteName('id'), $user->groups); $db->setQuery($query); $results = $db->loadObjectList(); // Set the titles for the user groups. for ($i = 0, $n = \count($results); $i < $n; $i++) { $user->groups[$results[$i]->id] = $results[$i]->id; } // Store the user object. $user->save(); // Set the group data for any preloaded user objects. $temp = Factory::getUser((int) $userId); $temp->groups = $user->groups; if (Factory::getSession()->getId()) { // Set the group data for the user object in the session. $temp = Factory::getUser(); if ($temp->id == $userId) { $temp->groups = $user->groups; } } return true; } /** * Gets the user profile information * * @param integer $userId The id of the user. * * @return object * * @since 1.7.0 */ public static function getProfile($userId = 0) { if ($userId == 0) { $user = Factory::getUser(); $userId = $user->id; } // Get the dispatcher and load the user's plugins. PluginHelper::importPlugin('user'); $data = new CMSObject(); $data->id = $userId; // Trigger the data preparation event. Factory::getApplication()->triggerEvent('onContentPrepareData', ['com_users.profile', &$data]); return $data; } /** * Method to activate a user * * @param string $activation Activation string * * @return boolean True on success * * @since 1.7.0 */ public static function activateUser($activation) { $db = Factory::getDbo(); // Let's get the id of the user we want to activate $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__users')) ->where($db->quoteName('activation') . ' = :activation') ->where($db->quoteName('block') . ' = 1') ->where($db->quoteName('lastvisitDate') . ' IS NULL') ->bind(':activation', $activation); $db->setQuery($query); $id = (int) $db->loadResult(); // Is it a valid user to activate? if ($id) { $user = User::getInstance($id); $user->set('block', '0'); $user->set('activation', ''); // Time to take care of business.... store the user. if (!$user->save()) { Log::add($user->getError(), Log::WARNING, 'jerror'); return false; } } else { Log::add(Text::_('JLIB_USER_ERROR_UNABLE_TO_FIND_USER'), Log::WARNING, 'jerror'); return false; } return true; } /** * Returns userid if a user exists * * @param string $username The username to search on. * * @return integer The user id or 0 if not found. * * @since 1.7.0 */ public static function getUserId($username) { // Initialise some variables $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__users')) ->where($db->quoteName('username') . ' = :username') ->bind(':username', $username) ->setLimit(1); $db->setQuery($query); return $db->loadResult(); } /** * Hashes a password using the current encryption. * * @param string $password The plaintext password to encrypt. * @param string|integer $algorithm The hashing algorithm to use, represented by `HASH_*` class constants, or a container service ID. * @param array $options The options for the algorithm to use. * * @return string The encrypted password. * * @since 3.2.1 * @throws \InvalidArgumentException when the algorithm is not supported */ public static function hashPassword($password, $algorithm = self::HASH_BCRYPT, array $options = []) { $container = Factory::getContainer(); // If the algorithm is a valid service ID, use that service to generate the hash if ($container->has($algorithm)) { return $container->get($algorithm)->hashPassword($password, $options); } // Try to load handler if (isset(self::HASH_ALGORITHMS[$algorithm])) { return $container->get(self::HASH_ALGORITHMS[$algorithm])->hashPassword($password, $options); } // Unsupported algorithm, sorry! throw new \InvalidArgumentException(sprintf('The %s algorithm is not supported for hashing passwords.', $algorithm)); } /** * Formats a password using the current encryption. If the user ID is given * and the hash does not fit the current hashing algorithm, it automatically * updates the hash. * * @param string $password The plaintext password to check. * @param string $hash The hash to verify against. * @param integer $userId ID of the user if the password hash should be updated * * @return boolean True if the password and hash match, false otherwise * * @since 3.2.1 */ public static function verifyPassword($password, $hash, $userId = 0) { $passwordAlgorithm = self::HASH_BCRYPT; $container = Factory::getContainer(); // Cheaply try to determine the algorithm in use otherwise fall back to the chained handler if (strpos($hash, '$P$') === 0) { /** @var PHPassHandler $handler */ $handler = $container->get(PHPassHandler::class); } elseif (strpos($hash, '$argon2id') === 0) { // Check for Argon2id hashes /** @var Argon2idHandler $handler */ $handler = $container->get(Argon2idHandler::class); $passwordAlgorithm = self::HASH_ARGON2ID; } elseif (strpos($hash, '$argon2i') === 0) { // Check for Argon2i hashes /** @var Argon2iHandler $handler */ $handler = $container->get(Argon2iHandler::class); $passwordAlgorithm = self::HASH_ARGON2I; } elseif (strpos($hash, '$2') === 0) { // Check for bcrypt hashes /** @var BCryptHandler $handler */ $handler = $container->get(BCryptHandler::class); } else { /** @var ChainedHandler $handler */ $handler = $container->get(ChainedHandler::class); } $match = $handler->validatePassword($password, $hash); $rehash = $handler instanceof CheckIfRehashNeededHandlerInterface ? $handler->checkIfRehashNeeded($hash) : false; // If we have a match and rehash = true, rehash the password with the current algorithm. if ((int) $userId > 0 && $match && $rehash) { $user = new User($userId); $user->password = static::hashPassword($password, $passwordAlgorithm); $user->save(); } return $match; } /** * Generate a random password * * @param integer $length Length of the password to generate * * @return string Random Password * * @since 1.7.0 */ public static function genRandomPassword($length = 8) { $salt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $base = \strlen($salt); $makepass = ''; /* * Start with a cryptographic strength random string, then convert it to * a string with the numeric base of the salt. * Shift the base conversion on each character so the character * distribution is even, and randomize the start shift so it's not * predictable. */ $random = Crypt::genRandomBytes($length + 1); $shift = \ord($random[0]); for ($i = 1; $i <= $length; ++$i) { $makepass .= $salt[($shift + \ord($random[$i])) % $base]; $shift += \ord($random[$i]); } return $makepass; } /** * Method to get a hashed user agent string that does not include browser version. * Used when frequent version changes cause problems. * * @return string A hashed user agent string with version replaced by 'abcd' * * @since 3.2 */ public static function getShortHashedUserAgent() { $ua = Factory::getApplication()->client; $uaString = $ua->userAgent; $browserVersion = $ua->browserVersion; if ($browserVersion) { $uaShort = str_replace($browserVersion, 'abcd', $uaString); } else { $uaShort = $uaString; } return md5(Uri::base() . $uaShort); } /** * Check if there is a super user in the user ids. * * @param array $userIds An array of user IDs on which to operate * * @return boolean True on success, false on failure * * @since 3.6.5 */ public static function checkSuperUserInUsers(array $userIds) { foreach ($userIds as $userId) { foreach (static::getUserGroups($userId) as $userGroupId) { if (Access::checkGroup($userGroupId, 'core.admin')) { return true; } } } return false; } /** * Destroy all active session for a given user id * * @param int $userId Id of user * @param boolean $keepCurrent Keep the session of the currently acting user * @param int $clientId Application client id * * @return boolean * * @since 3.9.28 */ public static function destroyUserSessions($userId, $keepCurrent = false, $clientId = null) { // Destroy all sessions for the user account if able if (!Factory::getApplication()->get('session_metadata', true)) { return false; } $db = Factory::getDbo(); try { $userId = (int) $userId; $query = $db->getQuery(true) ->select($db->quoteName('session_id')) ->from($db->quoteName('#__session')) ->where($db->quoteName('userid') . ' = :userid') ->bind(':userid', $userId, ParameterType::INTEGER); if ($clientId !== null) { $clientId = (int) $clientId; $query->where($db->quoteName('client_id') . ' = :client_id') ->bind(':client_id', $clientId, ParameterType::INTEGER); } $sessionIds = $db->setQuery($query)->loadColumn(); } catch (ExecutionFailureException $e) { return false; } // Convert PostgreSQL Session IDs into strings (see GitHub #33822) foreach ($sessionIds as &$sessionId) { if (is_resource($sessionId) && get_resource_type($sessionId) === 'stream') { $sessionId = stream_get_contents($sessionId); } } // If true, removes the current session id from the purge list if ($keepCurrent) { $sessionIds = array_diff($sessionIds, [Factory::getSession()->getId()]); } // If there aren't any active sessions then there's nothing to do here if (empty($sessionIds)) { return false; } /** @var SessionManager $sessionManager */ $sessionManager = Factory::getContainer()->get('session.manager'); $sessionManager->destroySessions($sessionIds); try { $db->setQuery( $db->getQuery(true) ->delete($db->quoteName('#__session')) ->whereIn($db->quoteName('session_id'), $sessionIds, ParameterType::LARGE_OBJECT) )->execute(); } catch (ExecutionFailureException $e) { // No issue, let things go } } } PK1A#]��?��!src/User/CurrentUserInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a current user. * * @since 4.2.0 */ interface CurrentUserInterface { /** * Sets the current user. * * @param User $currentUser The current user object * * @return void * * @since 4.2.0 */ public function setCurrentUser(User $currentUser): void; } PK1A#]��!��!src/User/UserFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\User; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create User objects * * @since 4.0.0 */ interface UserFactoryInterface { /** * Method to get an instance of a user for the given id. * * @param int $id The id * * @return User * * @since 4.0.0 */ public function loadUserById(int $id): User; /** * Method to get an instance of a user for the given username. * * @param string $username The username * * @return User * * @since 4.0.0 */ public function loadUserByUsername(string $username): User; } PK1A#]�ڛ�@�@src/Workflow/Workflow.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Workflow; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Extension\ComponentInterface; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Category; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Workflow Class. * * @since 4.0.0 */ class Workflow { /** * The booted component * * @var ComponentInterface */ protected $component = null; /** * Name of the extension the workflow belong to * * @var string * @since 4.0.0 */ protected $extension = null; /** * Application Object * * @var CMSApplication * @since 4.0.0 */ protected $app; /** * Database Driver * * @var DatabaseDriver * @since 4.0.0 */ protected $db; /** * Condition to names mapping * * @since 4.0.0 */ public const CONDITION_NAMES = [ self::CONDITION_PUBLISHED => 'JPUBLISHED', self::CONDITION_UNPUBLISHED => 'JUNPUBLISHED', self::CONDITION_TRASHED => 'JTRASHED', self::CONDITION_ARCHIVED => 'JARCHIVED', ]; /** * Every item with a state which has the condition PUBLISHED is visible/active on the page */ public const CONDITION_PUBLISHED = 1; /** * Every item with a state which has the condition UNPUBLISHED is not visible/inactive on the page */ public const CONDITION_UNPUBLISHED = 0; /** * Every item with a state which has the condition TRASHED is trashed */ public const CONDITION_TRASHED = -2; /** * Every item with a state which has the condition ARCHIVED is archived */ public const CONDITION_ARCHIVED = 2; /** * Class constructor * * @param string $extension The extension name * @param ?CMSApplication $app Application Object * @param ?DatabaseDriver $db Database Driver Object * * @since 4.0.0 */ public function __construct(string $extension, ?CMSApplication $app = null, ?DatabaseDriver $db = null) { $this->extension = $extension; // Initialise default objects if none have been provided if ($app === null) { @trigger_error('From 6.0 declaring the app dependency will be mandatory.', E_USER_DEPRECATED); $app = Factory::getApplication(); } $this->app = $app; if ($db === null) { @trigger_error('From 6.0 declaring the database dependency will be mandatory.', E_USER_DEPRECATED); $db = Factory::getContainer()->get(DatabaseDriver::class); } $this->db = $db; } /** * Returns the translated condition name, based on the given number * * @param integer $value The condition ID * * @return string * * @since 4.0.0 */ public function getConditionName(int $value): string { $component = $this->getComponent(); if ($component instanceof WorkflowServiceInterface) { $conditions = $component->getConditions($this->extension); } else { $conditions = self::CONDITION_NAMES; } return ArrayHelper::getValue($conditions, $value, '', 'string'); } /** * Returns the booted component * * @return ComponentInterface * * @since 4.0.0 */ protected function getComponent() { if (\is_null($this->component)) { $parts = explode('.', $this->extension); $this->component = $this->app->bootComponent($parts[0]); } return $this->component; } /** * Try to load a workflow default stage by category ID. * * @param integer $catId The category ID. * * @return boolean|integer An integer, holding the stage ID or false * @since 4.0.0 */ public function getDefaultStageByCategory($catId = 0) { // Let's check if a workflow ID is assigned to a category $category = new Category($this->db); $categories = array_reverse($category->getPath($catId)); $workflow_id = 0; foreach ($categories as $cat) { $cat->params = new Registry($cat->params); $workflow_id = $cat->params->get('workflow_id'); if ($workflow_id == 'inherit') { $workflow_id = 0; } elseif ($workflow_id == 'use_default') { $workflow_id = 0; break; } elseif ($workflow_id > 0) { break; } } // Check if the workflow exists if ($workflow_id = (int) $workflow_id) { $query = $this->db->getQuery(true); $query->select( [ $this->db->quoteName('ws.id'), ] ) ->from( [ $this->db->quoteName('#__workflow_stages', 'ws'), $this->db->quoteName('#__workflows', 'w'), ] ) ->where( [ $this->db->quoteName('ws.workflow_id') . ' = ' . $this->db->quoteName('w.id'), $this->db->quoteName('ws.default') . ' = 1', $this->db->quoteName('w.published') . ' = 1', $this->db->quoteName('ws.published') . ' = 1', $this->db->quoteName('w.id') . ' = :workflowId', $this->db->quoteName('w.extension') . ' = :extension', ] ) ->bind(':workflowId', $workflow_id, ParameterType::INTEGER) ->bind(':extension', $this->extension); $stage_id = (int) $this->db->setQuery($query)->loadResult(); if (!empty($stage_id)) { return $stage_id; } } // Use default workflow $query = $this->db->getQuery(true); $query->select( [ $this->db->quoteName('ws.id'), ] ) ->from( [ $this->db->quoteName('#__workflow_stages', 'ws'), $this->db->quoteName('#__workflows', 'w'), ] ) ->where( [ $this->db->quoteName('ws.default') . ' = 1', $this->db->quoteName('ws.workflow_id') . ' = ' . $this->db->quoteName('w.id'), $this->db->quoteName('w.published') . ' = 1', $this->db->quoteName('ws.published') . ' = 1', $this->db->quoteName('w.default') . ' = 1', $this->db->quoteName('w.extension') . ' = :extension', ] ) ->bind(':extension', $this->extension); $stage_id = (int) $this->db->setQuery($query)->loadResult(); // Last check if we have a workflow ID if (!empty($stage_id)) { return $stage_id; } return false; } /** * Check if a transition can be executed * * @param integer[] $pks The item IDs, which should use the transition * @param integer $transitionId The transition which should be executed * * @return object | null */ public function getValidTransition(array $pks, int $transitionId) { $pks = ArrayHelper::toInteger($pks); $pks = array_filter($pks); if (!\count($pks)) { return null; } $query = $this->db->getQuery(true); $user = $this->app->getIdentity(); $query->select( [ $this->db->quoteName('t.id'), $this->db->quoteName('t.to_stage_id'), $this->db->quoteName('t.from_stage_id'), $this->db->quoteName('t.options'), $this->db->quoteName('t.workflow_id'), ] ) ->from( [ $this->db->quoteName('#__workflow_transitions', 't'), ] ) ->join( 'INNER', $this->db->quoteName('#__workflows', 'w'), $this->db->quoteName('t.workflow_id') . ' = ' . $this->db->quoteName('w.id') ) ->join( 'LEFT', $this->db->quoteName('#__workflow_stages', 's'), $this->db->quoteName('s.id') . ' = ' . $this->db->quoteName('t.to_stage_id') ) ->where( [ $this->db->quoteName('t.id') . ' = :id', $this->db->quoteName('t.published') . ' = 1', $this->db->quoteName('w.extension') . ' = :extension', ] ) ->bind(':id', $transitionId, ParameterType::INTEGER) ->bind(':extension', $this->extension); $transition = $this->db->setQuery($query)->loadObject(); $parts = explode('.', $this->extension); $option = reset($parts); if (!empty($transition->id) && $user->authorise('core.execute.transition', $option . '.transition.' . (int) $transition->id)) { return $transition; } return null; } /** * Executes a transition to change the current state in the association table * * @param integer[] $pks The item IDs, which should use the transition * @param integer $transitionId The transition which should be executed * * @return boolean */ public function executeTransition(array $pks, int $transitionId): bool { $pks = ArrayHelper::toInteger($pks); $pks = array_filter($pks); if (!\count($pks)) { return true; } $transition = $this->getValidTransition($pks, $transitionId); if (is_null($transition)) { return false; } $transition->options = new Registry($transition->options); // Check if the items can execute this transition foreach ($pks as $pk) { $assoc = $this->getAssociation($pk); // The transition has to be in the same workflow if ( !\in_array($transition->from_stage_id, [ $assoc->stage_id, -1, ]) || $transition->workflow_id !== $assoc->workflow_id ) { return false; } } PluginHelper::importPlugin('workflow'); $eventResult = $this->app->getDispatcher()->dispatch( 'onWorkflowBeforeTransition', AbstractEvent::create( 'onWorkflowBeforeTransition', [ 'eventClass' => 'Joomla\CMS\Event\Workflow\WorkflowTransitionEvent', 'subject' => $this, 'extension' => $this->extension, 'pks' => $pks, 'transition' => $transition, 'stopTransition' => false, ] ) ); if ($eventResult->getArgument('stopTransition')) { return false; } $success = $this->updateAssociations($pks, (int) $transition->to_stage_id); if ($success) { $this->app->getDispatcher()->dispatch( 'onWorkflowAfterTransition', AbstractEvent::create( 'onWorkflowAfterTransition', [ 'eventClass' => 'Joomla\CMS\Event\Workflow\WorkflowTransitionEvent', 'subject' => $this, 'extension' => $this->extension, 'pks' => $pks, 'transition' => $transition, ] ) ); } return $success; } /** * Creates an association for the workflow_associations table * * @param integer $pk ID of the item * @param integer $state ID of state * * @return boolean * * @since 4.0.0 */ public function createAssociation(int $pk, int $state): bool { try { $query = $this->db->getQuery(true); $query->insert($this->db->quoteName('#__workflow_associations')) ->columns( [ $this->db->quoteName('item_id'), $this->db->quoteName('stage_id'), $this->db->quoteName('extension'), ] ) ->values(':pk, :state, :extension') ->bind(':pk', $pk, ParameterType::INTEGER) ->bind(':state', $state, ParameterType::INTEGER) ->bind(':extension', $this->extension); $this->db->setQuery($query)->execute(); } catch (\Exception $e) { return false; } return true; } /** * Update an existing association with a new state * * @param array $pks An Array of item IDs which should be changed * @param integer $state The new state ID * * @return boolean * * @since 4.0.0 */ public function updateAssociations(array $pks, int $state): bool { $pks = ArrayHelper::toInteger($pks); try { $query = $this->db->getQuery(true); $query->update($this->db->quoteName('#__workflow_associations')) ->set($this->db->quoteName('stage_id') . ' = :state') ->whereIn($this->db->quoteName('item_id'), $pks) ->where($this->db->quoteName('extension') . ' = :extension') ->bind(':state', $state, ParameterType::INTEGER) ->bind(':extension', $this->extension); $this->db->setQuery($query)->execute(); } catch (\Exception $e) { return false; } return true; } /** * Removes associations from the workflow_associations table * * @param integer[] $pks ID of content * * @return boolean * * @since 4.0.0 */ public function deleteAssociation(array $pks): bool { $pks = ArrayHelper::toInteger($pks); try { $query = $this->db->getQuery(true); $query ->delete($this->db->quoteName('#__workflow_associations')) ->whereIn($this->db->quoteName('item_id'), $pks) ->where($this->db->quoteName('extension') . ' = :extension') ->bind(':extension', $this->extension); $this->db->setQuery($query)->execute(); } catch (\Exception $e) { return false; } return true; } /** * Loads an existing association item with state and item ID * * @param integer $itemId The item ID to load * * @return \stdClass|null * * @since 4.0.0 */ public function getAssociation(int $itemId): ?\stdClass { $query = $this->db->getQuery(true); $query->select( [ $this->db->quoteName('a.item_id'), $this->db->quoteName('a.stage_id'), $this->db->quoteName('s.workflow_id'), ] ) ->from($this->db->quoteName('#__workflow_associations', 'a')) ->innerJoin( $this->db->quoteName('#__workflow_stages', 's'), $this->db->quoteName('a.stage_id') . ' = ' . $this->db->quoteName('s.id') ) ->where( [ $this->db->quoteName('item_id') . ' = :id', $this->db->quoteName('extension') . ' = :extension', ] ) ->bind(':id', $itemId, ParameterType::INTEGER) ->bind(':extension', $this->extension); return $this->db->setQuery($query)->loadObject(); } } PK1A#]�H-R R )src/Workflow/WorkflowServiceInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Workflow; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * The workflow service. * * @since 4.0.0 */ interface WorkflowServiceInterface { /** * Check if the functionality is supported by the context * * @param string $functionality The functionality * @param string $context The context of the functionality * * @return boolean * * @since 4.0.0 */ public function supportFunctionality($functionality, $context): bool; /** * Returns the model name, based on the context * * @param string $context The context of the workflow * * @return boolean */ public function getModelName($context): string; /** * Check if the workflow is active * * @param string $context The context of the workflow * * @return boolean */ public function isWorkflowActive($context): bool; /** * Method to filter transitions by given id of state. * * @param integer[] $transitions Array of transitions to filter for * @param integer $pk Id of the state on which the transitions are performed * * @return array * * @since 4.0.0 */ public function filterTransitions(array $transitions, int $pk): array; /** * Returns an array of possible conditions for the component. * * @param string $extension Full extension string * * @return array * * @since 4.0.0 */ public static function getConditions(string $extension): array; /** * Returns a table name for the state association * * @param string $section An optional section to differ different areas in the component * * @return string * * @since 4.0.0 */ public function getWorkflowTableBySection(?string $section = null): string; /** * Returns valid contexts. * * @return array * * @since 4.0.0 */ public function getWorkflowContexts(): array; /** * Returns the workflow context based on the given category section * * @param string $section The section * * @return string|null * * @since 4.0.0 */ public function getCategoryWorkflowContext(?string $section = null): string; } PK1A#]���,,$src/Workflow/WorkflowPluginTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Workflow; use Joomla\CMS\Form\Form; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for component workflow plugins. * * @since 4.0.0 */ trait WorkflowPluginTrait { /** * Add different parameter options to the transition view, we need when executing the transition * * @param Form $form The form * @param \stdClass $data The data * * @return boolean|\stdClass * * @since 4.0.0 */ protected function enhanceWorkflowTransitionForm(Form $form, $data) { $workflow_id = (int) ($data->workflow_id ?? $form->getValue('workflow_id')); $workflow = $this->getWorkflow($workflow_id); if (empty($workflow->id) || !$this->isSupported($workflow->extension)) { return false; } // Load XML file from "parent" plugin $path = dirname((new \ReflectionClass(static::class))->getFileName()); if (!is_file($path . '/forms/action.xml')) { $path = JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name; } if (is_file($path . '/forms/action.xml')) { $form->loadFile($path . '/forms/action.xml'); } return $workflow; } /** * Get the workflow for a given ID * * @param int|null $workflowId ID of the workflow * * @return CMSObject|boolean Object on success, false on failure. * * @since 4.0.0 */ protected function getWorkflow(int $workflowId = null) { $app = $this->getApplication() ?? $this->app; $workflowId = !empty($workflowId) ? $workflowId : $app->getInput()->getInt('workflow_id'); if (is_array($workflowId)) { return false; } return $app->bootComponent('com_workflow') ->getMVCFactory() ->createModel('Workflow', 'Administrator', ['ignore_request' => true]) ->getItem($workflowId); } /** * Check if the current plugin should execute workflow related activities * * @param string $context Context to check * * @return boolean * * @since 4.0.0 */ protected function isSupported($context) { return false; } /** * Check if the context is listed in the allowed of forbidden lists and return the result. * * @param string $context Context to check * * @return boolean */ protected function checkAllowedAndForbiddenlist($context) { $allowedlist = \array_filter((array) $this->params->get('allowedlist', [])); $forbiddenlist = \array_filter((array) $this->params->get('forbiddenlist', [])); if (!empty($allowedlist)) { foreach ($allowedlist as $allowed) { if ($context === $allowed) { return true; } } return false; } foreach ($forbiddenlist as $forbidden) { if ($context === $forbidden) { return false; } } return true; } /** * Check if the context supports a specific functionality. * * @param string $context Context to check * @param string $functionality The functionality * * @return boolean */ protected function checkExtensionSupport($context, $functionality) { $parts = explode('.', $context); $component = ($this->getApplication() ?? $this->app)->bootComponent($parts[0]); if ( !$component instanceof WorkflowServiceInterface || !$component->isWorkflowActive($context) || !$component->supportFunctionality($functionality, $context) ) { return false; } return true; } } PK1A#]>�ee%src/Workflow/WorkflowServiceTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Workflow; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\WorkflowModelInterface; use Joomla\Event\DispatcherAwareInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Trait for component workflow service. * * @since 4.0.0 */ trait WorkflowServiceTrait { /** * Get a MVC factory * * @return MVCFactoryInterface * * @since 4.0.0 */ abstract public function getMVCFactory(): MVCFactoryInterface; /** * Check if the functionality is supported by the component * The variable $supportFunctionality has the following structure * [ * 'core.featured' => [ * 'com_content.article', * ], * 'core.state' => [ * 'com_content.article', * ], * ] * * @param string $functionality The functionality * @param string $context The context of the functionality * * @return boolean */ public function supportFunctionality($functionality, $context): bool { if (empty($this->supportedFunctionality[$functionality])) { return false; } if (!is_array($this->supportedFunctionality[$functionality])) { return true; } return in_array($context, $this->supportedFunctionality[$functionality], true); } /** * Check if the functionality is used by a plugin * * @param string $functionality The functionality * @param string $extension The extension * * @return boolean * @throws \Exception * * @since 4.0.0 */ public function isFunctionalityUsed($functionality, $extension): bool { static $used = []; $cacheKey = $extension . '.' . $functionality; if (isset($used[$cacheKey])) { return $used[$cacheKey]; } // The container to get the services from $app = Factory::getApplication(); if (!($app instanceof DispatcherAwareInterface)) { return false; } $eventResult = $app->getDispatcher()->dispatch( 'onWorkflowFunctionalityUsed', AbstractEvent::create( 'onWorkflowFunctionalityUsed', [ 'eventClass' => 'Joomla\CMS\Event\Workflow\WorkflowFunctionalityUsedEvent', 'subject' => $this, 'extension' => $extension, 'functionality' => $functionality, ] ) ); $used[$cacheKey] = $eventResult->getArgument('used', false); return $used[$cacheKey]; } /** * Returns the model name, based on the context * * @param string $context The context of the workflow * * @return boolean * * @since 4.0.0 */ public function getModelName($context): string { $parts = explode('.', $context); if (count($parts) < 2) { return ''; } array_shift($parts); return ucfirst(array_shift($parts)); } /** * Returns an array of possible conditions for the component. * * @param string $extension The component and section separated by ".". * * @return array * * @since 4.0.0 */ public static function getConditions(string $extension): array { return \defined('self::CONDITION_NAMES') ? self::CONDITION_NAMES : Workflow::CONDITION_NAMES; } /** * Check if the workflow is active * * @param string $context The context of the workflow * * @return boolean */ public function isWorkflowActive($context): bool { $parts = explode('.', $context); $config = ComponentHelper::getParams($parts[0]); if (!$config->get('workflow_enabled')) { return false; } $modelName = $this->getModelName($context); if (empty($modelName)) { return false; } $component = $this->getMVCFactory(); $appName = Factory::getApplication()->getName(); $model = $component->createModel($modelName, $appName, ['ignore_request' => true]); return $model instanceof WorkflowModelInterface; } } PK1A#]r�g' src/Version.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS; use Joomla\CMS\Cache\CacheController; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Date\Date; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Version information class for the Joomla CMS. * * @since 1.0 */ final class Version { /** * Product name. * * @var string * @since 3.5 */ public const PRODUCT = 'Joomla!'; /** * Major release version. * * @var integer * @since 3.8.0 */ public const MAJOR_VERSION = 4; /** * Minor release version. * * @var integer * @since 3.8.0 */ public const MINOR_VERSION = 4; /** * Patch release version. * * @var integer * @since 3.8.0 */ public const PATCH_VERSION = 14; /** * Extra release version info. * * This constant when not empty adds an additional identifier to the version string to reflect the development state. * For example, for 3.8.0 when this is set to 'dev' the version string will be `3.8.0-dev`. * * @var string * @since 3.8.0 */ public const EXTRA_VERSION = ''; /** * Development status. * * @var string * @since 3.5 */ public const DEV_STATUS = 'Stable'; /** * Code name. * * @var string * @since 3.5 */ public const CODENAME = 'Pamoja'; /** * Release date. * * @var string * @since 3.5 */ public const RELDATE = '30-September-2025'; /** * Release time. * * @var string * @since 3.5 */ public const RELTIME = '16:00'; /** * Release timezone. * * @var string * @since 3.5 */ public const RELTZ = 'GMT'; /** * Copyright Notice. * * @var string * @since 3.5 */ public const COPYRIGHT = '(C) 2005 Open Source Matters, Inc. <https://www.joomla.org>'; /** * Link text. * * @var string * @since 3.5 */ public const URL = '<a href="https://www.joomla.org">Joomla!</a> is Free Software released under the GNU General Public License.'; /** * Media version string * * @var string * @since 4.2.0 */ private static $mediaVersion = null; /** * Check if we are in development mode * * @return boolean * * @since 3.4.3 */ public function isInDevelopmentState(): bool { return strtolower(self::DEV_STATUS) !== 'stable'; } /** * Compares two a "PHP standardized" version number against the current Joomla version. * * @param string $minimum The minimum version of the Joomla which is compatible. * * @return boolean True if the version is compatible. * * @link https://www.php.net/version_compare * @since 1.0 */ public function isCompatible(string $minimum): bool { return version_compare(JVERSION, $minimum, 'ge'); } /** * Method to get the help file version. * * @return string Version suffix for help files. * * @since 1.0 */ public function getHelpVersion(): string { return '.' . self::MAJOR_VERSION . self::MINOR_VERSION; } /** * Gets a "PHP standardized" version string for the current Joomla. * * @return string Version string. * * @since 1.5 */ public function getShortVersion(): string { $version = self::MAJOR_VERSION . '.' . self::MINOR_VERSION . '.' . self::PATCH_VERSION; if (!empty(self::EXTRA_VERSION)) { $version .= '-' . self::EXTRA_VERSION; } return $version; } /** * Gets a version string for the current Joomla with all release information. * * @return string Complete version string. * * @since 1.5 */ public function getLongVersion(): string { return self::PRODUCT . ' ' . $this->getShortVersion() . ' ' . self::DEV_STATUS . ' [ ' . self::CODENAME . ' ] ' . self::RELDATE . ' ' . self::RELTIME . ' ' . self::RELTZ; } /** * Returns the user agent. * * @param string $suffix String to append to resulting user agent. * @param bool $mask Mask as Mozilla/5.0 or not. * @param bool $addVersion Add version afterwards to component. * * @return string User Agent. * * @since 1.0 */ public function getUserAgent(string $suffix = '', bool $mask = false, bool $addVersion = true): string { if ($suffix === '') { $suffix = 'Framework'; } if ($addVersion) { $suffix .= '/' . self::MAJOR_VERSION . '.' . self::MINOR_VERSION; } // If masked pretend to look like Mozilla 5.0 but still identify ourselves. return ($mask ? 'Mozilla/5.0 ' : '') . self::PRODUCT . '/' . $this->getShortVersion() . ($suffix ? ' ' . $suffix : ''); } /** * Generate a media version string for assets * Public to allow third party developers to use it * * @return string * * @since 3.2 */ public function generateMediaVersion(): string { return md5($this->getLongVersion() . Factory::getApplication()->get('secret') . (new Date())->toSql()); } /** * Gets a media version which is used to append to Joomla core media files. * * This media version is used to append to Joomla core media in order to trick browsers into * reloading the CSS and JavaScript, because they think the files are renewed. * The media version is renewed after Joomla core update, install, discover_install and uninstallation. * * @return string The media version. * * @since 3.2 */ public function getMediaVersion(): string { // Load the media version and cache it for future use if (self::$mediaVersion === null) { self::$mediaVersion = $this->getMediaVersionCache() ->get([$this, 'generateMediaVersion'], [], md5('_media_version' . $this->getLongVersion())); } return self::$mediaVersion; } /** * Function to refresh the media version * * @return Version Instance of $this to allow chaining. * * @since 3.2 */ public function refreshMediaVersion(): Version { return $this->setMediaVersion($this->generateMediaVersion()); } /** * Sets the media version which is used to append to Joomla core media files. * * @param string $mediaVersion The media version. * * @return Version Instance of $this to allow chaining. * * @since 3.2 */ public function setMediaVersion(string $mediaVersion): Version { // Do not allow empty media versions if (!empty($mediaVersion)) { self::$mediaVersion = $mediaVersion; $this->getMediaVersionCache() ->store(['result' => $mediaVersion, 'output' => ''], md5('_media_version' . $this->getLongVersion())); } return $this; } /** * Get cache instance for MediaVersion caching. * * @return CacheController * * @since 4.2.0 */ private function getMediaVersionCache(): CacheController { /** @var CallbackController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('callback', ['defaultgroup' => '_media_version', 'caching' => true]); $cache->setLifeTime(5259600); // Disable cache when Debug is enabled if (JDEBUG) { $cache->setCaching(false); } return $cache; } } PK1A#]�>>5LL src/Language/LanguageFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Default factory for creating language objects * * @since 4.0.0 */ class LanguageFactory implements LanguageFactoryInterface { /** * Method to get an instance of a language. * * @param string $lang The language to use * @param boolean $debug The debug mode * * @return Language * * @since 4.0.0 */ public function createLanguage($lang, $debug = false): Language { return new Language($lang, $debug); } } PK1A#]wp�u)src/Language/LanguageFactoryInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface defining a factory which can create language objects * * @since 4.0.0 */ interface LanguageFactoryInterface { /** * Method to get an instance of a language. * * @param string $lang The language to use * @param boolean $debug The debug mode * * @return Language * * @since 4.0.0 */ public function createLanguage($lang, $debug = false): Language; } PK1A#]4�b�EVEVsrc/Language/LanguageHelper.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\OutputController; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Log\Log; use Joomla\Filesystem\File; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Language helper class * * @since 1.5 */ class LanguageHelper { /** * Builds a list of the system languages which can be used in a select option * * @param string $actualLanguage Client key for the area * @param string $basePath Base path to use * @param boolean $caching True if caching is used * @param boolean $installed Get only installed languages * * @return array List of system languages * * @since 1.5 */ public static function createLanguageList($actualLanguage, $basePath = JPATH_BASE, $caching = false, $installed = false) { $list = []; $clientId = $basePath === JPATH_ADMINISTRATOR ? 1 : 0; $languages = $installed ? static::getInstalledLanguages($clientId, true) : self::getKnownLanguages($basePath); foreach ($languages as $languageCode => $language) { $metadata = $installed ? $language->metadata : $language; $list[] = [ 'text' => $metadata['nativeName'] ?? $metadata['name'], 'value' => $languageCode, 'selected' => $languageCode === $actualLanguage ? 'selected="selected"' : null, ]; } return $list; } /** * Tries to detect the language. * * @return string locale or null if not found * * @since 1.5 */ public static function detectLanguage() { if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $browserLangs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); $systemLangs = self::getLanguages(); foreach ($browserLangs as $browserLang) { // Slice out the part before ; on first step, the part before - on second, place into array $browserLang = substr($browserLang, 0, strcspn($browserLang, ';')); $primary_browserLang = substr($browserLang, 0, 2); foreach ($systemLangs as $systemLang) { // Take off 3 letters iso code languages as they can't match browsers' languages and default them to en $Jinstall_lang = $systemLang->lang_code; if (\strlen($Jinstall_lang) < 6) { if (strtolower($browserLang) == strtolower(substr($systemLang->lang_code, 0, \strlen($browserLang)))) { return $systemLang->lang_code; } elseif ($primary_browserLang == substr($systemLang->lang_code, 0, 2)) { $primaryDetectedLang = $systemLang->lang_code; } } } if (isset($primaryDetectedLang)) { return $primaryDetectedLang; } } } } /** * Get available languages * * @param string $key Array key * * @return array An array of published languages * * @since 1.6 */ public static function getLanguages($key = 'default') { static $languages = []; if (!count($languages)) { // Installation uses available languages if (Factory::getApplication()->isClient('installation')) { $languages[$key] = []; $knownLangs = self::getKnownLanguages(JPATH_BASE); foreach ($knownLangs as $metadata) { // Take off 3 letters iso code languages as they can't match browsers' languages and default them to en $obj = new \stdClass(); $obj->lang_code = $metadata['tag']; $languages[$key][] = $obj; } } else { /** @var OutputController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('output', ['defaultgroup' => 'com_languages']); if ($cache->contains('languages')) { $languages = $cache->get('languages'); } else { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__languages')) ->where($db->quoteName('published') . ' = 1') ->order($db->quoteName('ordering') . ' ASC'); $db->setQuery($query); $languages['default'] = $db->loadObjectList(); $languages['sef'] = []; $languages['lang_code'] = []; if (isset($languages['default'][0])) { foreach ($languages['default'] as $lang) { $languages['sef'][$lang->sef] = $lang; $languages['lang_code'][$lang->lang_code] = $lang; } } $cache->store($languages, 'languages'); } } } return $languages[$key]; } /** * Get a list of installed languages. * * @param integer $clientId The client app id. * @param boolean $processMetaData Fetch Language metadata. * @param boolean $processManifest Fetch Language manifest. * @param string $pivot The pivot of the returning array. * @param string $orderField Field to order the results. * @param string $orderDirection Direction to order the results. * * @return array Array with the installed languages. * * @since 3.7.0 */ public static function getInstalledLanguages( $clientId = null, $processMetaData = false, $processManifest = false, $pivot = 'element', $orderField = null, $orderDirection = null ) { static $installedLanguages = null; if ($installedLanguages === null) { /** @var OutputController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('output', ['defaultgroup' => 'com_languages']); if ($cache->contains('installedlanguages')) { $installedLanguages = $cache->get('installedlanguages'); } else { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('element'), $db->quoteName('name'), $db->quoteName('client_id'), $db->quoteName('extension_id'), ] ) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('type') . ' = ' . $db->quote('language'), $db->quoteName('state') . ' = 0', $db->quoteName('enabled') . ' = 1', ] ); $installedLanguages = $db->setQuery($query)->loadObjectList(); $cache->store($installedLanguages, 'installedlanguages'); } } $clients = $clientId === null ? [0, 1] : [(int) $clientId]; $languages = [ 0 => [], 1 => [], ]; foreach ($installedLanguages as $language) { // If the language client is not needed continue cycle. Drop for performance. if (!\in_array((int) $language->client_id, $clients)) { continue; } $lang = $language; if ($processMetaData || $processManifest) { $clientPath = (int) $language->client_id === 0 ? JPATH_SITE : JPATH_ADMINISTRATOR; $metafile = self::getLanguagePath($clientPath, $language->element) . '/langmetadata.xml'; if (!is_file($metafile)) { $metafile = self::getLanguagePath($clientPath, $language->element) . '/' . $language->element . '.xml'; } // Process the language metadata. if ($processMetaData) { try { $lang->metadata = self::parseXMLLanguageFile($metafile); } catch (\Exception $e) { // Not able to process xml language file. Fail silently. Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METAFILE', $language->element, $metafile), Log::WARNING, 'language'); continue; } // No metadata found, not a valid language. Fail silently. if (!\is_array($lang->metadata)) { Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METADATA', $language->element, $metafile), Log::WARNING, 'language'); continue; } } // Process the language manifest. if ($processManifest) { try { $lang->manifest = Installer::parseXMLInstallFile($metafile); } catch (\Exception $e) { // Not able to process xml language file. Fail silently. Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METAFILE', $language->element, $metafile), Log::WARNING, 'language'); continue; } // No metadata found, not a valid language. Fail silently. if (!\is_array($lang->manifest)) { Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METADATA', $language->element, $metafile), Log::WARNING, 'language'); continue; } } } $languages[$language->client_id][] = $lang; } // Order the list, if needed. if ($orderField !== null && $orderDirection !== null) { $orderDirection = strtolower($orderDirection) === 'desc' ? -1 : 1; foreach ($languages as $cId => $language) { // If the language client is not needed continue cycle. Drop for performance. if (!\in_array($cId, $clients)) { continue; } $languages[$cId] = ArrayHelper::sortObjects($languages[$cId], $orderField, $orderDirection, true, true); } } // Add the pivot, if needed. if ($pivot !== null) { foreach ($languages as $cId => $language) { // If the language client is not needed continue cycle. Drop for performance. if (!\in_array($cId, $clients)) { continue; } $languages[$cId] = ArrayHelper::pivot($languages[$cId], $pivot); } } return $clientId !== null ? $languages[$clientId] : $languages; } /** * Get a list of content languages. * * @param array $publishedStates Array with the content language published states. Empty array for all. * @param boolean $checkInstalled Check if the content language is installed. * @param string $pivot The pivot of the returning array. * @param string $orderField Field to order the results. * @param string $orderDirection Direction to order the results. * * @return array Array of the content languages. * * @since 3.7.0 */ public static function getContentLanguages( $publishedStates = [1], $checkInstalled = true, $pivot = 'lang_code', $orderField = null, $orderDirection = null ) { static $contentLanguages = null; if ($contentLanguages === null) { /** @var OutputController $cache */ $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) ->createCacheController('output', ['defaultgroup' => 'com_languages']); if ($cache->contains('contentlanguages')) { $contentLanguages = $cache->get('contentlanguages'); } else { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__languages')); $contentLanguages = $db->setQuery($query)->loadObjectList(); $cache->store($contentLanguages, 'contentlanguages'); } } $languages = $contentLanguages; // B/C layer. Before 3.8.3. if ($publishedStates === true) { $publishedStates = [1]; } elseif ($publishedStates === false) { $publishedStates = []; } // Check the language published state, if needed. if (\count($publishedStates) > 0) { foreach ($languages as $key => $language) { if (!\in_array((int) $language->published, $publishedStates, true)) { unset($languages[$key]); } } } // Check if the language is installed, if needed. if ($checkInstalled) { $languages = array_values(array_intersect_key(ArrayHelper::pivot($languages, 'lang_code'), static::getInstalledLanguages(0))); } // Order the list, if needed. if ($orderField !== null && $orderDirection !== null) { $languages = ArrayHelper::sortObjects($languages, $orderField, strtolower($orderDirection) === 'desc' ? -1 : 1, true, true); } // Add the pivot, if needed. if ($pivot !== null) { $languages = ArrayHelper::pivot($languages, $pivot); } return $languages; } /** * Parse strings from a language file. * * @param string $fileName The language ini file path. * @param boolean $debug If set to true debug language ini file. * * @return array The strings parsed. * * @since 3.9.0 */ public static function parseIniFile($fileName, $debug = false) { // Check if file exists. if (!is_file($fileName)) { return []; } // Capture hidden PHP errors from the parsing. if ($debug === true) { // See https://www.php.net/manual/en/reserved.variables.phperrormsg.php $php_errormsg = null; $trackErrors = ini_get('track_errors'); ini_set('track_errors', true); } // This was required for https://github.com/joomla/joomla-cms/issues/17198 but not sure what server setup // issue it is solving $disabledFunctions = explode(',', ini_get('disable_functions')); $isParseIniFileDisabled = \in_array('parse_ini_file', array_map('trim', $disabledFunctions)); if (!\function_exists('parse_ini_file') || $isParseIniFileDisabled) { $contents = file_get_contents($fileName); $strings = @parse_ini_string($contents, false, INI_SCANNER_RAW); } else { $strings = @parse_ini_file($fileName, false, INI_SCANNER_RAW); } // Ini files are processed in the "RAW" mode of parse_ini_string, leaving escaped quotes untouched - lets postprocess them $strings = str_replace('\"', '"', $strings); // Restore error tracking to what it was before. if ($debug === true) { ini_set('track_errors', $trackErrors); } return \is_array($strings) ? $strings : []; } /** * Save strings to a language file. * * @param string $fileName The language ini file path. * @param array $strings The array of strings. * * @return boolean True if saved, false otherwise. * * @since 3.7.0 */ public static function saveToIniFile($fileName, array $strings) { // Escape double quotes. foreach ($strings as $key => $string) { $strings[$key] = addcslashes($string, '"'); } // Write override.ini file with the strings. $registry = new Registry($strings); return File::write($fileName, $registry->toString('INI')); } /** * Checks if a language exists. * * This is a simple, quick check for the directory that should contain language files for the given user. * * @param string $lang Language to check. * @param string $basePath Optional path to check. * * @return boolean True if the language exists. * * @since 3.7.0 */ public static function exists($lang, $basePath = JPATH_BASE) { static $paths = []; // Return false if no language was specified if (!$lang) { return false; } $path = $basePath . '/language/' . $lang; // Return previous check results if it exists if (isset($paths[$path])) { return $paths[$path]; } // Check if the language exists $paths[$path] = is_dir($path); return $paths[$path]; } /** * Returns an associative array holding the metadata. * * @param string $lang The name of the language. * * @return mixed If $lang exists return key/value pair with the language metadata, otherwise return NULL. * * @since 3.7.0 */ public static function getMetadata($lang) { $file = self::getLanguagePath(JPATH_BASE, $lang) . '/langmetadata.xml'; if (!is_file($file)) { $file = self::getLanguagePath(JPATH_BASE, $lang) . '/' . $lang . '.xml'; } $result = null; if (is_file($file)) { $result = self::parseXMLLanguageFile($file); } if (empty($result)) { return; } return $result; } /** * Returns a list of known languages for an area * * @param string $basePath The basepath to use * * @return array key/value pair with the language file and real name. * * @since 3.7.0 */ public static function getKnownLanguages($basePath = JPATH_BASE) { return self::parseLanguageFiles(self::getLanguagePath($basePath)); } /** * Get the path to a language * * @param string $basePath The basepath to use. * @param string $language The language tag. * * @return string language related path or null. * * @since 3.7.0 */ public static function getLanguagePath($basePath = JPATH_BASE, $language = null) { return $basePath . '/language' . (!empty($language) ? '/' . $language : ''); } /** * Searches for language directories within a certain base dir. * * @param string $dir directory of files. * * @return array Array holding the found languages as filename => real name pairs. * * @since 3.7.0 */ public static function parseLanguageFiles($dir = null) { $languages = []; // Search main language directory for subdirectories foreach (glob($dir . '/*', GLOB_NOSORT | GLOB_ONLYDIR) as $directory) { // But only directories with lang code format if (preg_match('#/[a-z]{2,3}-[A-Z]{2}$#', $directory)) { $dirPathParts = pathinfo($directory); $file = $directory . '/langmetadata.xml'; if (!is_file($file)) { $file = $directory . '/' . $dirPathParts['filename'] . '.xml'; } if (!is_file($file)) { continue; } try { // Get installed language metadata from xml file and merge it with lang array if ($metadata = self::parseXMLLanguageFile($file)) { $languages = array_replace($languages, [$dirPathParts['filename'] => $metadata]); } } catch (\RuntimeException $e) { // Ignore it } } } return $languages; } /** * Parse XML file for language information. * * @param string $path Path to the XML files. * * @return array Array holding the found metadata as a key => value pair. * * @since 3.7.0 * @throws \RuntimeException */ public static function parseXMLLanguageFile($path) { if (!is_readable($path)) { throw new \RuntimeException('File not found or not readable'); } // Try to load the file $xml = simplexml_load_file($path); if (!$xml) { return; } // Check that it's a metadata file if ((string) $xml->getName() !== 'metafile') { return; } $metadata = []; foreach ($xml->metadata->children() as $child) { $metadata[$child->getName()] = (string) $child; } return $metadata; } } PK1A#]S�fH��#src/Language/LanguageAwareTrait.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Defines the trait for a language aware class. * * @since 4.4.0 */ trait LanguageAwareTrait { /** * Language * * @var Language * @since 4.4.0 */ private $language; /** * Get the Language. * * @return Language * * @since 4.4.0 * @throws \UnexpectedValueException May be thrown if the language has not been set. */ protected function getLanguage(): Language { if ($this->language) { return $this->language; } throw new \UnexpectedValueException('Language not set in ' . __CLASS__); } /** * Set the language to use. * * @param Language $language The language to use * * @return void * * @since 4.4.0 */ public function setLanguage(Language $language): void { $this->language = $language; } } PK1A#]2�:��5�5src/Language/Text.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Text handling class. * * @since 1.7.0 */ class Text { /** * JavaScript strings * * @var array * @since 1.7.0 */ protected static $strings = []; /** * Translates a string into the current language. * * Examples: * `<script>alert(Joomla.Text._('<?php echo Text::_("JDEFAULT", array("script"=>true)); ?>'));</script>` * will generate an alert message containing 'Default' * `<?php echo Text::_("JDEFAULT"); ?>` will generate a 'Default' string * * @param string $string The string to translate. * @param mixed $jsSafe Boolean: Make the result javascript safe. * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation) * @param boolean $script To indicate that the string will be push in the javascript language store * * @return string The translated string or the key if $script is true * * @since 1.7.0 */ public static function _($string, $jsSafe = false, $interpretBackSlashes = true, $script = false) { if (\is_array($jsSafe)) { if (\array_key_exists('interpretBackSlashes', $jsSafe)) { $interpretBackSlashes = (bool) $jsSafe['interpretBackSlashes']; } if (\array_key_exists('script', $jsSafe)) { $script = (bool) $jsSafe['script']; } $jsSafe = \array_key_exists('jsSafe', $jsSafe) ? (bool) $jsSafe['jsSafe'] : false; } if (self::passSprintf($string, $jsSafe, $interpretBackSlashes, $script)) { return $string; } $lang = Factory::getLanguage(); if ($script) { static::$strings[$string] = $lang->_($string, $jsSafe, $interpretBackSlashes); return $string; } return $lang->_($string, $jsSafe, $interpretBackSlashes); } /** * Checks the string if it should be interpreted as sprintf and runs sprintf over it. * * @param string &$string The string to translate. * @param mixed $jsSafe Boolean: Make the result javascript safe. * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation) * @param boolean $script To indicate that the string will be push in the javascript language store * * @return boolean Whether the string be interpreted as sprintf * * @since 3.4.4 */ private static function passSprintf(&$string, $jsSafe = false, $interpretBackSlashes = true, $script = false) { // Check if string contains a comma if (empty($string) || strpos($string, ',') === false) { return false; } $lang = Factory::getLanguage(); $string_parts = explode(',', $string); // Pass all parts through the Text translator foreach ($string_parts as $i => $str) { $string_parts[$i] = $lang->_($str, $jsSafe, $interpretBackSlashes); } $first_part = array_shift($string_parts); // Replace custom named placeholders with sprintf style placeholders $first_part = preg_replace('/\[\[%([0-9]+):[^\]]*\]\]/', '%\1$s', $first_part); // Check if string contains sprintf placeholders if (!preg_match('/%([0-9]+\$)?s/', $first_part)) { return false; } $final_string = vsprintf($first_part, $string_parts); // Return false if string hasn't changed if ($first_part === $final_string) { return false; } $string = $final_string; if ($script) { foreach ($string_parts as $i => $str) { static::$strings[$str] = $string_parts[$i]; } } return true; } /** * Translates a string into the current language. * * Examples: * `<?php echo Text::alt('JALL', 'language'); ?>` will generate a 'All' string in English but a "Toutes" string in French * `<?php echo Text::alt('JALL', 'module'); ?>` will generate a 'All' string in English but a "Tous" string in French * * @param string $string The string to translate. * @param string $alt The alternate option for global string * @param mixed $jsSafe Boolean: Make the result javascript safe. * @param boolean $interpretBackSlashes To interpret backslashes (\\=\, \n=carriage return, \t=tabulation) * @param boolean $script To indicate that the string will be pushed in the javascript language store * * @return string The translated string or the key if $script is true * * @since 1.7.0 */ public static function alt($string, $alt, $jsSafe = false, $interpretBackSlashes = true, $script = false) { if (Factory::getLanguage()->hasKey($string . '_' . $alt)) { $string .= '_' . $alt; } return static::_($string, $jsSafe, $interpretBackSlashes, $script); } /** * Like Text::sprintf but tries to pluralise the string. * * Note that this method can take a mixed number of arguments as for the sprintf function. * * The last argument can take an array of options: * * array('jsSafe'=>boolean, 'interpretBackSlashes'=>boolean, 'script'=>boolean) * * where: * * jsSafe is a boolean to generate a javascript safe strings. * interpretBackSlashes is a boolean to interpret backslashes \\->\, \n->new line, \t->tabulation. * script is a boolean to indicate that the string will be push in the javascript language store. * * Examples: * `<script>alert(Joomla.Text._('<?php echo Text::plural("COM_PLUGINS_N_ITEMS_UNPUBLISHED", 1, array("script"=>true)); ?>'));</script>` * will generate an alert message containing '1 plugin successfully disabled' * `<?php echo Text::plural('COM_PLUGINS_N_ITEMS_UNPUBLISHED', 1); ?>` will generate a '1 plugin successfully disabled' string * * @param string $string The format string. * @param integer $n The number of items * * @return string The translated strings or the key if 'script' is true in the array of options * * @since 1.7.0 */ public static function plural($string, $n) { $lang = Factory::getLanguage(); $args = \func_get_args(); $count = \count($args); // Try the key from the language plural potential suffixes $found = false; $suffixes = $lang->getPluralSuffixes((int) $n); // Add the count as possible suffix to allow for eg "a dozen" with suffix _12. // Only do that if it is a real plural (more than one) to avoid issues with languages. See https://github.com/joomla/joomla-cms/pull/29029 if ($n != 1) { array_unshift($suffixes, (int) $n); } foreach ($suffixes as $suffix) { $key = $string . '_' . $suffix; if ($lang->hasKey($key)) { $found = true; break; } } // Not found so revert to the original. $key = !$found ? $string : $key; if (\is_array($args[$count - 1])) { $args[0] = $lang->_( $key, \array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false, \array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true ); if (\array_key_exists('script', $args[$count - 1]) && $args[$count - 1]['script']) { static::$strings[$key] = \call_user_func_array('sprintf', $args); return $key; } } else { $args[0] = $lang->_($key); } return \call_user_func_array('sprintf', $args); } /** * Passes a string thru a sprintf. * * Note that this method can take a mixed number of arguments as for the sprintf function. * * The last argument can take an array of options: * * array('jsSafe'=>boolean, 'interpretBackSlashes'=>boolean, 'script'=>boolean) * * where: * * jsSafe is a boolean to generate a javascript safe strings. * interpretBackSlashes is a boolean to interpret backslashes \\->\, \n->new line, \t->tabulation. * script is a boolean to indicate that the string will be push in the javascript language store. * * @param string $string The format string. * * @return string The translated strings or the key if 'script' is true in the array of options. * * @since 1.7.0 */ public static function sprintf($string) { $lang = Factory::getLanguage(); $args = \func_get_args(); $count = \count($args); if (\is_array($args[$count - 1])) { $args[0] = $lang->_( $string, \array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false, \array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true ); if (\array_key_exists('script', $args[$count - 1]) && $args[$count - 1]['script']) { static::$strings[$string] = \call_user_func_array('sprintf', $args); return $string; } } else { $args[0] = $lang->_($string); } // Replace custom named placeholders with sprintf style placeholders $args[0] = preg_replace('/\[\[%([0-9]+):[^\]]*\]\]/', '%\1$s', $args[0]); return \call_user_func_array('sprintf', $args); } /** * Passes a string through a printf. * * Note that this method can take a mixed number of arguments as for the sprintf function. * * @param string $string The format string. * * @return mixed * * @since 1.7.0 */ public static function printf($string) { $lang = Factory::getLanguage(); $args = \func_get_args(); $count = \count($args); if (\is_array($args[$count - 1])) { $args[0] = $lang->_( $string, \array_key_exists('jsSafe', $args[$count - 1]) ? $args[$count - 1]['jsSafe'] : false, \array_key_exists('interpretBackSlashes', $args[$count - 1]) ? $args[$count - 1]['interpretBackSlashes'] : true ); } else { $args[0] = $lang->_($string); } return \call_user_func_array('printf', $args); } /** * Translate a string into the current language and stores it in the JavaScript language store. * * @param string $string The Text key. * @param boolean $jsSafe Legacy parameter to add slashes to the string. * Set it as "false" because the method encodes the string as JSON with json_encode(). * @param boolean $interpretBackSlashes Interpret \t and \n. * * @return array * * @since 1.7.0 */ public static function script($string = null, $jsSafe = false, $interpretBackSlashes = true) { if ($string === null) { @trigger_error( sprintf( 'As of 3.7.0, passing a null value for the first argument of %1$s() is deprecated and will not be supported in 4.0.' . ' Use the %2$s::getScriptStrings() method to get the strings from the JavaScript language store instead.', __METHOD__, __CLASS__ ), E_USER_DEPRECATED ); } if (\is_array($jsSafe)) { if (\array_key_exists('interpretBackSlashes', $jsSafe)) { $interpretBackSlashes = (bool) $jsSafe['interpretBackSlashes']; } if (\array_key_exists('jsSafe', $jsSafe)) { $jsSafe = (bool) $jsSafe['jsSafe']; } else { $jsSafe = false; } } // Add the string to the array if not null. if ($string !== null) { $doc = Factory::getDocument(); // Get previously added strings $strings = $doc->getScriptOptions('joomla.jtext'); // Normalize the key and translate the string. $key = strtoupper($string); $strings[$key] = Factory::getLanguage()->_($string, $jsSafe, $interpretBackSlashes); static::$strings[$key] = $strings[$key]; // Load core.js dependency HTMLHelper::_('behavior.core'); // Update Joomla.Text script options $doc->addScriptOptions('joomla.jtext', $strings, false); } return static::getScriptStrings(); } /** * Get the strings that have been loaded to the JavaScript language store. * * @return array * * @since 3.7.0 */ public static function getScriptStrings() { return static::$strings; } } PK1A#]����'src/Language/LanguageAwareInterface.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Interface to be implemented by classes depending on a language. * * @since 4.4.0 */ interface LanguageAwareInterface { /** * Set the language to use. * * @param Language $language The language to use * * @return void * * @since 4.4.0 */ public function setLanguage(Language $language): void; } PK1A#]G�����src/Language/Language.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; use Joomla\CMS\Factory; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Languages/translation handler class * * @since 1.7.0 */ class Language { /** * Array of Language objects * * @var Language[] * @since 1.7.0 */ protected static $languages = []; /** * Debug language, If true, highlights if string isn't found. * * @var boolean * @since 1.7.0 */ protected $debug = false; /** * The default language, used when a language file in the requested language does not exist. * * @var string * @since 1.7.0 */ protected $default = 'en-GB'; /** * An array of orphaned text. * * @var array * @since 1.7.0 */ protected $orphans = []; /** * Array holding the language metadata. * * @var array * @since 1.7.0 */ protected $metadata = null; /** * Array holding the language locale or boolean null if none. * * @var array|boolean * @since 1.7.0 */ protected $locale = null; /** * The language to load. * * @var string * @since 1.7.0 */ protected $lang = null; /** * A nested array of language files that have been loaded * * @var array * @since 1.7.0 */ protected $paths = []; /** * List of language files that are in error state * * @var array * @since 1.7.0 */ protected $errorfiles = []; /** * Translations * * @var array * @since 1.7.0 */ protected $strings = []; /** * An array of used text, used during debugging. * * @var array * @since 1.7.0 */ protected $used = []; /** * Counter for number of loads. * * @var integer * @since 1.7.0 */ protected $counter = 0; /** * An array used to store overrides. * * @var array * @since 1.7.0 */ protected $override = []; /** * Name of the transliterator function for this language. * * @var callable * @since 1.7.0 */ protected $transliterator = null; /** * Name of the pluralSuffixesCallback function for this language. * * @var callable * @since 1.7.0 */ protected $pluralSuffixesCallback = null; /** * Name of the ignoredSearchWordsCallback function for this language. * * @var callable * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $ignoredSearchWordsCallback = null; /** * Name of the lowerLimitSearchWordCallback function for this language. * * @var callable * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $lowerLimitSearchWordCallback = null; /** * Name of the upperLimitSearchWordCallback function for this language. * * @var callable * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $upperLimitSearchWordCallback = null; /** * Name of the searchDisplayedCharactersNumberCallback function for this language. * * @var callable * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ protected $searchDisplayedCharactersNumberCallback = null; /** * Constructor activating the default information of the language. * * @param string $lang The language * @param boolean $debug Indicates if language debugging is enabled. * * @since 1.7.0 */ public function __construct($lang = null, $debug = false) { $this->strings = []; if ($lang == null) { $lang = $this->default; } $this->lang = $lang; $this->metadata = LanguageHelper::getMetadata($this->lang); $this->setDebug($debug); /* * Let's load the default override once, so we can profit from that, too * But make sure, that we don't enforce it on each language file load. * So don't put it in $this->override */ if (!$this->debug && $lang !== $this->default) { $this->loadLanguage(JPATH_BASE . '/language/overrides/' . $this->default . '.override.ini'); } $this->override = $this->parse(JPATH_BASE . '/language/overrides/' . $lang . '.override.ini'); // Look for a language specific localise class $class = str_replace('-', '_', $lang . 'Localise'); $paths = []; if (\defined('JPATH_SITE')) { // Note: Manual indexing to enforce load order. $paths[0] = JPATH_SITE . "/language/overrides/$lang.localise.php"; $paths[2] = JPATH_SITE . "/language/$lang/localise.php"; $paths[4] = JPATH_SITE . "/language/$lang/$lang.localise.php"; } if (\defined('JPATH_ADMINISTRATOR')) { // Note: Manual indexing to enforce load order. $paths[1] = JPATH_ADMINISTRATOR . "/language/overrides/$lang.localise.php"; $paths[3] = JPATH_ADMINISTRATOR . "/language/$lang/localise.php"; $paths[5] = JPATH_ADMINISTRATOR . "/language/$lang/$lang.localise.php"; } ksort($paths); $path = reset($paths); while (!class_exists($class) && $path) { if (is_file($path)) { require_once $path; } $path = next($paths); } if (class_exists($class)) { /** * Class exists. Try to find * -a transliterate method, * -a getPluralSuffixes method, * -a getIgnoredSearchWords method * -a getLowerLimitSearchWord method * -a getUpperLimitSearchWord method * -a getSearchDisplayCharactersNumber method */ if (method_exists($class, 'transliterate')) { $this->transliterator = [$class, 'transliterate']; } if (method_exists($class, 'getPluralSuffixes')) { $this->pluralSuffixesCallback = [$class, 'getPluralSuffixes']; } if (method_exists($class, 'getIgnoredSearchWords')) { $this->ignoredSearchWordsCallback = [$class, 'getIgnoredSearchWords']; } if (method_exists($class, 'getLowerLimitSearchWord')) { $this->lowerLimitSearchWordCallback = [$class, 'getLowerLimitSearchWord']; } if (method_exists($class, 'getUpperLimitSearchWord')) { $this->upperLimitSearchWordCallback = [$class, 'getUpperLimitSearchWord']; } if (method_exists($class, 'getSearchDisplayedCharactersNumber')) { $this->searchDisplayedCharactersNumberCallback = [$class, 'getSearchDisplayedCharactersNumber']; } } $this->load(); } /** * Returns a language object. * * @param string $lang The language to use. * @param boolean $debug The debug mode. * * @return Language The Language object. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use the language factory instead * Example: Factory::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($lang, $debug); */ public static function getInstance($lang, $debug = false) { if (!isset(self::$languages[$lang . $debug])) { self::$languages[$lang . $debug] = Factory::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($lang, $debug); } return self::$languages[$lang . $debug]; } /** * Translate function, mimics the php gettext (alias _) function. * * The function checks if $jsSafe is true, then if $interpretBackslashes is true. * * @param string $string The string to translate * @param boolean $jsSafe Parameter to add slashes to the string that will be rendered as JavaScript. * However, set as "false" if the string is going to be encoded by json_encode(). * @param boolean $interpretBackSlashes Interpret \t and \n * * @return string The translation of the string * * @since 1.7.0 */ public function _($string, $jsSafe = false, $interpretBackSlashes = true) { // Detect empty string if ($string == '') { return ''; } $key = strtoupper($string); if (isset($this->strings[$key])) { $string = $this->strings[$key]; // Store debug information if ($this->debug) { $value = Factory::getApplication()->get('debug_lang_const', true) ? $string : $key; $string = '**' . $value . '**'; $caller = $this->getCallerInfo(); if (!\array_key_exists($key, $this->used)) { $this->used[$key] = []; } $this->used[$key][] = $caller; } } else { if ($this->debug) { $info = []; $info['trace'] = $this->getTrace(); $info['key'] = $key; $info['string'] = $string; if (!\array_key_exists($key, $this->orphans)) { $this->orphans[$key] = []; } $this->orphans[$key][] = $info; $string = '??' . $string . '??'; } } if ($jsSafe) { // Javascript filter $string = addslashes($string); } elseif ($interpretBackSlashes) { if (strpos($string, '\\') !== false) { // Interpret \n and \t characters $string = str_replace(['\\\\', '\t', '\n'], ["\\", "\t", "\n"], $string); } } return $string; } /** * Transliterate function * * This method processes a string and replaces all accented UTF-8 characters by unaccented * ASCII-7 "equivalents". * * @param string $string The string to transliterate. * * @return string The transliteration of the string. * * @since 1.7.0 */ public function transliterate($string) { // First check for transliterator provided by translation if ($this->transliterator !== null) { $string = \call_user_func($this->transliterator, $string); // Check if all symbols were transliterated (contains only ASCII), otherwise continue if (!preg_match('/[\\x80-\\xff]/', $string)) { return $string; } } // Run our transliterator for common symbols, // This need to be executed before native php transliterator, because it may not have all required transliterators $string = Transliterate::utf8_latin_to_ascii($string); // Check if all symbols were transliterated (contains only ASCII), // Otherwise try to use native php function if available if (preg_match('/[\\x80-\\xff]/', $string) && function_exists('transliterator_transliterate') && function_exists('iconv')) { return iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", transliterator_transliterate('Any-Latin; Latin-ASCII; Lower()', $string)); } return StringHelper::strtolower($string); } /** * Getter for transliteration function * * @return callable The transliterator function * * @since 1.7.0 */ public function getTransliterator() { return $this->transliterator; } /** * Set the transliteration function. * * @param callable $function Function name or the actual function. * * @return callable The previous function. * * @since 1.7.0 */ public function setTransliterator(callable $function) { $previous = $this->transliterator; $this->transliterator = $function; return $previous; } /** * Returns an array of suffixes for plural rules. * * @param integer $count The count number the rule is for. * * @return array The array of suffixes. * * @since 1.7.0 */ public function getPluralSuffixes($count) { if ($this->pluralSuffixesCallback !== null) { return \call_user_func($this->pluralSuffixesCallback, $count); } else { return [(string) $count]; } } /** * Getter for pluralSuffixesCallback function. * * @return callable Function name or the actual function. * * @since 1.7.0 */ public function getPluralSuffixesCallback() { return $this->pluralSuffixesCallback; } /** * Set the pluralSuffixes function. * * @param callable $function Function name or actual function. * * @return callable The previous function. * * @since 1.7.0 */ public function setPluralSuffixesCallback(callable $function) { $previous = $this->pluralSuffixesCallback; $this->pluralSuffixesCallback = $function; return $previous; } /** * Returns an array of ignored search words * * @return array The array of ignored search words. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getIgnoredSearchWords() { if ($this->ignoredSearchWordsCallback !== null) { return \call_user_func($this->ignoredSearchWordsCallback); } else { return []; } } /** * Getter for ignoredSearchWordsCallback function. * * @return callable Function name or the actual function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getIgnoredSearchWordsCallback() { return $this->ignoredSearchWordsCallback; } /** * Setter for the ignoredSearchWordsCallback function * * @param callable $function Function name or actual function. * * @return callable The previous function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function setIgnoredSearchWordsCallback(callable $function) { $previous = $this->ignoredSearchWordsCallback; $this->ignoredSearchWordsCallback = $function; return $previous; } /** * Returns a lower limit integer for length of search words * * @return integer The lower limit integer for length of search words (3 if no value was set for a specific language). * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getLowerLimitSearchWord() { if ($this->lowerLimitSearchWordCallback !== null) { return \call_user_func($this->lowerLimitSearchWordCallback); } else { return 3; } } /** * Getter for lowerLimitSearchWordCallback function * * @return callable Function name or the actual function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getLowerLimitSearchWordCallback() { return $this->lowerLimitSearchWordCallback; } /** * Setter for the lowerLimitSearchWordCallback function. * * @param callable $function Function name or actual function. * * @return callable The previous function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function setLowerLimitSearchWordCallback(callable $function) { $previous = $this->lowerLimitSearchWordCallback; $this->lowerLimitSearchWordCallback = $function; return $previous; } /** * Returns an upper limit integer for length of search words * * @return integer The upper limit integer for length of search words (200 if no value was set or if default value is < 200). * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getUpperLimitSearchWord() { if ($this->upperLimitSearchWordCallback !== null && \call_user_func($this->upperLimitSearchWordCallback) > 200) { return \call_user_func($this->upperLimitSearchWordCallback); } return 200; } /** * Getter for upperLimitSearchWordCallback function * * @return callable Function name or the actual function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getUpperLimitSearchWordCallback() { return $this->upperLimitSearchWordCallback; } /** * Setter for the upperLimitSearchWordCallback function * * @param callable $function Function name or the actual function. * * @return callable The previous function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function setUpperLimitSearchWordCallback(callable $function) { $previous = $this->upperLimitSearchWordCallback; $this->upperLimitSearchWordCallback = $function; return $previous; } /** * Returns the number of characters displayed in search results. * * @return integer The number of characters displayed (200 if no value was set for a specific language). * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getSearchDisplayedCharactersNumber() { if ($this->searchDisplayedCharactersNumberCallback !== null) { return \call_user_func($this->searchDisplayedCharactersNumberCallback); } else { return 200; } } /** * Getter for searchDisplayedCharactersNumberCallback function * * @return callable Function name or the actual function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function getSearchDisplayedCharactersNumberCallback() { return $this->searchDisplayedCharactersNumberCallback; } /** * Setter for the searchDisplayedCharactersNumberCallback function. * * @param callable $function Function name or the actual function. * * @return callable The previous function. * * @since 1.7.0 * * @deprecated 4.4 will be removed in 6.0 without replacement */ public function setSearchDisplayedCharactersNumberCallback(callable $function) { $previous = $this->searchDisplayedCharactersNumberCallback; $this->searchDisplayedCharactersNumberCallback = $function; return $previous; } /** * Loads a single language file and appends the results to the existing strings * * @param string $extension The extension for which a language file should be loaded. * @param string $basePath The basepath to use. * @param string $lang The language to load, default null for the current language. * @param boolean $reload Flag that will force a language to be reloaded if set to true. * @param boolean $default Flag that force the default language to be loaded if the current does not exist. * * @return boolean True if the file has successfully loaded. * * @since 1.7.0 */ public function load($extension = 'joomla', $basePath = JPATH_BASE, $lang = null, $reload = false, $default = true) { // If language is null set as the current language. if (!$lang) { $lang = $this->lang; } // Load the default language first if we're not debugging and a non-default language is requested to be loaded // with $default set to true if (!$this->debug && ($lang != $this->default) && $default) { $this->load($extension, $basePath, $this->default, false, true); } $path = LanguageHelper::getLanguagePath($basePath, $lang); $internal = $extension === 'joomla' || $extension == ''; $filenames = []; if ($internal) { $filenames[] = "$path/joomla.ini"; $filenames[] = "$path/$lang.ini"; } else { // Try first without a language-prefixed filename. $filenames[] = "$path/$extension.ini"; $filenames[] = "$path/$lang.$extension.ini"; } foreach ($filenames as $filename) { if (isset($this->paths[$extension][$filename]) && !$reload) { // This file has already been tested for loading. $result = $this->paths[$extension][$filename]; } else { // Load the language file $result = $this->loadLanguage($filename, $extension); } if ($result) { return true; } } return false; } /** * Loads a language file. * * This method will not note the successful loading of a file - use load() instead. * * @param string $fileName The name of the file. * @param string $extension The name of the extension. * * @return boolean True if new strings have been added to the language * * @see Language::load() * @since 1.7.0 */ protected function loadLanguage($fileName, $extension = 'unknown') { $this->counter++; $result = false; $strings = $this->parse($fileName); if ($strings !== []) { $this->strings = array_replace($this->strings, $strings, $this->override); $result = true; } // Record the result of loading the extension's file. if (!isset($this->paths[$extension])) { $this->paths[$extension] = []; } $this->paths[$extension][$fileName] = $result; return $result; } /** * Parses a language file. * * @param string $fileName The name of the file. * * @return array The array of parsed strings. * * @since 1.7.0 */ protected function parse($fileName) { $strings = LanguageHelper::parseIniFile($fileName, $this->debug); // Debug the ini file if needed. if ($this->debug === true && is_file($fileName)) { $this->debugFile($fileName); } return $strings; } /** * Debugs a language file * * @param string $filename Absolute path to the file to debug * * @return integer A count of the number of parsing errors * * @since 3.6.3 * @throws \InvalidArgumentException */ public function debugFile($filename) { // Make sure our file actually exists if (!is_file($filename)) { throw new \InvalidArgumentException( sprintf('Unable to locate file "%s" for debugging', $filename) ); } // Initialise variables for manually parsing the file for common errors. $reservedWord = ['YES', 'NO', 'NULL', 'FALSE', 'ON', 'OFF', 'NONE', 'TRUE']; $debug = $this->getDebug(); $this->debug = false; $errors = []; $php_errormsg = null; // Open the file as a stream. $file = new \SplFileObject($filename); foreach ($file as $lineNumber => $line) { // Avoid BOM error as BOM is OK when using parse_ini. if ($lineNumber == 0) { $line = str_replace("\xEF\xBB\xBF", '', $line); } $line = trim($line); // Ignore comment lines. if (!\strlen($line) || $line['0'] == ';') { continue; } // Ignore grouping tag lines, like: [group] if (preg_match('#^\[[^\]]*\](\s*;.*)?$#', $line)) { continue; } // Remove any escaped double quotes \" from the equation $line = str_replace('\"', '', $line); $realNumber = $lineNumber + 1; // Check for odd number of double quotes. if (substr_count($line, '"') % 2 != 0) { $errors[] = $realNumber; continue; } // Check that the line passes the necessary format. if (!preg_match('#^[A-Z][A-Z0-9_:\*\-\.]*\s*=\s*".*"(\s*;.*)?$#', $line)) { $errors[] = $realNumber; continue; } // Check that the key is not in the reserved constants list. $key = strtoupper(trim(substr($line, 0, strpos($line, '=')))); if (\in_array($key, $reservedWord)) { $errors[] = $realNumber; } } // Check if we encountered any errors. if (\count($errors)) { $this->errorfiles[$filename] = $errors; } elseif ($php_errormsg) { // We didn't find any errors but there's probably a parse notice. $this->errorfiles['PHP' . $filename] = 'PHP parser errors :' . $php_errormsg; } $this->debug = $debug; return \count($errors); } /** * Get a metadata language property. * * @param string $property The name of the property. * @param mixed $default The default value. * * @return mixed The value of the property. * * @since 1.7.0 */ public function get($property, $default = null) { if (isset($this->metadata[$property])) { return $this->metadata[$property]; } return $default; } /** * Get a back trace. * * @return array * * @since 4.0.0 */ protected function getTrace() { return \function_exists('debug_backtrace') ? debug_backtrace() : []; } /** * Determine who called Language or Text. * * @return array Caller information. * * @since 1.7.0 */ protected function getCallerInfo() { // Try to determine the source if none was provided if (!\function_exists('debug_backtrace')) { return; } $backtrace = debug_backtrace(); $info = []; // Search through the backtrace to our caller $continue = true; while ($continue && next($backtrace)) { $step = current($backtrace); $class = @$step['class']; // We're looking for something outside of language.php if ($class != self::class && $class != Text::class) { $info['function'] = @$step['function']; $info['class'] = $class; $info['step'] = prev($backtrace); // Determine the file and name of the file $info['file'] = @$step['file']; $info['line'] = @$step['line']; $continue = false; } } return $info; } /** * Getter for Name. * * @return string Official name element of the language. * * @since 1.7.0 */ public function getName() { return $this->metadata['name']; } /** * Get a list of language files that have been loaded. * * @param string $extension An optional extension name. * * @return array * * @since 1.7.0 */ public function getPaths($extension = null) { if (isset($extension)) { if (isset($this->paths[$extension])) { return $this->paths[$extension]; } return []; } return $this->paths; } /** * Get a list of language files that are in error state. * * @return array * * @since 1.7.0 */ public function getErrorFiles() { return $this->errorfiles; } /** * Getter for the language tag (as defined in RFC 3066) * * @return string The language tag. * * @since 1.7.0 */ public function getTag() { return $this->metadata['tag']; } /** * Getter for the calendar type * * @return string The calendar type. * * @since 3.7.0 */ public function getCalendar() { if (isset($this->metadata['calendar'])) { return $this->metadata['calendar']; } else { return 'gregorian'; } } /** * Get the RTL property. * * @return boolean True is it an RTL language. * * @since 1.7.0 */ public function isRtl() { return (bool) $this->metadata['rtl']; } /** * Set the Debug property. * * @param boolean $debug The debug setting. * * @return boolean Previous value. * * @since 1.7.0 */ public function setDebug($debug) { $previous = $this->debug; $this->debug = (bool) $debug; return $previous; } /** * Get the Debug property. * * @return boolean True is in debug mode. * * @since 1.7.0 */ public function getDebug() { return $this->debug; } /** * Get the default language code. * * @return string Language code. * * @since 1.7.0 */ public function getDefault() { return $this->default; } /** * Set the default language code. * * @param string $lang The language code. * * @return string Previous value. * * @since 1.7.0 */ public function setDefault($lang) { $previous = $this->default; $this->default = $lang; return $previous; } /** * Get the list of orphaned strings if being tracked. * * @return array Orphaned text. * * @since 1.7.0 */ public function getOrphans() { return $this->orphans; } /** * Get the list of used strings. * * Used strings are those strings requested and found either as a string or a constant. * * @return array Used strings. * * @since 1.7.0 */ public function getUsed() { return $this->used; } /** * Determines is a key exists. * * @param string $string The key to check. * * @return boolean True, if the key exists. * * @since 1.7.0 */ public function hasKey($string) { if ($string === null) { return false; } return isset($this->strings[strtoupper($string)]); } /** * Get the language locale based on current language. * * @return array The locale according to the language. * * @since 1.7.0 */ public function getLocale() { if (!isset($this->locale)) { $locale = str_replace(' ', '', $this->metadata['locale'] ?? ''); if ($locale) { $this->locale = explode(',', $locale); } else { $this->locale = false; } } return $this->locale; } /** * Get the first day of the week for this language. * * @return integer The first day of the week according to the language * * @since 1.7.0 */ public function getFirstDay() { return (int) ($this->metadata['firstDay'] ?? 0); } /** * Get the weekends days for this language. * * @return string The weekend days of the week separated by a comma according to the language * * @since 3.2 */ public function getWeekEnd() { return $this->metadata['weekEnd'] ?? '0,6'; } } PK1A#]��``src/Language/Associations.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for associations in multilang * * @since 3.1 */ class Associations { /** * Get the associations. * * @param string $extension The name of the component. * @param string $tablename The name of the table. * @param string $context The context * @param integer $id The primary key value. * @param string $pk The name of the primary key in the given $table. * @param string $aliasField If the table has an alias field set it here. Null to not use it * @param string $catField If the table has a catid field set it here. Null to not use it * @param array $advClause Additional advanced 'where' clause; use c as parent column key, c2 as associations column key * * @return array The associated items * * @since 3.1 * * @throws \Exception */ public static function getAssociations( $extension, $tablename, $context, $id, $pk = 'id', $aliasField = 'alias', $catField = 'catid', $advClause = [] ) { // To avoid doing duplicate database queries. static $multilanguageAssociations = []; // Cast before creating cache key. $id = (int) $id; // Multilanguage association array key. If the key is already in the array we don't need to run the query again, just return it. $queryKey = md5(serialize(array_merge([$extension, $tablename, $context, $id], $advClause))); if (!isset($multilanguageAssociations[$queryKey])) { $multilanguageAssociations[$queryKey] = []; $db = Factory::getDbo(); $query = $db->getQuery(true); $categoriesExtraSql = ''; if ($tablename === '#__categories') { $categoriesExtraSql = ' AND c2.extension = :extension1'; $query->bind(':extension1', $extension); } $query->select($db->quoteName('c2.language')) ->from($db->quoteName($tablename, 'c')) ->join( 'INNER', $db->quoteName('#__associations', 'a'), $db->quoteName('a.id') . ' = ' . $db->quoteName('c.' . $pk) . ' AND ' . $db->quoteName('a.context') . ' = :context' ) ->bind(':context', $context) ->join('INNER', $db->quoteName('#__associations', 'a2'), $db->quoteName('a.key') . ' = ' . $db->quoteName('a2.key')) ->join( 'INNER', $db->quoteName($tablename, 'c2'), $db->quoteName('a2.id') . ' = ' . $db->quoteName('c2.' . $pk) . $categoriesExtraSql ); // Use alias field ? if (!empty($aliasField)) { $query->select( $query->concatenate( [ $db->quoteName('c2.' . $pk), $db->quoteName('c2.' . $aliasField), ], ':' ) . ' AS ' . $db->quoteName($pk) ); } else { $query->select($db->quoteName('c2.' . $pk)); } // Use catid field ? if (!empty($catField)) { $query->join( 'INNER', $db->quoteName('#__categories', 'ca'), $db->quoteName('c2.' . $catField) . ' = ' . $db->quoteName('ca.id') . ' AND ' . $db->quoteName('ca.extension') . ' = :extension2' ) ->bind(':extension2', $extension) ->select( $query->concatenate( [ $db->quoteName('ca.id'), $db->quoteName('ca.alias'), ], ':' ) . ' AS ' . $db->quoteName($catField) ); } $query->where($db->quoteName('c.' . $pk) . ' = :id') ->bind(':id', $id, ParameterType::INTEGER); if ($tablename === '#__categories') { $query->where($db->quoteName('c.extension') . ' = :extension3') ->bind(':extension3', $extension); } // Advanced where clause if (!empty($advClause)) { foreach ($advClause as $clause) { $query->where($clause); } } $db->setQuery($query); try { $items = $db->loadObjectList('language'); } catch (\RuntimeException $e) { throw new \Exception($e->getMessage(), 500, $e); } if ($items) { foreach ($items as $tag => $item) { // Do not return itself as result if ((int) $item->{$pk} !== $id) { $multilanguageAssociations[$queryKey][$tag] = $item; } } } } return $multilanguageAssociations[$queryKey]; } /** * Method to determine if the language filter Associations parameter is enabled. * This works for both site and administrator. * * @return boolean True if the parameter is implemented; false otherwise. * * @since 3.2 */ public static function isEnabled() { // Flag to avoid doing multiple database queries. static $tested = false; // Status of language filter parameter. static $enabled = false; if (Multilanguage::isEnabled()) { // If already tested, don't test again. if (!$tested) { $plugin = PluginHelper::getPlugin('system', 'languagefilter'); if (!empty($plugin)) { $params = new Registry($plugin->params); $enabled = (bool) $params->get('item_associations', true); } $tested = true; } } return $enabled; } } PK1A#] ��ѣ�'src/Language/CachingLanguageFactory.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Caching factory for creating language objects. The requested languages are * cached in memory. * * @since 4.0.0 */ class CachingLanguageFactory extends LanguageFactory { /** * Array of Language objects * * @var Language[] * @since 4.0.0 */ private static $languages = []; /** * Method to get an instance of a language. * * @param string $lang The language to use * @param boolean $debug The debug mode * * @return Language * * @since 4.0.0 */ public function createLanguage($lang, $debug = false): Language { if (!isset(self::$languages[$lang . $debug])) { self::$languages[$lang . $debug] = parent::createLanguage($lang, $debug); } return self::$languages[$lang . $debug]; } } PK1A#]psg��src/Language/Multilanguage.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Factory; use Joomla\Database\DatabaseInterface; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Utility class for multilang * * @since 2.5.4 */ class Multilanguage { /** * Flag indicating multilanguage functionality is enabled. * * @var boolean * @since 4.0.0 */ public static $enabled = false; /** * Method to determine if the language filter plugin is enabled. * This works for both site and administrator. * * @param ?CMSApplication $app The application * @param ?DatabaseInterface $db The database * * @return boolean True if site is supporting multiple languages; false otherwise. * * @since 2.5.4 */ public static function isEnabled(CMSApplication $app = null, DatabaseInterface $db = null) { // Flag to avoid doing multiple database queries. static $tested = false; // Do not proceed with testing if the flag is true if (static::$enabled) { return true; } // Get application object. $app = $app ?: Factory::getApplication(); // If being called from the frontend, we can avoid the database query. if ($app->isClient('site')) { static::$enabled = $app->getLanguageFilter(); return static::$enabled; } // If already tested, don't test again. if (!$tested) { // Determine status of language filter plugin. $db = $db ?: Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('enabled')) ->from($db->quoteName('#__extensions')) ->where( [ $db->quoteName('type') . ' = ' . $db->quote('plugin'), $db->quoteName('folder') . ' = ' . $db->quote('system'), $db->quoteName('element') . ' = ' . $db->quote('languagefilter'), ] ); $db->setQuery($query); static::$enabled = (bool) $db->loadResult(); $tested = true; } return static::$enabled; } /** * Method to return a list of language home page menu items. * * @param ?DatabaseInterface $db The database * * @return array of menu objects. * * @since 3.5 */ public static function getSiteHomePages(DatabaseInterface $db = null) { // To avoid doing duplicate database queries. static $multilangSiteHomePages = null; if (!isset($multilangSiteHomePages)) { // Check for Home pages languages. $db = $db ?: Factory::getDbo(); $query = $db->getQuery(true) ->select( [ $db->quoteName('language'), $db->quoteName('id'), ] ) ->from($db->quoteName('#__menu')) ->where( [ $db->quoteName('home') . ' = ' . $db->quote('1'), $db->quoteName('published') . ' = 1', $db->quoteName('client_id') . ' = 0', ] ); $db->setQuery($query); $multilangSiteHomePages = $db->loadObjectList('language'); } return $multilangSiteHomePages; } } PK1A#]}�)j� � src/Language/Transliterate.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Language; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class to transliterate strings * * @since 1.7.0 * @note Port of phputf8's utf8_accents_to_ascii() */ class Transliterate { /** * Returns strings transliterated from UTF-8 to Latin * * @param string $string String to transliterate * @param integer $case Optionally specify upper or lower case. Default to null. * * @return string Transliterated string * * @since 1.7.0 */ public static function utf8_latin_to_ascii($string, $case = 0) { static $UTF8_LOWER_ACCENTS = null; static $UTF8_UPPER_ACCENTS = null; if ($case <= 0) { if (\is_null($UTF8_LOWER_ACCENTS)) { $UTF8_LOWER_ACCENTS = [ 'à' => 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o', 'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k', 'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o', 'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o', 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c', 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't', 'ū' => 'u', 'č' => 'c', 'ö' => 'oe', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', 'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o', 'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j', 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o', 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g', 'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a', 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e', 'œ' => 'oe', ]; } $string = str_replace(array_keys($UTF8_LOWER_ACCENTS), array_values($UTF8_LOWER_ACCENTS), $string); } if ($case >= 0) { if (\is_null($UTF8_UPPER_ACCENTS)) { $UTF8_UPPER_ACCENTS = [ 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', 'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O', 'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O', 'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C', 'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T', 'Ū' => 'U', 'Č' => 'C', 'Ö' => 'Oe', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', 'Ŗ' => 'R', 'Ä' => 'Ae', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'Ue', 'Ò' => 'O', 'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J', 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O', 'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G', 'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A', 'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'Ĕ' => 'E', 'Œ' => 'Oe', ]; } $string = str_replace(array_keys($UTF8_UPPER_ACCENTS), array_values($UTF8_UPPER_ACCENTS), $string); } return $string; } } PK1A#]������#src/Utility/BufferStreamHandler.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * * Remove phpcs exception with deprecated autoloading BufferStreamHandler::stream_register(); * @phpcs:disable PSR1.Files.SideEffects */ namespace Joomla\CMS\Utility; \defined('JPATH_PLATFORM') or die; /** * @deprecated 3.8 will be removed in 5.0 * Workaround for B/C. (removal missed in 4.0, also remove phpcs exception). * If BufferStreamHandler is needed directly call BufferStreamHandler::stream_register(); */ BufferStreamHandler::stream_register(); /** * Generic Buffer stream handler * * This class provides a generic buffer stream. It can be used to store/retrieve/manipulate * string buffers with the standard PHP filesystem I/O methods. * * @since 1.7.0 */ class BufferStreamHandler { /** * Stream position * * @var integer * @since 1.7.0 */ public $position = 0; /** * Buffer name * * @var string * @since 1.7.0 */ public $name = null; /** * Buffer hash * * @var array * @since 3.0.0 */ public $buffers = []; /** * Status of registering the wrapper * * @var boolean * @since 3.8.2 */ private static $registered = false; /** * Function to register the stream wrapper * * @return void * * @since 3.8.2 */ public static function stream_register() { if (!self::$registered) { stream_wrapper_register('buffer', '\\Joomla\\CMS\\Utility\\BufferStreamHandler'); self::$registered = true; } } /** * Function to open file or url * * @param string $path The URL that was passed * @param string $mode Mode used to open the file @see fopen * @param integer $options Flags used by the API, may be STREAM_USE_PATH and * STREAM_REPORT_ERRORS * @param string &$openedPath Full path of the resource. Used with STREAM_USE_PATH option * * @return boolean * * @since 1.7.0 * @see streamWrapper::stream_open */ public function stream_open($path, $mode, $options, &$openedPath) { $url = parse_url($path); $this->name = $url['host']; $this->buffers[$this->name] = null; $this->position = 0; return true; } /** * Read stream * * @param integer $count How many bytes of data from the current position should be returned. * * @return mixed The data from the stream up to the specified number of bytes (all data if * the total number of bytes in the stream is less than $count. Null if * the stream is empty. * * @see streamWrapper::stream_read * @since 1.7.0 */ public function stream_read($count) { $ret = substr($this->buffers[$this->name], $this->position, $count); $this->position += \strlen($ret); return $ret; } /** * Write stream * * @param string $data The data to write to the stream. * * @return integer * * @see streamWrapper::stream_write * @since 1.7.0 */ public function stream_write($data) { $left = substr($this->buffers[$this->name], 0, $this->position); $right = substr($this->buffers[$this->name], $this->position + \strlen($data)); $this->buffers[$this->name] = $left . $data . $right; $this->position += \strlen($data); return \strlen($data); } /** * Function to get the current position of the stream * * @return integer * * @see streamWrapper::stream_tell * @since 1.7.0 */ public function stream_tell() { return $this->position; } /** * Function to test for end of file pointer * * @return boolean True if the pointer is at the end of the stream * * @see streamWrapper::stream_eof * @since 1.7.0 */ public function stream_eof() { return $this->position >= \strlen($this->buffers[$this->name]); } /** * The read write position updates in response to $offset and $whence * * @param integer $offset The offset in bytes * @param integer $whence Position the offset is added to * Options are SEEK_SET, SEEK_CUR, and SEEK_END * * @return boolean True if updated * * @see streamWrapper::stream_seek * @since 1.7.0 */ public function stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: return $this->seek_set($offset); case SEEK_CUR: return $this->seek_cur($offset); case SEEK_END: return $this->seek_end($offset); } return false; } /** * Set the position to the offset * * @param integer $offset The offset in bytes * * @return boolean */ protected function seek_set($offset) { if ($offset < 0 || $offset > \strlen($this->buffers[$this->name])) { return false; } $this->position = $offset; return true; } /** * Adds the offset to current position * * @param integer $offset The offset in bytes * * @return boolean */ protected function seek_cur($offset) { if ($offset < 0) { return false; } $this->position += $offset; return true; } /** * Sets the position to the end of the current buffer + offset * * @param integer $offset The offset in bytes * * @return boolean */ protected function seek_end($offset) { $offset += \strlen($this->buffers[$this->name]); if ($offset < 0) { return false; } $this->position = $offset; return true; } } PK1A#]����src/Utility/Utility.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Utility; use Joomla\CMS\HTML\HTMLHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * JUtility is a utility functions class * * @since 1.7.0 */ class Utility { /** * Method to extract key/value pairs out of a string with XML style attributes * * @param string $string String containing XML style attributes * * @return array Key/Value pairs for the attributes * * @since 1.7.0 */ public static function parseAttributes($string) { $attr = []; $retarray = []; // Let's grab all the key/value pairs using a regular expression preg_match_all('/([\w:-]+)[\s]?=[\s]?"([^"]*)"/i', $string, $attr); if (\is_array($attr)) { $numPairs = \count($attr[1]); for ($i = 0; $i < $numPairs; $i++) { $retarray[$attr[1][$i]] = $attr[2][$i]; } } return $retarray; } /** * Method to get the maximum allowed file size for the HTTP uploads based on the active PHP configuration * * @param mixed $custom A custom upper limit, if the PHP settings are all above this then this will be used * * @return mixed Size in number of bytes * * @since 3.7.0 */ public static function getMaxUploadSize($custom = null) { $sizes = []; if ($custom) { $custom = HTMLHelper::_('number.bytes', $custom, ''); if ($custom > 0) { $sizes[] = $custom; } } /* * Read INI settings which affects upload size limits * and Convert each into number of bytes so that we can compare */ $sizes[] = HTMLHelper::_('number.bytes', ini_get('post_max_size'), ''); $sizes[] = HTMLHelper::_('number.bytes', ini_get('upload_max_filesize'), ''); // The minimum of these is the limiting factor return min($sizes); } } PK1A#]���2�� .htaccessnu�[���# Apache 2.4+ <IfModule mod_authz_core.c> Require all denied </IfModule> # Apache 2.0-2.2 <IfModule !mod_authz_core.c> Deny from all </IfModule>PK1A#]�#�ҟa�a loader.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ defined('JPATH_PLATFORM') or die; /** * Static class to handle loading of libraries. * * @since 1.7.0 */ abstract class JLoader { /** * Container for already imported library paths. * * @var array * @since 1.7.0 */ protected static $classes = []; /** * Container for already imported library paths. * * @var array * @since 1.7.0 */ protected static $imported = []; /** * Container for registered library class prefixes and path lookups. * * @var array * @since 3.0.0 */ protected static $prefixes = []; /** * Holds proxy classes and the class names the proxy. * * @var array * @since 3.2 */ protected static $classAliases = []; /** * Holds the inverse lookup for proxy classes and the class names the proxy. * * @var array * @since 3.4 */ protected static $classAliasesInverse = []; /** * Container for namespace => path map. * * @var array * @since 3.1.4 */ protected static $namespaces = []; /** * Holds a reference for all deprecated aliases (mainly for use by a logging platform). * * @var array * @since 3.6.3 */ protected static $deprecatedAliases = []; /** * The root folders where extensions can be found. * * @var array * @since 4.0.0 */ protected static $extensionRootFolders = []; /** * Method to discover classes of a given type in a given path. * * @param string $classPrefix The class name prefix to use for discovery. * @param string $parentPath Full path to the parent folder for the classes to discover. * @param boolean $force True to overwrite the autoload path value for the class if it already exists. * @param boolean $recurse Recurse through all child directories as well as the parent path. * * @return void * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to * register an autoloader for your files. */ public static function discover($classPrefix, $parentPath, $force = true, $recurse = false) { try { if ($recurse) { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($parentPath), RecursiveIteratorIterator::SELF_FIRST ); } else { $iterator = new DirectoryIterator($parentPath); } /** @type $file DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if ($file->isFile() && $file->getExtension() === 'php') { // Get the class name and full path for each file. $class = strtolower($classPrefix . preg_replace('#\.php$#', '', $fileName)); // Register the class with the autoloader if not already registered or the force flag is set. if ($force || empty(self::$classes[$class])) { self::register($class, $file->getPath() . '/' . $fileName); } } } } catch (UnexpectedValueException $e) { // Exception will be thrown if the path is not a directory. Ignore it. } } /** * Method to get the list of registered classes and their respective file paths for the autoloader. * * @return array The array of class => path values for the autoloader. * * @since 1.7.0 */ public static function getClassList() { return self::$classes; } /** * Method to get the list of deprecated class aliases. * * @return array An associative array with deprecated class alias data. * * @since 3.6.3 */ public static function getDeprecatedAliases() { return self::$deprecatedAliases; } /** * Method to get the list of registered namespaces. * * @return array The array of namespace => path values for the autoloader. * * @since 3.1.4 */ public static function getNamespaces() { return self::$namespaces; } /** * Loads a class from specified directories. * * @param string $key The class name to look for (dot notation). * @param string $base Search this directory for the class. * * @return boolean True on success. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to * register an autoloader for your files. */ public static function import($key, $base = null) { // Only import the library if not already attempted. if (!isset(self::$imported[$key])) { // Setup some variables. $success = false; $parts = explode('.', $key); $class = array_pop($parts); $base = (!empty($base)) ? $base : __DIR__; $path = str_replace('.', DIRECTORY_SEPARATOR, $key); // Handle special case for helper classes. if ($class === 'helper') { $class = ucfirst(array_pop($parts)) . ucfirst($class); } else { // Standard class. $class = ucfirst($class); } // If we are importing a library from the Joomla namespace set the class to autoload. if (strpos($path, 'joomla') === 0) { // Since we are in the Joomla namespace prepend the classname with J. $class = 'J' . $class; // Only register the class for autoloading if the file exists. if (is_file($base . '/' . $path . '.php')) { self::$classes[strtolower($class)] = $base . '/' . $path . '.php'; $success = true; } } else { /** * If we are not importing a library from the Joomla namespace directly include the * file since we cannot assert the file/folder naming conventions. */ // If the file exists attempt to include it. if (is_file($base . '/' . $path . '.php')) { $success = (bool) include_once $base . '/' . $path . '.php'; } } // Add the import key to the memory cache container. self::$imported[$key] = $success; } return self::$imported[$key]; } /** * Load the file for a class. * * @param string $class The class to be loaded. * * @return boolean True on success * * @since 1.7.0 */ public static function load($class) { // Sanitize class name. $key = strtolower($class); // If the class already exists do nothing. if (class_exists($class, false)) { return true; } // If the class is registered include the file. if (isset(self::$classes[$key])) { $found = (bool) include_once self::$classes[$key]; if ($found) { self::loadAliasFor($class); } // If the class doesn't exists, we probably have a class alias available if (!class_exists($class, false)) { // Search the alias class, first none namespaced and then namespaced $original = array_search($class, self::$classAliases) ?: array_search('\\' . $class, self::$classAliases); // When we have an original and the class exists an alias should be created if ($original && class_exists($original, false)) { class_alias($original, $class); } } return true; } return false; } /** * Directly register a class to the autoload list. * * @param string $class The class name to register. * @param string $path Full path to the file that holds the class to register. * @param boolean $force True to overwrite the autoload path value for the class if it already exists. * * @return void * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to * register an autoloader for your files. */ public static function register($class, $path, $force = true) { // When an alias exists, register it as well if (array_key_exists(strtolower($class), self::$classAliases)) { self::register(self::stripFirstBackslash(self::$classAliases[strtolower($class)]), $path, $force); } // Sanitize class name. $class = strtolower($class); // Only attempt to register the class if the name and file exist. if (!empty($class) && is_file($path)) { // Register the class with the autoloader if not already registered or the force flag is set. if ($force || empty(self::$classes[$class])) { self::$classes[$class] = $path; } } } /** * Register a class prefix with lookup path. This will allow developers to register library * packages with different class prefixes to the system autoloader. More than one lookup path * may be registered for the same class prefix, but if this method is called with the reset flag * set to true then any registered lookups for the given prefix will be overwritten with the current * lookup path. When loaded, prefix paths are searched in a "last in, first out" order. * * @param string $prefix The class prefix to register. * @param string $path Absolute file path to the library root where classes with the given prefix can be found. * @param boolean $reset True to reset the prefix with only the given lookup path. * @param boolean $prepend If true, push the path to the beginning of the prefix lookup paths array. * * @return void * * @throws RuntimeException * * @since 3.0.0 */ public static function registerPrefix($prefix, $path, $reset = false, $prepend = false) { // Verify the library path exists. if (!is_dir($path)) { $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path); throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500); } // If the prefix is not yet registered or we have an explicit reset flag then set set the path. if ($reset || !isset(self::$prefixes[$prefix])) { self::$prefixes[$prefix] = [$path]; } else { // Otherwise we want to simply add the path to the prefix. if ($prepend) { array_unshift(self::$prefixes[$prefix], $path); } else { self::$prefixes[$prefix][] = $path; } } } /** * Offers the ability for "just in time" usage of `class_alias()`. * You cannot overwrite an existing alias. * * @param string $alias The alias name to register. * @param string $original The original class to alias. * @param string|boolean $version The version in which the alias will no longer be present. * * @return boolean True if registration was successful. False if the alias already exists. * * @since 3.2 */ public static function registerAlias($alias, $original, $version = false) { // PHP is case insensitive so support all kind of alias combination $lowercasedAlias = strtolower($alias); if (!isset(self::$classAliases[$lowercasedAlias])) { self::$classAliases[$lowercasedAlias] = $original; $original = self::stripFirstBackslash($original); if (!isset(self::$classAliasesInverse[$original])) { self::$classAliasesInverse[$original] = [$lowercasedAlias]; } else { self::$classAliasesInverse[$original][] = $lowercasedAlias; } // If given a version, log this alias as deprecated if ($version) { self::$deprecatedAliases[] = ['old' => $alias, 'new' => $original, 'version' => $version]; } return true; } return false; } /** * Register a namespace to the autoloader. When loaded, namespace paths are searched in a "last in, first out" order. * * @param string $namespace A case sensitive Namespace to register. * @param string $path A case sensitive absolute file path to the library root where classes of the given namespace can be found. * @param boolean $reset True to reset the namespace with only the given lookup path. * @param boolean $prepend If true, push the path to the beginning of the namespace lookup paths array. * * @return void * * @throws RuntimeException * * @since 3.1.4 */ public static function registerNamespace($namespace, $path, $reset = false, $prepend = false) { // Verify the library path exists. if (!is_dir($path)) { $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path); throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500); } // Trim leading and trailing backslashes from namespace, allowing "\Parent\Child", "Parent\Child\" and "\Parent\Child\" to be treated the same way. $namespace = trim($namespace, '\\'); // If the namespace is not yet registered or we have an explicit reset flag then set the path. if ($reset || !isset(self::$namespaces[$namespace])) { self::$namespaces[$namespace] = [$path]; } else { // Otherwise we want to simply add the path to the namespace. if ($prepend) { array_unshift(self::$namespaces[$namespace], $path); } else { self::$namespaces[$namespace][] = $path; } } } /** * Method to setup the autoloaders for the Joomla Platform. * Since the SPL autoloaders are called in a queue we will add our explicit * class-registration based loader first, then fall back on the autoloader based on conventions. * This will allow people to register a class in a specific location and override platform libraries * as was previously possible. * * @param boolean $enablePsr True to enable autoloading based on PSR-0. * @param boolean $enablePrefixes True to enable prefix based class loading (needed to auto load the Joomla core). * @param boolean $enableClasses True to enable class map based class loading (needed to auto load the Joomla core). * * @return void * * @since 3.1.4 */ public static function setup($enablePsr = true, $enablePrefixes = true, $enableClasses = true) { if ($enableClasses) { // Register the class map based autoloader. spl_autoload_register(['JLoader', 'load']); } if ($enablePrefixes) { // Register the prefix autoloader. spl_autoload_register(['JLoader', '_autoload']); } if ($enablePsr) { // Register the PSR based autoloader. spl_autoload_register(['JLoader', 'loadByPsr']); spl_autoload_register(['JLoader', 'loadByAlias']); } } /** * Method to autoload classes that are namespaced to the PSR-4 standard. * * @param string $class The fully qualified class name to autoload. * * @return boolean True on success, false otherwise. * * @since 3.7.0 * * @deprecated 4.3 will be removed in 6.0 * Use JLoader::loadByPsr instead */ public static function loadByPsr4($class) { return self::loadByPsr($class); } /** * Method to autoload classes that are namespaced to the PSR-4 standard. * * @param string $class The fully qualified class name to autoload. * * @return boolean True on success, false otherwise. * * @since 4.0.0 */ public static function loadByPsr($class) { $class = self::stripFirstBackslash($class); // Find the location of the last NS separator. $pos = strrpos($class, '\\'); // If one is found, we're dealing with a NS'd class. if ($pos !== false) { $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; $className = substr($class, $pos + 1); } else { // If not, no need to parse path. $classPath = null; $className = $class; } $classPath .= $className . '.php'; // Loop through registered namespaces until we find a match. foreach (self::$namespaces as $ns => $paths) { if (strpos($class, "{$ns}\\") === 0) { $nsPath = trim(str_replace('\\', DIRECTORY_SEPARATOR, $ns), DIRECTORY_SEPARATOR); // Loop through paths registered to this namespace until we find a match. foreach ($paths as $path) { $classFilePath = realpath($path . DIRECTORY_SEPARATOR . substr_replace($classPath, '', 0, strlen($nsPath) + 1)); // We do not allow files outside the namespace root to be loaded if (strpos($classFilePath, realpath($path)) !== 0) { continue; } // We check for class_exists to handle case-sensitive file systems if (is_file($classFilePath) && !class_exists($class, false)) { $found = (bool) include_once $classFilePath; if ($found) { self::loadAliasFor($class); } return $found; } } } } return false; } /** * Method to autoload classes that have been aliased using the registerAlias method. * * @param string $class The fully qualified class name to autoload. * * @return void * * @since 3.2 */ public static function loadByAlias($class) { $class = strtolower(self::stripFirstBackslash($class)); if (isset(self::$classAliases[$class])) { // Force auto-load of the regular class class_exists(self::$classAliases[$class], true); // Normally this shouldn't execute as the autoloader will execute applyAliasFor when the regular class is // auto-loaded above. if (!class_exists($class, false) && !interface_exists($class, false)) { class_alias(self::$classAliases[$class], $class); } } } /** * Applies a class alias for an already loaded class, if a class alias was created for it. * * @param string $class We'll look for and register aliases for this (real) class name * * @return void * * @since 3.4 */ public static function applyAliasFor($class) { $class = self::stripFirstBackslash($class); if (isset(self::$classAliasesInverse[$class])) { foreach (self::$classAliasesInverse[$class] as $alias) { class_alias($class, $alias); } } } /** * Autoload a class based on name. * * @param string $class The class to be loaded. * * @return boolean True if the class was loaded, false otherwise. * * @since 1.7.3 */ public static function _autoload($class) { foreach (self::$prefixes as $prefix => $lookup) { $chr = strlen($prefix) < strlen($class) ? $class[strlen($prefix)] : 0; if (strpos($class, $prefix) === 0 && ($chr === strtoupper($chr))) { return self::_load(substr($class, strlen($prefix)), $lookup); } } return false; } /** * Load a class based on name and lookup array. * * @param string $class The class to be loaded (without prefix). * @param array $lookup The array of base paths to use for finding the class file. * * @return boolean True if the class was loaded, false otherwise. * * @since 3.0.0 */ private static function _load($class, $lookup) { // Split the class name into parts separated by camelCase. $parts = preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $class); $partsCount = count($parts); foreach ($lookup as $base) { // Generate the path based on the class name parts. $path = realpath($base . '/' . implode('/', array_map('strtolower', $parts)) . '.php'); // Load the file if it exists and is in the lookup path. if (strpos($path, realpath($base)) === 0 && is_file($path)) { $found = (bool) include_once $path; if ($found) { self::loadAliasFor($class); } return $found; } // Backwards compatibility patch // If there is only one part we want to duplicate that part for generating the path. if ($partsCount === 1) { // Generate the path based on the class name parts. $path = realpath($base . '/' . implode('/', array_map('strtolower', [$parts[0], $parts[0]])) . '.php'); // Load the file if it exists and is in the lookup path. if (strpos($path, realpath($base)) === 0 && is_file($path)) { $found = (bool) include_once $path; if ($found) { self::loadAliasFor($class); } return $found; } } } return false; } /** * Loads the aliases for the given class. * * @param string $class The class. * * @return void * * @since 3.8.0 */ private static function loadAliasFor($class) { if (!array_key_exists($class, self::$classAliasesInverse)) { return; } foreach (self::$classAliasesInverse[$class] as $alias) { // Force auto-load of the alias class class_exists($alias, true); } } /** * Strips the first backslash from the given class if present. * * @param string $class The class to strip the first prefix from. * * @return string The striped class name. * * @since 3.8.0 */ private static function stripFirstBackslash($class) { return $class && $class[0] === '\\' ? substr($class, 1) : $class; } } // Check if jexit is defined first (our unit tests mock this) if (!function_exists('jexit')) { /** * Global application exit. * * This function provides a single exit point for the platform. * * @param mixed $message Exit code or string. Defaults to zero. * * @return void * * @codeCoverageIgnore * @since 1.7.0 */ function jexit($message = 0) { exit($message); } } /** * Intelligent file importer. * * @param string $path A dot syntax path. * @param string $base Search this directory for the class. * * @return boolean True on success. * * @since 1.7.0 * * @deprecated 4.3 will be removed in 6.0 * Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to * register an autoloader for your files. */ function jimport($path, $base = null) { return JLoader::import($path, $base); } PK1A#]A��:�`�`php-encryption/Crypto.phpnu�[���<?php /* * PHP Encryption Library * Copyright (c) 2014, Taylor Hornby * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Web: https://defuse.ca/secure-php-encryption.htm * GitHub: https://github.com/defuse/php-encryption * * WARNING: This encryption library is not a silver bullet. It only provides * symmetric encryption given a uniformly random key. This means you MUST NOT * use an ASCII string like a password as the key parameter, it MUST be * a uniformly random key generated by CreateNewRandomKey(). If you want to * encrypt something with a password, apply a password key derivation function * like PBKDF2 or scrypt with a random salt to generate a key. * * WARNING: Error handling is very important, especially for crypto code! * * How to use this code: * * Generating a Key * ---------------- * try { * $key = Crypto::CreateNewRandomKey(); * // WARNING: Do NOT encode $key with bin2hex() or base64_encode(), * // they may leak the key to the attacker through side channels. * } catch (CryptoTestFailedException $ex) { * die('Cannot safely create a key'); * } catch (CannotPerformOperationException $ex) { * die('Cannot safely create a key'); * } * * Encrypting a Message * -------------------- * $message = "ATTACK AT DAWN"; * try { * $ciphertext = Crypto::Encrypt($message, $key); * } catch (CryptoTestFailedException $ex) { * die('Cannot safely perform encryption'); * } catch (CannotPerformOperationException $ex) { * die('Cannot safely perform decryption'); * } * * Decrypting a Message * -------------------- * try { * $decrypted = Crypto::Decrypt($ciphertext, $key); * } catch (InvalidCiphertextException $ex) { // VERY IMPORTANT * // Either: * // 1. The ciphertext was modified by the attacker, * // 2. The key is wrong, or * // 3. $ciphertext is not a valid ciphertext or was corrupted. * // Assume the worst. * die('DANGER! DANGER! The ciphertext has been tampered with!'); * } catch (CryptoTestFailedException $ex) { * die('Cannot safely perform encryption'); * } catch (CannotPerformOperationException $ex) { * die('Cannot safely perform decryption'); * } */ /* * Raised by Decrypt() when one of the following conditions are met: * - The key is wrong. * - The ciphertext is invalid or not in the correct format. * - The attacker modified the ciphertext. */ class InvalidCiphertextException extends Exception {} /* If you see these, it means it is NOT SAFE to do encryption on your system. */ class CannotPerformOperationException extends Exception {} class CryptoTestFailedException extends Exception {} class Crypto { // Ciphertext format: [____HMAC____][____IV____][____CIPHERTEXT____]. /* Do not change these constants! */ const CIPHER = MCRYPT_RIJNDAEL_128; const KEY_BYTE_SIZE = 16; const CIPHER_MODE = 'cbc'; const HASH_FUNCTION = 'sha256'; const MAC_BYTE_SIZE = 32; const ENCRYPTION_INFO = 'DefusePHP|KeyForEncryption'; const AUTHENTICATION_INFO = 'DefusePHP|KeyForAuthentication'; /* * Use this to generate a random encryption key. */ public static function CreateNewRandomKey() { Crypto::RuntimeTest(); return self::SecureRandom(self::KEY_BYTE_SIZE); } /* * Encrypts a message. * $plaintext is the message to encrypt. * $key is the encryption key, a value generated by CreateNewRandomKey(). * You MUST catch exceptions thrown by this function. See docs above. */ public static function Encrypt($plaintext, $key) { Crypto::RuntimeTest(); if (self::our_strlen($key) !== self::KEY_BYTE_SIZE) { throw new CannotPerformOperationException("Bad key."); } // Generate a sub-key for encryption. $keysize = self::KEY_BYTE_SIZE; $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO); // Generate a random initialization vector. self::EnsureFunctionExists("mcrypt_get_iv_size"); $ivsize = mcrypt_get_iv_size(self::CIPHER, self::CIPHER_MODE); if ($ivsize === FALSE || $ivsize <= 0) { throw new CannotPerformOperationException(); } $iv = self::SecureRandom($ivsize); $ciphertext = $iv . self::PlainEncrypt($plaintext, $ekey, $iv); // Generate a sub-key for authentication and apply the HMAC. $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO); $auth = hash_hmac(self::HASH_FUNCTION, $ciphertext, $akey, true); $ciphertext = $auth . $ciphertext; return $ciphertext; } /* * Decrypts a ciphertext. * $ciphertext is the ciphertext to decrypt. * $key is the key that the ciphertext was encrypted with. * You MUST catch exceptions thrown by this function. See docs above. */ public static function Decrypt($ciphertext, $key) { Crypto::RuntimeTest(); // Extract the HMAC from the front of the ciphertext. if (self::our_strlen($ciphertext) <= self::MAC_BYTE_SIZE) { throw new InvalidCiphertextException(); } $hmac = self::our_substr($ciphertext, 0, self::MAC_BYTE_SIZE); if ($hmac === FALSE) { throw new CannotPerformOperationException(); } $ciphertext = self::our_substr($ciphertext, self::MAC_BYTE_SIZE); if ($ciphertext === FALSE) { throw new CannotPerformOperationException(); } // Regenerate the same authentication sub-key. $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO); if (self::VerifyHMAC($hmac, $ciphertext, $akey)) { // Regenerate the same encryption sub-key. $keysize = self::KEY_BYTE_SIZE; $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO); // Extract the initialization vector from the ciphertext. self::EnsureFunctionExists("mcrypt_get_iv_size"); $ivsize = mcrypt_get_iv_size(self::CIPHER, self::CIPHER_MODE); if ($ivsize === FALSE || $ivsize <= 0) { throw new CannotPerformOperationException(); } if (self::our_strlen($ciphertext) <= $ivsize) { throw new InvalidCiphertextException(); } $iv = self::our_substr($ciphertext, 0, $ivsize); if ($iv === FALSE) { throw new CannotPerformOperationException(); } $ciphertext = self::our_substr($ciphertext, $ivsize); if ($ciphertext === FALSE) { throw new CannotPerformOperationException(); } $plaintext = self::PlainDecrypt($ciphertext, $ekey, $iv); return $plaintext; } else { /* * We throw an exception instead of returning FALSE because we want * a script that doesn't handle this condition to CRASH, instead * of thinking the ciphertext decrypted to the value FALSE. */ throw new InvalidCiphertextException(); } } /* * Runs tests. * Raises CannotPerformOperationException or CryptoTestFailedException if * one of the tests fail. If any tests fails, your system is not capable of * performing encryption, so make sure you fail safe in that case. */ public static function RuntimeTest() { // 0: Tests haven't been run yet. // 1: Tests have passed. // 2: Tests are running right now. // 3: Tests have failed. static $test_state = 0; if ($test_state === 1 || $test_state === 2) { return; } try { $test_state = 2; self::AESTestVector(); self::HMACTestVector(); self::HKDFTestVector(); self::TestEncryptDecrypt(); if (self::our_strlen(Crypto::CreateNewRandomKey()) != self::KEY_BYTE_SIZE) { throw new CryptoTestFailedException(); } if (self::ENCRYPTION_INFO == self::AUTHENTICATION_INFO) { throw new CryptoTestFailedException(); } } catch (CryptoTestFailedException $ex) { // Do this, otherwise it will stay in the "tests are running" state. $test_state = 3; throw $ex; } // Change this to '0' make the tests always re-run (for benchmarking). $test_state = 1; } /* * Never call this method directly! */ private static function PlainEncrypt($plaintext, $key, $iv) { self::EnsureFunctionExists("mcrypt_module_open"); $crypt = mcrypt_module_open(self::CIPHER, "", self::CIPHER_MODE, ""); if ($crypt === FALSE) { throw new CannotPerformOperationException(); } // Pad the plaintext to a multiple of the block size. self::EnsureFunctionExists("mcrypt_enc_get_block_size"); $block = mcrypt_enc_get_block_size($crypt); $pad = $block - (self::our_strlen($plaintext) % $block); $plaintext .= str_repeat(chr($pad), $pad); self::EnsureFunctionExists("mcrypt_generic_init"); $ret = mcrypt_generic_init($crypt, $key, $iv); if ($ret !== 0) { throw new CannotPerformOperationException(); } self::EnsureFunctionExists("mcrypt_generic"); $ciphertext = mcrypt_generic($crypt, $plaintext); self::EnsureFunctionExists("mcrypt_generic_deinit"); $ret = mcrypt_generic_deinit($crypt); if ($ret !== TRUE) { throw new CannotPerformOperationException(); } self::EnsureFunctionExists("mcrypt_module_close"); $ret = mcrypt_module_close($crypt); if ($ret !== TRUE) { throw new CannotPerformOperationException(); } return $ciphertext; } /* * Never call this method directly! */ private static function PlainDecrypt($ciphertext, $key, $iv) { self::EnsureFunctionExists("mcrypt_module_open"); $crypt = mcrypt_module_open(self::CIPHER, "", self::CIPHER_MODE, ""); if ($crypt === FALSE) { throw new CannotPerformOperationException(); } self::EnsureFunctionExists("mcrypt_enc_get_block_size"); $block = mcrypt_enc_get_block_size($crypt); self::EnsureFunctionExists("mcrypt_generic_init"); $ret = mcrypt_generic_init($crypt, $key, $iv); if ($ret !== 0) { throw new CannotPerformOperationException(); } self::EnsureFunctionExists("mdecrypt_generic"); $plaintext = mdecrypt_generic($crypt, $ciphertext); self::EnsureFunctionExists("mcrypt_generic_deinit"); $ret = mcrypt_generic_deinit($crypt); if ($ret !== TRUE) { throw new CannotPerformOperationException(); } self::EnsureFunctionExists("mcrypt_module_close"); $ret = mcrypt_module_close($crypt); if ($ret !== TRUE) { throw new CannotPerformOperationException(); } // Remove the padding. $pad = ord($plaintext[self::our_strlen($plaintext) - 1]); if ($pad <= 0 || $pad > $block) { throw new CannotPerformOperationException(); } $plaintext = self::our_substr($plaintext, 0, self::our_strlen($plaintext) - $pad); if ($plaintext === FALSE) { throw new CannotPerformOperationException(); } return $plaintext; } /* * Returns a random binary string of length $octets bytes. */ private static function SecureRandom($octets) { self::EnsureFunctionExists("mcrypt_create_iv"); $random = mcrypt_create_iv($octets, MCRYPT_DEV_URANDOM); if ($random === FALSE) { throw new CannotPerformOperationException(); } else { return $random; } } /* * Use HKDF to derive multiple keys from one. * http://tools.ietf.org/html/rfc5869 */ private static function HKDF($hash, $ikm, $length, $info = '', $salt = NULL) { // Find the correct digest length as quickly as we can. $digest_length = self::MAC_BYTE_SIZE; if ($hash != self::HASH_FUNCTION) { $digest_length = self::our_strlen(hash_hmac($hash, '', '', true)); } // Sanity-check the desired output length. if (empty($length) || !is_int($length) || $length < 0 || $length > 255 * $digest_length) { throw new CannotPerformOperationException(); } // "if [salt] not provided, is set to a string of HashLen zeroes." if (is_null($salt)) { $salt = str_repeat("\x00", $digest_length); } // HKDF-Extract: // PRK = HMAC-Hash(salt, IKM) // The salt is the HMAC key. $prk = hash_hmac($hash, $ikm, $salt, true); // HKDF-Expand: // This check is useless, but it serves as a reminder to the spec. if (self::our_strlen($prk) < $digest_length) { throw new CannotPerformOperationException(); } // T(0) = '' $t = ''; $last_block = ''; for ($block_index = 1; self::our_strlen($t) < $length; $block_index++) { // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??) $last_block = hash_hmac( $hash, $last_block . $info . chr($block_index), $prk, true ); // T = T(1) | T(2) | T(3) | ... | T(N) $t .= $last_block; } // ORM = first L octets of T $orm = self::our_substr($t, 0, $length); if ($orm === FALSE) { throw new CannotPerformOperationException(); } return $orm; } private static function VerifyHMAC($correct_hmac, $message, $key) { $message_hmac = hash_hmac(self::HASH_FUNCTION, $message, $key, true); // We can't just compare the strings with '==', since it would make // timing attacks possible. We could use the XOR-OR constant-time // comparison algorithm, but I'm not sure if that's good enough way up // here in an interpreted language. So we use the method of HMACing the // strings we want to compare with a random key, then comparing those. // NOTE: This leaks information when the strings are not the same // length, but they should always be the same length here. Enforce it: if (self::our_strlen($correct_hmac) !== self::our_strlen($message_hmac)) { throw new CannotPerformOperationException(); } $blind = self::CreateNewRandomKey(); $message_compare = hash_hmac(self::HASH_FUNCTION, $message_hmac, $blind); $correct_compare = hash_hmac(self::HASH_FUNCTION, $correct_hmac, $blind); return $correct_compare === $message_compare; } private static function TestEncryptDecrypt() { $key = Crypto::CreateNewRandomKey(); $data = "EnCrYpT EvErYThInG\x00\x00"; // Make sure encrypting then decrypting doesn't change the message. $ciphertext = Crypto::Encrypt($data, $key); try { $decrypted = Crypto::Decrypt($ciphertext, $key); } catch (InvalidCiphertextException $ex) { // It's important to catch this and change it into a // CryptoTestFailedException, otherwise a test failure could trick // the user into thinking it's just an invalid ciphertext! throw new CryptoTestFailedException(); } if($decrypted !== $data) { throw new CryptoTestFailedException(); } // Modifying the ciphertext: Appending a string. try { Crypto::Decrypt($ciphertext . "a", $key); throw new CryptoTestFailedException(); } catch (InvalidCiphertextException $e) { /* expected */ } // Modifying the ciphertext: Changing an IV byte. try { $ciphertext[0] = chr((ord($ciphertext[0]) + 1) % 256); Crypto::Decrypt($ciphertext, $key); throw new CryptoTestFailedException(); } catch (InvalidCiphertextException $e) { /* expected */ } // Decrypting with the wrong key. $key = Crypto::CreateNewRandomKey(); $data = "abcdef"; $ciphertext = Crypto::Encrypt($data, $key); $wrong_key = Crypto::CreateNewRandomKey(); try { Crypto::Decrypt($ciphertext, $wrong_key); throw new CryptoTestFailedException(); } catch (InvalidCiphertextException $e) { /* expected */ } // Ciphertext too small (shorter than HMAC). $key = Crypto::CreateNewRandomKey(); $ciphertext = str_repeat("A", self::MAC_BYTE_SIZE - 1); try { Crypto::Decrypt($ciphertext, $key); throw new CryptoTestFailedException(); } catch (InvalidCiphertextException $e) { /* expected */ } } private static function HKDFTestVector() { // HKDF test vectors from RFC 5869 // Test Case 1 $ikm = str_repeat("\x0b", 22); $salt = self::hexToBytes("000102030405060708090a0b0c"); $info = self::hexToBytes("f0f1f2f3f4f5f6f7f8f9"); $length = 42; $okm = self::hexToBytes( "3cb25f25faacd57a90434f64d0362f2a" . "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" . "34007208d5b887185865" ); $computed_okm = self::HKDF("sha256", $ikm, $length, $info, $salt); if ($computed_okm !== $okm) { throw new CryptoTestFailedException(); } // Test Case 7 $ikm = str_repeat("\x0c", 22); $length = 42; $okm = self::hexToBytes( "2c91117204d745f3500d636a62f64f0a" . "b3bae548aa53d423b0d1f27ebba6f5e5" . "673a081d70cce7acfc48" ); $computed_okm = self::HKDF("sha1", $ikm, $length); if ($computed_okm !== $okm) { throw new CryptoTestFailedException(); } } private static function HMACTestVector() { // HMAC test vector From RFC 4231 (Test Case 1) $key = str_repeat("\x0b", 20); $data = "Hi There"; $correct = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"; if (hash_hmac(self::HASH_FUNCTION, $data, $key) != $correct) { throw new CryptoTestFailedException(); } } private static function AESTestVector() { // AES CBC mode test vector from NIST SP 800-38A $key = self::hexToBytes("2b7e151628aed2a6abf7158809cf4f3c"); $iv = self::hexToBytes("000102030405060708090a0b0c0d0e0f"); $plaintext = self::hexToBytes( "6bc1bee22e409f96e93d7e117393172a" . "ae2d8a571e03ac9c9eb76fac45af8e51" . "30c81c46a35ce411e5fbc1191a0a52ef" . "f69f2445df4f9b17ad2b417be66c3710" ); $ciphertext = self::hexToBytes( "7649abac8119b246cee98e9b12e9197d" . "5086cb9b507219ee95db113a917678b2" . "73bed6b8e3c1743b7116e69e22229516" . "3ff1caa1681fac09120eca307586e1a7" . /* Block due to padding. Not from NIST test vector. Padding Block: 10101010101010101010101010101010 Ciphertext: 3ff1caa1681fac09120eca307586e1a7 (+) 2fe1dab1780fbc19021eda206596f1b7 AES 8cb82807230e1321d3fae00d18cc2012 */ "8cb82807230e1321d3fae00d18cc2012" ); $computed_ciphertext = self::PlainEncrypt($plaintext, $key, $iv); if ($computed_ciphertext !== $ciphertext) { throw new CryptoTestFailedException(); } $computed_plaintext = self::PlainDecrypt($ciphertext, $key, $iv); if ($computed_plaintext !== $plaintext) { throw new CryptoTestFailedException(); } } /* WARNING: Do not call this function on secrets. It creates side channels. */ private static function hexToBytes($hex_string) { return pack("H*", $hex_string); } private static function EnsureFunctionExists($name) { if (!function_exists($name)) { throw new CannotPerformOperationException(); } } /* * We need these strlen() and substr() functions because when * 'mbstring.func_overload' is set in php.ini, the standard strlen() and * substr() are replaced by mb_strlen() and mb_substr(). */ private static function our_strlen($str) { if (function_exists('mb_strlen')) { $length = mb_strlen($str, '8bit'); if ($length === FALSE) { throw new CannotPerformOperationException(); } return $length; } else { return strlen($str); } } private static function our_substr($str, $start, $length = NULL) { if (function_exists('mb_substr')) { // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP // 5.3, so we have to find the length ourselves. if (!isset($length)) { if ($start >= 0) { $length = self::our_strlen($str) - $start; } else { $length = -$start; } } return mb_substr($str, $start, $length, '8bit'); } // Unlike mb_substr(), substr() doesn't accept NULL for length if (isset($length)) { return substr($str, $start, $length); } else { return substr($str, $start); } } } /* * We want to catch all uncaught exceptions that come from the Crypto class, * since by default, PHP will leak the key in the stack trace from an uncaught * exception. This is a really ugly hack, but I think it's justified. * * Everything up to handler() getting called should be reliable, so this should * reliably suppress the stack traces. The rest is just a bonus so that we don't * make it impossible to debug other exceptions. * * This bit of code was adapted from: http://stackoverflow.com/a/7939492 */ class CryptoExceptionHandler { private $rethrow = NULL; public function __construct() { set_exception_handler([$this, "handler"]); } public function handler($ex) { if ( $ex instanceof InvalidCiphertextException || $ex instanceof CannotPerformOperationException || $ex instanceof CryptoTestFailedException ) { echo "FATAL ERROR: Uncaught crypto exception. Suppresssing output.\n"; } else { /* Re-throw the exception in the destructor. */ $this->rethrow = $ex; } } public function __destruct() { if ($this->rethrow) { throw $this->rethrow; } } } $crypto_exception_handler_object_dont_touch_me = new CryptoExceptionHandler(); PK1A#]T�F����classmap.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access. defined('_JEXEC') or die; require_once __DIR__ . '/extensions.classmap.php'; JLoader::registerAlias('JRegistry', '\\Joomla\\Registry\\Registry', '6.0'); JLoader::registerAlias('JRegistryFormatIni', '\\Joomla\\Registry\\Format\\Ini', '6.0'); JLoader::registerAlias('JRegistryFormatJson', '\\Joomla\\Registry\\Format\\Json', '6.0'); JLoader::registerAlias('JRegistryFormatPhp', '\\Joomla\\Registry\\Format\\Php', '6.0'); JLoader::registerAlias('JRegistryFormatXml', '\\Joomla\\Registry\\Format\\Xml', '6.0'); JLoader::registerAlias('JStringInflector', '\\Joomla\\String\\Inflector', '6.0'); JLoader::registerAlias('JStringNormalise', '\\Joomla\\String\\Normalise', '6.0'); JLoader::registerAlias('JData', '\\Joomla\\Data\\DataObject', '6.0'); JLoader::registerAlias('JDataSet', '\\Joomla\\Data\\DataSet', '6.0'); JLoader::registerAlias('JDataDumpable', '\\Joomla\\Data\\DumpableInterface', '6.0'); JLoader::registerAlias('JApplicationAdministrator', '\\Joomla\\CMS\\Application\\AdministratorApplication', '6.0'); JLoader::registerAlias('JApplicationHelper', '\\Joomla\\CMS\\Application\\ApplicationHelper', '6.0'); JLoader::registerAlias('JApplicationBase', '\\Joomla\\CMS\\Application\\BaseApplication', '6.0'); JLoader::registerAlias('JApplicationCli', '\\Joomla\\CMS\\Application\\CliApplication', '6.0'); JLoader::registerAlias('JApplicationCms', '\\Joomla\\CMS\\Application\\CMSApplication', '6.0'); JLoader::registerAlias('JApplicationDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '6.0'); JLoader::registerAlias('JApplicationSite', '\\Joomla\\CMS\\Application\\SiteApplication', '6.0'); JLoader::registerAlias('JApplicationWeb', '\\Joomla\\CMS\\Application\\WebApplication', '6.0'); JLoader::registerAlias('JApplicationWebClient', '\\Joomla\\Application\\Web\\WebClient', '6.0'); JLoader::registerAlias('JDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '6.0'); JLoader::registerAlias('JCli', '\\Joomla\\CMS\\Application\\CliApplication', '6.0'); JLoader::registerAlias('JWeb', '\\Joomla\\CMS\\Application\\WebApplication', '4.0'); JLoader::registerAlias('JWebClient', '\\Joomla\\Application\\Web\\WebClient', '4.0'); JLoader::registerAlias('JModelAdmin', '\\Joomla\\CMS\\MVC\\Model\\AdminModel', '6.0'); JLoader::registerAlias('JModelForm', '\\Joomla\\CMS\\MVC\\Model\\FormModel', '6.0'); JLoader::registerAlias('JModelItem', '\\Joomla\\CMS\\MVC\\Model\\ItemModel', '6.0'); JLoader::registerAlias('JModelList', '\\Joomla\\CMS\\MVC\\Model\\ListModel', '6.0'); JLoader::registerAlias('JModelLegacy', '\\Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel', '6.0'); JLoader::registerAlias('JViewCategories', '\\Joomla\\CMS\\MVC\\View\\CategoriesView', '6.0'); JLoader::registerAlias('JViewCategory', '\\Joomla\\CMS\\MVC\\View\\CategoryView', '6.0'); JLoader::registerAlias('JViewCategoryfeed', '\\Joomla\\CMS\\MVC\\View\\CategoryFeedView', '6.0'); JLoader::registerAlias('JViewLegacy', '\\Joomla\\CMS\\MVC\\View\\HtmlView', '6.0'); JLoader::registerAlias('JControllerAdmin', '\\Joomla\\CMS\\MVC\\Controller\\AdminController', '6.0'); JLoader::registerAlias('JControllerLegacy', '\\Joomla\\CMS\\MVC\\Controller\\BaseController', '6.0'); JLoader::registerAlias('JControllerForm', '\\Joomla\\CMS\\MVC\\Controller\\FormController', '6.0'); JLoader::registerAlias('JTableInterface', '\\Joomla\\CMS\\Table\\TableInterface', '6.0'); JLoader::registerAlias('JTable', '\\Joomla\\CMS\\Table\\Table', '6.0'); JLoader::registerAlias('JTableNested', '\\Joomla\\CMS\\Table\\Nested', '6.0'); JLoader::registerAlias('JTableAsset', '\\Joomla\\CMS\\Table\\Asset', '6.0'); JLoader::registerAlias('JTableExtension', '\\Joomla\\CMS\\Table\\Extension', '6.0'); JLoader::registerAlias('JTableLanguage', '\\Joomla\\CMS\\Table\\Language', '6.0'); JLoader::registerAlias('JTableUpdate', '\\Joomla\\CMS\\Table\\Update', '6.0'); JLoader::registerAlias('JTableUpdatesite', '\\Joomla\\CMS\\Table\\UpdateSite', '6.0'); JLoader::registerAlias('JTableUser', '\\Joomla\\CMS\\Table\\User', '6.0'); JLoader::registerAlias('JTableUsergroup', '\\Joomla\\CMS\\Table\\Usergroup', '6.0'); JLoader::registerAlias('JTableViewlevel', '\\Joomla\\CMS\\Table\\ViewLevel', '6.0'); JLoader::registerAlias('JTableContenthistory', '\\Joomla\\CMS\\Table\\ContentHistory', '6.0'); JLoader::registerAlias('JTableContenttype', '\\Joomla\\CMS\\Table\\ContentType', '6.0'); JLoader::registerAlias('JTableCorecontent', '\\Joomla\\CMS\\Table\\CoreContent', '6.0'); JLoader::registerAlias('JTableUcm', '\\Joomla\\CMS\\Table\\Ucm', '6.0'); JLoader::registerAlias('JTableCategory', '\\Joomla\\CMS\\Table\\Category', '6.0'); JLoader::registerAlias('JTableContent', '\\Joomla\\CMS\\Table\\Content', '6.0'); JLoader::registerAlias('JTableMenu', '\\Joomla\\CMS\\Table\\Menu', '6.0'); JLoader::registerAlias('JTableMenuType', '\\Joomla\\CMS\\Table\\MenuType', '6.0'); JLoader::registerAlias('JTableModule', '\\Joomla\\CMS\\Table\\Module', '6.0'); JLoader::registerAlias('JAccess', '\\Joomla\\CMS\\Access\\Access', '6.0'); JLoader::registerAlias('JAccessRule', '\\Joomla\\CMS\\Access\\Rule', '6.0'); JLoader::registerAlias('JAccessRules', '\\Joomla\\CMS\\Access\\Rules', '6.0'); JLoader::registerAlias('JAccessExceptionNotallowed', '\\Joomla\\CMS\\Access\\Exception\\NotAllowed', '6.0'); JLoader::registerAlias('JRule', '\\Joomla\\CMS\\Access\\Rule', '6.0'); JLoader::registerAlias('JRules', '\\Joomla\\CMS\\Access\\Rules', '6.0'); JLoader::registerAlias('JHelp', '\\Joomla\\CMS\\Help\\Help', '6.0'); JLoader::registerAlias('JCaptcha', '\\Joomla\\CMS\\Captcha\\Captcha', '6.0'); JLoader::registerAlias('JLanguageAssociations', '\\Joomla\\CMS\\Language\\Associations', '6.0'); JLoader::registerAlias('JLanguage', '\\Joomla\\CMS\\Language\\Language', '6.0'); JLoader::registerAlias('JLanguageHelper', '\\Joomla\\CMS\\Language\\LanguageHelper', '6.0'); JLoader::registerAlias('JLanguageMultilang', '\\Joomla\\CMS\\Language\\Multilanguage', '6.0'); JLoader::registerAlias('JText', '\\Joomla\\CMS\\Language\\Text', '6.0'); JLoader::registerAlias('JLanguageTransliterate', '\\Joomla\\CMS\\Language\\Transliterate', '6.0'); JLoader::registerAlias('JComponentHelper', '\\Joomla\\CMS\\Component\\ComponentHelper', '6.0'); JLoader::registerAlias('JComponentRecord', '\\Joomla\\CMS\\Component\\ComponentRecord', '6.0'); JLoader::registerAlias('JComponentExceptionMissing', '\\Joomla\\CMS\\Component\\Exception\\MissingComponentException', '6.0'); JLoader::registerAlias('JComponentRouterBase', '\\Joomla\\CMS\\Component\\Router\\RouterBase', '6.0'); JLoader::registerAlias('JComponentRouterInterface', '\\Joomla\\CMS\\Component\\Router\\RouterInterface', '6.0'); JLoader::registerAlias('JComponentRouterLegacy', '\\Joomla\\CMS\\Component\\Router\\RouterLegacy', '6.0'); JLoader::registerAlias('JComponentRouterView', '\\Joomla\\CMS\\Component\\Router\\RouterView', '6.0'); JLoader::registerAlias('JComponentRouterViewconfiguration', '\\Joomla\\CMS\\Component\\Router\\RouterViewConfiguration', '6.0'); JLoader::registerAlias('JComponentRouterRulesMenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\MenuRules', '6.0'); JLoader::registerAlias('JComponentRouterRulesNomenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\NomenuRules', '6.0'); JLoader::registerAlias('JComponentRouterRulesInterface', '\\Joomla\\CMS\\Component\\Router\\Rules\\RulesInterface', '6.0'); JLoader::registerAlias('JComponentRouterRulesStandard', '\\Joomla\\CMS\\Component\\Router\\Rules\\StandardRules', '6.0'); JLoader::registerAlias('JEditor', '\\Joomla\\CMS\\Editor\\Editor', '6.0'); JLoader::registerAlias('JErrorPage', '\\Joomla\\CMS\\Exception\\ExceptionHandler', '6.0'); JLoader::registerAlias('JAuthenticationHelper', '\\Joomla\\CMS\\Helper\\AuthenticationHelper', '6.0'); JLoader::registerAlias('JHelper', '\\Joomla\\CMS\\Helper\\CMSHelper', '6.0'); JLoader::registerAlias('JHelperContent', '\\Joomla\\CMS\\Helper\\ContentHelper', '6.0'); JLoader::registerAlias('JLibraryHelper', '\\Joomla\\CMS\\Helper\\LibraryHelper', '6.0'); JLoader::registerAlias('JHelperMedia', '\\Joomla\\CMS\\Helper\\MediaHelper', '6.0'); JLoader::registerAlias('JModuleHelper', '\\Joomla\\CMS\\Helper\\ModuleHelper', '6.0'); JLoader::registerAlias('JHelperRoute', '\\Joomla\\CMS\\Helper\\RouteHelper', '6.0'); JLoader::registerAlias('JHelperTags', '\\Joomla\\CMS\\Helper\\TagsHelper', '6.0'); JLoader::registerAlias('JHelperUsergroups', '\\Joomla\\CMS\\Helper\\UserGroupsHelper', '6.0'); JLoader::registerAlias('JLayoutBase', '\\Joomla\\CMS\\Layout\\BaseLayout', '6.0'); JLoader::registerAlias('JLayoutFile', '\\Joomla\\CMS\\Layout\\FileLayout', '6.0'); JLoader::registerAlias('JLayoutHelper', '\\Joomla\\CMS\\Layout\\LayoutHelper', '6.0'); JLoader::registerAlias('JLayout', '\\Joomla\\CMS\\Layout\\LayoutInterface', '6.0'); JLoader::registerAlias('JResponseJson', '\\Joomla\\CMS\\Response\\JsonResponse', '6.0'); JLoader::registerAlias('JPlugin', '\\Joomla\\CMS\\Plugin\\CMSPlugin', '6.0'); JLoader::registerAlias('JPluginHelper', '\\Joomla\\CMS\\Plugin\\PluginHelper', '6.0'); JLoader::registerAlias('JMenu', '\\Joomla\\CMS\\Menu\\AbstractMenu', '6.0'); JLoader::registerAlias('JMenuAdministrator', '\\Joomla\\CMS\\Menu\\AdministratorMenu', '6.0'); JLoader::registerAlias('JMenuItem', '\\Joomla\\CMS\\Menu\\MenuItem', '6.0'); JLoader::registerAlias('JMenuSite', '\\Joomla\\CMS\\Menu\\SiteMenu', '6.0'); JLoader::registerAlias('JPagination', '\\Joomla\\CMS\\Pagination\\Pagination', '6.0'); JLoader::registerAlias('JPaginationObject', '\\Joomla\\CMS\\Pagination\\PaginationObject', '6.0'); JLoader::registerAlias('JPathway', '\\Joomla\\CMS\\Pathway\\Pathway', '6.0'); JLoader::registerAlias('JPathwaySite', '\\Joomla\\CMS\\Pathway\\SitePathway', '6.0'); JLoader::registerAlias('JSchemaChangeitem', '\\Joomla\\CMS\\Schema\\ChangeItem', '6.0'); JLoader::registerAlias('JSchemaChangeset', '\\Joomla\\CMS\\Schema\\ChangeSet', '6.0'); JLoader::registerAlias('JSchemaChangeitemMysql', '\\Joomla\\CMS\\Schema\\ChangeItem\\MysqlChangeItem', '6.0'); JLoader::registerAlias('JSchemaChangeitemPostgresql', '\\Joomla\\CMS\\Schema\\ChangeItem\\PostgresqlChangeItem', '6.0'); JLoader::registerAlias('JSchemaChangeitemSqlsrv', '\\Joomla\\CMS\\Schema\\ChangeItem\\SqlsrvChangeItem', '6.0'); JLoader::registerAlias('JUcm', '\\Joomla\\CMS\\UCM\\UCM', '6.0'); JLoader::registerAlias('JUcmBase', '\\Joomla\\CMS\\UCM\\UCMBase', '6.0'); JLoader::registerAlias('JUcmContent', '\\Joomla\\CMS\\UCM\\UCMContent', '6.0'); JLoader::registerAlias('JUcmType', '\\Joomla\\CMS\\UCM\\UCMType', '6.0'); JLoader::registerAlias('JToolbar', '\\Joomla\\CMS\\Toolbar\\Toolbar', '6.0'); JLoader::registerAlias('JToolbarButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '6.0'); JLoader::registerAlias('JToolbarButtonConfirm', '\\Joomla\\CMS\\Toolbar\\Button\\ConfirmButton', '6.0'); JLoader::registerAlias('JToolbarButtonCustom', '\\Joomla\\CMS\\Toolbar\\Button\\CustomButton', '6.0'); JLoader::registerAlias('JToolbarButtonHelp', '\\Joomla\\CMS\\Toolbar\\Button\\HelpButton', '6.0'); JLoader::registerAlias('JToolbarButtonLink', '\\Joomla\\CMS\\Toolbar\\Button\\LinkButton', '6.0'); JLoader::registerAlias('JToolbarButtonPopup', '\\Joomla\\CMS\\Toolbar\\Button\\PopupButton', '6.0'); JLoader::registerAlias('JToolbarButtonSeparator', '\\Joomla\\CMS\\Toolbar\\Button\\SeparatorButton', '6.0'); JLoader::registerAlias('JToolbarButtonStandard', '\\Joomla\\CMS\\Toolbar\\Button\\StandardButton', '6.0'); JLoader::registerAlias('JToolbarHelper', '\\Joomla\\CMS\\Toolbar\\ToolbarHelper', '6.0'); JLoader::registerAlias('JButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '6.0'); JLoader::registerAlias('JVersion', '\\Joomla\\CMS\\Version', '6.0'); JLoader::registerAlias('JAuthentication', '\\Joomla\\CMS\\Authentication\\Authentication', '6.0'); JLoader::registerAlias('JAuthenticationResponse', '\\Joomla\\CMS\\Authentication\\AuthenticationResponse', '6.0'); JLoader::registerAlias('JBrowser', '\\Joomla\\CMS\\Environment\\Browser', '6.0'); JLoader::registerAlias('JAssociationExtensionInterface', '\\Joomla\\CMS\\Association\\AssociationExtensionInterface', '6.0'); JLoader::registerAlias('JAssociationExtensionHelper', '\\Joomla\\CMS\\Association\\AssociationExtensionHelper', '6.0'); JLoader::registerAlias('JDocument', '\\Joomla\\CMS\\Document\\Document', '6.0'); JLoader::registerAlias('JDocumentError', '\\Joomla\\CMS\\Document\\ErrorDocument', '6.0'); JLoader::registerAlias('JDocumentFeed', '\\Joomla\\CMS\\Document\\FeedDocument', '6.0'); JLoader::registerAlias('JDocumentHtml', '\\Joomla\\CMS\\Document\\HtmlDocument', '6.0'); JLoader::registerAlias('JDocumentImage', '\\Joomla\\CMS\\Document\\ImageDocument', '6.0'); JLoader::registerAlias('JDocumentJson', '\\Joomla\\CMS\\Document\\JsonDocument', '6.0'); JLoader::registerAlias('JDocumentOpensearch', '\\Joomla\\CMS\\Document\\OpensearchDocument', '6.0'); JLoader::registerAlias('JDocumentRaw', '\\Joomla\\CMS\\Document\\RawDocument', '6.0'); JLoader::registerAlias('JDocumentRenderer', '\\Joomla\\CMS\\Document\\DocumentRenderer', '6.0'); JLoader::registerAlias('JDocumentXml', '\\Joomla\\CMS\\Document\\XmlDocument', '6.0'); JLoader::registerAlias('JDocumentRendererFeedAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererFeedRss', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererHtmlComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererHtmlHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererHtmlMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererHtmlModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererHtmlModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '6.0'); JLoader::registerAlias('JDocumentRendererAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererRSS', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '4.0'); JLoader::registerAlias('JFeedEnclosure', '\\Joomla\\CMS\\Document\\Feed\\FeedEnclosure', '6.0'); JLoader::registerAlias('JFeedImage', '\\Joomla\\CMS\\Document\\Feed\\FeedImage', '6.0'); JLoader::registerAlias('JFeedItem', '\\Joomla\\CMS\\Document\\Feed\\FeedItem', '6.0'); JLoader::registerAlias('JOpenSearchImage', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchImage', '6.0'); JLoader::registerAlias('JOpenSearchUrl', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchUrl', '6.0'); JLoader::registerAlias('JFilterInput', '\\Joomla\\CMS\\Filter\\InputFilter', '6.0'); JLoader::registerAlias('JFilterOutput', '\\Joomla\\CMS\\Filter\\OutputFilter', '6.0'); JLoader::registerAlias('JHttp', '\\Joomla\\CMS\\Http\\Http', '6.0'); JLoader::registerAlias('JHttpFactory', '\\Joomla\\CMS\\Http\\HttpFactory', '6.0'); JLoader::registerAlias('JHttpResponse', '\\Joomla\\CMS\\Http\\Response', '6.0'); JLoader::registerAlias('JHttpTransport', '\\Joomla\\CMS\\Http\\TransportInterface', '6.0'); JLoader::registerAlias('JHttpTransportCurl', '\\Joomla\\CMS\\Http\\Transport\\CurlTransport', '6.0'); JLoader::registerAlias('JHttpTransportSocket', '\\Joomla\\CMS\\Http\\Transport\\SocketTransport', '6.0'); JLoader::registerAlias('JHttpTransportStream', '\\Joomla\\CMS\\Http\\Transport\\StreamTransport', '6.0'); JLoader::registerAlias('JInstaller', '\\Joomla\\CMS\\Installer\\Installer', '6.0'); JLoader::registerAlias('JInstallerAdapter', '\\Joomla\\CMS\\Installer\\InstallerAdapter', '6.0'); JLoader::registerAlias('JInstallerExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '6.0'); JLoader::registerAlias('JExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '6.0'); JLoader::registerAlias('JInstallerHelper', '\\Joomla\\CMS\\Installer\\InstallerHelper', '6.0'); JLoader::registerAlias('JInstallerScript', '\\Joomla\\CMS\\Installer\\InstallerScript', '6.0'); JLoader::registerAlias('JInstallerManifest', '\\Joomla\\CMS\\Installer\\Manifest', '6.0'); JLoader::registerAlias('JInstallerAdapterComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '6.0'); JLoader::registerAlias('JInstallerComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '6.0'); JLoader::registerAlias('JInstallerFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '6.0'); JLoader::registerAlias('JInstallerLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '6.0'); JLoader::registerAlias('JInstallerLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '6.0'); JLoader::registerAlias('JInstallerModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '6.0'); JLoader::registerAlias('JInstallerPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '6.0'); JLoader::registerAlias('JInstallerPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '6.0'); JLoader::registerAlias('JInstallerAdapterTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '6.0'); JLoader::registerAlias('JInstallerTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '6.0'); JLoader::registerAlias('JInstallerManifestLibrary', '\\Joomla\\CMS\\Installer\\Manifest\\LibraryManifest', '6.0'); JLoader::registerAlias('JInstallerManifestPackage', '\\Joomla\\CMS\\Installer\\Manifest\\PackageManifest', '6.0'); JLoader::registerAlias('JRouterAdministrator', '\\Joomla\\CMS\\Router\\AdministratorRouter', '6.0'); JLoader::registerAlias('JRoute', '\\Joomla\\CMS\\Router\\Route', '6.0'); JLoader::registerAlias('JRouter', '\\Joomla\\CMS\\Router\\Router', '6.0'); JLoader::registerAlias('JRouterSite', '\\Joomla\\CMS\\Router\\SiteRouter', '6.0'); JLoader::registerAlias('JCategories', '\\Joomla\\CMS\\Categories\\Categories', '6.0'); JLoader::registerAlias('JCategoryNode', '\\Joomla\\CMS\\Categories\\CategoryNode', '6.0'); JLoader::registerAlias('JDate', '\\Joomla\\CMS\\Date\\Date', '6.0'); JLoader::registerAlias('JLog', '\\Joomla\\CMS\\Log\\Log', '6.0'); JLoader::registerAlias('JLogEntry', '\\Joomla\\CMS\\Log\\LogEntry', '6.0'); JLoader::registerAlias('JLogLogger', '\\Joomla\\CMS\\Log\\Logger', '6.0'); JLoader::registerAlias('JLogger', '\\Joomla\\CMS\\Log\\Logger', '6.0'); JLoader::registerAlias('JLogLoggerCallback', '\\Joomla\\CMS\\Log\\Logger\\CallbackLogger', '6.0'); JLoader::registerAlias('JLogLoggerDatabase', '\\Joomla\\CMS\\Log\\Logger\\DatabaseLogger', '6.0'); JLoader::registerAlias('JLogLoggerEcho', '\\Joomla\\CMS\\Log\\Logger\\EchoLogger', '6.0'); JLoader::registerAlias('JLogLoggerFormattedtext', '\\Joomla\\CMS\\Log\\Logger\\FormattedtextLogger', '6.0'); JLoader::registerAlias('JLogLoggerMessagequeue', '\\Joomla\\CMS\\Log\\Logger\\MessagequeueLogger', '6.0'); JLoader::registerAlias('JLogLoggerSyslog', '\\Joomla\\CMS\\Log\\Logger\\SyslogLogger', '6.0'); JLoader::registerAlias('JLogLoggerW3c', '\\Joomla\\CMS\\Log\\Logger\\W3cLogger', '6.0'); JLoader::registerAlias('JProfiler', '\\Joomla\\CMS\\Profiler\\Profiler', '6.0'); JLoader::registerAlias('JUri', '\\Joomla\\CMS\\Uri\\Uri', '6.0'); JLoader::registerAlias('JCache', '\\Joomla\\CMS\\Cache\\Cache', '6.0'); JLoader::registerAlias('JCacheController', '\\Joomla\\CMS\\Cache\\CacheController', '6.0'); JLoader::registerAlias('JCacheStorage', '\\Joomla\\CMS\\Cache\\CacheStorage', '6.0'); JLoader::registerAlias('JCacheControllerCallback', '\\Joomla\\CMS\\Cache\\Controller\\CallbackController', '6.0'); JLoader::registerAlias('JCacheControllerOutput', '\\Joomla\\CMS\\Cache\\Controller\\OutputController', '6.0'); JLoader::registerAlias('JCacheControllerPage', '\\Joomla\\CMS\\Cache\\Controller\\PageController', '6.0'); JLoader::registerAlias('JCacheControllerView', '\\Joomla\\CMS\\Cache\\Controller\\ViewController', '6.0'); JLoader::registerAlias('JCacheStorageApcu', '\\Joomla\\CMS\\Cache\\Storage\\ApcuStorage', '6.0'); JLoader::registerAlias('JCacheStorageHelper', '\\Joomla\\CMS\\Cache\\Storage\\CacheStorageHelper', '6.0'); JLoader::registerAlias('JCacheStorageFile', '\\Joomla\\CMS\\Cache\\Storage\\FileStorage', '6.0'); JLoader::registerAlias('JCacheStorageMemcached', '\\Joomla\\CMS\\Cache\\Storage\\MemcachedStorage', '6.0'); JLoader::registerAlias('JCacheStorageRedis', '\\Joomla\\CMS\\Cache\\Storage\\RedisStorage', '6.0'); JLoader::registerAlias('JCacheStorageWincache', '\\Joomla\\CMS\\Cache\\Storage\\WincacheStorage', '6.0'); JLoader::registerAlias('JCacheException', '\\Joomla\\CMS\\Cache\\Exception\\CacheExceptionInterface', '6.0'); JLoader::registerAlias('JCacheExceptionConnecting', '\\Joomla\\CMS\\Cache\\Exception\\CacheConnectingException', '6.0'); JLoader::registerAlias('JCacheExceptionUnsupported', '\\Joomla\\CMS\\Cache\\Exception\\UnsupportedCacheException', '6.0'); JLoader::registerAlias('JSession', '\\Joomla\\CMS\\Session\\Session', '6.0'); JLoader::registerAlias('JUser', '\\Joomla\\CMS\\User\\User', '6.0'); JLoader::registerAlias('JUserHelper', '\\Joomla\\CMS\\User\\UserHelper', '6.0'); JLoader::registerAlias('JForm', '\\Joomla\\CMS\\Form\\Form', '6.0'); JLoader::registerAlias('JFormField', '\\Joomla\\CMS\\Form\\FormField', '6.0'); JLoader::registerAlias('JFormHelper', '\\Joomla\\CMS\\Form\\FormHelper', '6.0'); JLoader::registerAlias('JFormRule', '\\Joomla\\CMS\\Form\\FormRule', '6.0'); JLoader::registerAlias('JFormFieldAccessLevel', '\\Joomla\\CMS\\Form\\Field\\AccesslevelField', '6.0'); JLoader::registerAlias('JFormFieldAliastag', '\\Joomla\\CMS\\Form\\Field\\AliastagField', '6.0'); JLoader::registerAlias('JFormFieldAuthor', '\\Joomla\\CMS\\Form\\Field\\AuthorField', '6.0'); JLoader::registerAlias('JFormFieldCacheHandler', '\\Joomla\\CMS\\Form\\Field\\CachehandlerField', '6.0'); JLoader::registerAlias('JFormFieldCalendar', '\\Joomla\\CMS\\Form\\Field\\CalendarField', '6.0'); JLoader::registerAlias('JFormFieldCaptcha', '\\Joomla\\CMS\\Form\\Field\\CaptchaField', '6.0'); JLoader::registerAlias('JFormFieldCategory', '\\Joomla\\CMS\\Form\\Field\\CategoryField', '6.0'); JLoader::registerAlias('JFormFieldCheckbox', '\\Joomla\\CMS\\Form\\Field\\CheckboxField', '6.0'); JLoader::registerAlias('JFormFieldCheckboxes', '\\Joomla\\CMS\\Form\\Field\\CheckboxesField', '6.0'); JLoader::registerAlias('JFormFieldChromeStyle', '\\Joomla\\CMS\\Form\\Field\\ChromestyleField', '6.0'); JLoader::registerAlias('JFormFieldColor', '\\Joomla\\CMS\\Form\\Field\\ColorField', '6.0'); JLoader::registerAlias('JFormFieldCombo', '\\Joomla\\CMS\\Form\\Field\\ComboField', '6.0'); JLoader::registerAlias('JFormFieldComponentlayout', '\\Joomla\\CMS\\Form\\Field\\ComponentlayoutField', '6.0'); JLoader::registerAlias('JFormFieldComponents', '\\Joomla\\CMS\\Form\\Field\\ComponentsField', '6.0'); JLoader::registerAlias('JFormFieldContenthistory', '\\Joomla\\CMS\\Form\\Field\\ContenthistoryField', '6.0'); JLoader::registerAlias('JFormFieldContentlanguage', '\\Joomla\\CMS\\Form\\Field\\ContentlanguageField', '6.0'); JLoader::registerAlias('JFormFieldContenttype', '\\Joomla\\CMS\\Form\\Field\\ContenttypeField', '6.0'); JLoader::registerAlias('JFormFieldDatabaseConnection', '\\Joomla\\CMS\\Form\\Field\\DatabaseconnectionField', '6.0'); JLoader::registerAlias('JFormFieldEditor', '\\Joomla\\CMS\\Form\\Field\\EditorField', '6.0'); JLoader::registerAlias('JFormFieldEMail', '\\Joomla\\CMS\\Form\\Field\\EmailField', '6.0'); JLoader::registerAlias('JFormFieldFile', '\\Joomla\\CMS\\Form\\Field\\FileField', '6.0'); JLoader::registerAlias('JFormFieldFileList', '\\Joomla\\CMS\\Form\\Field\\FilelistField', '6.0'); JLoader::registerAlias('JFormFieldFolderList', '\\Joomla\\CMS\\Form\\Field\\FolderlistField', '6.0'); JLoader::registerAlias('JFormFieldFrontend_Language', '\\Joomla\\CMS\\Form\\Field\\FrontendlanguageField', '6.0'); JLoader::registerAlias('JFormFieldGroupedList', '\\Joomla\\CMS\\Form\\Field\\GroupedlistField', '6.0'); JLoader::registerAlias('JFormFieldHeadertag', '\\Joomla\\CMS\\Form\\Field\\HeadertagField', '6.0'); JLoader::registerAlias('JFormFieldHidden', '\\Joomla\\CMS\\Form\\Field\\HiddenField', '6.0'); JLoader::registerAlias('JFormFieldImageList', '\\Joomla\\CMS\\Form\\Field\\ImagelistField', '6.0'); JLoader::registerAlias('JFormFieldInteger', '\\Joomla\\CMS\\Form\\Field\\IntegerField', '6.0'); JLoader::registerAlias('JFormFieldLanguage', '\\Joomla\\CMS\\Form\\Field\\LanguageField', '6.0'); JLoader::registerAlias('JFormFieldLastvisitDateRange', '\\Joomla\\CMS\\Form\\Field\\LastvisitdaterangeField', '6.0'); JLoader::registerAlias('JFormFieldLimitbox', '\\Joomla\\CMS\\Form\\Field\\LimitboxField', '6.0'); JLoader::registerAlias('JFormFieldList', '\\Joomla\\CMS\\Form\\Field\\ListField', '6.0'); JLoader::registerAlias('JFormFieldMedia', '\\Joomla\\CMS\\Form\\Field\\MediaField', '6.0'); JLoader::registerAlias('JFormFieldMenu', '\\Joomla\\CMS\\Form\\Field\\MenuField', '6.0'); JLoader::registerAlias('JFormFieldMenuitem', '\\Joomla\\CMS\\Form\\Field\\MenuitemField', '6.0'); JLoader::registerAlias('JFormFieldMeter', '\\Joomla\\CMS\\Form\\Field\\MeterField', '6.0'); JLoader::registerAlias('JFormFieldModulelayout', '\\Joomla\\CMS\\Form\\Field\\ModulelayoutField', '6.0'); JLoader::registerAlias('JFormFieldModuleOrder', '\\Joomla\\CMS\\Form\\Field\\ModuleorderField', '6.0'); JLoader::registerAlias('JFormFieldModulePosition', '\\Joomla\\CMS\\Form\\Field\\ModulepositionField', '6.0'); JLoader::registerAlias('JFormFieldModuletag', '\\Joomla\\CMS\\Form\\Field\\ModuletagField', '6.0'); JLoader::registerAlias('JFormFieldNote', '\\Joomla\\CMS\\Form\\Field\\NoteField', '6.0'); JLoader::registerAlias('JFormFieldNumber', '\\Joomla\\CMS\\Form\\Field\\NumberField', '6.0'); JLoader::registerAlias('JFormFieldOrdering', '\\Joomla\\CMS\\Form\\Field\\OrderingField', '6.0'); JLoader::registerAlias('JFormFieldPassword', '\\Joomla\\CMS\\Form\\Field\\PasswordField', '6.0'); JLoader::registerAlias('JFormFieldPlugins', '\\Joomla\\CMS\\Form\\Field\\PluginsField', '6.0'); JLoader::registerAlias('JFormFieldPlugin_Status', '\\Joomla\\CMS\\Form\\Field\\PluginstatusField', '6.0'); JLoader::registerAlias('JFormFieldPredefinedList', '\\Joomla\\CMS\\Form\\Field\\PredefinedListField', '6.0'); JLoader::registerAlias('JFormFieldRadio', '\\Joomla\\CMS\\Form\\Field\\RadioField', '6.0'); JLoader::registerAlias('JFormFieldRange', '\\Joomla\\CMS\\Form\\Field\\RangeField', '6.0'); JLoader::registerAlias('JFormFieldRedirect_Status', '\\Joomla\\CMS\\Form\\Field\\RedirectStatusField', '6.0'); JLoader::registerAlias('JFormFieldRegistrationDateRange', '\\Joomla\\CMS\\Form\\Field\\RegistrationdaterangeField', '6.0'); JLoader::registerAlias('JFormFieldRules', '\\Joomla\\CMS\\Form\\Field\\RulesField', '6.0'); JLoader::registerAlias('JFormFieldSessionHandler', '\\Joomla\\CMS\\Form\\Field\\SessionhandlerField', '6.0'); JLoader::registerAlias('JFormFieldSpacer', '\\Joomla\\CMS\\Form\\Field\\SpacerField', '6.0'); JLoader::registerAlias('JFormFieldSQL', '\\Joomla\\CMS\\Form\\Field\\SqlField', '6.0'); JLoader::registerAlias('JFormFieldStatus', '\\Joomla\\CMS\\Form\\Field\\StatusField', '6.0'); JLoader::registerAlias('JFormFieldSubform', '\\Joomla\\CMS\\Form\\Field\\SubformField', '6.0'); JLoader::registerAlias('JFormFieldTag', '\\Joomla\\CMS\\Form\\Field\\TagField', '6.0'); JLoader::registerAlias('JFormFieldTel', '\\Joomla\\CMS\\Form\\Field\\TelephoneField', '6.0'); JLoader::registerAlias('JFormFieldTemplatestyle', '\\Joomla\\CMS\\Form\\Field\\TemplatestyleField', '6.0'); JLoader::registerAlias('JFormFieldText', '\\Joomla\\CMS\\Form\\Field\\TextField', '6.0'); JLoader::registerAlias('JFormFieldTextarea', '\\Joomla\\CMS\\Form\\Field\\TextareaField', '6.0'); JLoader::registerAlias('JFormFieldTimezone', '\\Joomla\\CMS\\Form\\Field\\TimezoneField', '6.0'); JLoader::registerAlias('JFormFieldUrl', '\\Joomla\\CMS\\Form\\Field\\UrlField', '6.0'); JLoader::registerAlias('JFormFieldUserActive', '\\Joomla\\CMS\\Form\\Field\\UseractiveField', '6.0'); JLoader::registerAlias('JFormFieldUserGroupList', '\\Joomla\\CMS\\Form\\Field\\UsergrouplistField', '6.0'); JLoader::registerAlias('JFormFieldUserState', '\\Joomla\\CMS\\Form\\Field\\UserstateField', '6.0'); JLoader::registerAlias('JFormFieldUser', '\\Joomla\\CMS\\Form\\Field\\UserField', '6.0'); JLoader::registerAlias('JFormRuleBoolean', '\\Joomla\\CMS\\Form\\Rule\\BooleanRule', '6.0'); JLoader::registerAlias('JFormRuleCalendar', '\\Joomla\\CMS\\Form\\Rule\\CalendarRule', '6.0'); JLoader::registerAlias('JFormRuleCaptcha', '\\Joomla\\CMS\\Form\\Rule\\CaptchaRule', '6.0'); JLoader::registerAlias('JFormRuleColor', '\\Joomla\\CMS\\Form\\Rule\\ColorRule', '6.0'); JLoader::registerAlias('JFormRuleEmail', '\\Joomla\\CMS\\Form\\Rule\\EmailRule', '6.0'); JLoader::registerAlias('JFormRuleEquals', '\\Joomla\\CMS\\Form\\Rule\\EqualsRule', '6.0'); JLoader::registerAlias('JFormRuleNotequals', '\\Joomla\\CMS\\Form\\Rule\\NotequalsRule', '6.0'); JLoader::registerAlias('JFormRuleNumber', '\\Joomla\\CMS\\Form\\Rule\\NumberRule', '6.0'); JLoader::registerAlias('JFormRuleOptions', '\\Joomla\\CMS\\Form\\Rule\\OptionsRule', '6.0'); JLoader::registerAlias('JFormRulePassword', '\\Joomla\\CMS\\Form\\Rule\\PasswordRule', '6.0'); JLoader::registerAlias('JFormRuleRules', '\\Joomla\\CMS\\Form\\Rule\\RulesRule', '6.0'); JLoader::registerAlias('JFormRuleTel', '\\Joomla\\CMS\\Form\\Rule\\TelRule', '6.0'); JLoader::registerAlias('JFormRuleUrl', '\\Joomla\\CMS\\Form\\Rule\\UrlRule', '6.0'); JLoader::registerAlias('JFormRuleUsername', '\\Joomla\\CMS\\Form\\Rule\\UsernameRule', '6.0'); JLoader::registerAlias('JMicrodata', '\\Joomla\\CMS\\Microdata\\Microdata', '6.0'); JLoader::registerAlias('JDatabaseDriver', '\\Joomla\\Database\\DatabaseDriver', '6.0'); JLoader::registerAlias('JDatabaseExporter', '\\Joomla\\Database\\DatabaseExporter', '6.0'); JLoader::registerAlias('JDatabaseFactory', '\\Joomla\\Database\\DatabaseFactory', '6.0'); JLoader::registerAlias('JDatabaseImporter', '\\Joomla\\Database\\DatabaseImporter', '6.0'); JLoader::registerAlias('JDatabaseInterface', '\\Joomla\\Database\\DatabaseInterface', '6.0'); JLoader::registerAlias('JDatabaseIterator', '\\Joomla\\Database\\DatabaseIterator', '6.0'); JLoader::registerAlias('JDatabaseQuery', '\\Joomla\\Database\\DatabaseQuery', '6.0'); JLoader::registerAlias('JDatabaseDriverMysqli', '\\Joomla\\Database\\Mysqli\\MysqliDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverPdo', '\\Joomla\\Database\\Pdo\\PdoDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverPdomysql', '\\Joomla\\Database\\Mysql\\MysqlDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverSqlazure', '\\Joomla\\Database\\Sqlazure\\SqlazureDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverSqlite', '\\Joomla\\Database\\Sqlite\\SqliteDriver', '6.0'); JLoader::registerAlias('JDatabaseDriverSqlsrv', '\\Joomla\\Database\\Sqlsrv\\SqlsrvDriver', '6.0'); JLoader::registerAlias('JDatabaseExceptionConnecting', '\\Joomla\\Database\\Exception\\ConnectionFailureException', '6.0'); JLoader::registerAlias('JDatabaseExceptionExecuting', '\\Joomla\\Database\\Exception\\ExecutionFailureException', '6.0'); JLoader::registerAlias('JDatabaseExceptionUnsupported', '\\Joomla\\Database\\Exception\\UnsupportedAdapterException', '6.0'); JLoader::registerAlias('JDatabaseExporterMysqli', '\\Joomla\\Database\\Mysqli\\MysqliExporter', '6.0'); JLoader::registerAlias('JDatabaseExporterPdomysql', '\\Joomla\\Database\\Mysql\\MysqlExporter', '6.0'); JLoader::registerAlias('JDatabaseExporterPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlExporter', '6.0'); JLoader::registerAlias('JDatabaseImporterMysqli', '\\Joomla\\Database\\Mysqli\\MysqliImporter', '6.0'); JLoader::registerAlias('JDatabaseImporterPdomysql', '\\Joomla\\Database\\Mysql\\MysqlImporter', '6.0'); JLoader::registerAlias('JDatabaseImporterPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlImporter', '6.0'); JLoader::registerAlias('JDatabaseQueryElement', '\\Joomla\\Database\\Query\\QueryElement', '6.0'); JLoader::registerAlias('JDatabaseQueryLimitable', '\\Joomla\\Database\\Query\\LimitableInterface', '6.0'); JLoader::registerAlias('JDatabaseQueryPreparable', '\\Joomla\\Database\\Query\\PreparableInterface', '6.0'); JLoader::registerAlias('JDatabaseQueryMysqli', '\\Joomla\\Database\\Mysqli\\MysqliQuery', '6.0'); JLoader::registerAlias('JDatabaseQueryPdo', '\\Joomla\\Database\\Pdo\\PdoQuery', '6.0'); JLoader::registerAlias('JDatabaseQueryPdomysql', '\\Joomla\\Database\\Mysql\\MysqlQuery', '6.0'); JLoader::registerAlias('JDatabaseQueryPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlQuery', '6.0'); JLoader::registerAlias('JDatabaseQuerySqlazure', '\\Joomla\\Database\\Sqlazure\\SqlazureQuery', '6.0'); JLoader::registerAlias('JDatabaseQuerySqlite', '\\Joomla\\Database\\Sqlite\\SqliteQuery', '6.0'); JLoader::registerAlias('JDatabaseQuerySqlsrv', '\\Joomla\\Database\\Sqlsrv\\SqlsrvQuery', '6.0'); JLoader::registerAlias('JFactory', '\\Joomla\\CMS\\Factory', '6.0'); JLoader::registerAlias('JMail', '\\Joomla\\CMS\\Mail\\Mail', '6.0'); JLoader::registerAlias('JMailHelper', '\\Joomla\\CMS\\Mail\\MailHelper', '6.0'); JLoader::registerAlias('JClientHelper', '\\Joomla\\CMS\\Client\\ClientHelper', '6.0'); JLoader::registerAlias('JClientFtp', '\\Joomla\\CMS\\Client\\FtpClient', '6.0'); JLoader::registerAlias('JFTP', '\\Joomla\\CMS\\Client\\FtpClient', '4.0'); JLoader::registerAlias('JClientLdap', '\\Joomla\\Ldap\\LdapClient', '6.0'); JLoader::registerAlias('JLDAP', '\\Joomla\\Ldap\\LdapClient', '4.0'); JLoader::registerAlias('JUpdate', '\\Joomla\\CMS\\Updater\\Update', '6.0'); JLoader::registerAlias('JUpdateAdapter', '\\Joomla\\CMS\\Updater\\UpdateAdapter', '6.0'); JLoader::registerAlias('JUpdater', '\\Joomla\\CMS\\Updater\\Updater', '6.0'); JLoader::registerAlias('JUpdaterCollection', '\\Joomla\\CMS\\Updater\\Adapter\\CollectionAdapter', '6.0'); JLoader::registerAlias('JUpdaterExtension', '\\Joomla\\CMS\\Updater\\Adapter\\ExtensionAdapter', '6.0'); JLoader::registerAlias('JCrypt', '\\Joomla\\CMS\\Crypt\\Crypt', '6.0'); JLoader::registerAlias('JCryptCipher', '\\Joomla\\Crypt\\CipherInterface', '6.0'); JLoader::registerAlias('JCryptKey', '\\Joomla\\Crypt\\Key', '6.0'); JLoader::registerAlias('\\Joomla\\CMS\\Crypt\\CipherInterface', '\\Joomla\\Crypt\\CipherInterface', '6.0'); JLoader::registerAlias('\\Joomla\\CMS\\Crypt\\Key', '\\Joomla\\Crypt\\Key', '6.0'); JLoader::registerAlias('JCryptCipherCrypto', '\\Joomla\\CMS\\Crypt\\Cipher\\CryptoCipher', '6.0'); JLoader::registerAlias('JStringPunycode', '\\Joomla\\CMS\\String\\PunycodeHelper', '6.0'); JLoader::registerAlias('JBuffer', '\\Joomla\\CMS\\Utility\\BufferStreamHandler', '6.0'); JLoader::registerAlias('JUtility', '\\Joomla\\CMS\\Utility\\Utility', '6.0'); JLoader::registerAlias('JInputCli', '\\Joomla\\CMS\\Input\\Cli', '6.0'); JLoader::registerAlias('JInputCookie', '\\Joomla\\CMS\\Input\\Cookie', '6.0'); JLoader::registerAlias('JInputFiles', '\\Joomla\\CMS\\Input\\Files', '6.0'); JLoader::registerAlias('JInput', '\\Joomla\\CMS\\Input\\Input', '6.0'); JLoader::registerAlias('JInputJSON', '\\Joomla\\CMS\\Input\\Json', '6.0'); JLoader::registerAlias('JFeed', '\\Joomla\\CMS\\Feed\\Feed', '6.0'); JLoader::registerAlias('JFeedEntry', '\\Joomla\\CMS\\Feed\\FeedEntry', '6.0'); JLoader::registerAlias('JFeedFactory', '\\Joomla\\CMS\\Feed\\FeedFactory', '6.0'); JLoader::registerAlias('JFeedLink', '\\Joomla\\CMS\\Feed\\FeedLink', '6.0'); JLoader::registerAlias('JFeedParser', '\\Joomla\\CMS\\Feed\\FeedParser', '6.0'); JLoader::registerAlias('JFeedPerson', '\\Joomla\\CMS\\Feed\\FeedPerson', '6.0'); JLoader::registerAlias('JFeedParserAtom', '\\Joomla\\CMS\\Feed\\Parser\\AtomParser', '6.0'); JLoader::registerAlias('JFeedParserNamespace', '\\Joomla\\CMS\\Feed\\Parser\\NamespaceParserInterface', '6.0'); JLoader::registerAlias('JFeedParserRss', '\\Joomla\\CMS\\Feed\\Parser\\RssParser', '6.0'); JLoader::registerAlias('JFeedParserRssItunes', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\ItunesRssParser', '6.0'); JLoader::registerAlias('JFeedParserRssMedia', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\MediaRssParser', '6.0'); JLoader::registerAlias('JImage', '\\Joomla\\CMS\\Image\\Image', '6.0'); JLoader::registerAlias('JImageFilter', '\\Joomla\\CMS\\Image\\ImageFilter', '6.0'); JLoader::registerAlias('JImageFilterBackgroundfill', '\\Joomla\\CMS\\Image\\Filter\\Backgroundfill', '6.0'); JLoader::registerAlias('JImageFilterBrightness', '\\Joomla\\CMS\\Image\\Filter\\Brightness', '6.0'); JLoader::registerAlias('JImageFilterContrast', '\\Joomla\\CMS\\Image\\Filter\\Contrast', '6.0'); JLoader::registerAlias('JImageFilterEdgedetect', '\\Joomla\\CMS\\Image\\Filter\\Edgedetect', '6.0'); JLoader::registerAlias('JImageFilterEmboss', '\\Joomla\\CMS\\Image\\Filter\\Emboss', '6.0'); JLoader::registerAlias('JImageFilterNegate', '\\Joomla\\CMS\\Image\\Filter\\Negate', '6.0'); JLoader::registerAlias('JImageFilterSmooth', '\\Joomla\\CMS\\Image\\Filter\\Smooth', '6.0'); JLoader::registerAlias('JObject', '\\Joomla\\CMS\\Object\\CMSObject', '6.0'); JLoader::registerAlias('JExtensionHelper', '\\Joomla\\CMS\\Extension\\ExtensionHelper', '6.0'); JLoader::registerAlias('JHtml', '\\Joomla\\CMS\\HTML\\HTMLHelper', '6.0'); JLoader::registerAlias('\\Joomla\\Application\\Cli\\CliInput', '\\Joomla\\CMS\\Application\\CLI\\CliInput', '6.0'); JLoader::registerAlias('\\Joomla\\Application\\Cli\\CliOutput', '\\Joomla\\CMS\\Application\\CLI\\CliOutput', '6.0'); JLoader::registerAlias('\\Joomla\\Application\\Cli\\ColorStyle', '\\Joomla\\CMS\\Application\\CLI\\ColorStyle', '6.0'); JLoader::registerAlias('\\Joomla\\Application\\Cli\\Output\\Stdout', '\\Joomla\\CMS\\Application\\CLI\\Output\\Stdout', '6.0'); JLoader::registerAlias('\\Joomla\\Application\\Cli\\Output\\Xml', '\\Joomla\\CMS\\Application\\CLI\\Output\\Xml', '6.0'); JLoader::registerAlias( '\\Joomla\\Application\\Cli\\Output\\Processor\\ColorProcessor', '\\Joomla\\CMS\\Application\\CLI\\Output\\Processor\\ColorProcessor', '6.0' ); JLoader::registerAlias( '\\Joomla\\Application\\Cli\\Output\\Processor\\ProcessorInterface', '\\Joomla\\CMS\\Application\\CLI\\Output\\Processor\\ProcessorInterface', '6.0' ); JLoader::registerAlias('JFile', '\\Joomla\\CMS\\Filesystem\\File', '6.0'); JLoader::registerAlias('JFolder', '\\Joomla\\CMS\\Filesystem\\Folder', '6.0'); JLoader::registerAlias('JFilesystemHelper', '\\Joomla\\CMS\\Filesystem\\FilesystemHelper', '6.0'); JLoader::registerAlias('JFilesystemPatcher', '\\Joomla\\CMS\\Filesystem\\Patcher', '6.0'); JLoader::registerAlias('JPath', '\\Joomla\\CMS\\Filesystem\\Path', '6.0'); JLoader::registerAlias('JStream', '\\Joomla\\CMS\\Filesystem\\Stream', '6.0'); JLoader::registerAlias('JStreamString', '\\Joomla\\CMS\\Filesystem\\Streams\\StreamString', '6.0'); JLoader::registerAlias('JStringController', '\\Joomla\\CMS\\Filesystem\\Support\\StringController', '6.0'); JLoader::registerAlias('JClassLoader', '\\Joomla\\CMS\\Autoload\\ClassLoader', '6.0'); JLoader::registerAlias('JFormFilterInt_Array', '\\Joomla\\CMS\\Form\\Filter\\IntarrayFilter', '6.0'); JLoader::registerAlias('JAdapter', '\\Joomla\\CMS\\Adapter\\Adapter', '6.0'); JLoader::registerAlias('JAdapterInstance', '\\Joomla\\CMS\\Adapter\\AdapterInstance', '6.0'); JLoader::registerAlias('JHtmlAccess', '\\Joomla\\CMS\\HTML\\Helpers\\Access', '6.0'); JLoader::registerAlias('JHtmlActionsDropdown', '\\Joomla\\CMS\\HTML\\Helpers\\ActionsDropdown', '6.0'); JLoader::registerAlias('JHtmlAdminLanguage', '\\Joomla\\CMS\\HTML\\Helpers\\AdminLanguage', '6.0'); JLoader::registerAlias('JHtmlBehavior', '\\Joomla\\CMS\\HTML\\Helpers\\Behavior', '6.0'); JLoader::registerAlias('JHtmlBootstrap', '\\Joomla\\CMS\\HTML\\Helpers\\Bootstrap', '6.0'); JLoader::registerAlias('JHtmlCategory', '\\Joomla\\CMS\\HTML\\Helpers\\Category', '6.0'); JLoader::registerAlias('JHtmlContent', '\\Joomla\\CMS\\HTML\\Helpers\\Content', '6.0'); JLoader::registerAlias('JHtmlContentlanguage', '\\Joomla\\CMS\\HTML\\Helpers\\ContentLanguage', '6.0'); JLoader::registerAlias('JHtmlDate', '\\Joomla\\CMS\\HTML\\Helpers\\Date', '6.0'); JLoader::registerAlias('JHtmlDebug', '\\Joomla\\CMS\\HTML\\Helpers\\Debug', '6.0'); JLoader::registerAlias('JHtmlDraggablelist', '\\Joomla\\CMS\\HTML\\Helpers\\DraggableList', '6.0'); JLoader::registerAlias('JHtmlDropdown', '\\Joomla\\CMS\\HTML\\Helpers\\Dropdown', '6.0'); JLoader::registerAlias('JHtmlEmail', '\\Joomla\\CMS\\HTML\\Helpers\\Email', '6.0'); JLoader::registerAlias('JHtmlForm', '\\Joomla\\CMS\\HTML\\Helpers\\Form', '6.0'); JLoader::registerAlias('JHtmlFormbehavior', '\\Joomla\\CMS\\HTML\\Helpers\\FormBehavior', '6.0'); JLoader::registerAlias('JHtmlGrid', '\\Joomla\\CMS\\HTML\\Helpers\\Grid', '6.0'); JLoader::registerAlias('JHtmlIcons', '\\Joomla\\CMS\\HTML\\Helpers\\Icons', '6.0'); JLoader::registerAlias('JHtmlJGrid', '\\Joomla\\CMS\\HTML\\Helpers\\JGrid', '6.0'); JLoader::registerAlias('JHtmlJquery', '\\Joomla\\CMS\\HTML\\Helpers\\Jquery', '6.0'); JLoader::registerAlias('JHtmlLinks', '\\Joomla\\CMS\\HTML\\Helpers\\Links', '6.0'); JLoader::registerAlias('JHtmlList', '\\Joomla\\CMS\\HTML\\Helpers\\ListHelper', '6.0'); JLoader::registerAlias('JHtmlMenu', '\\Joomla\\CMS\\HTML\\Helpers\\Menu', '6.0'); JLoader::registerAlias('JHtmlNumber', '\\Joomla\\CMS\\HTML\\Helpers\\Number', '6.0'); JLoader::registerAlias('JHtmlSearchtools', '\\Joomla\\CMS\\HTML\\Helpers\\SearchTools', '6.0'); JLoader::registerAlias('JHtmlSelect', '\\Joomla\\CMS\\HTML\\Helpers\\Select', '6.0'); JLoader::registerAlias('JHtmlSidebar', '\\Joomla\\CMS\\HTML\\Helpers\\Sidebar', '6.0'); JLoader::registerAlias('JHtmlSortableList', '\\Joomla\\CMS\\HTML\\Helpers\\SortableList', '6.0'); JLoader::registerAlias('JHtmlString', '\\Joomla\\CMS\\HTML\\Helpers\\StringHelper', '6.0'); JLoader::registerAlias('JHtmlTag', '\\Joomla\\CMS\\HTML\\Helpers\\Tag', '6.0'); JLoader::registerAlias('JHtmlTel', '\\Joomla\\CMS\\HTML\\Helpers\\Telephone', '6.0'); JLoader::registerAlias('JHtmlUser', '\\Joomla\\CMS\\HTML\\Helpers\\User', '6.0'); PK1A#]^ª��� web.confignu�[���<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="*" /> </authorization> </system.web> </configuration> PK1A#]����xxphpass/PasswordHash.phpnu�[���<?php # # Portable PHP password hashing framework. # # Version 0.5 / genuine. # # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. # # There's absolutely no warranty. # # The homepage URL for this framework is: # # http://www.openwall.com/phpass/ # # Please be sure to update the Version line if you edit this file in any way. # It is suggested that you leave the main version number intact, but indicate # your project name (after the slash) and add your own revision information. # # Please do not change the "private" password hashing method implemented in # here, thereby making your hashes incompatible. However, if you must, please # change the hash type identifier (the "$P$") to something different. # # Obviously, since this code is in the public domain, the above are not # requirements (there can be none), but merely suggestions. # class PasswordHash { var $itoa64; var $iteration_count_log2; var $portable_hashes; var $random_state; function __construct($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) $iteration_count_log2 = 8; $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $this->random_state = microtime(); if (function_exists('getmypid')) $this->random_state .= getmypid(); } function PasswordHash($iteration_count_log2, $portable_hashes) { self::__construct($iteration_count_log2, $portable_hashes); } function get_random_bytes($count) { $output = ''; if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) { $output = fread($fh, $count); fclose($fh); } if (strlen($output) < $count) { $output = ''; for ($i = 0; $i < $count; $i += 16) { $this->random_state = md5(microtime() . $this->random_state); $output .= md5($this->random_state, TRUE); } $output = substr($output, 0, $count); } return $output; } function encode64($input, $count) { $output = ''; $i = 0; do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; if ($i < $count) $value |= ord($input[$i]) << 8; $output .= $this->itoa64[($value >> 6) & 0x3f]; if ($i++ >= $count) break; if ($i < $count) $value |= ord($input[$i]) << 16; $output .= $this->itoa64[($value >> 12) & 0x3f]; if ($i++ >= $count) break; $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); return $output; } function gensalt_private($input) { $output = '$P$'; $output .= $this->itoa64[min($this->iteration_count_log2 + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; $output .= $this->encode64($input, 6); return $output; } function crypt_private($password, $setting) { $output = '*0'; if (substr($setting, 0, 2) === $output) $output = '*1'; $id = substr($setting, 0, 3); # We use "$P$", phpBB3 uses "$H$" for the same thing if ($id !== '$P$' && $id !== '$H$') return $output; $count_log2 = strpos($this->itoa64, $setting[3]); if ($count_log2 < 7 || $count_log2 > 30) return $output; $count = 1 << $count_log2; $salt = substr($setting, 4, 8); if (strlen($salt) !== 8) return $output; # We were kind of forced to use MD5 here since it's the only # cryptographic primitive that was available in all versions # of PHP in use. To implement our own low-level crypto in PHP # would have resulted in much worse performance and # consequently in lower iteration counts and hashes that are # quicker to crack (by non-PHP code). $hash = md5($salt . $password, TRUE); do { $hash = md5($hash . $password, TRUE); } while (--$count); $output = substr($setting, 0, 12); $output .= $this->encode64($hash, 16); return $output; } function gensalt_blowfish($input) { # This one needs to use a different order of characters and a # different encoding scheme from the one in encode64() above. # We care because the last character in our encoded string will # only represent 2 bits. While two known implementations of # bcrypt will happily accept and correct a salt string which # has the 4 unused bits set to non-zero, we do not want to take # chances and we also do not want to waste an additional byte # of entropy. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $this->iteration_count_log2 / 10); $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } function HashPassword($password) { $random = ''; if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) { $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); if (strlen($hash) === 60) return $hash; } if (strlen($random) < 6) $random = $this->get_random_bytes(6); $hash = $this->crypt_private($password, $this->gensalt_private($random)); if (strlen($hash) === 34) return $hash; # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new # hashes and for validating passwords against existing hashes. return '*'; } function CheckPassword($password, $stored_hash) { $hash = $this->crypt_private($password, $stored_hash); if ($hash[0] === '*') $hash = crypt($password, $stored_hash); # This is not constant-time. In order to keep the code simple, # for timing safety we currently rely on the salts being # unpredictable, which they are at least in the non-fallback # cases (that is, when we use /dev/urandom and bcrypt). return $hash === $stored_hash; } } ?> PK1A#]Eo�? ? cms.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; trigger_error( sprintf( 'Bootstrapping Joomla using the %1$s file is deprecated. Use %2$s instead.', __FILE__, __DIR__ . '/bootstrap.php' ), E_USER_DEPRECATED ); /** * Set the platform root path as a constant if necessary. * * @deprecated 4.4.0 will be removed in 6.0 * Use defined('_JEXEC') or die; to detect if the CMS is loaded correctly **/ if (!defined('JPATH_PLATFORM')) { define('JPATH_PLATFORM', __DIR__); } // Import the library loader if necessary if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; } // Make sure that the Joomla Platform has been successfully loaded if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Platform not loaded.'); } // Create the Composer autoloader $loader = require JPATH_LIBRARIES . '/vendor/autoload.php'; // We need to pull our decorated class loader into memory before unregistering Composer's loader class_exists('\\Joomla\\CMS\\Autoload\\ClassLoader'); $loader->unregister(); // Decorate Composer autoloader spl_autoload_register([new \Joomla\CMS\Autoload\ClassLoader($loader), 'loadClass'], true, true); // Register the class aliases for Framework classes that have replaced their Platform equivalents require_once JPATH_LIBRARIES . '/classmap.php'; // Suppress phar stream wrapper for non .phar files $behavior = new \TYPO3\PharStreamWrapper\Behavior(); \TYPO3\PharStreamWrapper\Manager::initialize( $behavior->withAssertion(new \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor()) ); if (in_array('phar', stream_get_wrappers())) { stream_wrapper_unregister('phar'); stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper'); } // Define the Joomla version if not already defined if (!defined('JVERSION')) { define('JVERSION', (new \Joomla\CMS\Version())->getShortVersion()); } // Register a handler for uncaught exceptions that shows a pretty error page when possible set_exception_handler(['Joomla\CMS\Exception\ExceptionHandler', 'handleException']); // Set up the message queue logger for web requests if (array_key_exists('REQUEST_METHOD', $_SERVER)) { \Joomla\CMS\Log\Log::addLogger(['logger' => 'messagequeue'], \Joomla\CMS\Log\Log::ALL, ['jerror']); } // Register the Crypto lib JLoader::register('Crypto', JPATH_PLATFORM . '/php-encryption/Crypto.php'); PK1A#]��-�&�&namespacemap.phpnu�[���<?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ use Joomla\CMS\Log\Log; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class JNamespacePsr4Map * * @since 4.0.0 */ class JNamespacePsr4Map { /** * Path to the autoloader * * @var string * @since 4.0.0 */ protected $file = JPATH_CACHE . '/autoload_psr4.php'; /** * @var array|null * @since 4.0.0 */ private $cachedMap = null; /** * Check if the file exists * * @return boolean * * @since 4.0.0 */ public function exists() { return is_file($this->file); } /** * Check if the namespace mapping file exists, if not create it * * @return void * * @since 4.0.0 */ public function ensureMapFileExists() { if (!$this->exists()) { $this->create(); } } /** * Create the namespace file * * @return boolean * * @since 4.0.0 */ public function create() { $extensions = array_merge( $this->getNamespaces('component'), $this->getNamespaces('module'), $this->getNamespaces('template'), $this->getNamespaces('plugin'), $this->getNamespaces('library') ); ksort($extensions); $this->writeNamespaceFile($extensions); return true; } /** * Load the PSR4 file * * @return boolean * * @since 4.0.0 */ public function load() { if (!$this->exists()) { $this->create(); } $map = $this->cachedMap ?: require $this->file; $loader = include JPATH_LIBRARIES . '/vendor/autoload.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } return true; } /** * Write the Namespace mapping file * * @param array $elements Array of elements * * @return void * * @since 4.0.0 */ protected function writeNamespaceFile($elements) { $content = []; $content[] = "<?php"; $content[] = 'defined(\'_JEXEC\') or die;'; $content[] = 'return ['; foreach ($elements as $namespace => $path) { $content[] = "\t'" . $namespace . "'" . ' => [' . $path . '],'; } $content[] = '];'; /** * Backup the current error_reporting level and set a new level * * We do this because file_put_contents can raise a Warning if it cannot write the autoload_psr4.php file * and this will output to the response BEFORE the session has started, causing the session start to fail * and ultimately leading us to a 500 Internal Server Error page just because of the output warning, which * we can safely ignore as we can use an in-memory autoload_psr4 map temporarily, and display real errors later. */ $error_reporting = error_reporting(0); // Convert array of lines to string $content = implode("\n", $content); try { File::write($this->file, $content); } catch (Exception $e) { Log::add('Could not save ' . $this->file, Log::WARNING); $map = []; $constants = ['JPATH_ADMINISTRATOR', 'JPATH_API', 'JPATH_SITE', 'JPATH_PLUGINS', 'JPATH_LIBRARIES']; foreach ($elements as $namespace => $path) { foreach ($constants as $constant) { $path = preg_replace(['/^(' . $constant . ")\s\.\s\'/", '/\'$/'], [constant($constant), ''], $path); } $namespace = str_replace('\\\\', '\\', $namespace); $map[$namespace] = [ $path ]; } $this->cachedMap = $map; } // Restore previous value of error_reporting error_reporting($error_reporting); } /** * Get an array of namespaces with their respective path for the given extension type. * * @param string $type The extension type * * @return array * * @since 4.0.0 */ private function getNamespaces(string $type): array { $extensions = []; foreach ($this->getExtensions($type) as $extensionPath => $file) { // Load the manifest file $xml = simplexml_load_file($file, 'SimpleXMLElement', LIBXML_NOERROR); // When invalid, ignore if (!$xml) { continue; } // The namespace node $namespaceNode = $xml->namespace; // The namespace string $namespace = (string) $namespaceNode; // Ignore when the string is empty if (!$namespace) { continue; } // Normalize the namespace string $namespace = str_replace('\\', '\\\\', $namespace) . '\\\\'; $namespacePath = rtrim($extensionPath . '/' . $namespaceNode->attributes()->path, '/'); if ($type === 'plugin' || $type === 'library') { $baseDir = $type === 'plugin' ? 'JPATH_PLUGINS . \'' : 'JPATH_LIBRARIES . \''; $path = substr($namespacePath, strlen($type === 'plugin' ? JPATH_PLUGINS : JPATH_LIBRARIES)); // Set the namespace $extensions[$namespace] = $baseDir . $path . '\''; continue; } // Check if we need to use administrator path $isAdministrator = strpos($namespacePath, JPATH_ADMINISTRATOR) === 0; $path = substr($namespacePath, strlen($isAdministrator ? JPATH_ADMINISTRATOR : JPATH_SITE)); // Add the site path when a component if ($type === 'component') { if (is_dir(JPATH_SITE . $path)) { $extensions[$namespace . 'Site\\\\'] = 'JPATH_SITE . \'' . $path . '\''; } if (is_dir(JPATH_API . $path)) { $extensions[$namespace . 'Api\\\\'] = 'JPATH_API . \'' . $path . '\''; } } // Add the application specific segment when a component or module $baseDir = $isAdministrator ? 'JPATH_ADMINISTRATOR . \'' : 'JPATH_SITE . \''; $realPath = ($isAdministrator ? JPATH_ADMINISTRATOR : JPATH_SITE) . $path; $namespace .= $isAdministrator ? 'Administrator\\\\' : 'Site\\\\'; // Validate if the directory exists if (!is_dir($realPath)) { continue; } // Set the namespace $extensions[$namespace] = $baseDir . $path . '\''; } // Return the namespaces return $extensions; } /** * Returns an array of extensions with their respective paths as keys and manifest paths as values. * * @param string $type The extension type * * @return array * * @since 4.3.0 */ private function getExtensions(string $type): array { $manifests = []; if ($type === 'library') { try { // Scan library manifest directories for XML files foreach (Folder::files(JPATH_MANIFESTS . '/libraries', '\.xml$', true, true) as $file) { // Match manifest to extension directory $manifests[JPATH_LIBRARIES . '/' . File::stripExt(substr($file, strlen(JPATH_MANIFESTS . '/libraries') + 1))] = $file; } } catch (UnexpectedValueException $e) { return []; } return $manifests; } if ($type === 'component') { $directories = [JPATH_ADMINISTRATOR . '/components']; } elseif ($type === 'module') { $directories = [JPATH_SITE . '/modules', JPATH_ADMINISTRATOR . '/modules']; } elseif ($type === 'template') { $directories = [JPATH_SITE . '/templates', JPATH_ADMINISTRATOR . '/templates']; } else { try { $directories = Folder::folders(JPATH_PLUGINS, '.', false, true); } catch (UnexpectedValueException $e) { $directories = []; } } foreach ($directories as $directory) { try { $extensionDirectories = Folder::folders($directory, '.', false, false); } catch (UnexpectedValueException $e) { continue; } foreach ($extensionDirectories as $extension) { // Compile the extension path $extensionPath = $directory . '/' . $extension; if ($type === 'component') { // Strip the com_ from the extension name for components $file = $extensionPath . '/' . substr($extension, 4) . '.xml'; if (!is_file($file)) { $file = $extensionPath . '/' . $extension . '.xml'; } } elseif ($type === 'template') { // Template manifestfiles have a fix filename $file = $extensionPath . '/templateDetails.xml'; } else { $file = $extensionPath . '/' . $extension . '.xml'; } if (is_file($file)) { $manifests[$extensionPath] = $file; } } } return $manifests; } } PK1A#]s=j�99"vendor/phpseclib/phpseclib/LICENSEnu�[���Copyright (c) 2011-2019 TerraFrost and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.PK1A#]5��||Rvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.phpnu�[���<?php /** * GMP Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\GMP; use phpseclib3\Math\BigInteger\Engines\GMP; /** * GMP Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DefaultEngine extends GMP { /** * Performs modular exponentiation. * * @param GMP $x * @param GMP $e * @param GMP $n * @return GMP */ protected static function powModHelper(GMP $x, GMP $e, GMP $n) { $temp = new GMP(); $temp->value = gmp_powm($x->value, $e->value, $n->value); return $x->normalize($temp); } } PK1A#]U��w�@�@Dvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.phpnu�[���<?php /** * GMP BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; use phpseclib3\Exception\BadConfigurationException; /** * GMP Engine. * * @author Jim Wigginton <terrafrost@php.net> */ class GMP extends Engine { /** * Can Bitwise operations be done fast? * * @see parent::bitwise_leftRotate() * @see parent::bitwise_rightRotate() */ const FAST_BITWISE = true; /** * Engine Directory * * @see parent::setModExpEngine */ const ENGINE_DIR = 'GMP'; /** * Test for engine validity * * @return bool * @see parent::__construct() */ public static function isValidEngine() { return extension_loaded('gmp'); } /** * Default constructor * * @param mixed $x integer Base-10 number or base-$base number if $base set. * @param int $base * @see parent::__construct() */ public function __construct($x = 0, $base = 10) { if (!isset(static::$isValidEngine[static::class])) { static::$isValidEngine[static::class] = self::isValidEngine(); } if (!static::$isValidEngine[static::class]) { throw new BadConfigurationException('GMP is not setup correctly on this system'); } if ($x instanceof \GMP) { $this->value = $x; return; } $this->value = gmp_init(0); parent::__construct($x, $base); } /** * Initialize a GMP BigInteger Engine instance * * @param int $base * @see parent::__construct() */ protected function initialize($base) { switch (abs($base)) { case 256: $this->value = gmp_import($this->value); if ($this->is_negative) { $this->value = -$this->value; } break; case 16: $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value; $this->value = gmp_init($temp); break; case 10: $this->value = gmp_init(isset($this->value) ? $this->value : '0'); } } /** * Converts a BigInteger to a base-10 number. * * @return string */ public function toString() { return (string)$this->value; } /** * Converts a BigInteger to a bit string (eg. base-2). * * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * * @param bool $twos_compliment * @return string */ public function toBits($twos_compliment = false) { $hex = $this->toHex($twos_compliment); $bits = gmp_strval(gmp_init($hex, 16), 2); if ($this->precision > 0) { $bits = substr($bits, -$this->precision); } if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { return '0' . $bits; } return $bits; } /** * Converts a BigInteger to a byte string (eg. base-256). * * @param bool $twos_compliment * @return string */ public function toBytes($twos_compliment = false) { if ($twos_compliment) { return $this->toBytesHelper(); } if (gmp_cmp($this->value, gmp_init(0)) == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = gmp_export($this->value); return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($temp, chr(0)); } /** * Adds two BigIntegers. * * @param GMP $y * @return GMP */ public function add(GMP $y) { $temp = new self(); $temp->value = $this->value + $y->value; return $this->normalize($temp); } /** * Subtracts two BigIntegers. * * @param GMP $y * @return GMP */ public function subtract(GMP $y) { $temp = new self(); $temp->value = $this->value - $y->value; return $this->normalize($temp); } /** * Multiplies two BigIntegers. * * @param GMP $x * @return GMP */ public function multiply(GMP $x) { $temp = new self(); $temp->value = $this->value * $x->value; return $this->normalize($temp); } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * @param GMP $y * @return array{GMP, GMP} */ public function divide(GMP $y) { $quotient = new self(); $remainder = new self(); list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); if (gmp_sign($remainder->value) < 0) { $remainder->value = $remainder->value + gmp_abs($y->value); } return [$this->normalize($quotient), $this->normalize($remainder)]; } /** * Compares two numbers. * * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this * is demonstrated thusly: * * $x > $y: $x->compare($y) > 0 * $x < $y: $x->compare($y) < 0 * $x == $y: $x->compare($y) == 0 * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} * * @param GMP $y * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @see self::equals() */ public function compare(GMP $y) { $r = gmp_cmp($this->value, $y->value); if ($r < -1) { $r = -1; } if ($r > 1) { $r = 1; } return $r; } /** * Tests the equality of two numbers. * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * * @param GMP $x * @return bool */ public function equals(GMP $x) { return $this->value == $x->value; } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * * @param GMP $n * @return false|GMP */ public function modInverse(GMP $n) { $temp = new self(); $temp->value = gmp_invert($this->value, $n->value); return $temp->value === false ? false : $this->normalize($temp); } /** * Calculates the greatest common divisor and Bezout's identity. * * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which * combination is returned is dependent upon which mode is in use. See * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * @param GMP $n * @return GMP[] */ public function extendedGCD(GMP $n) { extract(gmp_gcdext($this->value, $n->value)); return [ 'gcd' => $this->normalize(new self($g)), 'x' => $this->normalize(new self($s)), 'y' => $this->normalize(new self($t)) ]; } /** * Calculates the greatest common divisor * * Say you have 693 and 609. The GCD is 21. * * @param GMP $n * @return GMP */ public function gcd(GMP $n) { $r = gmp_gcd($this->value, $n->value); return $this->normalize(new self($r)); } /** * Absolute value. * * @return GMP */ public function abs() { $temp = new self(); $temp->value = gmp_abs($this->value); return $temp; } /** * Logical And * * @param GMP $x * @return GMP */ public function bitwise_and(GMP $x) { $temp = new self(); $temp->value = $this->value & $x->value; return $this->normalize($temp); } /** * Logical Or * * @param GMP $x * @return GMP */ public function bitwise_or(GMP $x) { $temp = new self(); $temp->value = $this->value | $x->value; return $this->normalize($temp); } /** * Logical Exclusive Or * * @param GMP $x * @return GMP */ public function bitwise_xor(GMP $x) { $temp = new self(); $temp->value = $this->value ^ $x->value; return $this->normalize($temp); } /** * Logical Right Shift * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * * @param int $shift * @return GMP */ public function bitwise_rightShift($shift) { // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems) // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF') $temp = new self(); $temp->value = $this->value >> $shift; return $this->normalize($temp); } /** * Logical Left Shift * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * * @param int $shift * @return GMP */ public function bitwise_leftShift($shift) { $temp = new self(); $temp->value = $this->value << $shift; return $this->normalize($temp); } /** * Performs modular exponentiation. * * @param GMP $e * @param GMP $n * @return GMP */ public function modPow(GMP $e, GMP $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * Alias for modPow(). * * @param GMP $e * @param GMP $n * @return GMP */ public function powMod(GMP $e, GMP $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * @param GMP $e * @param GMP $n * @return GMP */ protected function powModInner(GMP $e, GMP $n) { $class = static::$modexpEngine[static::class]; return $class::powModHelper($this, $e, $n); } /** * Normalize * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * * @param GMP $result * @return GMP */ protected function normalize(GMP $result) { $result->precision = $this->precision; $result->bitmask = $this->bitmask; if ($result->bitmask !== false) { $flip = $result->value < 0; if ($flip) { $result->value = -$result->value; } $result->value = $result->value & $result->bitmask->value; if ($flip) { $result->value = -$result->value; } } return $result; } /** * Performs some post-processing for randomRangePrime * * @param Engine $x * @param Engine $min * @param Engine $max * @return GMP */ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max) { $p = gmp_nextprime($x->value); if ($p <= $max->value) { return new self($p); } if ($min->value != $x->value) { $x = new self($x->value - 1); } return self::randomRangePrime($min, $x); } /** * Generate a random prime number between a range * * If there's not a prime within the given range, false will be returned. * * @param GMP $min * @param GMP $max * @return false|GMP */ public static function randomRangePrime(GMP $min, GMP $max) { return self::randomRangePrimeOuter($min, $max); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param GMP $min * @param GMP $max * @return GMP */ public static function randomRange(GMP $min, GMP $max) { return self::randomRangeHelper($min, $max); } /** * Make the current number odd * * If the current number is odd it'll be unchanged. If it's even, one will be added to it. * * @see self::randomPrime() */ protected function make_odd() { gmp_setbit($this->value, 0); } /** * Tests Primality * * @param int $t * @return bool */ protected function testPrimality($t) { return gmp_prob_prime($this->value, $t) != 0; } /** * Calculates the nth root of a biginteger. * * Returns the nth root of a positive biginteger, where n defaults to 2 * * @param int $n * @return GMP */ protected function rootInner($n) { $root = new self(); $root->value = gmp_root($this->value, $n); return $this->normalize($root); } /** * Performs exponentiation. * * @param GMP $n * @return GMP */ public function pow(GMP $n) { $temp = new self(); $temp->value = $this->value ** $n->value; return $this->normalize($temp); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param GMP ...$nums * @return GMP */ public static function min(GMP ...$nums) { return self::minHelper($nums); } /** * Return the maximum BigInteger between an arbitrary number of BigIntegers. * * @param GMP ...$nums * @return GMP */ public static function max(GMP ...$nums) { return self::maxHelper($nums); } /** * Tests BigInteger to see if it is between two integers, inclusive * * @param GMP $min * @param GMP $max * @return bool */ public function between(GMP $min, GMP $max) { return $this->compare($min) >= 0 && $this->compare($max) <= 0; } /** * Create Recurring Modulo Function * * Sometimes it may be desirable to do repeated modulos with the same number outside of * modular exponentiation * * @return callable */ public function createRecurringModuloFunction() { $temp = $this->value; return function (GMP $x) use ($temp) { return new GMP($x->value % $temp); }; } /** * Scan for 1 and right shift by that amount * * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); * * @param GMP $r * @return int */ public static function scan1divide(GMP $r) { $s = gmp_scan1($r->value, 0); $r->value >>= $s; return $s; } /** * Is Odd? * * @return bool */ public function isOdd() { return gmp_testbit($this->value, 0); } /** * Tests if a bit is set * * @return bool */ public function testBit($x) { return gmp_testbit($this->value, $x); } /** * Is Negative? * * @return bool */ public function isNegative() { return gmp_sign($this->value) == -1; } /** * Negate * * Given $k, returns -$k * * @return GMP */ public function negate() { $temp = clone $this; $temp->value = -$this->value; return $temp; } } PK1A#]��GA�A�Dvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.phpnu�[���<?php /** * Pure-PHP BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\BadConfigurationException; /** * Pure-PHP Engine. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PHP extends Engine { /**#@+ * Array constants * * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. * */ /** * $result[self::VALUE] contains the value. */ const VALUE = 0; /** * $result[self::SIGN] contains the sign. */ const SIGN = 1; /**#@-*/ /** * Karatsuba Cutoff * * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? * */ const KARATSUBA_CUTOFF = 25; /** * Can Bitwise operations be done fast? * * @see parent::bitwise_leftRotate() * @see parent::bitwise_rightRotate() */ const FAST_BITWISE = true; /** * Engine Directory * * @see parent::setModExpEngine */ const ENGINE_DIR = 'PHP'; /** * Default constructor * * @param mixed $x integer Base-10 number or base-$base number if $base set. * @param int $base * @return PHP * @see parent::__construct() */ public function __construct($x = 0, $base = 10) { if (!isset(static::$isValidEngine[static::class])) { static::$isValidEngine[static::class] = static::isValidEngine(); } if (!static::$isValidEngine[static::class]) { throw new BadConfigurationException(static::class . ' is not setup correctly on this system'); } $this->value = []; parent::__construct($x, $base); } /** * Initialize a PHP BigInteger Engine instance * * @param int $base * @see parent::__construct() */ protected function initialize($base) { switch (abs($base)) { case 16: $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value; $temp = new static(Strings::hex2bin($x), 256); $this->value = $temp->value; break; case 10: $temp = new static(); $multiplier = new static(); $multiplier->value = [static::MAX10]; $x = $this->value; if ($x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = str_pad( $x, strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN, 0, STR_PAD_LEFT ); while (strlen($x)) { $temp = $temp->multiply($multiplier); $temp = $temp->add(new static($this->int2bytes(substr($x, 0, static::MAX10LEN)), 256)); $x = substr($x, static::MAX10LEN); } $this->value = $temp->value; } } /** * Pads strings so that unpack may be used on them * * @param string $str * @return string */ protected function pad($str) { $length = strlen($str); $pad = 4 - (strlen($str) % 4); return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT); } /** * Converts a BigInteger to a base-10 number. * * @return string */ public function toString() { if (!count($this->value)) { return '0'; } $temp = clone $this; $temp->bitmask = false; $temp->is_negative = false; $divisor = new static(); $divisor->value = [static::MAX10]; $result = ''; while (count($temp->value)) { list($temp, $mod) = $temp->divide($divisor); $result = str_pad( isset($mod->value[0]) ? $mod->value[0] : '', static::MAX10LEN, '0', STR_PAD_LEFT ) . $result; } $result = ltrim($result, '0'); if (empty($result)) { $result = '0'; } if ($this->is_negative) { $result = '-' . $result; } return $result; } /** * Converts a BigInteger to a byte string (eg. base-256). * * @param bool $twos_compliment * @return string */ public function toBytes($twos_compliment = false) { if ($twos_compliment) { return $this->toBytesHelper(); } if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $result = $this->bitwise_small_split(8); $result = implode('', array_map('chr', $result)); return $this->precision > 0 ? str_pad( substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT ) : $result; } /** * Performs addition. * * @param array $x_value * @param bool $x_negative * @param array $y_value * @param bool $y_negative * @return array */ protected static function addHelper(array $x_value, $x_negative, array $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return [ self::VALUE => $y_value, self::SIGN => $y_negative ]; } elseif ($y_size == 0) { return [ self::VALUE => $x_value, self::SIGN => $x_negative ]; } // subtract, if appropriate if ($x_negative != $y_negative) { if ($x_value == $y_value) { return [ self::VALUE => [], self::SIGN => false ]; } $temp = self::subtractHelper($x_value, false, $y_value, false); $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative; return $temp; } if ($x_size < $y_size) { $size = $x_size; $value = $y_value; } else { $size = $y_size; $value = $x_value; } $value[count($value)] = 0; // just in case the carry adds an extra digit $carry = 0; for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) { //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry; $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry; $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum; $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31); $value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) $value[$j] = $temp; } if ($j == $size) { // ie. if $y_size is odd $sum = $x_value[$i] + $y_value[$i] + $carry; $carry = $sum >= static::BASE_FULL; $value[$i] = $carry ? $sum - static::BASE_FULL : $sum; ++$i; // ie. let $i = $j since we've just done $value[$i] } if ($carry) { for (; $value[$i] == static::MAX_DIGIT; ++$i) { $value[$i] = 0; } ++$value[$i]; } return [ self::VALUE => self::trim($value), self::SIGN => $x_negative ]; } /** * Performs subtraction. * * @param array $x_value * @param bool $x_negative * @param array $y_value * @param bool $y_negative * @return array */ public static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return [ self::VALUE => $y_value, self::SIGN => !$y_negative ]; } elseif ($y_size == 0) { return [ self::VALUE => $x_value, self::SIGN => $x_negative ]; } // add, if appropriate (ie. -$x - +$y or +$x - -$y) if ($x_negative != $y_negative) { $temp = self::addHelper($x_value, false, $y_value, false); $temp[self::SIGN] = $x_negative; return $temp; } $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative); if (!$diff) { return [ self::VALUE => [], self::SIGN => false ]; } // switch $x and $y around, if appropriate. if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; $x_negative = !$x_negative; $x_size = count($x_value); $y_size = count($y_value); } // at this point, $x_value should be at least as big as - if not bigger than - $y_value $carry = 0; for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) { $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum; $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31); $x_value[$i] = (int)($sum - static::BASE_FULL * $temp); $x_value[$j] = $temp; } if ($j == $y_size) { // ie. if $y_size is odd $sum = $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { $x_value[$i] = static::MAX_DIGIT; } --$x_value[$i]; } return [ self::VALUE => self::trim($x_value), self::SIGN => $x_negative ]; } /** * Performs multiplication. * * @param array $x_value * @param bool $x_negative * @param array $y_value * @param bool $y_negative * @return array */ protected static function multiplyHelper(array $x_value, $x_negative, array $y_value, $y_negative) { //if ( $x_value == $y_value ) { // return [ // self::VALUE => self::square($x_value), // self::SIGN => $x_sign != $y_value // ]; //} $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { // a 0 is being multiplied return [ self::VALUE => [], self::SIGN => false ]; } return [ self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? self::trim(self::regularMultiply($x_value, $y_value)) : self::trim(self::karatsuba($x_value, $y_value)), self::SIGN => $x_negative != $y_negative ]; } /** * Performs Karatsuba multiplication on two BigIntegers * * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. * * @param array $x_value * @param array $y_value * @return array */ private static function karatsuba(array $x_value, array $y_value) { $m = min(count($x_value) >> 1, count($y_value) >> 1); if ($m < self::KARATSUBA_CUTOFF) { return self::regularMultiply($x_value, $y_value); } $x1 = array_slice($x_value, $m); $x0 = array_slice($x_value, 0, $m); $y1 = array_slice($y_value, $m); $y0 = array_slice($y_value, 0, $m); $z2 = self::karatsuba($x1, $y1); $z0 = self::karatsuba($x0, $y0); $z1 = self::addHelper($x1, false, $x0, false); $temp = self::addHelper($y1, false, $y0, false); $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]); $temp = self::addHelper($z2, false, $z0, false); $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]); $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false); return $xy[self::VALUE]; } /** * Performs long multiplication on two BigIntegers * * Modeled after 'multiply' in MutableBigInteger.java. * * @param array $x_value * @param array $y_value * @return array */ protected static function regularMultiply(array $x_value, array $y_value) { $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { // a 0 is being multiplied return []; } $product_value = self::array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but // doing so would also make the result in one set of unnecessary adds, // since on the outermost loops first pass, $product->value[$k] is going // to always be 0 $carry = 0; for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$j] = (int)($temp - static::BASE_FULL * $carry); } $product_value[$j] = $carry; // the above for loop is what the previous comment was talking about. the // following for loop is the "one with nested for loops" for ($i = 1; $i < $y_length; ++$i) { $carry = 0; for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$k] = (int)($temp - static::BASE_FULL * $carry); } $product_value[$k] = $carry; } return $product_value; } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * @return array{static, static} * @internal This function is based off of * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. */ protected function divideHelper(PHP $y) { if (count($y->value) == 1) { list($q, $r) = $this->divide_digit($this->value, $y->value[0]); $quotient = new static(); $remainder = new static(); $quotient->value = $q; $remainder->value = [$r]; $quotient->is_negative = $this->is_negative != $y->is_negative; return [$this->normalize($quotient), $this->normalize($remainder)]; } $x = clone $this; $y = clone $y; $x_sign = $x->is_negative; $y_sign = $y->is_negative; $x->is_negative = $y->is_negative = false; $diff = $x->compare($y); if (!$diff) { $temp = new static(); $temp->value = [1]; $temp->is_negative = $x_sign != $y_sign; return [$this->normalize($temp), $this->normalize(static::$zero[static::class])]; } if ($diff < 0) { // if $x is negative, "add" $y. if ($x_sign) { $x = $y->subtract($x); } return [$this->normalize(static::$zero[static::class]), $this->normalize($x)]; } // normalize $x and $y as described in HAC 14.23 / 14.24 $msb = $y->value[count($y->value) - 1]; for ($shift = 0; !($msb & static::MSB); ++$shift) { $msb <<= 1; } $x->lshift($shift); $y->lshift($shift); $y_value = &$y->value; $x_max = count($x->value) - 1; $y_max = count($y->value) - 1; $quotient = new static(); $quotient_value = &$quotient->value; $quotient_value = self::array_repeat(0, $x_max - $y_max + 1); static $temp, $lhs, $rhs; if (!isset($temp)) { $temp = new static(); $lhs = new static(); $rhs = new static(); } if (static::class != get_class($temp)) { $temp = new static(); $lhs = new static(); $rhs = new static(); } $temp_value = &$temp->value; $rhs_value = &$rhs->value; // $temp = $y << ($x_max - $y_max-1) in base 2**26 $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value); while ($x->compare($temp) >= 0) { // calculate the "common residue" ++$quotient_value[$x_max - $y_max]; $x = $x->subtract($temp); $x_max = count($x->value) - 1; } for ($i = $x_max; $i >= $y_max + 1; --$i) { $x_value = &$x->value; $x_window = [ isset($x_value[$i]) ? $x_value[$i] : 0, isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0 ]; $y_window = [ $y_value[$y_max], ($y_max > 0) ? $y_value[$y_max - 1] : 0 ]; $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { $quotient_value[$q_index] = static::MAX_DIGIT; } else { $quotient_value[$q_index] = self::safe_divide( $x_window[0] * static::BASE_FULL + $x_window[1], $y_window[0] ); } $temp_value = [$y_window[1], $y_window[0]]; $lhs->value = [$quotient_value[$q_index]]; $lhs = $lhs->multiply($temp); $rhs_value = [$x_window[2], $x_window[1], $x_window[0]]; while ($lhs->compare($rhs) > 0) { --$quotient_value[$q_index]; $lhs->value = [$quotient_value[$q_index]]; $lhs = $lhs->multiply($temp); } $adjust = self::array_repeat(0, $q_index); $temp_value = [$quotient_value[$q_index]]; $temp = $temp->multiply($y); $temp_value = &$temp->value; if (count($temp_value)) { $temp_value = array_merge($adjust, $temp_value); } $x = $x->subtract($temp); if ($x->compare(static::$zero[static::class]) < 0) { $temp_value = array_merge($adjust, $y_value); $x = $x->add($temp); --$quotient_value[$q_index]; } $x_max = count($x_value) - 1; } // unnormalize the remainder $x->rshift($shift); $quotient->is_negative = $x_sign != $y_sign; // calculate the "common residue", if appropriate if ($x_sign) { $y->rshift($shift); $x = $y->subtract($x); } return [$this->normalize($quotient), $this->normalize($x)]; } /** * Divides a BigInteger by a regular integer * * abc / x = a00 / x + b0 / x + c / x * * @param array $dividend * @param int $divisor * @return array */ private static function divide_digit(array $dividend, $divisor) { $carry = 0; $result = []; for ($i = count($dividend) - 1; $i >= 0; --$i) { $temp = static::BASE_FULL * $carry + $dividend[$i]; $result[$i] = self::safe_divide($temp, $divisor); $carry = (int)($temp - $divisor * $result[$i]); } return [$result, $carry]; } /** * Single digit division * * Even if int64 is being used the division operator will return a float64 value * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't * have the precision of int64 this is a problem so, when int64 is being used, * we'll guarantee that the dividend is divisible by first subtracting the remainder. * * @param int $x * @param int $y * @return int */ private static function safe_divide($x, $y) { if (static::BASE === 26) { return (int)($x / $y); } // static::BASE === 31 /** @var int */ return ($x - ($x % $y)) / $y; } /** * Convert an array / boolean to a PHP BigInteger object * * @param array $arr * @return static */ protected function convertToObj(array $arr) { $result = new static(); $result->value = $arr[self::VALUE]; $result->is_negative = $arr[self::SIGN]; return $this->normalize($result); } /** * Normalize * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * * @param PHP $result * @return static */ protected function normalize(PHP $result) { $result->precision = $this->precision; $result->bitmask = $this->bitmask; $value = &$result->value; if (!count($value)) { $result->is_negative = false; return $result; } $value = static::trim($value); if (!empty($result->bitmask->value)) { $length = min(count($value), count($result->bitmask->value)); $value = array_slice($value, 0, $length); for ($i = 0; $i < $length; ++$i) { $value[$i] = $value[$i] & $result->bitmask->value[$i]; } $value = static::trim($value); } return $result; } /** * Compares two numbers. * * @param array $x_value * @param bool $x_negative * @param array $y_value * @param bool $y_negative * @return int * @see static::compare() */ protected static function compareHelper(array $x_value, $x_negative, array $y_value, $y_negative) { if ($x_negative != $y_negative) { return (!$x_negative && $y_negative) ? 1 : -1; } $result = $x_negative ? -1 : 1; if (count($x_value) != count($y_value)) { return (count($x_value) > count($y_value)) ? $result : -$result; } $size = max(count($x_value), count($y_value)); $x_value = array_pad($x_value, $size, 0); $y_value = array_pad($y_value, $size, 0); for ($i = count($x_value) - 1; $i >= 0; --$i) { if ($x_value[$i] != $y_value[$i]) { return ($x_value[$i] > $y_value[$i]) ? $result : -$result; } } return 0; } /** * Absolute value. * * @return PHP */ public function abs() { $temp = new static(); $temp->value = $this->value; return $temp; } /** * Trim * * Removes leading zeros * * @param list<static> $value * @return list<static> */ protected static function trim(array $value) { for ($i = count($value) - 1; $i >= 0; --$i) { if ($value[$i]) { break; } unset($value[$i]); } return $value; } /** * Logical Right Shift * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * * @param int $shift * @return PHP */ public function bitwise_rightShift($shift) { $temp = new static(); // could just replace lshift with this, but then all lshift() calls would need to be rewritten // and I don't want to do that... $temp->value = $this->value; $temp->rshift($shift); return $this->normalize($temp); } /** * Logical Left Shift * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * * @param int $shift * @return PHP */ public function bitwise_leftShift($shift) { $temp = new static(); // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten // and I don't want to do that... $temp->value = $this->value; $temp->lshift($shift); return $this->normalize($temp); } /** * Converts 32-bit integers to bytes. * * @param int $x * @return string */ private static function int2bytes($x) { return ltrim(pack('N', $x), chr(0)); } /** * Array Repeat * * @param int $input * @param int $multiplier * @return array */ protected static function array_repeat($input, $multiplier) { return $multiplier ? array_fill(0, $multiplier, $input) : []; } /** * Logical Left Shift * * Shifts BigInteger's by $shift bits. * * @param int $shift */ protected function lshift($shift) { if ($shift == 0) { return; } $num_digits = (int)($shift / static::BASE); $shift %= static::BASE; $shift = 1 << $shift; $carry = 0; for ($i = 0; $i < count($this->value); ++$i) { $temp = $this->value[$i] * $shift + $carry; $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $this->value[$i] = (int)($temp - $carry * static::BASE_FULL); } if ($carry) { $this->value[count($this->value)] = $carry; } while ($num_digits--) { array_unshift($this->value, 0); } } /** * Logical Right Shift * * Shifts BigInteger's by $shift bits. * * @param int $shift */ protected function rshift($shift) { if ($shift == 0) { return; } $num_digits = (int)($shift / static::BASE); $shift %= static::BASE; $carry_shift = static::BASE - $shift; $carry_mask = (1 << $shift) - 1; if ($num_digits) { $this->value = array_slice($this->value, $num_digits); } $carry = 0; for ($i = count($this->value) - 1; $i >= 0; --$i) { $temp = $this->value[$i] >> $shift | $carry; $carry = ($this->value[$i] & $carry_mask) << $carry_shift; $this->value[$i] = $temp; } $this->value = static::trim($this->value); } /** * Performs modular exponentiation. * * @param PHP $e * @param PHP $n * @return PHP */ protected function powModInner(PHP $e, PHP $n) { try { $class = static::$modexpEngine[static::class]; return $class::powModHelper($this, $e, $n, static::class); } catch (\Exception $err) { return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class); } } /** * Performs squaring * * @param list<static> $x * @return list<static> */ protected static function square(array $x) { return count($x) < 2 * self::KARATSUBA_CUTOFF ? self::trim(self::baseSquare($x)) : self::trim(self::karatsubaSquare($x)); } /** * Performs traditional squaring on two BigIntegers * * Squaring can be done faster than multiplying a number by itself can be. See * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. * * @param array $value * @return array */ protected static function baseSquare(array $value) { if (empty($value)) { return []; } $square_value = self::array_repeat(0, 2 * count($value)); for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $square_value[$i2] = (int)($temp - static::BASE_FULL * $carry); // note how we start from $i+1 instead of 0 as we do in multiplication. for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $square_value[$k] = (int)($temp - static::BASE_FULL * $carry); } // the following line can yield values larger 2**15. at this point, PHP should switch // over to floats. $square_value[$i + $max_index + 1] = $carry; } return $square_value; } /** * Performs Karatsuba "squaring" on two BigIntegers * * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. * * @param array $value * @return array */ protected static function karatsubaSquare(array $value) { $m = count($value) >> 1; if ($m < self::KARATSUBA_CUTOFF) { return self::baseSquare($value); } $x1 = array_slice($value, $m); $x0 = array_slice($value, 0, $m); $z2 = self::karatsubaSquare($x1); $z0 = self::karatsubaSquare($x0); $z1 = self::addHelper($x1, false, $x0, false); $z1 = self::karatsubaSquare($z1[self::VALUE]); $temp = self::addHelper($z2, false, $z0, false); $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]); $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false); return $xx[self::VALUE]; } /** * Make the current number odd * * If the current number is odd it'll be unchanged. If it's even, one will be added to it. * * @see self::randomPrime() */ protected function make_odd() { $this->value[0] |= 1; } /** * Test the number against small primes. * * @see self::isPrime() */ protected function testSmallPrimes() { if ($this->value == [1]) { return false; } if ($this->value == [2]) { return true; } if (~$this->value[0] & 1) { return false; } $value = $this->value; foreach (static::PRIMES as $prime) { list(, $r) = self::divide_digit($value, $prime); if (!$r) { return count($value) == 1 && $value[0] == $prime; } } return true; } /** * Scan for 1 and right shift by that amount * * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); * * @param PHP $r * @return int * @see self::isPrime() */ public static function scan1divide(PHP $r) { $r_value = &$r->value; for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { $temp = ~$r_value[$i] & static::MAX_DIGIT; for ($j = 1; ($temp >> $j) & 1; ++$j) { } if ($j <= static::BASE) { break; } } $s = static::BASE * $i + $j; $r->rshift($s); return $s; } /** * Performs exponentiation. * * @param PHP $n * @return PHP */ protected function powHelper(PHP $n) { if ($n->compare(static::$zero[static::class]) == 0) { return new static(1); } // n^0 = 1 $temp = clone $this; while (!$n->equals(static::$one[static::class])) { $temp = $temp->multiply($this); $n = $n->subtract(static::$one[static::class]); } return $temp; } /** * Is Odd? * * @return bool */ public function isOdd() { return (bool)($this->value[0] & 1); } /** * Tests if a bit is set * * @return bool */ public function testBit($x) { $digit = (int) floor($x / static::BASE); $bit = $x % static::BASE; if (!isset($this->value[$digit])) { return false; } return (bool)($this->value[$digit] & (1 << $bit)); } /** * Is Negative? * * @return bool */ public function isNegative() { return $this->is_negative; } /** * Negate * * Given $k, returns -$k * * @return static */ public function negate() { $temp = clone $this; $temp->is_negative = !$temp->is_negative; return $temp; } /** * Bitwise Split * * Splits BigInteger's into chunks of $split bits * * @param int $split * @return list<static> */ public function bitwise_split($split) { if ($split < 1) { throw new \RuntimeException('Offset must be greater than 1'); } $width = (int)($split / static::BASE); if (!$width) { $arr = $this->bitwise_small_split($split); return array_map(function ($digit) { $temp = new static(); $temp->value = $digit != 0 ? [$digit] : []; return $temp; }, $arr); } $vals = []; $val = $this->value; $i = $overflow = 0; $len = count($val); while ($i < $len) { $digit = []; if (!$overflow) { $digit = array_slice($val, $i, $width); $i += $width; $overflow = $split % static::BASE; if ($overflow) { $mask = (1 << $overflow) - 1; $temp = isset($val[$i]) ? $val[$i] : 0; $digit[] = $temp & $mask; } } else { $remaining = static::BASE - $overflow; $tempsplit = $split - $remaining; $tempwidth = (int)($tempsplit / static::BASE + 1); $digit = array_slice($val, $i, $tempwidth); $i += $tempwidth; $tempoverflow = $tempsplit % static::BASE; if ($tempoverflow) { $tempmask = (1 << $tempoverflow) - 1; $temp = isset($val[$i]) ? $val[$i] : 0; $digit[] = $temp & $tempmask; } $newbits = 0; for ($j = count($digit) - 1; $j >= 0; $j--) { $temp = $digit[$j] & $mask; $digit[$j] = ($digit[$j] >> $overflow) | ($newbits << $remaining); $newbits = $temp; } $overflow = $tempoverflow; $mask = $tempmask; } $temp = new static(); $temp->value = static::trim($digit); $vals[] = $temp; } return array_reverse($vals); } /** * Bitwise Split where $split < static::BASE * * @param int $split * @return list<int> */ private function bitwise_small_split($split) { $vals = []; $val = $this->value; $mask = (1 << $split) - 1; $i = $overflow = 0; $len = count($val); $val[] = 0; $remaining = static::BASE; while ($i != $len) { $digit = $val[$i] & $mask; $val[$i] >>= $split; if (!$overflow) { $remaining -= $split; $overflow = $split <= $remaining ? 0 : $split - $remaining; if (!$remaining) { $i++; $remaining = static::BASE; $overflow = 0; } } elseif (++$i != $len) { $tempmask = (1 << $overflow) - 1; $digit |= ($val[$i] & $tempmask) << $remaining; $val[$i] >>= $overflow; $remaining = static::BASE - $overflow; $overflow = $split <= $remaining ? 0 : $split - $remaining; } $vals[] = $digit; } while ($vals[count($vals) - 1] == 0) { unset($vals[count($vals) - 1]); } return array_reverse($vals); } /** * @return bool */ protected static function testJITOnWindows() { // see https://github.com/php/php-src/issues/11917 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) { $status = opcache_get_status(); if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) { return true; } } return false; } /** * Return the size of a BigInteger in bits * * @return int */ public function getLength() { $max = count($this->value) - 1; return $max != -1 ? $max * static::BASE + intval(ceil(log($this->value[$max] + 1, 2))) : 0; } } PK1A#]�ݙ��Gvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.phpnu�[���<?php /** * Base BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Random; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Math\BigInteger; /** * Base Engine. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Engine implements \JsonSerializable { /* final protected */ const PRIMES = [ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, ]; /** * BigInteger(0) * * @var array<class-string<static>, static> */ protected static $zero = []; /** * BigInteger(1) * * @var array<class-string<static>, static> */ protected static $one = []; /** * BigInteger(2) * * @var array<class-string<static>, static> */ protected static $two = []; /** * Modular Exponentiation Engine * * @var array<class-string<static>, class-string<static>> */ protected static $modexpEngine; /** * Engine Validity Flag * * @var array<class-string<static>, bool> */ protected static $isValidEngine; /** * Holds the BigInteger's value * * @var \GMP|string|array|int */ protected $value; /** * Holds the BigInteger's sign * * @var bool */ protected $is_negative; /** * Precision * * @see static::setPrecision() * @var int */ protected $precision = -1; /** * Precision Bitmask * * @see static::setPrecision() * @var static|false */ protected $bitmask = false; /** * Recurring Modulo Function * * @var callable */ protected $reduce; /** * Mode independent value used for serialization. * * @see self::__sleep() * @see self::__wakeup() * @var string */ protected $hex; /** * Default constructor * * @param int|numeric-string $x integer Base-10 number or base-$base number if $base set. * @param int $base */ public function __construct($x = 0, $base = 10) { if (!array_key_exists(static::class, static::$zero)) { static::$zero[static::class] = null; // Placeholder to prevent infinite loop. static::$zero[static::class] = new static(0); static::$one[static::class] = new static(1); static::$two[static::class] = new static(2); } // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 // '0' is the only value like this per http://php.net/empty if (empty($x) && (abs($base) != 256 || $x !== '0')) { return; } switch ($base) { case -256: case 256: if ($base == -256 && (ord($x[0]) & 0x80)) { $this->value = ~$x; $this->is_negative = true; } else { $this->value = $x; $this->is_negative = false; } $this->initialize($base); if ($this->is_negative) { $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; case -16: case 16: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x); $is_negative = false; if ($base < 0 && hexdec($x[0]) >= 8) { $this->is_negative = $is_negative = true; $x = Strings::bin2hex(~Strings::hex2bin($x)); } $this->value = $x; $this->initialize($base); if ($is_negative) { $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; case -10: case 10: // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) // [^-0-9].*: find any non-numeric characters and then any characters that follow that $this->value = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s', '', $x); if (!strlen($this->value) || $this->value == '-') { $this->value = '0'; } $this->initialize($base); break; case -2: case 2: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = preg_replace('#^([01]*).*#s', '$1', $x); $temp = new static(Strings::bits2bin($x), 128 * $base); // ie. either -16 or +16 $this->value = $temp->value; if ($temp->is_negative) { $this->is_negative = true; } break; default: // base not supported, so we'll let $this == 0 } } /** * Sets engine type. * * Throws an exception if the type is invalid * * @param class-string<Engine> $engine */ public static function setModExpEngine($engine) { $fqengine = '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine; if (!class_exists($fqengine) || !method_exists($fqengine, 'isValidEngine')) { throw new \InvalidArgumentException("$engine is not a valid engine"); } if (!$fqengine::isValidEngine()) { throw new BadConfigurationException("$engine is not setup correctly on this system"); } static::$modexpEngine[static::class] = $fqengine; } /** * Converts a BigInteger to a byte string (eg. base-256). * * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * @return string */ protected function toBytesHelper() { $comparison = $this->compare(new static()); if ($comparison == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = $comparison < 0 ? $this->add(new static(1)) : $this; $bytes = $temp->toBytes(); if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 $bytes = chr(0); } if (ord($bytes[0]) & 0x80) { $bytes = chr(0) . $bytes; } return $comparison < 0 ? ~$bytes : $bytes; } /** * Converts a BigInteger to a hex string (eg. base-16). * * @param bool $twos_compliment * @return string */ public function toHex($twos_compliment = false) { return Strings::bin2hex($this->toBytes($twos_compliment)); } /** * Converts a BigInteger to a bit string (eg. base-2). * * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * * @param bool $twos_compliment * @return string */ public function toBits($twos_compliment = false) { $hex = $this->toBytes($twos_compliment); $bits = Strings::bin2bits($hex); $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { return '0' . $result; } return $result; } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * * {@internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.} * * @param Engine $n * @return static|false */ protected function modInverseHelper(Engine $n) { // $x mod -$n == $x mod $n. $n = $n->abs(); if ($this->compare(static::$zero[static::class]) < 0) { $temp = $this->abs(); $temp = $temp->modInverse($n); return $this->normalize($n->subtract($temp)); } extract($this->extendedGCD($n)); /** * @var Engine $gcd * @var Engine $x */ if (!$gcd->equals(static::$one[static::class])) { return false; } $x = $x->compare(static::$zero[static::class]) < 0 ? $x->add($n) : $x; return $this->compare(static::$zero[static::class]) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x); } /** * Serialize * * Will be called, automatically, when serialize() is called on a BigInteger object. * * @return array */ public function __sleep() { $this->hex = $this->toHex(true); $vars = ['hex']; if ($this->precision > 0) { $vars[] = 'precision'; } return $vars; } /** * Serialize * * Will be called, automatically, when unserialize() is called on a BigInteger object. * * @return void */ public function __wakeup() { $temp = new static($this->hex, -16); $this->value = $temp->value; $this->is_negative = $temp->is_negative; if ($this->precision > 0) { // recalculate $this->bitmask $this->setPrecision($this->precision); } } /** * JSON Serialize * * Will be called, automatically, when json_encode() is called on a BigInteger object. * * @return array{hex: string, precision?: int] */ #[\ReturnTypeWillChange] public function jsonSerialize() { $result = ['hex' => $this->toHex(true)]; if ($this->precision > 0) { $result['precision'] = $this->precision; } return $result; } /** * Converts a BigInteger to a base-10 number. * * @return string */ public function __toString() { return $this->toString(); } /** * __debugInfo() magic method * * Will be called, automatically, when print_r() or var_dump() are called * * @return array */ public function __debugInfo() { $result = [ 'value' => '0x' . $this->toHex(true), 'engine' => basename(static::class) ]; return $this->precision > 0 ? $result + ['precision' => $this->precision] : $result; } /** * Set Precision * * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * * @param int $bits */ public function setPrecision($bits) { if ($bits < 1) { $this->precision = -1; $this->bitmask = false; return; } $this->precision = $bits; $this->bitmask = static::setBitmask($bits); $temp = $this->normalize($this); $this->value = $temp->value; } /** * Get Precision * * Returns the precision if it exists, -1 if it doesn't * * @return int */ public function getPrecision() { return $this->precision; } /** * Set Bitmask * @return static * @param int $bits * @see self::setPrecision() */ protected static function setBitmask($bits) { return new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); } /** * Logical Not * * @return Engine|string */ public function bitwise_not() { // calculuate "not" without regard to $this->precision // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) $temp = $this->toBytes(); if ($temp == '') { return $this->normalize(static::$zero[static::class]); } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; $msb = decbin(ord($temp[0])); if (strlen($msb) == 8) { $msb = substr($msb, strpos($msb, '0')); } $temp[0] = chr(bindec($msb)); // see if we need to add extra leading 1's $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; $new_bits = $this->precision - $current_bits; if ($new_bits <= 0) { return $this->normalize(new static($temp, 256)); } // generate as many leading 1's as we need to. $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); self::base256_lshift($leading_ones, $current_bits); $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); return $this->normalize(new static($leading_ones | $temp, 256)); } /** * Logical Left Shift * * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. * * @param string $x * @param int $shift * @return void */ protected static function base256_lshift(&$x, $shift) { if ($shift == 0) { return; } $num_bytes = $shift >> 3; // eg. floor($shift/8) $shift &= 7; // eg. $shift % 8 $carry = 0; for ($i = strlen($x) - 1; $i >= 0; --$i) { $temp = ord($x[$i]) << $shift | $carry; $x[$i] = chr($temp); $carry = $temp >> 8; } $carry = ($carry != 0) ? chr($carry) : ''; $x = $carry . $x . str_repeat(chr(0), $num_bytes); } /** * Logical Left Rotate * * Instead of the top x bits being dropped they're appended to the shifted bit string. * * @param int $shift * @return Engine */ public function bitwise_leftRotate($shift) { $bits = $this->toBytes(); if ($this->precision > 0) { $precision = $this->precision; if (static::FAST_BITWISE) { $mask = $this->bitmask->toBytes(); } else { $mask = $this->bitmask->subtract(new static(1)); $mask = $mask->toBytes(); } } else { $temp = ord($bits[0]); for ($i = 0; $temp >> $i; ++$i) { } $precision = 8 * strlen($bits) - 8 + $i; $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); } if ($shift < 0) { $shift += $precision; } $shift %= $precision; if (!$shift) { return clone $this; } $left = $this->bitwise_leftShift($shift); $left = $left->bitwise_and(new static($mask, 256)); $right = $this->bitwise_rightShift($precision - $shift); $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right); return $this->normalize($result); } /** * Logical Right Rotate * * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. * * @param int $shift * @return Engine */ public function bitwise_rightRotate($shift) { return $this->bitwise_leftRotate(-$shift); } /** * Returns the smallest and largest n-bit number * * @param int $bits * @return array{min: static, max: static} */ public static function minMaxBits($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); $max = str_repeat(chr(0xFF), $bytes); $msb = $bits & 7; if ($msb) { $min = chr(1 << ($msb - 1)) . $min; $max = chr((1 << $msb) - 1) . $max; } else { $min[0] = chr(0x80); } return [ 'min' => new static($min, 256), 'max' => new static($max, 256) ]; } /** * Return the size of a BigInteger in bits * * @return int */ public function getLength() { return strlen($this->toBits()); } /** * Return the size of a BigInteger in bytes * * @return int */ public function getLengthInBytes() { return (int) ceil($this->getLength() / 8); } /** * Performs some pre-processing for powMod * * @param Engine $e * @param Engine $n * @return static|false */ protected function powModOuter(Engine $e, Engine $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); if ($e->compare(new static()) < 0) { $e = $e->abs(); $temp = $this->modInverse($n); if ($temp === false) { return false; } return $this->normalize($temp->powModInner($e, $n)); } if ($this->compare($n) > 0) { list(, $temp) = $this->divide($n); return $temp->powModInner($e, $n); } return $this->powModInner($e, $n); } /** * Sliding Window k-ary Modular Exponentiation * * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, * however, this function performs a modular reduction after every multiplication and squaring operation. * As such, this function has the same preconditions that the reductions being used do. * * @template T of Engine * @param Engine $x * @param Engine $e * @param Engine $n * @param class-string<T> $class * @return T */ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class) { static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1 $e_bits = $e->toBits(); $e_length = strlen($e_bits); // calculate the appropriate window size. // $window_size == 3 if $window_ranges is between 25 and 81, for example. for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { } $n_value = $n->value; if (method_exists(static::class, 'generateCustomReduction')) { static::generateCustomReduction($n, $class); } // precompute $this^0 through $this^$window_size $powers = []; $powers[1] = static::prepareReduce($x->value, $n_value, $class); $powers[2] = static::squareReduce($powers[1], $n_value, $class); // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end // in a 1. ie. it's supposed to be odd. $temp = 1 << ($window_size - 1); for ($i = 1; $i < $temp; ++$i) { $i2 = $i << 1; $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class); } $result = new $class(1); $result = static::prepareReduce($result->value, $n_value, $class); for ($i = 0; $i < $e_length;) { if (!$e_bits[$i]) { $result = static::squareReduce($result, $n_value, $class); ++$i; } else { for ($j = $window_size - 1; $j > 0; --$j) { if (!empty($e_bits[$i + $j])) { break; } } // eg. the length of substr($e_bits, $i, $j + 1) for ($k = 0; $k <= $j; ++$k) { $result = static::squareReduce($result, $n_value, $class); } $result = static::multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class); $i += $j + 1; } } $temp = new $class(); $temp->value = static::reduce($result, $n_value, $class); return $temp; } /** * Generates a random number of a certain size * * Bit length is equal to $size * * @param int $size * @return Engine */ public static function random($size) { extract(static::minMaxBits($size)); /** * @var BigInteger $min * @var BigInteger $max */ return static::randomRange($min, $max); } /** * Generates a random prime number of a certain size * * Bit length is equal to $size * * @param int $size * @return Engine */ public static function randomPrime($size) { extract(static::minMaxBits($size)); /** * @var static $min * @var static $max */ return static::randomRangePrime($min, $max); } /** * Performs some pre-processing for randomRangePrime * * @param Engine $min * @param Engine $max * @return static|false */ protected static function randomRangePrimeOuter(Engine $min, Engine $max) { $compare = $max->compare($min); if (!$compare) { return $min->isPrime() ? $min : false; } elseif ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; $min = $temp; } $length = $max->getLength(); if ($length > 8196) { throw new \RuntimeException("Generation of random prime numbers larger than 8196 has been disabled ($length)"); } $x = static::randomRange($min, $max); return static::randomRangePrimeInner($x, $min, $max); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param Engine $min * @param Engine $max * @return Engine */ protected static function randomRangeHelper(Engine $min, Engine $max) { $compare = $max->compare($min); if (!$compare) { return $min; } elseif ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; $min = $temp; } if (!isset(static::$one[static::class])) { static::$one[static::class] = new static(1); } $max = $max->subtract($min->subtract(static::$one[static::class])); $size = strlen(ltrim($max->toBytes(), chr(0))); /* doing $random % $max doesn't work because some numbers will be more likely to occur than others. eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 would produce 5 whereas the only value of random that could produce 139 would be 139. ie. not all numbers would be equally likely. some would be more likely than others. creating a whole new random number until you find one that is within the range doesn't work because, for sufficiently small ranges, the likelihood that you'd get a number within that range would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability would be pretty high that $random would be greater than $max. phpseclib works around this using the technique described here: http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string */ $random_max = new static(chr(1) . str_repeat("\0", $size), 256); $random = new static(Random::string($size), 256); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); while ($random->compare($max_multiple) >= 0) { $random = $random->subtract($max_multiple); $random_max = $random_max->subtract($max_multiple); $random = $random->bitwise_leftShift(8); $random = $random->add(new static(Random::string(1), 256)); $random_max = $random_max->bitwise_leftShift(8); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); } list(, $random) = $random->divide($max); return $random->add($min); } /** * Performs some post-processing for randomRangePrime * * @param Engine $x * @param Engine $min * @param Engine $max * @return static|false */ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max) { if (!isset(static::$two[static::class])) { static::$two[static::class] = new static('2'); } $x->make_odd(); if ($x->compare($max) > 0) { // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range if ($min->equals($max)) { return false; } $x = clone $min; $x->make_odd(); } $initial_x = clone $x; while (true) { if ($x->isPrime()) { return $x; } $x = $x->add(static::$two[static::class]); if ($x->compare($max) > 0) { $x = clone $min; if ($x->equals(static::$two[static::class])) { return $x; } $x->make_odd(); } if ($x->equals($initial_x)) { return false; } } } /** * Sets the $t parameter for primality testing * * @return int */ protected function setupIsPrime() { $length = $this->getLengthInBytes(); // see HAC 4.49 "Note (controlling the error probability)" // @codingStandardsIgnoreStart if ($length >= 163) { $t = 2; } // floor(1300 / 8) else if ($length >= 106) { $t = 3; } // floor( 850 / 8) else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8) else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8) else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8) else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8) else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8) else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8) else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8) else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8) else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8) else { $t = 27; } // @codingStandardsIgnoreEnd return $t; } /** * Tests Primality * * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info. * * @param int $t * @return bool */ protected function testPrimality($t) { if (!$this->testSmallPrimes()) { return false; } $n = clone $this; $n_1 = $n->subtract(static::$one[static::class]); $n_2 = $n->subtract(static::$two[static::class]); $r = clone $n_1; $s = static::scan1divide($r); for ($i = 0; $i < $t; ++$i) { $a = static::randomRange(static::$two[static::class], $n_2); $y = $a->modPow($r, $n); if (!$y->equals(static::$one[static::class]) && !$y->equals($n_1)) { for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { $y = $y->modPow(static::$two[static::class], $n); if ($y->equals(static::$one[static::class])) { return false; } } if (!$y->equals($n_1)) { return false; } } } return true; } /** * Checks a numer to see if it's prime * * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads * on a website instead of just one. * * @param int|bool $t * @return bool */ public function isPrime($t = false) { // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on $length = $this->getLength(); if ($length > 8196) { throw new \RuntimeException("Primality testing is not supported for numbers larger than 8196 bits ($length)"); } if (!$t) { $t = $this->setupIsPrime(); } return $this->testPrimality($t); } /** * Performs a few preliminary checks on root * * @param int $n * @return Engine */ protected function rootHelper($n) { if ($n < 1) { return clone static::$zero[static::class]; } // we want positive exponents if ($this->compare(static::$one[static::class]) < 0) { return clone static::$zero[static::class]; } // we want positive numbers if ($this->compare(static::$two[static::class]) < 0) { return clone static::$one[static::class]; } // n-th root of 1 or 2 is 1 return $this->rootInner($n); } /** * Calculates the nth root of a biginteger. * * Returns the nth root of a positive biginteger, where n defaults to 2 * * {@internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.} * * @param int $n * @return Engine */ protected function rootInner($n) { $n = new static($n); // g is our guess number $g = static::$two[static::class]; // while (g^n < num) g=g*2 while ($g->pow($n)->compare($this) < 0) { $g = $g->multiply(static::$two[static::class]); } // if (g^n==num) num is a power of 2, we're lucky, end of job // == 0 bccomp(bcpow($g, $n), $n->value)==0 if ($g->pow($n)->equals($this) > 0) { $root = $g; return $this->normalize($root); } // if we're here num wasn't a power of 2 :( $og = $g; // og means original guess and here is our upper bound $g = $g->divide(static::$two[static::class])[0]; // g is set to be our lower bound $step = $og->subtract($g)->divide(static::$two[static::class])[0]; // step is the half of upper bound - lower bound $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval // while step>1 while ($step->compare(static::$one[static::class]) == 1) { $guess = $g->pow($n); $step = $step->divide(static::$two[static::class])[0]; $comp = $guess->compare($this); // compare our guess with real number switch ($comp) { case -1: // if guess is lower we add the new step $g = $g->add($step); break; case 1: // if guess is higher we sub the new step $g = $g->subtract($step); break; case 0: // if guess is exactly the num we're done, we return the value $root = $g; break 2; } } if ($comp == 1) { $g = $g->subtract($step); } // whatever happened, g is the closest guess we can make so return it $root = $g; return $this->normalize($root); } /** * Calculates the nth root of a biginteger. * * @param int $n * @return Engine */ public function root($n = 2) { return $this->rootHelper($n); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param array $nums * @return Engine */ protected static function minHelper(array $nums) { if (count($nums) == 1) { return $nums[0]; } $min = $nums[0]; for ($i = 1; $i < count($nums); $i++) { $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min; } return $min; } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param array $nums * @return Engine */ protected static function maxHelper(array $nums) { if (count($nums) == 1) { return $nums[0]; } $max = $nums[0]; for ($i = 1; $i < count($nums); $i++) { $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max; } return $max; } /** * Create Recurring Modulo Function * * Sometimes it may be desirable to do repeated modulos with the same number outside of * modular exponentiation * * @return callable */ public function createRecurringModuloFunction() { $class = static::class; $fqengine = !method_exists(static::$modexpEngine[static::class], 'reduce') ? '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' : static::$modexpEngine[static::class]; if (method_exists($fqengine, 'generateCustomReduction')) { $func = $fqengine::generateCustomReduction($this, static::class); return eval('return function(' . static::class . ' $x) use ($func, $class) { $r = new $class(); $r->value = $func($x->value); return $r; };'); } $n = $this->value; return eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) { $r = new $class(); $r->value = $fqengine::reduce($x->value, $n, $class); return $r; };'); } /** * Calculates the greatest common divisor and Bezout's identity. * * @param Engine $n * @return array{gcd: Engine, x: Engine, y: Engine} */ protected function extendedGCDHelper(Engine $n) { $u = clone $this; $v = clone $n; $one = new static(1); $zero = new static(); $a = clone $one; $b = clone $zero; $c = clone $zero; $d = clone $one; while (!$v->equals($zero)) { list($q) = $u->divide($v); $temp = $u; $u = $v; $v = $temp->subtract($v->multiply($q)); $temp = $a; $a = $c; $c = $temp->subtract($a->multiply($q)); $temp = $b; $b = $d; $d = $temp->subtract($b->multiply($q)); } return [ 'gcd' => $u, 'x' => $a, 'y' => $b ]; } /** * Bitwise Split * * Splits BigInteger's into chunks of $split bits * * @param int $split * @return Engine[] */ public function bitwise_split($split) { if ($split < 1) { throw new \RuntimeException('Offset must be greater than 1'); } $mask = static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]); $num = clone $this; $vals = []; while (!$num->equals(static::$zero[static::class])) { $vals[] = $num->bitwise_and($mask); $num = $num->bitwise_rightShift($split); } return array_reverse($vals); } /** * Logical And * * @param Engine $x * @return Engine */ protected function bitwiseAndHelper(Engine $x) { $left = $this->toBytes(true); $right = $x->toBytes(true); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->normalize(new static($left & $right, -256)); } /** * Logical Or * * @param Engine $x * @return Engine */ protected function bitwiseOrHelper(Engine $x) { $left = $this->toBytes(true); $right = $x->toBytes(true); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->normalize(new static($left | $right, -256)); } /** * Logical Exclusive Or * * @param Engine $x * @return Engine */ protected function bitwiseXorHelper(Engine $x) { $left = $this->toBytes(true); $right = $x->toBytes(true); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->normalize(new static($left ^ $right, -256)); } } PK1A#]�+u,E,EGvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.phpnu�[���<?php /** * BCMath BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\BadConfigurationException; /** * BCMath Engine. * * @author Jim Wigginton <terrafrost@php.net> */ class BCMath extends Engine { /** * Can Bitwise operations be done fast? * * @see parent::bitwise_leftRotate() * @see parent::bitwise_rightRotate() */ const FAST_BITWISE = false; /** * Engine Directory * * @see parent::setModExpEngine */ const ENGINE_DIR = 'BCMath'; /** * Test for engine validity * * @return bool * @see parent::__construct() */ public static function isValidEngine() { return extension_loaded('bcmath'); } /** * Default constructor * * @param mixed $x integer Base-10 number or base-$base number if $base set. * @param int $base * @see parent::__construct() */ public function __construct($x = 0, $base = 10) { if (!isset(static::$isValidEngine[static::class])) { static::$isValidEngine[static::class] = self::isValidEngine(); } if (!static::$isValidEngine[static::class]) { throw new BadConfigurationException('BCMath is not setup correctly on this system'); } $this->value = '0'; parent::__construct($x, $base); } /** * Initialize a BCMath BigInteger Engine instance * * @param int $base * @see parent::__construct() */ protected function initialize($base) { switch (abs($base)) { case 256: // round $len to the nearest 4 $len = (strlen($this->value) + 3) & ~3; $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT); $this->value = '0'; for ($i = 0; $i < $len; $i += 4) { $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32 $this->value = bcadd( $this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord( $x[$i + 2] ) << 8) | ord($x[$i + 3])), 0 ); } if ($this->is_negative) { $this->value = '-' . $this->value; } break; case 16: $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value; $temp = new self(Strings::hex2bin($x), 256); $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; $this->is_negative = false; break; case 10: // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different // results then doing it on '-1' does (modInverse does $x[0]) $this->value = $this->value === '-' ? '0' : (string)$this->value; } } /** * Converts a BigInteger to a base-10 number. * * @return string */ public function toString() { if ($this->value === '0') { return '0'; } return ltrim($this->value, '0'); } /** * Converts a BigInteger to a byte string (eg. base-256). * * @param bool $twos_compliment * @return string */ public function toBytes($twos_compliment = false) { if ($twos_compliment) { return $this->toBytesHelper(); } $value = ''; $current = $this->value; if ($current[0] == '-') { $current = substr($current, 1); } while (bccomp($current, '0', 0) > 0) { $temp = bcmod($current, '16777216'); $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value; $current = bcdiv($current, '16777216', 0); } return $this->precision > 0 ? substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($value, chr(0)); } /** * Adds two BigIntegers. * * @param BCMath $y * @return BCMath */ public function add(BCMath $y) { $temp = new self(); $temp->value = bcadd($this->value, $y->value); return $this->normalize($temp); } /** * Subtracts two BigIntegers. * * @param BCMath $y * @return BCMath */ public function subtract(BCMath $y) { $temp = new self(); $temp->value = bcsub($this->value, $y->value); return $this->normalize($temp); } /** * Multiplies two BigIntegers. * * @param BCMath $x * @return BCMath */ public function multiply(BCMath $x) { $temp = new self(); $temp->value = bcmul($this->value, $x->value); return $this->normalize($temp); } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * @param BCMath $y * @return array{static, static} */ public function divide(BCMath $y) { $quotient = new self(); $remainder = new self(); $quotient->value = bcdiv($this->value, $y->value, 0); $remainder->value = bcmod($this->value, $y->value); if ($remainder->value[0] == '-') { $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); } return [$this->normalize($quotient), $this->normalize($remainder)]; } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * * @param BCMath $n * @return false|BCMath */ public function modInverse(BCMath $n) { return $this->modInverseHelper($n); } /** * Calculates the greatest common divisor and Bezout's identity. * * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which * combination is returned is dependent upon which mode is in use. See * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * @param BCMath $n * @return array{gcd: static, x: static, y: static} */ public function extendedGCD(BCMath $n) { // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, // the basic extended euclidean algorithim is what we're using. $u = $this->value; $v = $n->value; $a = '1'; $b = '0'; $c = '0'; $d = '1'; while (bccomp($v, '0', 0) != 0) { $q = bcdiv($u, $v, 0); $temp = $u; $u = $v; $v = bcsub($temp, bcmul($v, $q, 0), 0); $temp = $a; $a = $c; $c = bcsub($temp, bcmul($a, $q, 0), 0); $temp = $b; $b = $d; $d = bcsub($temp, bcmul($b, $q, 0), 0); } return [ 'gcd' => $this->normalize(new static($u)), 'x' => $this->normalize(new static($a)), 'y' => $this->normalize(new static($b)) ]; } /** * Calculates the greatest common divisor * * Say you have 693 and 609. The GCD is 21. * * @param BCMath $n * @return BCMath */ public function gcd(BCMath $n) { extract($this->extendedGCD($n)); /** @var BCMath $gcd */ return $gcd; } /** * Absolute value. * * @return BCMath */ public function abs() { $temp = new static(); $temp->value = strlen($this->value) && $this->value[0] == '-' ? substr($this->value, 1) : $this->value; return $temp; } /** * Logical And * * @param BCMath $x * @return BCMath */ public function bitwise_and(BCMath $x) { return $this->bitwiseAndHelper($x); } /** * Logical Or * * @param BCMath $x * @return BCMath */ public function bitwise_or(BCMath $x) { return $this->bitwiseXorHelper($x); } /** * Logical Exclusive Or * * @param BCMath $x * @return BCMath */ public function bitwise_xor(BCMath $x) { return $this->bitwiseXorHelper($x); } /** * Logical Right Shift * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * * @param int $shift * @return BCMath */ public function bitwise_rightShift($shift) { $temp = new static(); $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); return $this->normalize($temp); } /** * Logical Left Shift * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * * @param int $shift * @return BCMath */ public function bitwise_leftShift($shift) { $temp = new static(); $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); return $this->normalize($temp); } /** * Compares two numbers. * * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this * is demonstrated thusly: * * $x > $y: $x->compare($y) > 0 * $x < $y: $x->compare($y) < 0 * $x == $y: $x->compare($y) == 0 * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} * * @param BCMath $y * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @see self::equals() */ public function compare(BCMath $y) { return bccomp($this->value, $y->value, 0); } /** * Tests the equality of two numbers. * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * * @param BCMath $x * @return bool */ public function equals(BCMath $x) { return $this->value == $x->value; } /** * Performs modular exponentiation. * * @param BCMath $e * @param BCMath $n * @return BCMath */ public function modPow(BCMath $e, BCMath $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * Alias for modPow(). * * @param BCMath $e * @param BCMath $n * @return BCMath */ public function powMod(BCMath $e, BCMath $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * @param BCMath $e * @param BCMath $n * @return BCMath */ protected function powModInner(BCMath $e, BCMath $n) { try { $class = static::$modexpEngine[static::class]; return $class::powModHelper($this, $e, $n, static::class); } catch (\Exception $err) { return BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class); } } /** * Normalize * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * * @param BCMath $result * @return BCMath */ protected function normalize(BCMath $result) { $result->precision = $this->precision; $result->bitmask = $this->bitmask; if ($result->bitmask !== false) { $result->value = bcmod($result->value, $result->bitmask->value); } return $result; } /** * Generate a random prime number between a range * * If there's not a prime within the given range, false will be returned. * * @param BCMath $min * @param BCMath $max * @return false|BCMath */ public static function randomRangePrime(BCMath $min, BCMath $max) { return self::randomRangePrimeOuter($min, $max); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param BCMath $min * @param BCMath $max * @return BCMath */ public static function randomRange(BCMath $min, BCMath $max) { return self::randomRangeHelper($min, $max); } /** * Make the current number odd * * If the current number is odd it'll be unchanged. If it's even, one will be added to it. * * @see self::randomPrime() */ protected function make_odd() { if (!$this->isOdd()) { $this->value = bcadd($this->value, '1'); } } /** * Test the number against small primes. * * @see self::isPrime() */ protected function testSmallPrimes() { if ($this->value === '1') { return false; } if ($this->value === '2') { return true; } if ($this->value[strlen($this->value) - 1] % 2 == 0) { return false; } $value = $this->value; foreach (self::PRIMES as $prime) { $r = bcmod($this->value, $prime); if ($r == '0') { return $this->value == $prime; } } return true; } /** * Scan for 1 and right shift by that amount * * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); * * @param BCMath $r * @return int * @see self::isPrime() */ public static function scan1divide(BCMath $r) { $r_value = &$r->value; $s = 0; // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one[static::class]) check earlier while ($r_value[strlen($r_value) - 1] % 2 == 0) { $r_value = bcdiv($r_value, '2', 0); ++$s; } return $s; } /** * Performs exponentiation. * * @param BCMath $n * @return BCMath */ public function pow(BCMath $n) { $temp = new self(); $temp->value = bcpow($this->value, $n->value); return $this->normalize($temp); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param BCMath ...$nums * @return BCMath */ public static function min(BCMath ...$nums) { return self::minHelper($nums); } /** * Return the maximum BigInteger between an arbitrary number of BigIntegers. * * @param BCMath ...$nums * @return BCMath */ public static function max(BCMath ...$nums) { return self::maxHelper($nums); } /** * Tests BigInteger to see if it is between two integers, inclusive * * @param BCMath $min * @param BCMath $max * @return bool */ public function between(BCMath $min, BCMath $max) { return $this->compare($min) >= 0 && $this->compare($max) <= 0; } /** * Set Bitmask * * @param int $bits * @return Engine * @see self::setPrecision() */ protected static function setBitmask($bits) { $temp = parent::setBitmask($bits); return $temp->add(static::$one[static::class]); } /** * Is Odd? * * @return bool */ public function isOdd() { return $this->value[strlen($this->value) - 1] % 2 == 1; } /** * Tests if a bit is set * * @return bool */ public function testBit($x) { return bccomp( bcmod($this->value, bcpow('2', $x + 1, 0)), bcpow('2', $x, 0), 0 ) >= 0; } /** * Is Negative? * * @return bool */ public function isNegative() { return strlen($this->value) && $this->value[0] == '-'; } /** * Negate * * Given $k, returns -$k * * @return BCMath */ public function negate() { $temp = clone $this; if (!strlen($temp->value)) { return $temp; } $temp->value = $temp->value[0] == '-' ? substr($this->value, 1) : '-' . $this->value; return $temp; } } PK1A#]h�OQ�#�#Fvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.phpnu�[���<?php /** * Pure-PHP 64-bit BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; /** * Pure-PHP 64-bit Engine. * * Uses 64-bit integers if int size is 8 bits * * @author Jim Wigginton <terrafrost@php.net> */ class PHP64 extends PHP { // Constants used by PHP.php const BASE = 31; const BASE_FULL = 0x80000000; const MAX_DIGIT = 0x7FFFFFFF; const MSB = 0x40000000; /** * MAX10 in greatest MAX10LEN satisfying * MAX10 = 10**MAX10LEN <= 2**BASE. */ const MAX10 = 1000000000; /** * MAX10LEN in greatest MAX10LEN satisfying * MAX10 = 10**MAX10LEN <= 2**BASE. */ const MAX10LEN = 9; const MAX_DIGIT2 = 4611686018427387904; /** * Initialize a PHP64 BigInteger Engine instance * * @param int $base * @see parent::initialize() */ protected function initialize($base) { if ($base != 256 && $base != -256) { return parent::initialize($base); } $val = $this->value; $this->value = []; $vals = &$this->value; $i = strlen($val); if (!$i) { return; } while (true) { $i -= 4; if ($i < 0) { if ($i == -4) { break; } $val = substr($val, 0, 4 + $i); $val = str_pad($val, 4, "\0", STR_PAD_LEFT); if ($val == "\0\0\0\0") { break; } $i = 0; } list(, $digit) = unpack('N', substr($val, $i, 4)); $step = count($vals) & 7; if (!$step) { $digit &= static::MAX_DIGIT; $i++; } else { $shift = 8 - $step; $digit >>= $shift; $shift = 32 - $shift; $digit &= (1 << $shift) - 1; $temp = $i > 0 ? ord($val[$i - 1]) : 0; $digit |= ($temp << $shift) & 0x7F000000; } $vals[] = $digit; } while (end($vals) === 0) { array_pop($vals); } reset($vals); } /** * Test for engine validity * * @see parent::__construct() * @return bool */ public static function isValidEngine() { return PHP_INT_SIZE >= 8 && !self::testJITOnWindows(); } /** * Adds two BigIntegers. * * @param PHP64 $y * @return PHP64 */ public function add(PHP64 $y) { $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Subtracts two BigIntegers. * * @param PHP64 $y * @return PHP64 */ public function subtract(PHP64 $y) { $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Multiplies two BigIntegers. * * @param PHP64 $y * @return PHP64 */ public function multiply(PHP64 $y) { $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * @param PHP64 $y * @return array{PHP64, PHP64} */ public function divide(PHP64 $y) { return $this->divideHelper($y); } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * @param PHP64 $n * @return false|PHP64 */ public function modInverse(PHP64 $n) { return $this->modInverseHelper($n); } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * @param PHP64 $n * @return PHP64[] */ public function extendedGCD(PHP64 $n) { return $this->extendedGCDHelper($n); } /** * Calculates the greatest common divisor * * Say you have 693 and 609. The GCD is 21. * * @param PHP64 $n * @return PHP64 */ public function gcd(PHP64 $n) { return $this->extendedGCD($n)['gcd']; } /** * Logical And * * @param PHP64 $x * @return PHP64 */ public function bitwise_and(PHP64 $x) { return $this->bitwiseAndHelper($x); } /** * Logical Or * * @param PHP64 $x * @return PHP64 */ public function bitwise_or(PHP64 $x) { return $this->bitwiseOrHelper($x); } /** * Logical Exclusive Or * * @param PHP64 $x * @return PHP64 */ public function bitwise_xor(PHP64 $x) { return $this->bitwiseXorHelper($x); } /** * Compares two numbers. * * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is * demonstrated thusly: * * $x > $y: $x->compare($y) > 0 * $x < $y: $x->compare($y) < 0 * $x == $y: $x->compare($y) == 0 * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} * * @param PHP64 $y * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @see self::equals() */ public function compare(PHP64 $y) { return parent::compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative); } /** * Tests the equality of two numbers. * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * * @param PHP64 $x * @return bool */ public function equals(PHP64 $x) { return $this->value === $x->value && $this->is_negative == $x->is_negative; } /** * Performs modular exponentiation. * * @param PHP64 $e * @param PHP64 $n * @return PHP64 */ public function modPow(PHP64 $e, PHP64 $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * Alias for modPow(). * * @param PHP64 $e * @param PHP64 $n * @return PHP64|false */ public function powMod(PHP64 $e, PHP64 $n) { return $this->powModOuter($e, $n); } /** * Generate a random prime number between a range * * If there's not a prime within the given range, false will be returned. * * @param PHP64 $min * @param PHP64 $max * @return false|PHP64 */ public static function randomRangePrime(PHP64 $min, PHP64 $max) { return self::randomRangePrimeOuter($min, $max); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param PHP64 $min * @param PHP64 $max * @return PHP64 */ public static function randomRange(PHP64 $min, PHP64 $max) { return self::randomRangeHelper($min, $max); } /** * Performs exponentiation. * * @param PHP64 $n * @return PHP64 */ public function pow(PHP64 $n) { return $this->powHelper($n); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param PHP64 ...$nums * @return PHP64 */ public static function min(PHP64 ...$nums) { return self::minHelper($nums); } /** * Return the maximum BigInteger between an arbitrary number of BigIntegers. * * @param PHP64 ...$nums * @return PHP64 */ public static function max(PHP64 ...$nums) { return self::maxHelper($nums); } /** * Tests BigInteger to see if it is between two integers, inclusive * * @param PHP64 $min * @param PHP64 $max * @return bool */ public function between(PHP64 $min, PHP64 $max) { return $this->compare($min) >= 0 && $this->compare($max) <= 0; } } PK1A#]fm����Hvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.phpnu�[���<?php /** * OpenSSL Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; use phpseclib3\Crypt\RSA\Formats\Keys\PKCS8; use phpseclib3\Math\BigInteger; /** * OpenSSL Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSL { /** * Test for engine validity * * @return bool */ public static function isValidEngine() { return extension_loaded('openssl') && static::class != __CLASS__; } /** * Performs modular exponentiation. * * @param Engine $x * @param Engine $e * @param Engine $n * @return Engine */ public static function powModHelper(Engine $x, Engine $e, Engine $n) { if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes() > 16384) { throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted'); } $key = PKCS8::savePublicKey( new BigInteger($n), new BigInteger($e) ); $plaintext = str_pad($x->toBytes(), $n->getLengthInBytes(), "\0", STR_PAD_LEFT); // this is easily prone to failure. if the modulo is a multiple of 2 or 3 or whatever it // won't work and you'll get a "failure: error:0906D06C:PEM routines:PEM_read_bio:no start line" // error. i suppose, for even numbers, we could do what PHP\Montgomery.php does, but then what // about odd numbers divisible by 3, by 5, etc? if (!openssl_public_encrypt($plaintext, $result, $key, OPENSSL_NO_PADDING)) { throw new \UnexpectedValueException(openssl_error_string()); } $class = get_class($x); return new $class($result, 256); } } PK1A#]7 �[Zvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.phpnu�[���<?php /** * PHP Montgomery Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor; /** * PHP Montgomery Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Montgomery extends Progenitor { /** * Prepare a number for use in Montgomery Modular Reductions * * @param array $x * @param array $n * @param string $class * @return array */ protected static function prepareReduce(array $x, array $n, $class) { $lhs = new $class(); $lhs->value = array_merge(self::array_repeat(0, count($n)), $x); $rhs = new $class(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } /** * Montgomery Multiply * * Interleaves the montgomery reduction and long multiplication algorithms together as described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} * * @param array $x * @param array $n * @param string $class * @return array */ protected static function reduce(array $x, array $n, $class) { static $cache = [ self::VARIABLE => [], self::DATA => [] ]; if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $x; $cache[self::DATA][] = self::modInverse67108864($n, $class); } $k = count($n); $result = [self::VALUE => $x]; for ($i = 0; $i < $k; ++$i) { $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $class::regularMultiply([$temp], $n); $temp = array_merge(self::array_repeat(0, $i), $temp); $result = $class::addHelper($result[self::VALUE], false, $temp, false); } $result[self::VALUE] = array_slice($result[self::VALUE], $k); if (self::compareHelper($result, false, $n, false) >= 0) { $result = $class::subtractHelper($result[self::VALUE], false, $n, false); } return $result[self::VALUE]; } /** * Modular Inverse of a number mod 2**26 (eg. 67108864) * * Based off of the bnpInvDigit function implemented and justified in the following URL: * * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} * * The following URL provides more info: * * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} * * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to * 40 bits, which only 64-bit floating points will support. * * Thanks to Pedro Gimeno Fortea for input! * * @param array $x * @param string $class * @return int */ protected static function modInverse67108864(array $x, $class) // 2**26 == 67,108,864 { $x = -$x[0]; $result = $x & 0x3; // x**-1 mod 2**2 $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 $result = $class::BASE == 26 ? fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : // x**-1 mod 2**26 ($result * (2 - ($x * $result) % $class::BASE_FULL)) % $class::BASE_FULL; return $result & $class::MAX_DIGIT; } } PK1A#]����oo^vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.phpnu�[���<?php /** * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP; /** * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication * * @author Jim Wigginton <terrafrost@php.net> */ abstract class MontgomeryMult extends Montgomery { /** * Montgomery Multiply * * Interleaves the montgomery reduction and long multiplication algorithms together as described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} * * @see self::_prepMontgomery() * @see self::_montgomery() * @param array $x * @param array $y * @param array $m * @param class-string<PHP> $class * @return array */ public static function multiplyReduce(array $x, array $y, array $m, $class) { // the following code, although not callable, can be run independently of the above code // although the above code performed better in my benchmarks the following could might // perform better under different circumstances. in lieu of deleting it it's just been // made uncallable static $cache = [ self::VARIABLE => [], self::DATA => [] ]; if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; $cache[self::DATA][] = self::modInverse67108864($m, $class); } $n = max(count($x), count($y), count($m)); $x = array_pad($x, $n, 0); $y = array_pad($y, $n, 0); $m = array_pad($m, $n, 0); $a = [self::VALUE => self::array_repeat(0, $n + 1)]; for ($i = 0; $i < $n; ++$i) { $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $temp * $cache[self::DATA][$key]; $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $class::addHelper($class::regularMultiply([$x[$i]], $y), false, $class::regularMultiply([$temp], $m), false); $a = $class::addHelper($a[self::VALUE], false, $temp[self::VALUE], false); $a[self::VALUE] = array_slice($a[self::VALUE], 1); } if (self::compareHelper($a[self::VALUE], false, $m, false) >= 0) { $a = $class::subtractHelper($a[self::VALUE], false, $m, false); } return $a[self::VALUE]; } } PK1A#]�k����Wvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.phpnu�[���<?php /** * PHP Classic Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP\Base; /** * PHP Classic Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Classic extends Base { /** * Regular Division * * @param array $x * @param array $n * @param string $class * @return array */ protected static function reduce(array $x, array $n, $class) { $lhs = new $class(); $lhs->value = $x; $rhs = new $class(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } } PK1A#]��EH�+�+Wvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.phpnu�[���<?php /** * PHP Barrett Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\PHP\Base; /** * PHP Barrett Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Barrett extends Base { /** * Barrett Modular Reduction * * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, * so as not to require negative numbers (initially, this script didn't support negative numbers). * * Employs "folding", as described at * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." * * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that * usable on account of (1) its not using reasonable radix points as discussed in * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line * comments for details. * * @param array $n * @param array $m * @param class-string<PHP> $class * @return array */ protected static function reduce(array $n, array $m, $class) { static $cache = [ self::VARIABLE => [], self::DATA => [] ]; $m_length = count($m); // if (self::compareHelper($n, $static::square($m)) >= 0) { if (count($n) > 2 * $m_length) { $lhs = new $class(); $rhs = new $class(); $lhs->value = $n; $rhs->value = $m; list(, $temp) = $lhs->divide($rhs); return $temp->value; } // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced if ($m_length < 5) { return self::regularBarrett($n, $m, $class); } // n = 2 * m.length if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; $lhs = new $class(); $lhs_value = &$lhs->value; $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; $rhs = new $class(); $rhs->value = $m; list($u, $m1) = $lhs->divide($rhs); $u = $u->value; $m1 = $m1->value; $cache[self::DATA][] = [ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) 'm1' => $m1 // m.length ]; } else { extract($cache[self::DATA][$key]); } $cutoff = $m_length + ($m_length >> 1); $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) $msd = array_slice($n, $cutoff); // m.length >> 1 $lsd = self::trim($lsd); $temp = $class::multiplyHelper($msd, false, $m1, false); // m.length + (m.length >> 1) $n = $class::addHelper($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers) //if ($m_length & 1) { // return self::regularBarrett($n[self::VALUE], $m, $class); //} // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 $temp = array_slice($n[self::VALUE], $m_length - 1); // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 $temp = $class::multiplyHelper($temp, false, $u, false); // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) $temp = $class::multiplyHelper($temp, false, $m, false); // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). $result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false); while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false); } return $result[self::VALUE]; } /** * (Regular) Barrett Modular Reduction * * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this * is that this function does not fold the denominator into a smaller form. * * @param array $x * @param array $n * @param string $class * @return array */ private static function regularBarrett(array $x, array $n, $class) { static $cache = [ self::VARIABLE => [], self::DATA => [] ]; $n_length = count($n); if (count($x) > 2 * $n_length) { $lhs = new $class(); $rhs = new $class(); $lhs->value = $x; $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $n; $lhs = new $class(); $lhs_value = &$lhs->value; $lhs_value = self::array_repeat(0, 2 * $n_length); $lhs_value[] = 1; $rhs = new $class(); $rhs->value = $n; list($temp, ) = $lhs->divide($rhs); // m.length $cache[self::DATA][] = $temp->value; } // 2 * m.length - (m.length - 1) = m.length + 1 $temp = array_slice($x, $n_length - 1); // (m.length + 1) + m.length = 2 * m.length + 1 $temp = $class::multiplyHelper($temp, false, $cache[self::DATA][$key], false); // (2 * m.length + 1) - (m.length - 1) = m.length + 2 $temp = array_slice($temp[self::VALUE], $n_length + 1); // m.length + 1 $result = array_slice($x, 0, $n_length + 1); // m.length + 1 $temp = self::multiplyLower($temp, false, $n, false, $n_length + 1, $class); // $temp == array_slice($class::regularMultiply($temp, false, $n, false)->value, 0, $n_length + 1) if (self::compareHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { $corrector_value = self::array_repeat(0, $n_length + 1); $corrector_value[count($corrector_value)] = 1; $result = $class::addHelper($result, false, $corrector_value, false); $result = $result[self::VALUE]; } // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits $result = $class::subtractHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]); while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $n, false); } return $result[self::VALUE]; } /** * Performs long multiplication up to $stop digits * * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. * * @see self::regularBarrett() * @param array $x_value * @param bool $x_negative * @param array $y_value * @param bool $y_negative * @param int $stop * @param string $class * @return array */ private static function multiplyLower(array $x_value, $x_negative, array $y_value, $y_negative, $stop, $class) { $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { // a 0 is being multiplied return [ self::VALUE => [], self::SIGN => false ]; } if ($x_length < $y_length) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; $x_length = count($x_value); $y_length = count($y_value); } $product_value = self::array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but // doing so would also make the result in one set of unnecessary adds, // since on the outermost loops first pass, $product->value[$k] is going // to always be 0 $carry = 0; for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$j] = (int) ($temp - $class::BASE_FULL * $carry); } if ($j < $stop) { $product_value[$j] = $carry; } // the above for loop is what the previous comment was talking about. the // following for loop is the "one with nested for loops" for ($i = 1; $i < $y_length; ++$i) { $carry = 0; for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$k] = (int) ($temp - $class::BASE_FULL * $carry); } if ($k < $stop) { $product_value[$k] = $carry; } } return [ self::VALUE => self::trim($product_value), self::SIGN => $x_negative != $y_negative ]; } } PK1A#] ��``Zvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.phpnu�[���<?php /** * PHP Power of Two Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP\Base; /** * PHP Power Of Two Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PowerOfTwo extends Base { /** * Prepare a number for use in Montgomery Modular Reductions * * @param array $x * @param array $n * @param string $class * @return array */ protected static function prepareReduce(array $x, array $n, $class) { return self::reduce($x, $n, $class); } /** * Power Of Two Reduction * * @param array $x * @param array $n * @param string $class * @return array */ protected static function reduce(array $x, array $n, $class) { $lhs = new $class(); $lhs->value = $x; $rhs = new $class(); $rhs->value = $n; $temp = new $class(); $temp->value = [1]; $result = $lhs->bitwise_and($rhs->subtract($temp)); return $result->value; } } PK1A#]4}O�<�<[vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.phpnu�[���<?php /** * PHP Dynamic Barrett Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions; use phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\PHP\Base; /** * PHP Dynamic Barrett Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EvalBarrett extends Base { /** * Custom Reduction Function * * @see self::generateCustomReduction */ private static $custom_reduction; /** * Barrett Modular Reduction * * This calls a dynamically generated loop unrolled function that's specific to a given modulo. * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc. * * @param array $n * @param array $m * @param string $class * @return array */ protected static function reduce(array $n, array $m, $class) { $inline = self::$custom_reduction; return $inline($n); } /** * Generate Custom Reduction * * @param PHP $m * @param string $class * @return callable */ protected static function generateCustomReduction(PHP $m, $class) { $m_length = count($m->value); if ($m_length < 5) { $code = ' $lhs = new ' . $class . '(); $lhs->value = $x; $rhs = new ' . $class . '(); $rhs->value = [' . implode(',', array_map(self::class . '::float2string', $m->value)) . ']; list(, $temp) = $lhs->divide($rhs); return $temp->value; '; eval('$func = function ($x) { ' . $code . '};'); self::$custom_reduction = $func; //self::$custom_reduction = \Closure::bind($func, $m, $class); return $func; } $lhs = new $class(); $lhs_value = &$lhs->value; $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; $rhs = new $class(); list($u, $m1) = $lhs->divide($m); if ($class::BASE != 26) { $u = $u->value; } else { $lhs_value = self::array_repeat(0, 2 * $m_length); $lhs_value[] = 1; $rhs = new $class(); list($u) = $lhs->divide($m); $u = $u->value; } $m = $m->value; $m1 = $m1->value; $cutoff = count($m) + (count($m) >> 1); $code = ' if (count($n) > ' . (2 * count($m)) . ') { $lhs = new ' . $class . '(); $rhs = new ' . $class . '(); $lhs->value = $n; $rhs->value = [' . implode(',', array_map(self::class . '::float2string', $m)) . ']; list(, $temp) = $lhs->divide($rhs); return $temp->value; } $lsd = array_slice($n, 0, ' . $cutoff . '); $msd = array_slice($n, ' . $cutoff . ');'; $code .= self::generateInlineTrim('msd'); $code .= self::generateInlineMultiply('msd', $m1, 'temp', $class); $code .= self::generateInlineAdd('lsd', 'temp', 'n', $class); $code .= '$temp = array_slice($n, ' . (count($m) - 1) . ');'; $code .= self::generateInlineMultiply('temp', $u, 'temp2', $class); $code .= self::generateInlineTrim('temp2'); $code .= $class::BASE == 26 ? '$temp = array_slice($temp2, ' . (count($m) + 1) . ');' : '$temp = array_slice($temp2, ' . ((count($m) >> 1) + 1) . ');'; $code .= self::generateInlineMultiply('temp', $m, 'temp2', $class); $code .= self::generateInlineTrim('temp2'); /* if ($class::BASE == 26) { $code.= '$n = array_slice($n, 0, ' . (count($m) + 1) . '); $temp2 = array_slice($temp2, 0, ' . (count($m) + 1) . ');'; } */ $code .= self::generateInlineSubtract2('n', 'temp2', 'temp', $class); $subcode = self::generateInlineSubtract1('temp', $m, 'temp2', $class); $subcode .= '$temp = $temp2;'; $code .= self::generateInlineCompare($m, 'temp', $subcode); $code .= 'return $temp;'; eval('$func = function ($n) { ' . $code . '};'); self::$custom_reduction = $func; return $func; //self::$custom_reduction = \Closure::bind($func, $m, $class); } /** * Inline Trim * * Removes leading zeros * * @param string $name * @return string */ private static function generateInlineTrim($name) { return ' for ($i = count($' . $name . ') - 1; $i >= 0; --$i) { if ($' . $name . '[$i]) { break; } unset($' . $name . '[$i]); }'; } /** * Inline Multiply (unknown, known) * * @param string $input * @param array $arr * @param string $output * @param string $class * @return string */ private static function generateInlineMultiply($input, array $arr, $output, $class) { if (!count($arr)) { return 'return [];'; } $regular = ' $length = count($' . $input . '); if (!$length) { $' . $output . ' = []; }else{ $' . $output . ' = array_fill(0, $length + ' . count($arr) . ', 0); $carry = 0;'; for ($i = 0; $i < count($arr); $i++) { $regular .= ' $subtemp = $' . $input . '[0] * ' . $arr[$i]; $regular .= $i ? ' + $carry;' : ';'; $regular .= '$carry = '; $regular .= $class::BASE === 26 ? 'intval($subtemp / 0x4000000);' : '$subtemp >> 31;'; $regular .= '$' . $output . '[' . $i . '] = '; if ($class::BASE === 26) { $regular .= '(int) ('; } $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry'; $regular .= $class::BASE === 26 ? ');' : ';'; } $regular .= '$' . $output . '[' . count($arr) . '] = $carry;'; $regular .= ' for ($i = 1; $i < $length; ++$i) {'; for ($j = 0; $j < count($arr); $j++) { $regular .= $j ? '$k++;' : '$k = $i;'; $regular .= ' $subtemp = $' . $output . '[$k] + $' . $input . '[$i] * ' . $arr[$j]; $regular .= $j ? ' + $carry;' : ';'; $regular .= '$carry = '; $regular .= $class::BASE === 26 ? 'intval($subtemp / 0x4000000);' : '$subtemp >> 31;'; $regular .= '$' . $output . '[$k] = '; if ($class::BASE === 26) { $regular .= '(int) ('; } $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry'; $regular .= $class::BASE === 26 ? ');' : ';'; } $regular .= '$' . $output . '[++$k] = $carry; $carry = 0;'; $regular .= '}}'; //if (count($arr) < 2 * self::KARATSUBA_CUTOFF) { //} return $regular; } /** * Inline Addition * * @param string $x * @param string $y * @param string $result * @param string $class * @return string */ private static function generateInlineAdd($x, $y, $result, $class) { $code = ' $length = max(count($' . $x . '), count($' . $y . ')); $' . $result . ' = array_pad($' . $x . ', $length + 1, 0); $_' . $y . ' = array_pad($' . $y . ', $length, 0); $carry = 0; for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) { $sum = ($' . $result . '[$j] + $_' . $y . '[$j]) * ' . $class::BASE_FULL . ' + $' . $result . '[$i] + $_' . $y . '[$i] + $carry; $carry = $sum >= ' . self::float2string($class::MAX_DIGIT2) . '; $sum = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;'; $code .= $class::BASE === 26 ? '$upper = intval($sum / 0x4000000); $' . $result . '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' * $upper);' : '$upper = $sum >> 31; $' . $result . '[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;'; $code .= ' $' . $result . '[$j] = $upper; } if ($j == $length) { $sum = $' . $result . '[$i] + $_' . $y . '[$i] + $carry; $carry = $sum >= ' . self::float2string($class::BASE_FULL) . '; $' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::BASE_FULL) . ' : $sum; ++$i; } if ($carry) { for (; $' . $result . '[$i] == ' . $class::MAX_DIGIT . '; ++$i) { $' . $result . '[$i] = 0; } ++$' . $result . '[$i]; }'; $code .= self::generateInlineTrim($result); return $code; } /** * Inline Subtraction 2 * * For when $known is more digits than $unknown. This is the harder use case to optimize for. * * @param string $known * @param string $unknown * @param string $result * @param string $class * @return string */ private static function generateInlineSubtract2($known, $unknown, $result, $class) { $code = ' $' . $result . ' = $' . $known . '; $carry = 0; $size = count($' . $unknown . '); for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) { $sum = ($' . $known . '[$j] - $' . $unknown . '[$j]) * ' . $class::BASE_FULL . ' + $' . $known . '[$i] - $' . $unknown . '[$i] - $carry; $carry = $sum < 0; if ($carry) { $sum+= ' . self::float2string($class::MAX_DIGIT2) . '; } $subtemp = '; $code .= $class::BASE === 26 ? 'intval($sum / 0x4000000);' : '$sum >> 31;'; $code .= '$' . $result . '[$i] = '; if ($class::BASE === 26) { $code .= '(int) ('; } $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp'; if ($class::BASE === 26) { $code .= ')'; } $code .= '; $' . $result . '[$j] = $subtemp; } if ($j == $size) { $sum = $' . $known . '[$i] - $' . $unknown . '[$i] - $carry; $carry = $sum < 0; $' . $result . '[$i] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum; ++$i; } if ($carry) { for (; !$' . $result . '[$i]; ++$i) { $' . $result . '[$i] = ' . $class::MAX_DIGIT . '; } --$' . $result . '[$i]; }'; $code .= self::generateInlineTrim($result); return $code; } /** * Inline Subtraction 1 * * For when $unknown is more digits than $known. This is the easier use case to optimize for. * * @param string $unknown * @param array $known * @param string $result * @param string $class * @return string */ private static function generateInlineSubtract1($unknown, array $known, $result, $class) { $code = '$' . $result . ' = $' . $unknown . ';'; for ($i = 0, $j = 1; $j < count($known); $i += 2, $j += 2) { $code .= '$sum = $' . $unknown . '[' . $j . '] * ' . $class::BASE_FULL . ' + $' . $unknown . '[' . $i . '] - '; $code .= self::float2string($known[$j] * $class::BASE_FULL + $known[$i]); if ($i != 0) { $code .= ' - $carry'; } $code .= '; if ($carry = $sum < 0) { $sum+= ' . self::float2string($class::MAX_DIGIT2) . '; } $subtemp = '; $code .= $class::BASE === 26 ? 'intval($sum / 0x4000000);' : '$sum >> 31;'; $code .= ' $' . $result . '[' . $i . '] = '; if ($class::BASE === 26) { $code .= ' (int) ('; } $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp'; if ($class::BASE === 26) { $code .= ')'; } $code .= '; $' . $result . '[' . $j . '] = $subtemp;'; } $code .= '$i = ' . $i . ';'; if ($j == count($known)) { $code .= ' $sum = $' . $unknown . '[' . $i . '] - ' . $known[$i] . ' - $carry; $carry = $sum < 0; $' . $result . '[' . $i . '] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum; ++$i;'; } $code .= ' if ($carry) { for (; !$' . $result . '[$i]; ++$i) { $' . $result . '[$i] = ' . $class::MAX_DIGIT . '; } --$' . $result . '[$i]; }'; $code .= self::generateInlineTrim($result); return $code; } /** * Inline Comparison * * If $unknown >= $known then loop * * @param array $known * @param string $unknown * @param string $subcode * @return string */ private static function generateInlineCompare(array $known, $unknown, $subcode) { $uniqid = uniqid(); $code = 'loop_' . $uniqid . ': $clength = count($' . $unknown . '); switch (true) { case $clength < ' . count($known) . ': goto end_' . $uniqid . '; case $clength > ' . count($known) . ':'; for ($i = count($known) - 1; $i >= 0; $i--) { $code .= ' case $' . $unknown . '[' . $i . '] > ' . $known[$i] . ': goto subcode_' . $uniqid . '; case $' . $unknown . '[' . $i . '] < ' . $known[$i] . ': goto end_' . $uniqid . ';'; } $code .= ' default: // do subcode } subcode_' . $uniqid . ':' . $subcode . ' goto loop_' . $uniqid . '; end_' . $uniqid . ':'; return $code; } /** * Convert a float to a string * * If you do echo floatval(pow(2, 52)) you'll get 4.6116860184274E+18. It /can/ be displayed without a loss of * precision but displayed in this way there will be precision loss, hence the need for this method. * * @param int|float $num * @return string */ private static function float2string($num) { if (!is_float($num)) { return (string) $num; } if ($num < 0) { return '-' . self::float2string(abs($num)); } $temp = ''; while ($num) { $temp = fmod($num, 10) . $temp; $num = floor($num / 10); } return $temp; } } PK1A#]0_ _ Ovendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.phpnu�[���<?php /** * PHP Montgomery Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\Engine; use phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo; /** * PHP Montgomery Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Montgomery extends Base { /** * Test for engine validity * * @return bool */ public static function isValidEngine() { return static::class != __CLASS__; } /** * Performs modular exponentiation. * * @template T of Engine * @param Engine $x * @param Engine $e * @param Engine $n * @param class-string<T> $class * @return T */ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class) { // is the modulo odd? if ($n->value[0] & 1) { return parent::slidingWindow($x, $e, $n, $class); } // if it's not, it's even // find the lowest set bit (eg. the max pow of 2 that divides $n) for ($i = 0; $i < count($n->value); ++$i) { if ($n->value[$i]) { $temp = decbin($n->value[$i]); $j = strlen($temp) - strrpos($temp, '1') - 1; $j += $class::BASE * $i; break; } } // at this point, 2^$j * $n/(2^$j) == $n $mod1 = clone $n; $mod1->rshift($j); $mod2 = new $class(); $mod2->value = [1]; $mod2->lshift($j); $part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e, $mod1, $class) : new $class(); $part2 = PowerOfTwo::slidingWindow($x, $e, $mod2, $class); $y1 = $mod2->modInverse($mod1); $y2 = $mod1->modInverse($mod2); $result = $part1->multiply($mod2); $result = $result->multiply($y1); $temp = $part2->multiply($mod1); $temp = $temp->multiply($y2); $result = $result->add($temp); list(, $result) = $result->divide($n); return $result; } } PK1A#]X�4��Ivendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.phpnu�[���<?php /** * PHP Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\PHP; /** * PHP Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Base extends PHP { /** * Cache constants * * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. * */ const VARIABLE = 0; /** * $cache[self::DATA] contains the cached data. * */ const DATA = 1; /** * Test for engine validity * * @return bool */ public static function isValidEngine() { return static::class != __CLASS__; } /** * Performs modular exponentiation. * * The most naive approach to modular exponentiation has very unreasonable requirements, and * and although the approach involving repeated squaring does vastly better, it, too, is impractical * for our purposes. The reason being that division - by far the most complicated and time-consuming * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. * * Modular reductions resolve this issue. Although an individual modular reduction takes more time * then an individual division, when performed in succession (with the same modulo), they're a lot faster. * * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because * the product of two odd numbers is odd), but what about when RSA isn't used? * * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. * * @param PHP $x * @param PHP $e * @param PHP $n * @param string $class * @return PHP */ protected static function powModHelper(PHP $x, PHP $e, PHP $n, $class) { if (empty($e->value)) { $temp = new $class(); $temp->value = [1]; return $x->normalize($temp); } if ($e->value == [1]) { list(, $temp) = $x->divide($n); return $x->normalize($temp); } if ($e->value == [2]) { $temp = new $class(); $temp->value = $class::square($x->value); list(, $temp) = $temp->divide($n); return $x->normalize($temp); } return $x->normalize(static::slidingWindow($x, $e, $n, $class)); } /** * Modular reduction preparation * * @param array $x * @param array $n * @param string $class * @see self::slidingWindow() * @return array */ protected static function prepareReduce(array $x, array $n, $class) { return static::reduce($x, $n, $class); } /** * Modular multiply * * @param array $x * @param array $y * @param array $n * @param string $class * @see self::slidingWindow() * @return array */ protected static function multiplyReduce(array $x, array $y, array $n, $class) { $temp = $class::multiplyHelper($x, false, $y, false); return static::reduce($temp[self::VALUE], $n, $class); } /** * Modular square * * @param array $x * @param array $n * @param string $class * @see self::slidingWindow() * @return array */ protected static function squareReduce(array $x, array $n, $class) { return static::reduce($class::square($x), $n, $class); } } PK1A#]b'"44Lvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.phpnu�[���<?php /** * OpenSSL Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor; /** * OpenSSL Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSL extends Progenitor { } PK1A#]`I�HHRvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.phpnu�[���<?php /** * PHP Default Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\PHP; use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett; /** * PHP Default Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DefaultEngine extends EvalBarrett { } PK1A#]��77Ovendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.phpnu�[���<?php /** * OpenSSL Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath; use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor; /** * OpenSSL Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSL extends Progenitor { } PK1A#]>���Ovendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.phpnu�[���<?php /** * Built-In BCMath Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath; use phpseclib3\Math\BigInteger\Engines\BCMath; /** * Built-In BCMath Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BuiltIn extends BCMath { /** * Performs modular exponentiation. * * @param BCMath $x * @param BCMath $e * @param BCMath $n * @return BCMath */ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n) { $temp = new BCMath(); $temp->value = bcpowmod($x->value, $e->value, $n->value); return $x->normalize($temp); } } PK1A#]E⨕g g Lvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.phpnu�[���<?php /** * Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath; use phpseclib3\Math\BigInteger\Engines\BCMath; /** * Sliding Window Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Base extends BCMath { /** * Cache constants * * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. * */ const VARIABLE = 0; /** * $cache[self::DATA] contains the cached data. * */ const DATA = 1; /** * Test for engine validity * * @return bool */ public static function isValidEngine() { return static::class != __CLASS__; } /** * Performs modular exponentiation. * * @param BCMath $x * @param BCMath $e * @param BCMath $n * @param string $class * @return BCMath */ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n, $class) { if (empty($e->value)) { $temp = new $class(); $temp->value = '1'; return $x->normalize($temp); } return $x->normalize(static::slidingWindow($x, $e, $n, $class)); } /** * Modular reduction preparation * * @param string $x * @param string $n * @param string $class * @see self::slidingWindow() * @return string */ protected static function prepareReduce($x, $n, $class) { return static::reduce($x, $n); } /** * Modular multiply * * @param string $x * @param string $y * @param string $n * @param string $class * @see self::slidingWindow() * @return string */ protected static function multiplyReduce($x, $y, $n, $class) { return static::reduce(bcmul($x, $y), $n); } /** * Modular square * * @param string $x * @param string $n * @param string $class * @see self::slidingWindow() * @return string */ protected static function squareReduce($x, $n, $class) { return static::reduce(bcmul($x, $x), $n); } } PK1A#]���IIUvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.phpnu�[���<?php /** * BCMath Default Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath; use phpseclib3\Math\BigInteger\Engines\BCMath\Reductions\Barrett; /** * PHP Default Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DefaultEngine extends Barrett { } PK1A#]ϽR�BB^vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.phpnu�[���<?php /** * BCMath Dynamic Barrett Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions; use phpseclib3\Math\BigInteger\Engines\BCMath; use phpseclib3\Math\BigInteger\Engines\BCMath\Base; /** * PHP Barrett Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EvalBarrett extends Base { /** * Custom Reduction Function * * @see self::generateCustomReduction */ private static $custom_reduction; /** * Barrett Modular Reduction * * This calls a dynamically generated loop unrolled function that's specific to a given modulo. * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc. * * @param string $n * @param string $m * @return string */ protected static function reduce($n, $m) { $inline = self::$custom_reduction; return $inline($n); } /** * Generate Custom Reduction * * @param BCMath $m * @param string $class * @return callable|void */ protected static function generateCustomReduction(BCMath $m, $class) { $m_length = strlen($m); if ($m_length < 5) { $code = 'return bcmod($x, $n);'; eval('$func = function ($n) { ' . $code . '};'); self::$custom_reduction = $func; return; } $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1)); $u = bcdiv($lhs, $m, 0); $m1 = bcsub($lhs, bcmul($u, $m)); $cutoff = $m_length + ($m_length >> 1); $m = "'$m'"; $u = "'$u'"; $m1 = "'$m1'"; $code = ' $lsd = substr($n, -' . $cutoff . '); $msd = substr($n, 0, -' . $cutoff . '); $temp = bcmul($msd, ' . $m1 . '); $n = bcadd($lsd, $temp); $temp = substr($n, 0, ' . (-$m_length + 1) . '); $temp = bcmul($temp, ' . $u . '); $temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . '); $temp = bcmul($temp, ' . $m . '); $result = bcsub($n, $temp); if ($result[0] == \'-\') { $temp = \'1' . str_repeat('0', $m_length + 1) . '\'; $result = bcadd($result, $temp); } while (bccomp($result, ' . $m . ') >= 0) { $result = bcsub($result, ' . $m . '); } return $result;'; eval('$func = function ($n) { ' . $code . '};'); self::$custom_reduction = $func; return $func; } } PK1A#]opBuZvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.phpnu�[���<?php /** * BCMath Barrett Modular Exponentiation Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions; use phpseclib3\Math\BigInteger\Engines\BCMath\Base; /** * PHP Barrett Modular Exponentiation Engine * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Barrett extends Base { /** * Cache constants * * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. * */ const VARIABLE = 0; /** * $cache[self::DATA] contains the cached data. * */ const DATA = 1; /** * Barrett Modular Reduction * * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, * so as not to require negative numbers (initially, this script didn't support negative numbers). * * Employs "folding", as described at * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." * * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that * usable on account of (1) its not using reasonable radix points as discussed in * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line * comments for details. * * @param string $n * @param string $m * @return string */ protected static function reduce($n, $m) { static $cache = [ self::VARIABLE => [], self::DATA => [] ]; $m_length = strlen($m); if (strlen($n) > 2 * $m_length) { return bcmod($n, $m); } // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced if ($m_length < 5) { return self::regularBarrett($n, $m); } // n = 2 * m.length if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1)); $u = bcdiv($lhs, $m, 0); $m1 = bcsub($lhs, bcmul($u, $m)); $cache[self::DATA][] = [ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) 'm1' => $m1 // m.length ]; } else { extract($cache[self::DATA][$key]); } $cutoff = $m_length + ($m_length >> 1); $lsd = substr($n, -$cutoff); $msd = substr($n, 0, -$cutoff); $temp = bcmul($msd, $m1); // m.length + (m.length >> 1) $n = bcadd($lsd, $temp); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers) //if ($m_length & 1) { // return self::regularBarrett($n, $m); //} // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 $temp = substr($n, 0, -$m_length + 1); // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 $temp = bcmul($temp, $u); // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) $temp = substr($temp, 0, -($m_length >> 1) - 1); // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) $temp = bcmul($temp, $m); // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). $result = bcsub($n, $temp); //if (bccomp($result, '0') < 0) { if ($result[0] == '-') { $temp = '1' . str_repeat('0', $m_length + 1); $result = bcadd($result, $temp); } while (bccomp($result, $m) >= 0) { $result = bcsub($result, $m); } return $result; } /** * (Regular) Barrett Modular Reduction * * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this * is that this function does not fold the denominator into a smaller form. * * @param string $x * @param string $n * @return string */ private static function regularBarrett($x, $n) { static $cache = [ self::VARIABLE => [], self::DATA => [] ]; $n_length = strlen($n); if (strlen($x) > 2 * $n_length) { return bcmod($x, $n); } if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $n; $lhs = '1' . str_repeat('0', 2 * $n_length); $cache[self::DATA][] = bcdiv($lhs, $n, 0); } $temp = substr($x, 0, -$n_length + 1); $temp = bcmul($temp, $cache[self::DATA][$key]); $temp = substr($temp, 0, -$n_length - 1); $r1 = substr($x, -$n_length - 1); $r2 = substr(bcmul($temp, $n), -$n_length - 1); $result = bcsub($r1, $r2); //if (bccomp($result, '0') < 0) { if ($result[0] == '-') { $q = '1' . str_repeat('0', $n_length + 1); $result = bcadd($result, $q); } while (bccomp($result, $n) >= 0) { $result = bcsub($result, $n); } return $result; } } PK1A#]�\ Cy#y#Fvendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.phpnu�[���<?php /** * Pure-PHP 32-bit BigInteger Engine * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math\BigInteger\Engines; /** * Pure-PHP 32-bit Engine. * * Uses 64-bit floats if int size is 4 bits * * @author Jim Wigginton <terrafrost@php.net> */ class PHP32 extends PHP { // Constants used by PHP.php const BASE = 26; const BASE_FULL = 0x4000000; const MAX_DIGIT = 0x3FFFFFF; const MSB = 0x2000000; /** * MAX10 in greatest MAX10LEN satisfying * MAX10 = 10**MAX10LEN <= 2**BASE. */ const MAX10 = 10000000; /** * MAX10LEN in greatest MAX10LEN satisfying * MAX10 = 10**MAX10LEN <= 2**BASE. */ const MAX10LEN = 7; const MAX_DIGIT2 = 4503599627370496; /** * Initialize a PHP32 BigInteger Engine instance * * @param int $base * @see parent::initialize() */ protected function initialize($base) { if ($base != 256 && $base != -256) { return parent::initialize($base); } $val = $this->value; $this->value = []; $vals = &$this->value; $i = strlen($val); if (!$i) { return; } while (true) { $i -= 4; if ($i < 0) { if ($i == -4) { break; } $val = substr($val, 0, 4 + $i); $val = str_pad($val, 4, "\0", STR_PAD_LEFT); if ($val == "\0\0\0\0") { break; } $i = 0; } list(, $digit) = unpack('N', substr($val, $i, 4)); if ($digit < 0) { $digit += 0xFFFFFFFF + 1; } $step = count($vals) & 3; if ($step) { $digit = (int) floor($digit / pow(2, 2 * $step)); } if ($step != 3) { $digit = (int) fmod($digit, static::BASE_FULL); $i++; } $vals[] = $digit; } while (end($vals) === 0) { array_pop($vals); } reset($vals); } /** * Test for engine validity * * @see parent::__construct() * @return bool */ public static function isValidEngine() { return PHP_INT_SIZE >= 4 && !self::testJITOnWindows(); } /** * Adds two BigIntegers. * * @param PHP32 $y * @return PHP32 */ public function add(PHP32 $y) { $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Subtracts two BigIntegers. * * @param PHP32 $y * @return PHP32 */ public function subtract(PHP32 $y) { $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Multiplies two BigIntegers. * * @param PHP32 $y * @return PHP32 */ public function multiply(PHP32 $y) { $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative); return $this->convertToObj($temp); } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * @param PHP32 $y * @return array{PHP32, PHP32} */ public function divide(PHP32 $y) { return $this->divideHelper($y); } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * @param PHP32 $n * @return false|PHP32 */ public function modInverse(PHP32 $n) { return $this->modInverseHelper($n); } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * @param PHP32 $n * @return PHP32[] */ public function extendedGCD(PHP32 $n) { return $this->extendedGCDHelper($n); } /** * Calculates the greatest common divisor * * Say you have 693 and 609. The GCD is 21. * * @param PHP32 $n * @return PHP32 */ public function gcd(PHP32 $n) { return $this->extendedGCD($n)['gcd']; } /** * Logical And * * @param PHP32 $x * @return PHP32 */ public function bitwise_and(PHP32 $x) { return $this->bitwiseAndHelper($x); } /** * Logical Or * * @param PHP32 $x * @return PHP32 */ public function bitwise_or(PHP32 $x) { return $this->bitwiseOrHelper($x); } /** * Logical Exclusive Or * * @param PHP32 $x * @return PHP32 */ public function bitwise_xor(PHP32 $x) { return $this->bitwiseXorHelper($x); } /** * Compares two numbers. * * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is * demonstrated thusly: * * $x > $y: $x->compare($y) > 0 * $x < $y: $x->compare($y) < 0 * $x == $y: $x->compare($y) == 0 * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} * * @param PHP32 $y * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @see self::equals() */ public function compare(PHP32 $y) { return $this->compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative); } /** * Tests the equality of two numbers. * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * * @param PHP32 $x * @return bool */ public function equals(PHP32 $x) { return $this->value === $x->value && $this->is_negative == $x->is_negative; } /** * Performs modular exponentiation. * * @param PHP32 $e * @param PHP32 $n * @return PHP32 */ public function modPow(PHP32 $e, PHP32 $n) { return $this->powModOuter($e, $n); } /** * Performs modular exponentiation. * * Alias for modPow(). * * @param PHP32 $e * @param PHP32 $n * @return PHP32 */ public function powMod(PHP32 $e, PHP32 $n) { return $this->powModOuter($e, $n); } /** * Generate a random prime number between a range * * If there's not a prime within the given range, false will be returned. * * @param PHP32 $min * @param PHP32 $max * @return false|PHP32 */ public static function randomRangePrime(PHP32 $min, PHP32 $max) { return self::randomRangePrimeOuter($min, $max); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param PHP32 $min * @param PHP32 $max * @return PHP32 */ public static function randomRange(PHP32 $min, PHP32 $max) { return self::randomRangeHelper($min, $max); } /** * Performs exponentiation. * * @param PHP32 $n * @return PHP32 */ public function pow(PHP32 $n) { return $this->powHelper($n); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param PHP32 ...$nums * @return PHP32 */ public static function min(PHP32 ...$nums) { return self::minHelper($nums); } /** * Return the maximum BigInteger between an arbitrary number of BigIntegers. * * @param PHP32 ...$nums * @return PHP32 */ public static function max(PHP32 ...$nums) { return self::maxHelper($nums); } /** * Tests BigInteger to see if it is between two integers, inclusive * * @param PHP32 $min * @param PHP32 $max * @return bool */ public function between(PHP32 $min, PHP32 $max) { return $this->compare($min) >= 0 && $this->compare($max) <= 0; } } PK1A#]q1÷�5�5Avendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.phpnu�[���<?php /** * Binary Finite Fields * * In a binary finite field numbers are actually polynomial equations. If you * represent the number as a sequence of bits you get a sequence of 1's or 0's. * These 1's or 0's represent the coefficients of the x**n, where n is the * location of the given bit. When you add numbers over a binary finite field * the result should have a coefficient of 1 or 0 as well. Hence addition * and subtraction become the same operation as XOR. * eg. 1 + 1 + 1 == 3 % 2 == 1 or 0 - 1 == -1 % 2 == 1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math\BinaryField; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BigInteger; use phpseclib3\Math\BinaryField; use phpseclib3\Math\Common\FiniteField\Integer as Base; /** * Binary Finite Fields * * @author Jim Wigginton <terrafrost@php.net> */ class Integer extends Base { /** * Holds the BinaryField's value * * @var string */ protected $value; /** * Keeps track of current instance * * @var int */ protected $instanceID; /** * Holds the PrimeField's modulo * * @var array<int, string> */ protected static $modulo; /** * Holds a pre-generated function to perform modulo reductions * * @var callable[] */ protected static $reduce; /** * Default constructor */ public function __construct($instanceID, $num = '') { $this->instanceID = $instanceID; if (!strlen($num)) { $this->value = ''; } else { $reduce = static::$reduce[$instanceID]; $this->value = $reduce($num); } } /** * Set the modulo for a given instance * @param int $instanceID * @param string $modulo */ public static function setModulo($instanceID, $modulo) { static::$modulo[$instanceID] = $modulo; } /** * Set the modulo for a given instance */ public static function setRecurringModuloFunction($instanceID, callable $function) { static::$reduce[$instanceID] = $function; } /** * Tests a parameter to see if it's of the right instance * * Throws an exception if the incorrect class is being utilized */ private static function checkInstance(self $x, self $y) { if ($x->instanceID != $y->instanceID) { throw new \UnexpectedValueException('The instances of the two BinaryField\Integer objects do not match'); } } /** * Tests the equality of two numbers. * * @return bool */ public function equals(self $x) { static::checkInstance($this, $x); return $this->value == $x->value; } /** * Compares two numbers. * * @return int */ public function compare(self $x) { static::checkInstance($this, $x); $a = $this->value; $b = $x->value; $length = max(strlen($a), strlen($b)); $a = str_pad($a, $length, "\0", STR_PAD_LEFT); $b = str_pad($b, $length, "\0", STR_PAD_LEFT); return strcmp($a, $b); } /** * Returns the degree of the polynomial * * @param string $x * @return int */ private static function deg($x) { $x = ltrim($x, "\0"); $xbit = decbin(ord($x[0])); $xlen = $xbit == '0' ? 0 : strlen($xbit); $len = strlen($x); if (!$len) { return -1; } return 8 * strlen($x) - 9 + $xlen; } /** * Perform polynomial division * * @return string[] * @link https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division */ private static function polynomialDivide($x, $y) { // in wikipedia's description of the algorithm, lc() is the leading coefficient. over a binary field that's // always going to be 1. $q = chr(0); $d = static::deg($y); $r = $x; while (($degr = static::deg($r)) >= $d) { $s = '1' . str_repeat('0', $degr - $d); $s = BinaryField::base2ToBase256($s); $length = max(strlen($s), strlen($q)); $q = !isset($q) ? $s : str_pad($q, $length, "\0", STR_PAD_LEFT) ^ str_pad($s, $length, "\0", STR_PAD_LEFT); $s = static::polynomialMultiply($s, $y); $length = max(strlen($r), strlen($s)); $r = str_pad($r, $length, "\0", STR_PAD_LEFT) ^ str_pad($s, $length, "\0", STR_PAD_LEFT); } return [ltrim($q, "\0"), ltrim($r, "\0")]; } /** * Perform polynomial multiplation in the traditional way * * @return string * @link https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication */ private static function regularPolynomialMultiply($x, $y) { $precomputed = [ltrim($x, "\0")]; $x = strrev(BinaryField::base256ToBase2($x)); $y = strrev(BinaryField::base256ToBase2($y)); if (strlen($x) == strlen($y)) { $length = strlen($x); } else { $length = max(strlen($x), strlen($y)); $x = str_pad($x, $length, '0'); $y = str_pad($y, $length, '0'); } $result = str_repeat('0', 2 * $length - 1); $result = BinaryField::base2ToBase256($result); $size = strlen($result); $x = strrev($x); // precompute left shift 1 through 7 for ($i = 1; $i < 8; $i++) { $precomputed[$i] = BinaryField::base2ToBase256($x . str_repeat('0', $i)); } for ($i = 0; $i < strlen($y); $i++) { if ($y[$i] == '1') { $temp = $precomputed[$i & 7] . str_repeat("\0", $i >> 3); $result ^= str_pad($temp, $size, "\0", STR_PAD_LEFT); } } return $result; } /** * Perform polynomial multiplation * * Uses karatsuba multiplication to reduce x-bit multiplications to a series of 32-bit multiplications * * @return string * @link https://en.wikipedia.org/wiki/Karatsuba_algorithm */ private static function polynomialMultiply($x, $y) { if (strlen($x) == strlen($y)) { $length = strlen($x); } else { $length = max(strlen($x), strlen($y)); $x = str_pad($x, $length, "\0", STR_PAD_LEFT); $y = str_pad($y, $length, "\0", STR_PAD_LEFT); } switch (true) { case PHP_INT_SIZE == 8 && $length <= 4: return $length != 4 ? self::subMultiply(str_pad($x, 4, "\0", STR_PAD_LEFT), str_pad($y, 4, "\0", STR_PAD_LEFT)) : self::subMultiply($x, $y); case PHP_INT_SIZE == 4 || $length > 32: return self::regularPolynomialMultiply($x, $y); } $m = $length >> 1; $x1 = substr($x, 0, -$m); $x0 = substr($x, -$m); $y1 = substr($y, 0, -$m); $y0 = substr($y, -$m); $z2 = self::polynomialMultiply($x1, $y1); $z0 = self::polynomialMultiply($x0, $y0); $z1 = self::polynomialMultiply( self::subAdd2($x1, $x0), self::subAdd2($y1, $y0) ); $z1 = self::subAdd3($z1, $z2, $z0); $xy = self::subAdd3( $z2 . str_repeat("\0", 2 * $m), $z1 . str_repeat("\0", $m), $z0 ); return ltrim($xy, "\0"); } /** * Perform polynomial multiplication on 2x 32-bit numbers, returning * a 64-bit number * * @param string $x * @param string $y * @return string * @link https://www.bearssl.org/constanttime.html#ghash-for-gcm */ private static function subMultiply($x, $y) { $x = unpack('N', $x)[1]; $y = unpack('N', $y)[1]; $x0 = $x & 0x11111111; $x1 = $x & 0x22222222; $x2 = $x & 0x44444444; $x3 = $x & 0x88888888; $y0 = $y & 0x11111111; $y1 = $y & 0x22222222; $y2 = $y & 0x44444444; $y3 = $y & 0x88888888; $z0 = ($x0 * $y0) ^ ($x1 * $y3) ^ ($x2 * $y2) ^ ($x3 * $y1); $z1 = ($x0 * $y1) ^ ($x1 * $y0) ^ ($x2 * $y3) ^ ($x3 * $y2); $z2 = ($x0 * $y2) ^ ($x1 * $y1) ^ ($x2 * $y0) ^ ($x3 * $y3); $z3 = ($x0 * $y3) ^ ($x1 * $y2) ^ ($x2 * $y1) ^ ($x3 * $y0); $z0 &= 0x1111111111111111; $z1 &= 0x2222222222222222; $z2 &= 0x4444444444444444; $z3 &= -8608480567731124088; // 0x8888888888888888 gets interpreted as a float $z = $z0 | $z1 | $z2 | $z3; return pack('J', $z); } /** * Adds two numbers * * @param string $x * @param string $y * @return string */ private static function subAdd2($x, $y) { $length = max(strlen($x), strlen($y)); $x = str_pad($x, $length, "\0", STR_PAD_LEFT); $y = str_pad($y, $length, "\0", STR_PAD_LEFT); return $x ^ $y; } /** * Adds three numbers * * @param string $x * @param string $y * @return string */ private static function subAdd3($x, $y, $z) { $length = max(strlen($x), strlen($y), strlen($z)); $x = str_pad($x, $length, "\0", STR_PAD_LEFT); $y = str_pad($y, $length, "\0", STR_PAD_LEFT); $z = str_pad($z, $length, "\0", STR_PAD_LEFT); return $x ^ $y ^ $z; } /** * Adds two BinaryFieldIntegers. * * @return static */ public function add(self $y) { static::checkInstance($this, $y); $length = strlen(static::$modulo[$this->instanceID]); $x = str_pad($this->value, $length, "\0", STR_PAD_LEFT); $y = str_pad($y->value, $length, "\0", STR_PAD_LEFT); return new static($this->instanceID, $x ^ $y); } /** * Subtracts two BinaryFieldIntegers. * * @return static */ public function subtract(self $x) { return $this->add($x); } /** * Multiplies two BinaryFieldIntegers. * * @return static */ public function multiply(self $y) { static::checkInstance($this, $y); return new static($this->instanceID, static::polynomialMultiply($this->value, $y->value)); } /** * Returns the modular inverse of a BinaryFieldInteger * * @return static */ public function modInverse() { $remainder0 = static::$modulo[$this->instanceID]; $remainder1 = $this->value; if ($remainder1 == '') { return new static($this->instanceID); } $aux0 = "\0"; $aux1 = "\1"; while ($remainder1 != "\1") { list($q, $r) = static::polynomialDivide($remainder0, $remainder1); $remainder0 = $remainder1; $remainder1 = $r; // the auxiliary in row n is given by the sum of the auxiliary in // row n-2 and the product of the quotient and the auxiliary in row // n-1 $temp = static::polynomialMultiply($aux1, $q); $aux = str_pad($aux0, strlen($temp), "\0", STR_PAD_LEFT) ^ str_pad($temp, strlen($aux0), "\0", STR_PAD_LEFT); $aux0 = $aux1; $aux1 = $aux; } $temp = new static($this->instanceID); $temp->value = ltrim($aux1, "\0"); return $temp; } /** * Divides two PrimeFieldIntegers. * * @return static */ public function divide(self $x) { static::checkInstance($this, $x); $x = $x->modInverse(); return $this->multiply($x); } /** * Negate * * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo * so 0-12 is the same thing as modulo-12 * * @return object */ public function negate() { $x = str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT); return new static($this->instanceID, $x ^ static::$modulo[$this->instanceID]); } /** * Returns the modulo * * @return string */ public static function getModulo($instanceID) { return static::$modulo[$instanceID]; } /** * Converts an Integer to a byte string (eg. base-256). * * @return string */ public function toBytes() { return str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT); } /** * Converts an Integer to a hex string (eg. base-16). * * @return string */ public function toHex() { return Strings::bin2hex($this->toBytes()); } /** * Converts an Integer to a bit string (eg. base-2). * * @return string */ public function toBits() { //return str_pad(BinaryField::base256ToBase2($this->value), strlen(static::$modulo[$this->instanceID]), '0', STR_PAD_LEFT); return BinaryField::base256ToBase2($this->value); } /** * Converts an Integer to a BigInteger * * @return string */ public function toBigInteger() { return new BigInteger($this->value, 256); } /** * __toString() magic method * */ public function __toString() { return (string) $this->toBigInteger(); } /** * __debugInfo() magic method * */ public function __debugInfo() { return ['value' => $this->toHex()]; } } PK1A#]�ջ^�V�V8vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.phpnu�[���<?php /** * Pure-PHP arbitrary precision integer arithmetic library. * * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available, * and an internal implementation, otherwise. * * PHP version 5 and 7 * * Here's an example of how to use this library: * <code> * <?php * $a = new \phpseclib3\Math\BigInteger(2); * $b = new \phpseclib3\Math\BigInteger(3); * * $c = $a->add($b); * * echo $c->toString(); // outputs 5 * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Math\BigInteger\Engines\Engine; /** * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 * numbers. * * @author Jim Wigginton <terrafrost@php.net> */ class BigInteger implements \JsonSerializable { /** * Main Engine * * @var class-string<Engine> */ private static $mainEngine; /** * Selected Engines * * @var list<string> */ private static $engines; /** * The actual BigInteger object * * @var object */ private $value; /** * Mode independent value used for serialization. * * @see self::__sleep() * @see self::__wakeup() * @var string */ private $hex; /** * Precision (used only for serialization) * * @see self::__sleep() * @see self::__wakeup() * @var int */ private $precision; /** * Sets engine type. * * Throws an exception if the type is invalid * * @param string $main * @param list<string> $modexps optional * @return void */ public static function setEngine($main, array $modexps = ['DefaultEngine']) { self::$engines = []; $fqmain = 'phpseclib3\\Math\\BigInteger\\Engines\\' . $main; if (!class_exists($fqmain) || !method_exists($fqmain, 'isValidEngine')) { throw new \InvalidArgumentException("$main is not a valid engine"); } if (!$fqmain::isValidEngine()) { throw new BadConfigurationException("$main is not setup correctly on this system"); } /** @var class-string<Engine> $fqmain */ self::$mainEngine = $fqmain; $found = false; foreach ($modexps as $modexp) { try { $fqmain::setModExpEngine($modexp); $found = true; break; } catch (\Exception $e) { } } if (!$found) { throw new BadConfigurationException("No valid modular exponentiation engine found for $main"); } self::$engines = [$main, $modexp]; } /** * Returns the engine type * * @return string[] */ public static function getEngine() { self::initialize_static_variables(); return self::$engines; } /** * Initialize static variables */ private static function initialize_static_variables() { if (!isset(self::$mainEngine)) { $engines = [ ['GMP', ['DefaultEngine']], ['PHP64', ['OpenSSL']], ['BCMath', ['OpenSSL']], ['PHP32', ['OpenSSL']], ['PHP64', ['DefaultEngine']], ['PHP32', ['DefaultEngine']] ]; foreach ($engines as $engine) { try { self::setEngine($engine[0], $engine[1]); return; } catch (\Exception $e) { } } throw new \UnexpectedValueException('No valid BigInteger found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available so either disable JIT or install GMP / BCMath'); } } /** * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. * * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. * * @param string|int|BigInteger\Engines\Engine $x Base-10 number or base-$base number if $base set. * @param int $base */ public function __construct($x = 0, $base = 10) { self::initialize_static_variables(); if ($x instanceof self::$mainEngine) { $this->value = clone $x; } elseif ($x instanceof BigInteger\Engines\Engine) { $this->value = new static("$x"); $this->value->setPrecision($x->getPrecision()); } else { $this->value = new self::$mainEngine($x, $base); } } /** * Converts a BigInteger to a base-10 number. * * @return string */ public function toString() { return $this->value->toString(); } /** * __toString() magic method */ public function __toString() { return (string)$this->value; } /** * __debugInfo() magic method * * Will be called, automatically, when print_r() or var_dump() are called */ public function __debugInfo() { return $this->value->__debugInfo(); } /** * Converts a BigInteger to a byte string (eg. base-256). * * @param bool $twos_compliment * @return string */ public function toBytes($twos_compliment = false) { return $this->value->toBytes($twos_compliment); } /** * Converts a BigInteger to a hex string (eg. base-16). * * @param bool $twos_compliment * @return string */ public function toHex($twos_compliment = false) { return $this->value->toHex($twos_compliment); } /** * Converts a BigInteger to a bit string (eg. base-2). * * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * * @param bool $twos_compliment * @return string */ public function toBits($twos_compliment = false) { return $this->value->toBits($twos_compliment); } /** * Adds two BigIntegers. * * @param BigInteger $y * @return BigInteger */ public function add(BigInteger $y) { return new static($this->value->add($y->value)); } /** * Subtracts two BigIntegers. * * @param BigInteger $y * @return BigInteger */ public function subtract(BigInteger $y) { return new static($this->value->subtract($y->value)); } /** * Multiplies two BigIntegers * * @param BigInteger $x * @return BigInteger */ public function multiply(BigInteger $x) { return new static($this->value->multiply($x->value)); } /** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * * Here's an example: * <code> * <?php * $a = new \phpseclib3\Math\BigInteger('10'); * $b = new \phpseclib3\Math\BigInteger('20'); * * list($quotient, $remainder) = $a->divide($b); * * echo $quotient->toString(); // outputs 0 * echo "\r\n"; * echo $remainder->toString(); // outputs 10 * ?> * </code> * * @param BigInteger $y * @return BigInteger[] */ public function divide(BigInteger $y) { list($q, $r) = $this->value->divide($y->value); return [ new static($q), new static($r) ]; } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * * @param BigInteger $n * @return BigInteger */ public function modInverse(BigInteger $n) { return new static($this->value->modInverse($n->value)); } /** * Calculates modular inverses. * * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. * * @param BigInteger $n * @return BigInteger[] */ public function extendedGCD(BigInteger $n) { extract($this->value->extendedGCD($n->value)); /** * @var BigInteger $gcd * @var BigInteger $x * @var BigInteger $y */ return [ 'gcd' => new static($gcd), 'x' => new static($x), 'y' => new static($y) ]; } /** * Calculates the greatest common divisor * * Say you have 693 and 609. The GCD is 21. * * @param BigInteger $n * @return BigInteger */ public function gcd(BigInteger $n) { return new static($this->value->gcd($n->value)); } /** * Absolute value. * * @return BigInteger */ public function abs() { return new static($this->value->abs()); } /** * Set Precision * * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * * @param int $bits */ public function setPrecision($bits) { $this->value->setPrecision($bits); } /** * Get Precision * * Returns the precision if it exists, false if it doesn't * * @return int|bool */ public function getPrecision() { return $this->value->getPrecision(); } /** * Serialize * * Will be called, automatically, when serialize() is called on a BigInteger object. * * __sleep() / __wakeup() have been around since PHP 4.0 * * \Serializable was introduced in PHP 5.1 and deprecated in PHP 8.1: * https://wiki.php.net/rfc/phase_out_serializable * * __serialize() / __unserialize() were introduced in PHP 7.4: * https://wiki.php.net/rfc/custom_object_serialization * * @return array */ public function __sleep() { $this->hex = $this->toHex(true); $vars = ['hex']; if ($this->getPrecision() > 0) { $vars[] = 'precision'; } return $vars; } /** * Serialize * * Will be called, automatically, when unserialize() is called on a BigInteger object. */ public function __wakeup() { $temp = new static($this->hex, -16); $this->value = $temp->value; if ($this->precision > 0) { // recalculate $this->bitmask $this->setPrecision($this->precision); } } /** * JSON Serialize * * Will be called, automatically, when json_encode() is called on a BigInteger object. * * @return array{hex: string, precision?: int] */ #[\ReturnTypeWillChange] public function jsonSerialize() { $result = ['hex' => $this->toHex(true)]; if ($this->precision > 0) { $result['precision'] = $this->getPrecision(); } return $result; } /** * Performs modular exponentiation. * * @param BigInteger $e * @param BigInteger $n * @return BigInteger */ public function powMod(BigInteger $e, BigInteger $n) { return new static($this->value->powMod($e->value, $n->value)); } /** * Performs modular exponentiation. * * @param BigInteger $e * @param BigInteger $n * @return BigInteger */ public function modPow(BigInteger $e, BigInteger $n) { return new static($this->value->modPow($e->value, $n->value)); } /** * Compares two numbers. * * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this * is demonstrated thusly: * * $x > $y: $x->compare($y) > 0 * $x < $y: $x->compare($y) < 0 * $x == $y: $x->compare($y) == 0 * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} * * @param BigInteger $y * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @see self::equals() */ public function compare(BigInteger $y) { return $this->value->compare($y->value); } /** * Tests the equality of two numbers. * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * * @param BigInteger $x * @return bool */ public function equals(BigInteger $x) { return $this->value->equals($x->value); } /** * Logical Not * * @return BigInteger */ public function bitwise_not() { return new static($this->value->bitwise_not()); } /** * Logical And * * @param BigInteger $x * @return BigInteger */ public function bitwise_and(BigInteger $x) { return new static($this->value->bitwise_and($x->value)); } /** * Logical Or * * @param BigInteger $x * @return BigInteger */ public function bitwise_or(BigInteger $x) { return new static($this->value->bitwise_or($x->value)); } /** * Logical Exclusive Or * * @param BigInteger $x * @return BigInteger */ public function bitwise_xor(BigInteger $x) { return new static($this->value->bitwise_xor($x->value)); } /** * Logical Right Shift * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * * @param int $shift * @return BigInteger */ public function bitwise_rightShift($shift) { return new static($this->value->bitwise_rightShift($shift)); } /** * Logical Left Shift * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * * @param int $shift * @return BigInteger */ public function bitwise_leftShift($shift) { return new static($this->value->bitwise_leftShift($shift)); } /** * Logical Left Rotate * * Instead of the top x bits being dropped they're appended to the shifted bit string. * * @param int $shift * @return BigInteger */ public function bitwise_leftRotate($shift) { return new static($this->value->bitwise_leftRotate($shift)); } /** * Logical Right Rotate * * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. * * @param int $shift * @return BigInteger */ public function bitwise_rightRotate($shift) { return new static($this->value->bitwise_rightRotate($shift)); } /** * Returns the smallest and largest n-bit number * * @param int $bits * @return BigInteger[] */ public static function minMaxBits($bits) { self::initialize_static_variables(); $class = self::$mainEngine; extract($class::minMaxBits($bits)); /** @var BigInteger $min * @var BigInteger $max */ return [ 'min' => new static($min), 'max' => new static($max) ]; } /** * Return the size of a BigInteger in bits * * @return int */ public function getLength() { return $this->value->getLength(); } /** * Return the size of a BigInteger in bytes * * @return int */ public function getLengthInBytes() { return $this->value->getLengthInBytes(); } /** * Generates a random number of a certain size * * Bit length is equal to $size * * @param int $size * @return BigInteger */ public static function random($size) { self::initialize_static_variables(); $class = self::$mainEngine; return new static($class::random($size)); } /** * Generates a random prime number of a certain size * * Bit length is equal to $size * * @param int $size * @return BigInteger */ public static function randomPrime($size) { self::initialize_static_variables(); $class = self::$mainEngine; return new static($class::randomPrime($size)); } /** * Generate a random prime number between a range * * If there's not a prime within the given range, false will be returned. * * @param BigInteger $min * @param BigInteger $max * @return false|BigInteger */ public static function randomRangePrime(BigInteger $min, BigInteger $max) { $class = self::$mainEngine; return new static($class::randomRangePrime($min->value, $max->value)); } /** * Generate a random number between a range * * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * * BigInteger::randomRange($min, $max) * BigInteger::randomRange($max, $min) * * @param BigInteger $min * @param BigInteger $max * @return BigInteger */ public static function randomRange(BigInteger $min, BigInteger $max) { $class = self::$mainEngine; return new static($class::randomRange($min->value, $max->value)); } /** * Checks a numer to see if it's prime * * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads * on a website instead of just one. * * @param int|bool $t * @return bool */ public function isPrime($t = false) { return $this->value->isPrime($t); } /** * Calculates the nth root of a biginteger. * * Returns the nth root of a positive biginteger, where n defaults to 2 * * @param int $n optional * @return BigInteger */ public function root($n = 2) { return new static($this->value->root($n)); } /** * Performs exponentiation. * * @param BigInteger $n * @return BigInteger */ public function pow(BigInteger $n) { return new static($this->value->pow($n->value)); } /** * Return the minimum BigInteger between an arbitrary number of BigIntegers. * * @param BigInteger ...$nums * @return BigInteger */ public static function min(BigInteger ...$nums) { $class = self::$mainEngine; $nums = array_map(function ($num) { return $num->value; }, $nums); return new static($class::min(...$nums)); } /** * Return the maximum BigInteger between an arbitrary number of BigIntegers. * * @param BigInteger ...$nums * @return BigInteger */ public static function max(BigInteger ...$nums) { $class = self::$mainEngine; $nums = array_map(function ($num) { return $num->value; }, $nums); return new static($class::max(...$nums)); } /** * Tests BigInteger to see if it is between two integers, inclusive * * @param BigInteger $min * @param BigInteger $max * @return bool */ public function between(BigInteger $min, BigInteger $max) { return $this->value->between($min->value, $max->value); } /** * Clone */ public function __clone() { $this->value = clone $this->value; } /** * Is Odd? * * @return bool */ public function isOdd() { return $this->value->isOdd(); } /** * Tests if a bit is set * * @param int $x * @return bool */ public function testBit($x) { return $this->value->testBit($x); } /** * Is Negative? * * @return bool */ public function isNegative() { return $this->value->isNegative(); } /** * Negate * * Given $k, returns -$k * * @return BigInteger */ public function negate() { return new static($this->value->negate()); } /** * Scan for 1 and right shift by that amount * * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); * * @param BigInteger $r * @return int */ public static function scan1divide(BigInteger $r) { $class = self::$mainEngine; return $class::scan1divide($r->value); } /** * Create Recurring Modulo Function * * Sometimes it may be desirable to do repeated modulos with the same number outside of * modular exponentiation * * @return callable */ public function createRecurringModuloFunction() { $func = $this->value->createRecurringModuloFunction(); return function (BigInteger $x) use ($func) { return new static($func($x->value)); }; } /** * Bitwise Split * * Splits BigInteger's into chunks of $split bits * * @param int $split * @return BigInteger[] */ public function bitwise_split($split) { return array_map(function ($val) { return new static($val); }, $this->value->bitwise_split($split)); } } PK1A#]e=�G��9vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.phpnu�[���<?php /** * Binary Finite Fields * * Utilizes the factory design pattern * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BinaryField\Integer; use phpseclib3\Math\Common\FiniteField; /** * Binary Finite Fields * * @author Jim Wigginton <terrafrost@php.net> */ class BinaryField extends FiniteField { /** * Instance Counter * * @var int */ private static $instanceCounter = 0; /** * Keeps track of current instance * * @var int */ protected $instanceID; /** @var BigInteger */ private $randomMax; /** * Default constructor */ public function __construct(...$indices) { $m = array_shift($indices); if ($m > 571) { /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines altho theoretically there may be legit reasons to use binary finite fields with larger degrees imposing a limit on the maximum size is both reasonable and precedented. in particular, http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says "implementations SHOULD check that the packet length is reasonable in order for the implementation to avoid denial of service and/or buffer overflow attacks" */ throw new \OutOfBoundsException('Degrees larger than 571 are not supported'); } $val = str_repeat('0', $m) . '1'; foreach ($indices as $index) { $val[$index] = '1'; } $modulo = static::base2ToBase256(strrev($val)); $mStart = 2 * $m - 2; $t = ceil($m / 8); $finalMask = chr((1 << ($m % 8)) - 1); if ($finalMask == "\0") { $finalMask = "\xFF"; } $bitLen = $mStart + 1; $pad = ceil($bitLen / 8); $h = $bitLen & 7; $h = $h ? 8 - $h : 0; $r = rtrim(substr($val, 0, -1), '0'); $u = [static::base2ToBase256(strrev($r))]; for ($i = 1; $i < 8; $i++) { $u[] = static::base2ToBase256(strrev(str_repeat('0', $i) . $r)); } // implements algorithm 2.40 (in section 2.3.5) in "Guide to Elliptic Curve Cryptography" // with W = 8 $reduce = function ($c) use ($u, $mStart, $m, $t, $finalMask, $pad, $h) { $c = str_pad($c, $pad, "\0", STR_PAD_LEFT); for ($i = $mStart; $i >= $m;) { $g = $h >> 3; $mask = $h & 7; $mask = $mask ? 1 << (7 - $mask) : 0x80; for (; $mask > 0; $mask >>= 1, $i--, $h++) { if (ord($c[$g]) & $mask) { $temp = $i - $m; $j = $temp >> 3; $k = $temp & 7; $t1 = $j ? substr($c, 0, -$j) : $c; $length = strlen($t1); if ($length) { $t2 = str_pad($u[$k], $length, "\0", STR_PAD_LEFT); $temp = $t1 ^ $t2; $c = $j ? substr_replace($c, $temp, 0, $length) : $temp; } } } } $c = substr($c, -$t); if (strlen($c) == $t) { $c[0] = $c[0] & $finalMask; } return ltrim($c, "\0"); }; $this->instanceID = self::$instanceCounter++; Integer::setModulo($this->instanceID, $modulo); Integer::setRecurringModuloFunction($this->instanceID, $reduce); $this->randomMax = new BigInteger($modulo, 2); } /** * Returns an instance of a dynamically generated PrimeFieldInteger class * * @param string $num * @return Integer */ public function newInteger($num) { return new Integer($this->instanceID, $num instanceof BigInteger ? $num->toBytes() : $num); } /** * Returns an integer on the finite field between one and the prime modulo * * @return Integer */ public function randomInteger() { static $one; if (!isset($one)) { $one = new BigInteger(1); } return new Integer($this->instanceID, BigInteger::randomRange($one, $this->randomMax)->toBytes()); } /** * Returns the length of the modulo in bytes * * @return int */ public function getLengthInBytes() { return strlen(Integer::getModulo($this->instanceID)); } /** * Returns the length of the modulo in bits * * @return int */ public function getLength() { return strlen(Integer::getModulo($this->instanceID)) << 3; } /** * Converts a base-2 string to a base-256 string * * @param string $x * @param int|null $size * @return string */ public static function base2ToBase256($x, $size = null) { $str = Strings::bits2bin($x); $pad = strlen($x) >> 3; if (strlen($x) & 3) { $pad++; } $str = str_pad($str, $pad, "\0", STR_PAD_LEFT); if (isset($size)) { $str = str_pad($str, $size, "\0", STR_PAD_LEFT); } return $str; } /** * Converts a base-256 string to a base-2 string * * @param string $x * @return string */ public static function base256ToBase2($x) { if (function_exists('gmp_import')) { return gmp_strval(gmp_import($x), 2); } return Strings::bin2bits($x); } } PK1A#])��(�(@vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.phpnu�[���<?php /** * Prime Finite Fields * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math\PrimeField; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BigInteger; use phpseclib3\Math\Common\FiniteField\Integer as Base; /** * Prime Finite Fields * * @author Jim Wigginton <terrafrost@php.net> */ class Integer extends Base { /** * Holds the PrimeField's value * * @var BigInteger */ protected $value; /** * Keeps track of current instance * * @var int */ protected $instanceID; /** * Holds the PrimeField's modulo * * @var array<int, BigInteger> */ protected static $modulo; /** * Holds a pre-generated function to perform modulo reductions * * @var array<int, callable(BigInteger):BigInteger> */ protected static $reduce; /** * Zero * * @var BigInteger */ protected static $zero; /** * Default constructor * * @param int $instanceID */ public function __construct($instanceID, BigInteger $num = null) { $this->instanceID = $instanceID; if (!isset($num)) { $this->value = clone static::$zero[static::class]; } else { $reduce = static::$reduce[$instanceID]; $this->value = $reduce($num); } } /** * Set the modulo for a given instance * * @param int $instanceID * @return void */ public static function setModulo($instanceID, BigInteger $modulo) { static::$modulo[$instanceID] = $modulo; } /** * Set the modulo for a given instance * * @param int $instanceID * @return void */ public static function setRecurringModuloFunction($instanceID, callable $function) { static::$reduce[$instanceID] = $function; if (!isset(static::$zero[static::class])) { static::$zero[static::class] = new BigInteger(); } } /** * Delete the modulo for a given instance */ public static function cleanupCache($instanceID) { unset(static::$modulo[$instanceID]); unset(static::$reduce[$instanceID]); } /** * Returns the modulo * * @param int $instanceID * @return BigInteger */ public static function getModulo($instanceID) { return static::$modulo[$instanceID]; } /** * Tests a parameter to see if it's of the right instance * * Throws an exception if the incorrect class is being utilized * * @return void */ public static function checkInstance(self $x, self $y) { if ($x->instanceID != $y->instanceID) { throw new \UnexpectedValueException('The instances of the two PrimeField\Integer objects do not match'); } } /** * Tests the equality of two numbers. * * @return bool */ public function equals(self $x) { static::checkInstance($this, $x); return $this->value->equals($x->value); } /** * Compares two numbers. * * @return int */ public function compare(self $x) { static::checkInstance($this, $x); return $this->value->compare($x->value); } /** * Adds two PrimeFieldIntegers. * * @return static */ public function add(self $x) { static::checkInstance($this, $x); $temp = new static($this->instanceID); $temp->value = $this->value->add($x->value); if ($temp->value->compare(static::$modulo[$this->instanceID]) >= 0) { $temp->value = $temp->value->subtract(static::$modulo[$this->instanceID]); } return $temp; } /** * Subtracts two PrimeFieldIntegers. * * @return static */ public function subtract(self $x) { static::checkInstance($this, $x); $temp = new static($this->instanceID); $temp->value = $this->value->subtract($x->value); if ($temp->value->isNegative()) { $temp->value = $temp->value->add(static::$modulo[$this->instanceID]); } return $temp; } /** * Multiplies two PrimeFieldIntegers. * * @return static */ public function multiply(self $x) { static::checkInstance($this, $x); return new static($this->instanceID, $this->value->multiply($x->value)); } /** * Divides two PrimeFieldIntegers. * * @return static */ public function divide(self $x) { static::checkInstance($this, $x); $denominator = $x->value->modInverse(static::$modulo[$this->instanceID]); return new static($this->instanceID, $this->value->multiply($denominator)); } /** * Performs power operation on a PrimeFieldInteger. * * @return static */ public function pow(BigInteger $x) { $temp = new static($this->instanceID); $temp->value = $this->value->powMod($x, static::$modulo[$this->instanceID]); return $temp; } /** * Calculates the square root * * @link https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm * @return static|false */ public function squareRoot() { static $one, $two; if (!isset($one)) { $one = new BigInteger(1); $two = new BigInteger(2); } $reduce = static::$reduce[$this->instanceID]; $p_1 = static::$modulo[$this->instanceID]->subtract($one); $q = clone $p_1; $s = BigInteger::scan1divide($q); list($pow) = $p_1->divide($two); for ($z = $one; !$z->equals(static::$modulo[$this->instanceID]); $z = $z->add($one)) { $temp = $z->powMod($pow, static::$modulo[$this->instanceID]); if ($temp->equals($p_1)) { break; } } $m = new BigInteger($s); $c = $z->powMod($q, static::$modulo[$this->instanceID]); $t = $this->value->powMod($q, static::$modulo[$this->instanceID]); list($temp) = $q->add($one)->divide($two); $r = $this->value->powMod($temp, static::$modulo[$this->instanceID]); while (!$t->equals($one)) { for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) { if ($t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) { break; } } if ($i->compare($m) == 0) { return false; } $b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), static::$modulo[$this->instanceID]); $m = $i; $c = $reduce($b->multiply($b)); $t = $reduce($t->multiply($c)); $r = $reduce($r->multiply($b)); } return new static($this->instanceID, $r); } /** * Is Odd? * * @return bool */ public function isOdd() { return $this->value->isOdd(); } /** * Negate * * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo * so 0-12 is the same thing as modulo-12 * * @return static */ public function negate() { return new static($this->instanceID, static::$modulo[$this->instanceID]->subtract($this->value)); } /** * Converts an Integer to a byte string (eg. base-256). * * @return string */ public function toBytes() { if (isset(static::$modulo[$this->instanceID])) { $length = static::$modulo[$this->instanceID]->getLengthInBytes(); return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); } return $this->value->toBytes(); } /** * Converts an Integer to a hex string (eg. base-16). * * @return string */ public function toHex() { return Strings::bin2hex($this->toBytes()); } /** * Converts an Integer to a bit string (eg. base-2). * * @return string */ public function toBits() { // return $this->value->toBits(); static $length; if (!isset($length)) { $length = static::$modulo[$this->instanceID]->getLength(); } return str_pad($this->value->toBits(), $length, '0', STR_PAD_LEFT); } /** * Returns the w-ary non-adjacent form (wNAF) * * @param int $w optional * @return array<int, int> */ public function getNAF($w = 1) { $w++; $mask = new BigInteger((1 << $w) - 1); $sub = new BigInteger(1 << $w); //$sub = new BigInteger(1 << ($w - 1)); $d = $this->toBigInteger(); $d_i = []; $i = 0; while ($d->compare(static::$zero[static::class]) > 0) { if ($d->isOdd()) { // start mods $bigInteger = $d->testBit($w - 1) ? $d->bitwise_and($mask)->subtract($sub) : //$sub->subtract($d->bitwise_and($mask)) : $d->bitwise_and($mask); // end mods $d = $d->subtract($bigInteger); $d_i[$i] = (int) $bigInteger->toString(); } else { $d_i[$i] = 0; } $shift = !$d->equals(static::$zero[static::class]) && $d->bitwise_and($mask)->equals(static::$zero[static::class]) ? $w : 1; // $w or $w + 1? $d = $d->bitwise_rightShift($shift); while (--$shift > 0) { $d_i[++$i] = 0; } $i++; } return $d_i; } /** * Converts an Integer to a BigInteger * * @return BigInteger */ public function toBigInteger() { return clone $this->value; } /** * __toString() magic method * * @return string */ public function __toString() { return (string) $this->value; } /** * __debugInfo() magic method * * @return array */ public function __debugInfo() { return ['value' => $this->toHex()]; } } PK1A#]��www@vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.phpnu�[���<?php /** * Finite Fields Base Class * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math\Common; /** * Finite Fields * * @author Jim Wigginton <terrafrost@php.net> */ abstract class FiniteField { } PK1A#]@|�Hvendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.phpnu�[���<?php /** * Finite Field Integer Base Class * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Math\Common\FiniteField; /** * Finite Field Integer * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Integer implements \JsonSerializable { /** * JSON Serialize * * Will be called, automatically, when json_encode() is called on a BigInteger object. * * PHP Serialize isn't supported because unserializing would require the factory be * serialized as well and that just sounds like too much * * @return array{hex: string} */ #[\ReturnTypeWillChange] public function jsonSerialize() { return ['hex' => $this->toHex(true)]; } /** * Converts an Integer to a hex string (eg. base-16). * * @return string */ abstract public function toHex(); } PK1A#]�IV V 8vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.phpnu�[���<?php /** * Prime Finite Fields * * Utilizes the factory design pattern * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Math; use phpseclib3\Math\Common\FiniteField; use phpseclib3\Math\PrimeField\Integer; /** * Prime Finite Fields * * @author Jim Wigginton <terrafrost@php.net> */ class PrimeField extends FiniteField { /** * Instance Counter * * @var int */ private static $instanceCounter = 0; /** * Keeps track of current instance * * @var int */ protected $instanceID; /** * Default constructor */ public function __construct(BigInteger $modulo) { if (!$modulo->isPrime()) { throw new \UnexpectedValueException('PrimeField requires a prime number be passed to the constructor'); } $this->instanceID = self::$instanceCounter++; Integer::setModulo($this->instanceID, $modulo); Integer::setRecurringModuloFunction($this->instanceID, $modulo->createRecurringModuloFunction()); } /** * Use a custom defined modular reduction function * * @return void */ public function setReduction(\Closure $func) { $this->reduce = $func->bindTo($this, $this); } /** * Returns an instance of a dynamically generated PrimeFieldInteger class * * @return Integer */ public function newInteger(BigInteger $num) { return new Integer($this->instanceID, $num); } /** * Returns an integer on the finite field between one and the prime modulo * * @return Integer */ public function randomInteger() { static $one; if (!isset($one)) { $one = new BigInteger(1); } return new Integer($this->instanceID, BigInteger::randomRange($one, Integer::getModulo($this->instanceID))); } /** * Returns the length of the modulo in bytes * * @return int */ public function getLengthInBytes() { return Integer::getModulo($this->instanceID)->getLengthInBytes(); } /** * Returns the length of the modulo in bits * * @return int */ public function getLength() { return Integer::getModulo($this->instanceID)->getLength(); } /** * Destructor */ public function __destruct() { Integer::cleanupCache($this->instanceID); } } PK1A#]�hh0vendor/phpseclib/phpseclib/phpseclib/openssl.cnfnu�[���# minimalist openssl.cnf file for use with phpseclib HOME = . RANDFILE = $ENV::HOME/.rnd [ v3_ca ] PK1A#]�y��2vendor/phpseclib/phpseclib/phpseclib/bootstrap.phpnu�[���<?php /** * Bootstrapping File for phpseclib * * composer isn't a requirement for phpseclib 2.0 but this file isn't really required * either. it's a bonus for those using composer but if you're not phpseclib will * still work * * @license http://www.opensource.org/licenses/mit-license.html MIT License */ if (extension_loaded('mbstring')) { // 2 - MB_OVERLOAD_STRING // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0. if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) { throw new UnexpectedValueException( 'Overloading of string functions using mbstring.func_overload ' . 'is not supported by phpseclib.' ); } } PK1A#]�����Lvendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.phpnu�[���<?php /** * BadConfigurationException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * BadConfigurationException * * @author Jim Wigginton <terrafrost@php.net> */ class BadConfigurationException extends \RuntimeException { } PK1A#]ȍ���Cvendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.phpnu�[���<?php /** * BadModeException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * BadModeException * * @author Jim Wigginton <terrafrost@php.net> */ class BadModeException extends \RuntimeException { } PK1A#]��J���Qvendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.phpnu�[���<?php /** * NoSupportedAlgorithmsException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * NoSupportedAlgorithmsException * * @author Jim Wigginton <terrafrost@php.net> */ class NoSupportedAlgorithmsException extends \RuntimeException { } PK1A#]B��ۻ�Gvendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.phpnu�[���<?php /** * NoKeyLoadedException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * NoKeyLoadedException * * @author Jim Wigginton <terrafrost@php.net> */ class NoKeyLoadedException extends \RuntimeException { } PK1A#]y�=G��Mvendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.phpnu�[���<?php /** * InsufficientSetupException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * InsufficientSetupException * * @author Jim Wigginton <terrafrost@php.net> */ class InsufficientSetupException extends \RuntimeException { } PK1A#]��/���Mvendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.phpnu�[���<?php /** * InconsistentSetupException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * InconsistentSetupException * * @author Jim Wigginton <terrafrost@php.net> */ class InconsistentSetupException extends \RuntimeException { } PK1A#]Z��W��Pvendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.phpnu�[���<?php /** * UnsupportedOperationException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * UnsupportedOperationException * * @author Jim Wigginton <terrafrost@php.net> */ class UnsupportedOperationException extends \RuntimeException { } PK1A#]Pf���Mvendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.phpnu�[���<?php /** * UnsupportedFormatException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * UnsupportedFormatException * * @author Jim Wigginton <terrafrost@php.net> */ class UnsupportedFormatException extends \RuntimeException { } PK1A#]e�k��Kvendor/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.phpnu�[���<?php /** * UnableToConnectException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * UnableToConnectException * * @author Jim Wigginton <terrafrost@php.net> */ class UnableToConnectException extends \RuntimeException { } PK1A#]���Ivendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.phpnu�[���<?php /** * BadDecryptionException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * BadDecryptionException * * @author Jim Wigginton <terrafrost@php.net> */ class BadDecryptionException extends \RuntimeException { } PK1A#]QZ���Pvendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.phpnu�[���<?php /** * UnsupportedAlgorithmException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * UnsupportedAlgorithmException * * @author Jim Wigginton <terrafrost@php.net> */ class UnsupportedAlgorithmException extends \RuntimeException { } PK1A#]����Lvendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.phpnu�[���<?php /** * ConnectionClosedException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * ConnectionClosedException * * @author Jim Wigginton <terrafrost@php.net> */ class ConnectionClosedException extends \RuntimeException { } PK1A#]YԘm��Hvendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.phpnu�[���<?php /** * FileNotFoundException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * FileNotFoundException * * @author Jim Wigginton <terrafrost@php.net> */ class FileNotFoundException extends \RuntimeException { } PK1A#]� ���Lvendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.phpnu�[���<?php /** * UnsupportedCurveException * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Exception; /** * UnsupportedCurveException * * @author Jim Wigginton <terrafrost@php.net> */ class UnsupportedCurveException extends \RuntimeException { } PK1A#]i�K�!�!Bvendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.phpnu�[���<?php /** * Pure-PHP ssh-agent client. * * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent} * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\System\SSH\Agent; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\PrivateKey; use phpseclib3\Crypt\Common\PublicKey; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\EC; use phpseclib3\Crypt\RSA; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\System\SSH\Agent; use phpseclib3\System\SSH\Common\Traits\ReadBytes; /** * Pure-PHP ssh-agent client identity object * * Instantiation should only be performed by \phpseclib3\System\SSH\Agent class. * This could be thought of as implementing an interface that phpseclib3\Crypt\RSA * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. * The methods in this interface would be getPublicKey and sign since those are the * methods phpseclib looks for to perform public key authentication. * * @author Jim Wigginton <terrafrost@php.net> * @internal */ class Identity implements PrivateKey { use ReadBytes; // Signature Flags // See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 const SSH_AGENT_RSA2_256 = 2; const SSH_AGENT_RSA2_512 = 4; /** * Key Object * * @var PublicKey * @see self::getPublicKey() */ private $key; /** * Key Blob * * @var string * @see self::sign() */ private $key_blob; /** * Socket Resource * * @var resource * @see self::sign() */ private $fsock; /** * Signature flags * * @var int * @see self::sign() * @see self::setHash() */ private $flags = 0; /** * Curve Aliases * * @var array */ private static $curveAliases = [ 'secp256r1' => 'nistp256', 'secp384r1' => 'nistp384', 'secp521r1' => 'nistp521', 'Ed25519' => 'Ed25519' ]; /** * Default Constructor. * * @param resource $fsock */ public function __construct($fsock) { $this->fsock = $fsock; } /** * Set Public Key * * Called by \phpseclib3\System\SSH\Agent::requestIdentities() * * @param \phpseclib3\Crypt\Common\PublicKey $key */ public function withPublicKey(PublicKey $key) { if ($key instanceof EC) { if (is_array($key->getCurve()) || !isset(self::$curveAliases[$key->getCurve()])) { throw new UnsupportedAlgorithmException('The only supported curves are nistp256, nistp384, nistp512 and Ed25519'); } } $new = clone $this; $new->key = $key; return $new; } /** * Set Public Key * * Called by \phpseclib3\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key * but this saves a small amount of computation. * * @param string $key_blob */ public function withPublicKeyBlob($key_blob) { $new = clone $this; $new->key_blob = $key_blob; return $new; } /** * Get Public Key * * Wrapper for $this->key->getPublicKey() * * @param string $type optional * @return mixed */ public function getPublicKey($type = 'PKCS8') { return $this->key; } /** * Sets the hash * * @param string $hash */ public function withHash($hash) { $new = clone $this; $hash = strtolower($hash); if ($this->key instanceof RSA) { $new->flags = 0; switch ($hash) { case 'sha1': break; case 'sha256': $new->flags = self::SSH_AGENT_RSA2_256; break; case 'sha512': $new->flags = self::SSH_AGENT_RSA2_512; break; default: throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512'); } } if ($this->key instanceof EC) { switch ($this->key->getCurve()) { case 'secp256r1': $expectedHash = 'sha256'; break; case 'secp384r1': $expectedHash = 'sha384'; break; //case 'secp521r1': //case 'Ed25519': default: $expectedHash = 'sha512'; } if ($hash != $expectedHash) { throw new UnsupportedAlgorithmException('The only supported hash for ' . self::$curveAliases[$this->key->getCurve()] . ' is ' . $expectedHash); } } if ($this->key instanceof DSA) { if ($hash != 'sha1') { throw new UnsupportedAlgorithmException('The only supported hash for DSA is sha1'); } } return $new; } /** * Sets the padding * * Only PKCS1 padding is supported * * @param string $padding */ public function withPadding($padding) { if (!$this->key instanceof RSA) { throw new UnsupportedAlgorithmException('Only RSA keys support padding'); } if ($padding != RSA::SIGNATURE_PKCS1 && $padding != RSA::SIGNATURE_RELAXED_PKCS1) { throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures'); } return $this; } /** * Determines the signature padding mode * * Valid values are: ASN1, SSH2, Raw * * @param string $format */ public function withSignatureFormat($format) { if ($this->key instanceof RSA) { throw new UnsupportedAlgorithmException('Only DSA and EC keys support signature format setting'); } if ($format != 'SSH2') { throw new UnsupportedAlgorithmException('Only SSH2-formatted signatures are currently supported'); } return $this; } /** * Returns the curve * * Returns a string if it's a named curve, an array if not * * @return string|array */ public function getCurve() { if (!$this->key instanceof EC) { throw new UnsupportedAlgorithmException('Only EC keys have curves'); } return $this->key->getCurve(); } /** * Create a signature * * See "2.6.2 Protocol 2 private key signature request" * * @param string $message * @return string * @throws \RuntimeException on connection errors * @throws \phpseclib3\Exception\UnsupportedAlgorithmException if the algorithm is unsupported */ public function sign($message) { // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE $packet = Strings::packSSH2( 'CssN', Agent::SSH_AGENTC_SIGN_REQUEST, $this->key_blob, $message, $this->flags ); $packet = Strings::packSSH2('s', $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { throw new \RuntimeException('Connection closed during signing'); } $length = current(unpack('N', $this->readBytes(4))); $packet = $this->readBytes($length); list($type, $signature_blob) = Strings::unpackSSH2('Cs', $packet); if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { throw new \RuntimeException('Unable to retrieve signature'); } if (!$this->key instanceof RSA) { return $signature_blob; } list($type, $signature_blob) = Strings::unpackSSH2('ss', $signature_blob); return $signature_blob; } /** * Returns the private key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key'); } /** * Sets the password * * @param string|bool $password * @return never */ public function withPassword($password = false) { throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key'); } } PK1A#]�Ȓ5@!@!9vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.phpnu�[���<?php /** * Pure-PHP ssh-agent client. * * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent} * * PHP version 5 * * Here are some examples of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $agent = new \phpseclib3\System\SSH\Agent(); * * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld'); * if (!$ssh->login('username', $agent)) { * exit('Login Failed'); * } * * echo $ssh->exec('pwd'); * echo $ssh->exec('ls -la'); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2014 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\System\SSH; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\RSA; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Net\SSH2; use phpseclib3\System\SSH\Agent\Identity; /** * Pure-PHP ssh-agent client identity factory * * requestIdentities() method pumps out \phpseclib3\System\SSH\Agent\Identity objects * * @author Jim Wigginton <terrafrost@php.net> */ class Agent { use Common\Traits\ReadBytes; // Message numbers // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) const SSH_AGENTC_REQUEST_IDENTITIES = 11; // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). const SSH_AGENT_IDENTITIES_ANSWER = 12; // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) const SSH_AGENTC_SIGN_REQUEST = 13; // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) const SSH_AGENT_SIGN_RESPONSE = 14; // Agent forwarding status // no forwarding requested and not active const FORWARD_NONE = 0; // request agent forwarding when opportune const FORWARD_REQUEST = 1; // forwarding has been request and is active const FORWARD_ACTIVE = 2; /** * Unused */ const SSH_AGENT_FAILURE = 5; /** * Socket Resource * * @var resource */ private $fsock; /** * Agent forwarding status * * @var int */ private $forward_status = self::FORWARD_NONE; /** * Buffer for accumulating forwarded authentication * agent data arriving on SSH data channel destined * for agent unix socket * * @var string */ private $socket_buffer = ''; /** * Tracking the number of bytes we are expecting * to arrive for the agent socket on the SSH data * channel * * @var int */ private $expected_bytes = 0; /** * Default Constructor * * @return \phpseclib3\System\SSH\Agent * @throws \phpseclib3\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found * @throws \RuntimeException on connection errors */ public function __construct($address = null) { if (!$address) { switch (true) { case isset($_SERVER['SSH_AUTH_SOCK']): $address = $_SERVER['SSH_AUTH_SOCK']; break; case isset($_ENV['SSH_AUTH_SOCK']): $address = $_ENV['SSH_AUTH_SOCK']; break; default: throw new BadConfigurationException('SSH_AUTH_SOCK not found'); } } if (in_array('unix', stream_get_transports())) { $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); if (!$this->fsock) { throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)"); } } else { if (substr($address, 0, 9) != '\\\\.\\pipe\\' || strpos(substr($address, 9), '\\') !== false) { throw new \RuntimeException('Address is not formatted as a named pipe should be'); } $this->fsock = fopen($address, 'r+b'); if (!$this->fsock) { throw new \RuntimeException('Unable to open address'); } } } /** * Request Identities * * See "2.5.2 Requesting a list of protocol 2 keys" * Returns an array containing zero or more \phpseclib3\System\SSH\Agent\Identity objects * * @return array * @throws \RuntimeException on receipt of unexpected packets */ public function requestIdentities() { if (!$this->fsock) { return []; } $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { throw new \RuntimeException('Connection closed while requesting identities'); } $length = current(unpack('N', $this->readBytes(4))); $packet = $this->readBytes($length); list($type, $keyCount) = Strings::unpackSSH2('CN', $packet); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { throw new \RuntimeException('Unable to request identities'); } $identities = []; for ($i = 0; $i < $keyCount; $i++) { list($key_blob, $comment) = Strings::unpackSSH2('ss', $packet); $temp = $key_blob; list($key_type) = Strings::unpackSSH2('s', $temp); switch ($key_type) { case 'ssh-rsa': case 'ssh-dss': case 'ssh-ed25519': case 'ecdsa-sha2-nistp256': case 'ecdsa-sha2-nistp384': case 'ecdsa-sha2-nistp521': $key = PublicKeyLoader::load($key_type . ' ' . base64_encode($key_blob)); } // resources are passed by reference by default if (isset($key)) { $identity = (new Identity($this->fsock)) ->withPublicKey($key) ->withPublicKeyBlob($key_blob); $identities[] = $identity; unset($key); } } return $identities; } /** * Signal that agent forwarding should * be requested when a channel is opened * * @return void */ public function startSSHForwarding() { if ($this->forward_status == self::FORWARD_NONE) { $this->forward_status = self::FORWARD_REQUEST; } } /** * Request agent forwarding of remote server * * @param \phpseclib3\Net\SSH2 $ssh * @return bool */ private function request_forwarding(SSH2 $ssh) { if (!$ssh->requestAgentForwarding()) { return false; } $this->forward_status = self::FORWARD_ACTIVE; return true; } /** * On successful channel open * * This method is called upon successful channel * open to give the SSH Agent an opportunity * to take further action. i.e. request agent forwarding * * @param \phpseclib3\Net\SSH2 $ssh */ public function registerChannelOpen(SSH2 $ssh) { if ($this->forward_status == self::FORWARD_REQUEST) { $this->request_forwarding($ssh); } } /** * Forward data to SSH Agent and return data reply * * @param string $data * @return string Data from SSH Agent * @throws \RuntimeException on connection errors */ public function forwardData($data) { if ($this->expected_bytes > 0) { $this->socket_buffer .= $data; $this->expected_bytes -= strlen($data); } else { $agent_data_bytes = current(unpack('N', $data)); $current_data_bytes = strlen($data); $this->socket_buffer = $data; if ($current_data_bytes != $agent_data_bytes + 4) { $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; return false; } } if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); } $this->socket_buffer = ''; $this->expected_bytes = 0; $agent_reply_bytes = current(unpack('N', $this->readBytes(4))); $agent_reply_data = $this->readBytes($agent_reply_bytes); $agent_reply_data = current(unpack('a*', $agent_reply_data)); return pack('Na*', $agent_reply_bytes, $agent_reply_data); } } PK1A#]�iKvendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.phpnu�[���<?php /** * ReadBytes trait * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\System\SSH\Common\Traits; /** * ReadBytes trait * * @author Jim Wigginton <terrafrost@php.net> */ trait ReadBytes { /** * Read data * * @param int $length * @throws \RuntimeException on connection errors */ public function readBytes($length) { $temp = fread($this->fsock, $length); if (strlen($temp) != $length) { throw new \RuntimeException("Expected $length bytes; got " . strlen($temp)); } return $temp; } } PK1A#]�$I I <vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.phpnu�[���<?php /** * DSA Public Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; /** * DSA Public Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PublicKey extends DSA implements Common\PublicKey { use Common\Traits\Fingerprint; /** * Verify a signature * * @see self::verify() * @param string $message * @param string $signature * @return mixed */ public function verify($message, $signature) { $format = $this->sigFormat; $params = $format::load($signature); if ($params === false || count($params) != 2) { return false; } extract($params); if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature; $result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash()); if ($result != -1) { return (bool) $result; } } $q_1 = $this->q->subtract(self::$one); if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) { return false; } $w = $s->modInverse($this->q); $h = $this->hash->hash($message); $h = $this->bits2int($h); list(, $u1) = $h->multiply($w)->divide($this->q); list(, $u2) = $r->multiply($w)->divide($this->q); $v1 = $this->g->powMod($u1, $this->p); $v2 = $this->y->powMod($u2, $this->p); list(, $v) = $v1->multiply($v2)->divide($this->p); list(, $v) = $v->divide($this->q); return $v->equals($r); } /** * Returns the public key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePublicKey'); return $type::savePublicKey($this->p, $this->q, $this->g, $this->y, $options); } } PK1A#]W6��Ivendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.phpnu�[���<?php /** * ASN1 Signature Handler * * PHP version 5 * * Handles signatures in the format described in * https://tools.ietf.org/html/rfc3279#section-2.2.2 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Signature; use phpseclib3\File\ASN1 as Encoder; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * ASN1 Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ASN1 { /** * Loads a signature * * @param string $sig * @return array|bool */ public static function load($sig) { if (!is_string($sig)) { return false; } $decoded = Encoder::decodeBER($sig); if (empty($decoded)) { return false; } $components = Encoder::asn1map($decoded[0], Maps\DssSigValue::MAP); return $components; } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @return string */ public static function save(BigInteger $r, BigInteger $s) { return Encoder::encodeDER(compact('r', 's'), Maps\DssSigValue::MAP); } } PK1A#]�ʻ� Hvendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.phpnu�[���<?php /** * Raw DSA Signature Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Signature; use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor; /** * Raw DSA Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Raw extends Progenitor { } PK1A#]�����Ivendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.phpnu�[���<?php /** * SSH2 Signature Handler * * PHP version 5 * * Handles signatures in the format used by SSH2 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Signature; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BigInteger; /** * SSH2 Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SSH2 { /** * Loads a signature * * @param string $sig * @return mixed */ public static function load($sig) { if (!is_string($sig)) { return false; } $result = Strings::unpackSSH2('ss', $sig); if ($result === false) { return false; } list($type, $blob) = $result; if ($type != 'ssh-dss' || strlen($blob) != 40) { return false; } return [ 'r' => new BigInteger(substr($blob, 0, 20), 256), 's' => new BigInteger(substr($blob, 20), 256) ]; } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @return string */ public static function save(BigInteger $r, BigInteger $s) { if ($r->getLength() > 160 || $s->getLength() > 160) { return false; } return Strings::packSSH2( 'ss', 'ssh-dss', str_pad($r->toBytes(), 20, "\0", STR_PAD_LEFT) . str_pad($s->toBytes(), 20, "\0", STR_PAD_LEFT) ); } } PK1A#]��� � Cvendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.phpnu�[���<?php /** * Raw DSA Key Handler * * PHP version 5 * * Reads and creates arrays as DSA keys * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Math\BigInteger; /** * Raw DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Raw { /** * Break a public or private key down into its constituent components * * @param array $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!is_array($key)) { throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key)); } switch (true) { case !isset($key['p']) || !isset($key['q']) || !isset($key['g']): case !$key['p'] instanceof BigInteger: case !$key['q'] instanceof BigInteger: case !$key['g'] instanceof BigInteger: case !isset($key['x']) && !isset($key['y']): case isset($key['x']) && !$key['x'] instanceof BigInteger: case isset($key['y']) && !$key['y'] instanceof BigInteger: throw new \UnexpectedValueException('Key appears to be malformed'); } $options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1]; return array_intersect_key($key, $options); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param \phpseclib3\Math\BigInteger $x * @param string $password optional * @return string */ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '') { return compact('p', 'q', 'g', 'y', 'x'); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y) { return compact('p', 'q', 'g', 'y'); } } PK1A#]���Evendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.phpnu�[���<?php /** * PKCS#8 Formatted DSA Key Handler * * PHP version 5 * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 * is specific to private keys it's basically creating a DER-encoded wrapper * for keys. This just extends that same concept to public keys (much like ssh-keygen) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#8 Formatted DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS8 extends Progenitor { /** * OID Name * * @var string */ const OID_NAME = 'id-dsa'; /** * OID Value * * @var string */ const OID_VALUE = '1.2.840.10040.4.1'; /** * Child OIDs loaded * * @var bool */ protected static $childOIDsLoaded = false; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); if (!$decoded) { throw new \RuntimeException('Unable to decode BER of parameters'); } $components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP); if (!is_array($components)) { throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); } $decoded = ASN1::decodeBER($key[$type]); if (empty($decoded)) { throw new \RuntimeException('Unable to decode BER'); } $var = $type == 'privateKey' ? 'x' : 'y'; $components[$var] = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP); if (!$components[$var] instanceof BigInteger) { throw new \RuntimeException('Unable to perform ASN1 mapping'); } if (isset($key['meta'])) { $components['meta'] = $key['meta']; } return $components; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param \phpseclib3\Math\BigInteger $x * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = []) { $params = [ 'p' => $p, 'q' => $q, 'g' => $g ]; $params = ASN1::encodeDER($params, Maps\DSAParams::MAP); $params = new ASN1\Element($params); $key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP); return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = []) { $params = [ 'p' => $p, 'q' => $q, 'g' => $g ]; $params = ASN1::encodeDER($params, Maps\DSAParams::MAP); $params = new ASN1\Element($params); $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP); return self::wrapPublicKey($key, $params); } } PK1A#]܃���Evendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.phpnu�[���<?php /** * PKCS#1 Formatted DSA Key Handler * * PHP version 5 * * Used by File/X509.php * * Processes keys with the following headers: * * -----BEGIN DSA PRIVATE KEY----- * -----BEGIN DSA PUBLIC KEY----- * -----BEGIN DSA PARAMETERS----- * * Analogous to ssh-keygen's pem format (as specified by -m) * * Also, technically, PKCS1 decribes RSA but I am not aware of a formal specification for DSA. * The DSA private key format seems to have been adapted from the RSA private key format so * we're just re-using that as the name. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#1 Formatted DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS1 extends Progenitor { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP); if (is_array($key)) { return $key; } $key = ASN1::asn1map($decoded[0], Maps\DSAPrivateKey::MAP); if (is_array($key)) { return $key; } $key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP); if (is_array($key)) { return $key; } throw new \RuntimeException('Unable to perform ASN1 mapping'); } /** * Convert DSA parameters to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @return string */ public static function saveParameters(BigInteger $p, BigInteger $q, BigInteger $g) { $key = [ 'p' => $p, 'q' => $q, 'g' => $g ]; $key = ASN1::encodeDER($key, Maps\DSAParams::MAP); return "-----BEGIN DSA PARAMETERS-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END DSA PARAMETERS-----\r\n"; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param \phpseclib3\Math\BigInteger $x * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = []) { $key = [ 'version' => 0, 'p' => $p, 'q' => $q, 'g' => $g, 'y' => $y, 'x' => $x ]; $key = ASN1::encodeDER($key, Maps\DSAPrivateKey::MAP); return self::wrapPrivateKey($key, 'DSA', $password, $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y) { $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP); return self::wrapPublicKey($key, 'DSA'); } } PK1A#]E�IQQCvendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.phpnu�[���<?php /** * XML Formatted DSA Key Handler * * While XKMS defines a private key format for RSA it does not do so for DSA. Quoting that standard: * * "[XKMS] does not specify private key parameters for the DSA signature algorithm since the algorithm only * supports signature modes and so the application of server generated keys and key recovery is of limited * value" * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Math\BigInteger; /** * XML Formatted DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class XML { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (!class_exists('DOMDocument')) { throw new BadConfigurationException('The dom extension is not setup correctly on this system'); } $use_errors = libxml_use_internal_errors(true); $dom = new \DOMDocument(); if (substr($key, 0, 5) != '<?xml') { $key = '<xml>' . $key . '</xml>'; } if (!$dom->loadXML($key)) { libxml_use_internal_errors($use_errors); throw new \UnexpectedValueException('Key does not appear to contain XML'); } $xpath = new \DOMXPath($dom); $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter']; foreach ($keys as $key) { // $dom->getElementsByTagName($key) is case-sensitive $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']"); if (!$temp->length) { continue; } $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256); switch ($key) { case 'p': // a prime modulus meeting the [DSS] requirements // Parameters P, Q, and G can be public and common to a group of users. They might be known // from application context. As such, they are optional but P and Q must either both appear // or both be absent $components['p'] = $value; break; case 'q': // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1 $components['q'] = $value; break; case 'g': // an integer with certain properties with respect to P and Q $components['g'] = $value; break; case 'y': // G**X mod P (where X is part of the private key and not made public) $components['y'] = $value; // the remaining options do not do anything case 'j': // (P - 1) / Q // Parameter J is available for inclusion solely for efficiency as it is calculatable from // P and Q case 'seed': // a DSA prime generation seed // Parameters seed and pgenCounter are used in the DSA prime number generation algorithm // specified in [DSS]. As such, they are optional but must either both be present or both // be absent case 'pgencounter': // a DSA prime generation counter } } libxml_use_internal_errors($use_errors); if (!isset($components['y'])) { throw new \UnexpectedValueException('Key is missing y component'); } switch (true) { case !isset($components['p']): case !isset($components['q']): case !isset($components['g']): return ['y' => $components['y']]; } return $components; } /** * Convert a public key to the appropriate format * * See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y) { return "<DSAKeyValue>\r\n" . ' <P>' . Strings::base64_encode($p->toBytes()) . "</P>\r\n" . ' <Q>' . Strings::base64_encode($q->toBytes()) . "</Q>\r\n" . ' <G>' . Strings::base64_encode($g->toBytes()) . "</G>\r\n" . ' <Y>' . Strings::base64_encode($y->toBytes()) . "</Y>\r\n" . '</DSAKeyValue>'; } } PK1A#]�9��~ ~ Evendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.phpnu�[���<?php /** * PuTTY Formatted DSA Key Handler * * puttygen does not generate DSA keys with an N of anything other than 160, however, * it can still load them and convert them. PuTTY will load them, too, but SSH servers * won't accept them. Since PuTTY formatted keys are primarily used with SSH this makes * keys with N > 160 kinda useless, hence this handlers not supporting such keys. * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; use phpseclib3\Math\BigInteger; /** * PuTTY Formatted DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PuTTY extends Progenitor { /** * Public Handler * * @var string */ const PUBLIC_HANDLER = 'phpseclib3\Crypt\DSA\Formats\Keys\OpenSSH'; /** * Algorithm Identifier * * @var array */ protected static $types = ['ssh-dss']; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $components = parent::load($key, $password); if (!isset($components['private'])) { return $components; } extract($components); unset($components['public'], $components['private']); list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $public); list($x) = Strings::unpackSSH2('i', $private); return compact('p', 'q', 'g', 'y', 'x', 'comment'); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param \phpseclib3\Math\BigInteger $x * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = false, array $options = []) { if ($q->getLength() != 160) { throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); } $public = Strings::packSSH2('iiii', $p, $q, $g, $y); $private = Strings::packSSH2('i', $x); return self::wrapPrivateKey($public, $private, 'ssh-dss', $password, $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y) { if ($q->getLength() != 160) { throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); } return self::wrapPublicKey(Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dss'); } } PK1A#]�C����Gvendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.phpnu�[���<?php /** * OpenSSH Formatted DSA Key Handler * * PHP version 5 * * Place in $HOME/.ssh/authorized_keys * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; use phpseclib3\Math\BigInteger; /** * OpenSSH Formatted DSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSH extends Progenitor { /** * Supported Key Types * * @var array */ protected static $types = ['ssh-dss']; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $parsed = parent::load($key, $password); if (isset($parsed['paddedKey'])) { list($type) = Strings::unpackSSH2('s', $parsed['paddedKey']); if ($type != $parsed['type']) { throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])"); } list($p, $q, $g, $y, $x, $comment) = Strings::unpackSSH2('i5s', $parsed['paddedKey']); return compact('p', 'q', 'g', 'y', 'x', 'comment'); } list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $parsed['publicKey']); $comment = $parsed['comment']; return compact('p', 'q', 'g', 'y', 'comment'); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = []) { if ($q->getLength() != 160) { throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); } // from <http://tools.ietf.org/html/rfc4253#page-15>: // string "ssh-dss" // mpint p // mpint q // mpint g // mpint y $DSAPublicKey = Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y); if (isset($options['binary']) ? $options['binary'] : self::$binary) { return $DSAPublicKey; } $comment = isset($options['comment']) ? $options['comment'] : self::$comment; $DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey) . ' ' . $comment; return $DSAPublicKey; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $p * @param \phpseclib3\Math\BigInteger $q * @param \phpseclib3\Math\BigInteger $g * @param \phpseclib3\Math\BigInteger $y * @param \phpseclib3\Math\BigInteger $x * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = []) { $publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => true]); $privateKey = Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x); return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); } } PK1A#]i�����=vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.phpnu�[���<?php /** * DSA Private Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; use phpseclib3\Math\BigInteger; /** * DSA Private Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PrivateKey extends DSA implements Common\PrivateKey { use Common\Traits\PasswordProtected; /** * DSA secret exponent x * * @var \phpseclib3\Math\BigInteger */ protected $x; /** * Returns the public key * * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING. * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified * by getting a DSA PKCS8 public key: * * "openssl dsa -in private.dsa -pubout -outform PEM" * * ie. just swap out rsa with dsa in the rsa command above. * * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature * without the parameters and the PKCS1 DSA public key format does not include the parameters. * * @see self::getPrivateKey() * @return mixed */ public function getPublicKey() { $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); if (!isset($this->y)) { $this->y = $this->g->powMod($this->x, $this->p); } $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y); return DSA::loadFormat('PKCS8', $key) ->withHash($this->hash->getHash()) ->withSignatureFormat($this->shortFormat); } /** * Create a signature * * @see self::verify() * @param string $message * @return mixed */ public function sign($message) { $format = $this->sigFormat; if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { $signature = ''; $result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash()); if ($result) { if ($this->shortFormat == 'ASN1') { return $signature; } extract(ASN1Signature::load($signature)); return $format::save($r, $s); } } $h = $this->hash->hash($message); $h = $this->bits2int($h); while (true) { $k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one)); $r = $this->g->powMod($k, $this->p); list(, $r) = $r->divide($this->q); if ($r->equals(self::$zero)) { continue; } $kinv = $k->modInverse($this->q); $temp = $h->add($this->x->multiply($r)); $temp = $kinv->multiply($temp); list(, $s) = $temp->divide($this->q); if (!$s->equals(self::$zero)) { break; } } // the following is an RFC6979 compliant implementation of deterministic DSA // it's unused because it's mainly intended for use when a good CSPRNG isn't // available. if phpseclib's CSPRNG isn't good then even key generation is // suspect /* $h1 = $this->hash->hash($message); $k = $this->computek($h1); $r = $this->g->powMod($k, $this->p); list(, $r) = $r->divide($this->q); $kinv = $k->modInverse($this->q); $h1 = $this->bits2int($h1); $temp = $h1->add($this->x->multiply($r)); $temp = $kinv->multiply($temp); list(, $s) = $temp->divide($this->q); */ return $format::save($r, $s); } /** * Returns the private key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); if (!isset($this->y)) { $this->y = $this->g->powMod($this->x, $this->p); } return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options); } } PK1A#]�h�=vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.phpnu�[���<?php /** * DSA Parameters * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DSA; use phpseclib3\Crypt\DSA; /** * DSA Parameters * * @author Jim Wigginton <terrafrost@php.net> */ final class Parameters extends DSA { /** * Returns the parameters * * @param string $type * @param array $options optional * @return string */ public function toString($type = 'PKCS1', array $options = []) { $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); return $type::saveParameters($this->p, $this->q, $this->g, $options); } } PK1A#]�cNu��2vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.phpnu�[���<?php /** * Pure-PHP implementation of DES. * * Uses mcrypt, if available, and an internal implementation, otherwise. * * PHP version 5 * * Useful resources are as follows: * * - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material} * - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard} * - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example} * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $des = new \phpseclib3\Crypt\DES('ctr'); * * $des->setKey('abcdefgh'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $des->decrypt($des->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\BlockCipher; use phpseclib3\Exception\BadModeException; /** * Pure-PHP implementation of DES. * * @author Jim Wigginton <terrafrost@php.net> */ class DES extends BlockCipher { /** * Contains $keys[self::ENCRYPT] * * @see \phpseclib3\Crypt\DES::setupKey() * @see \phpseclib3\Crypt\DES::processBlock() */ const ENCRYPT = 0; /** * Contains $keys[self::DECRYPT] * * @see \phpseclib3\Crypt\DES::setupKey() * @see \phpseclib3\Crypt\DES::processBlock() */ const DECRYPT = 1; /** * Block Length of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size * @var int */ protected $block_size = 8; /** * Key Length (in bytes) * * @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength() * @var int */ protected $key_length = 8; /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'des'; /** * The OpenSSL names of the cipher / modes * * @see \phpseclib3\Crypt\Common\SymmetricKey::openssl_mode_names * @var array */ protected $openssl_mode_names = [ self::MODE_ECB => 'des-ecb', self::MODE_CBC => 'des-cbc', self::MODE_CFB => 'des-cfb', self::MODE_OFB => 'des-ofb' // self::MODE_CTR is undefined for DES ]; /** * Optimizing value while CFB-encrypting * * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len * @var int */ protected $cfb_init_len = 500; /** * Switch for DES/3DES encryption * * Used only if $engine == self::ENGINE_INTERNAL * * @see self::setupKey() * @see self::processBlock() * @var int */ protected $des_rounds = 1; /** * max possible size of $key * * @see self::setKey() * @var string */ protected $key_length_max = 8; /** * The Key Schedule * * @see self::setupKey() * @var array */ private $keys; /** * Key Cache "key" * * @see self::setupKey() * @var array */ private $kl; /** * Shuffle table. * * For each byte value index, the entry holds an 8-byte string * with each byte containing all bits in the same state as the * corresponding bit in the index value. * * @see self::processBlock() * @see self::setupKey() * @var array */ protected static $shuffle = [ "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF", "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF", "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF", "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF", "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF", "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF", "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF", "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF", "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF", "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF", "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF", "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF", "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF", "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF", "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF", "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF", "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF", "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF", "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF", "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF", "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF", "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF", "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF", "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF", "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF", "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF", "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF", "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF", "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF", "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF", "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF", "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF", "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF", "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF", "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF", "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF", "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF", "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF", "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF", "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF", "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF", "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF", "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF", "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF", "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF", "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF", "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF", "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF", "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF", "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF", "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF", "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF", "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF", "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF", "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF", "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF", "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF", "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF", "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF", "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF", "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF", "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF", "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF", "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF", "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF", "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF", "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF", "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF", "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF", "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF", "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF", "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF", "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF", "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF", "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF", "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF", "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF", "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF", "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF", "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF", "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF", "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF", "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF", "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF", "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF", "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF", "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF", "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF", "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF", "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF", "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF", "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF", "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF", "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF", "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF", "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF", "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF", "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF", "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF", "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF", "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF", "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF", "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF", "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF", "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF", "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF", "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF", "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF", "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF", "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF", "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF", "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF", "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF", "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF", "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF", "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF", "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF", "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF", "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ]; /** * IP mapping helper table. * * Indexing this table with each source byte performs the initial bit permutation. * * @var array */ protected static $ipmap = [ 0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31, 0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33, 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, 0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73, 0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35, 0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37, 0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75, 0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77, 0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1, 0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3, 0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1, 0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3, 0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5, 0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7, 0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5, 0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7, 0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39, 0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B, 0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79, 0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B, 0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D, 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, 0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D, 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, 0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9, 0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB, 0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9, 0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB, 0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD, 0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF, 0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD, 0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF ]; /** * Inverse IP mapping helper table. * Indexing this table with a byte value reverses the bit order. * * @var array */ protected static $invipmap = [ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF ]; /** * Pre-permuted S-box1 * * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the * P table: concatenation can then be replaced by exclusive ORs. * * @var array */ protected static $sbox1 = [ 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000, 0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002, 0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202, 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000, 0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200, 0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202, 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200, 0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002 ]; /** * Pre-permuted S-box2 * * @var array */ protected static $sbox2 = [ 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010, 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010, 0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000, 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010, 0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000, 0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000, 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010, 0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000 ]; /** * Pre-permuted S-box3 * * @var array */ protected static $sbox3 = [ 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100, 0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104, 0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104, 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000, 0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000, 0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004, 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004, 0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100 ]; /** * Pre-permuted S-box4 * * @var array */ protected static $sbox4 = [ 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000, 0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000, 0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040, 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040, 0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000, 0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040, 0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040 ]; /** * Pre-permuted S-box5 * * @var array */ protected static $sbox5 = [ 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000, 0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000, 0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080, 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080, 0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080, 0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000, 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000, 0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080 ]; /** * Pre-permuted S-box6 * * @var array */ protected static $sbox6 = [ 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000, 0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008, 0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008, 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000, 0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008, 0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000, 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008, 0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008 ]; /** * Pre-permuted S-box7 * * @var array */ protected static $sbox7 = [ 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400, 0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401, 0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001, 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400, 0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001, 0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400, 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401, 0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001 ]; /** * Pre-permuted S-box8 * * @var array */ protected static $sbox8 = [ 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000, 0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020, 0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800, 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000, 0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820, 0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820, 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000, 0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800 ]; /** * Default Constructor. * * @param string $mode * @throws BadModeException if an invalid / unsupported mode is provided */ public function __construct($mode) { parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new BadModeException('Block ciphers cannot be ran in stream mode'); } } /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { if ($this->key_length_max == 8) { if ($engine == self::ENGINE_OPENSSL) { // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { return false; } $this->cipher_name_openssl_ecb = 'des-ecb'; $this->cipher_name_openssl = 'des-' . $this->openssl_translate_mode(); } } return parent::isValidEngineHelper($engine); } /** * Sets the key. * * Keys must be 64-bits long or 8 bytes long. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() * @param string $key */ public function setKey($key) { if (!($this instanceof TripleDES) && strlen($key) != 8) { throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported'); } // Sets the key parent::setKey($key); } /** * Encrypts a block * * @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock() * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see self::encrypt() * @param string $in * @return string */ protected function encryptBlock($in) { return $this->processBlock($in, self::ENCRYPT); } /** * Decrypts a block * * @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @see self::decrypt() * @param string $in * @return string */ protected function decryptBlock($in) { return $this->processBlock($in, self::DECRYPT); } /** * Encrypts or decrypts a 64-bit block * * $mode should be either self::ENCRYPT or self::DECRYPT. See * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general * idea of what this function does. * * @see self::encryptBlock() * @see self::decryptBlock() * @param string $block * @param int $mode * @return string */ private function processBlock($block, $mode) { static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; if (!$sbox1) { $sbox1 = array_map('intval', self::$sbox1); $sbox2 = array_map('intval', self::$sbox2); $sbox3 = array_map('intval', self::$sbox3); $sbox4 = array_map('intval', self::$sbox4); $sbox5 = array_map('intval', self::$sbox5); $sbox6 = array_map('intval', self::$sbox6); $sbox7 = array_map('intval', self::$sbox7); $sbox8 = array_map('intval', self::$sbox8); /* Merge $shuffle with $[inv]ipmap */ for ($i = 0; $i < 256; ++$i) { $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; } } $keys = $this->keys[$mode]; $ki = -1; // Do the initial IP permutation. $t = unpack('Nl/Nr', $block); list($l, $r) = [$t['l'], $t['r']]; $block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); // Extract L0 and R0. $t = unpack('Nl/Nr', $block); list($l, $r) = [$t['l'], $t['r']]; for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { // Perform the 16 steps. for ($i = 0; $i < 16; $i++) { // start of "the Feistel (F) function" - see the following URL: // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png // Merge key schedule. $b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki]; $b2 = (($r >> 31) & 0x00000001) ^ ($r << 1) ^ $keys[++$ki]; // S-box indexing. $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ $l; // end of "the Feistel (F) function" $l = $r; $r = $t; } // Last step should not permute L & R. $t = $l; $l = $r; $r = $t; } // Perform the inverse IP permutation. return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleinvip[($r >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleinvip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleinvip[ $r & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleinvip[ $l & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); } /** * Creates the key schedule * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() */ protected function setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) { // already expanded return; } $this->kl = ['key' => $this->key, 'des_rounds' => $this->des_rounds]; static $shifts = [ // number of key bits shifted per round 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ]; static $pc1map = [ 0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C, 0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E, 0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C, 0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E, 0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C, 0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E, 0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C, 0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E, 0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C, 0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E, 0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C, 0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E, 0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C, 0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E, 0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C, 0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E, 0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C, 0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E, 0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C, 0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E, 0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC, 0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE, 0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC, 0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE, 0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC, 0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE, 0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC, 0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE, 0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC, 0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE, 0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC, 0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE ]; // Mapping tables for the PC-2 transformation. static $pc2mapc1 = [ 0x00000000, 0x00000400, 0x00200000, 0x00200400, 0x00000001, 0x00000401, 0x00200001, 0x00200401, 0x02000000, 0x02000400, 0x02200000, 0x02200400, 0x02000001, 0x02000401, 0x02200001, 0x02200401 ]; static $pc2mapc2 = [ 0x00000000, 0x00000800, 0x08000000, 0x08000800, 0x00010000, 0x00010800, 0x08010000, 0x08010800, 0x00000000, 0x00000800, 0x08000000, 0x08000800, 0x00010000, 0x00010800, 0x08010000, 0x08010800, 0x00000100, 0x00000900, 0x08000100, 0x08000900, 0x00010100, 0x00010900, 0x08010100, 0x08010900, 0x00000100, 0x00000900, 0x08000100, 0x08000900, 0x00010100, 0x00010900, 0x08010100, 0x08010900, 0x00000010, 0x00000810, 0x08000010, 0x08000810, 0x00010010, 0x00010810, 0x08010010, 0x08010810, 0x00000010, 0x00000810, 0x08000010, 0x08000810, 0x00010010, 0x00010810, 0x08010010, 0x08010810, 0x00000110, 0x00000910, 0x08000110, 0x08000910, 0x00010110, 0x00010910, 0x08010110, 0x08010910, 0x00000110, 0x00000910, 0x08000110, 0x08000910, 0x00010110, 0x00010910, 0x08010110, 0x08010910, 0x00040000, 0x00040800, 0x08040000, 0x08040800, 0x00050000, 0x00050800, 0x08050000, 0x08050800, 0x00040000, 0x00040800, 0x08040000, 0x08040800, 0x00050000, 0x00050800, 0x08050000, 0x08050800, 0x00040100, 0x00040900, 0x08040100, 0x08040900, 0x00050100, 0x00050900, 0x08050100, 0x08050900, 0x00040100, 0x00040900, 0x08040100, 0x08040900, 0x00050100, 0x00050900, 0x08050100, 0x08050900, 0x00040010, 0x00040810, 0x08040010, 0x08040810, 0x00050010, 0x00050810, 0x08050010, 0x08050810, 0x00040010, 0x00040810, 0x08040010, 0x08040810, 0x00050010, 0x00050810, 0x08050010, 0x08050810, 0x00040110, 0x00040910, 0x08040110, 0x08040910, 0x00050110, 0x00050910, 0x08050110, 0x08050910, 0x00040110, 0x00040910, 0x08040110, 0x08040910, 0x00050110, 0x00050910, 0x08050110, 0x08050910, 0x01000000, 0x01000800, 0x09000000, 0x09000800, 0x01010000, 0x01010800, 0x09010000, 0x09010800, 0x01000000, 0x01000800, 0x09000000, 0x09000800, 0x01010000, 0x01010800, 0x09010000, 0x09010800, 0x01000100, 0x01000900, 0x09000100, 0x09000900, 0x01010100, 0x01010900, 0x09010100, 0x09010900, 0x01000100, 0x01000900, 0x09000100, 0x09000900, 0x01010100, 0x01010900, 0x09010100, 0x09010900, 0x01000010, 0x01000810, 0x09000010, 0x09000810, 0x01010010, 0x01010810, 0x09010010, 0x09010810, 0x01000010, 0x01000810, 0x09000010, 0x09000810, 0x01010010, 0x01010810, 0x09010010, 0x09010810, 0x01000110, 0x01000910, 0x09000110, 0x09000910, 0x01010110, 0x01010910, 0x09010110, 0x09010910, 0x01000110, 0x01000910, 0x09000110, 0x09000910, 0x01010110, 0x01010910, 0x09010110, 0x09010910, 0x01040000, 0x01040800, 0x09040000, 0x09040800, 0x01050000, 0x01050800, 0x09050000, 0x09050800, 0x01040000, 0x01040800, 0x09040000, 0x09040800, 0x01050000, 0x01050800, 0x09050000, 0x09050800, 0x01040100, 0x01040900, 0x09040100, 0x09040900, 0x01050100, 0x01050900, 0x09050100, 0x09050900, 0x01040100, 0x01040900, 0x09040100, 0x09040900, 0x01050100, 0x01050900, 0x09050100, 0x09050900, 0x01040010, 0x01040810, 0x09040010, 0x09040810, 0x01050010, 0x01050810, 0x09050010, 0x09050810, 0x01040010, 0x01040810, 0x09040010, 0x09040810, 0x01050010, 0x01050810, 0x09050010, 0x09050810, 0x01040110, 0x01040910, 0x09040110, 0x09040910, 0x01050110, 0x01050910, 0x09050110, 0x09050910, 0x01040110, 0x01040910, 0x09040110, 0x09040910, 0x01050110, 0x01050910, 0x09050110, 0x09050910 ]; static $pc2mapc3 = [ 0x00000000, 0x00000004, 0x00001000, 0x00001004, 0x00000000, 0x00000004, 0x00001000, 0x00001004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x00000020, 0x00000024, 0x00001020, 0x00001024, 0x00000020, 0x00000024, 0x00001020, 0x00001024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x00080000, 0x00080004, 0x00081000, 0x00081004, 0x00080000, 0x00080004, 0x00081000, 0x00081004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x00080020, 0x00080024, 0x00081020, 0x00081024, 0x00080020, 0x00080024, 0x00081020, 0x00081024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x00000002, 0x00000006, 0x00001002, 0x00001006, 0x00000002, 0x00000006, 0x00001002, 0x00001006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x00000022, 0x00000026, 0x00001022, 0x00001026, 0x00000022, 0x00000026, 0x00001022, 0x00001026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x00080002, 0x00080006, 0x00081002, 0x00081006, 0x00080002, 0x00080006, 0x00081002, 0x00081006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x00080022, 0x00080026, 0x00081022, 0x00081026, 0x00080022, 0x00080026, 0x00081022, 0x00081026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026 ]; static $pc2mapc4 = [ 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208 ]; static $pc2mapd1 = [ 0x00000000, 0x00000001, 0x08000000, 0x08000001, 0x00200000, 0x00200001, 0x08200000, 0x08200001, 0x00000002, 0x00000003, 0x08000002, 0x08000003, 0x00200002, 0x00200003, 0x08200002, 0x08200003 ]; static $pc2mapd2 = [ 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04 ]; static $pc2mapd3 = [ 0x00000000, 0x00010000, 0x02000000, 0x02010000, 0x00000020, 0x00010020, 0x02000020, 0x02010020, 0x00040000, 0x00050000, 0x02040000, 0x02050000, 0x00040020, 0x00050020, 0x02040020, 0x02050020, 0x00002000, 0x00012000, 0x02002000, 0x02012000, 0x00002020, 0x00012020, 0x02002020, 0x02012020, 0x00042000, 0x00052000, 0x02042000, 0x02052000, 0x00042020, 0x00052020, 0x02042020, 0x02052020, 0x00000000, 0x00010000, 0x02000000, 0x02010000, 0x00000020, 0x00010020, 0x02000020, 0x02010020, 0x00040000, 0x00050000, 0x02040000, 0x02050000, 0x00040020, 0x00050020, 0x02040020, 0x02050020, 0x00002000, 0x00012000, 0x02002000, 0x02012000, 0x00002020, 0x00012020, 0x02002020, 0x02012020, 0x00042000, 0x00052000, 0x02042000, 0x02052000, 0x00042020, 0x00052020, 0x02042020, 0x02052020, 0x00000010, 0x00010010, 0x02000010, 0x02010010, 0x00000030, 0x00010030, 0x02000030, 0x02010030, 0x00040010, 0x00050010, 0x02040010, 0x02050010, 0x00040030, 0x00050030, 0x02040030, 0x02050030, 0x00002010, 0x00012010, 0x02002010, 0x02012010, 0x00002030, 0x00012030, 0x02002030, 0x02012030, 0x00042010, 0x00052010, 0x02042010, 0x02052010, 0x00042030, 0x00052030, 0x02042030, 0x02052030, 0x00000010, 0x00010010, 0x02000010, 0x02010010, 0x00000030, 0x00010030, 0x02000030, 0x02010030, 0x00040010, 0x00050010, 0x02040010, 0x02050010, 0x00040030, 0x00050030, 0x02040030, 0x02050030, 0x00002010, 0x00012010, 0x02002010, 0x02012010, 0x00002030, 0x00012030, 0x02002030, 0x02012030, 0x00042010, 0x00052010, 0x02042010, 0x02052010, 0x00042030, 0x00052030, 0x02042030, 0x02052030, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030 ]; static $pc2mapd4 = [ 0x00000000, 0x00000400, 0x01000000, 0x01000400, 0x00000000, 0x00000400, 0x01000000, 0x01000400, 0x00000100, 0x00000500, 0x01000100, 0x01000500, 0x00000100, 0x00000500, 0x01000100, 0x01000500, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x00080000, 0x00080400, 0x01080000, 0x01080400, 0x00080000, 0x00080400, 0x01080000, 0x01080400, 0x00080100, 0x00080500, 0x01080100, 0x01080500, 0x00080100, 0x00080500, 0x01080100, 0x01080500, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x00000008, 0x00000408, 0x01000008, 0x01000408, 0x00000008, 0x00000408, 0x01000008, 0x01000408, 0x00000108, 0x00000508, 0x01000108, 0x01000508, 0x00000108, 0x00000508, 0x01000108, 0x01000508, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x00080008, 0x00080408, 0x01080008, 0x01080408, 0x00080008, 0x00080408, 0x01080008, 0x01080408, 0x00080108, 0x00080508, 0x01080108, 0x01080508, 0x00080108, 0x00080508, 0x01080108, 0x01080508, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x00001000, 0x00001400, 0x01001000, 0x01001400, 0x00001000, 0x00001400, 0x01001000, 0x01001400, 0x00001100, 0x00001500, 0x01001100, 0x01001500, 0x00001100, 0x00001500, 0x01001100, 0x01001500, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x00081000, 0x00081400, 0x01081000, 0x01081400, 0x00081000, 0x00081400, 0x01081000, 0x01081400, 0x00081100, 0x00081500, 0x01081100, 0x01081500, 0x00081100, 0x00081500, 0x01081100, 0x01081500, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x00001008, 0x00001408, 0x01001008, 0x01001408, 0x00001008, 0x00001408, 0x01001008, 0x01001408, 0x00001108, 0x00001508, 0x01001108, 0x01001508, 0x00001108, 0x00001508, 0x01001108, 0x01001508, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x00081008, 0x00081408, 0x01081008, 0x01081408, 0x00081008, 0x00081408, 0x01081008, 0x01081408, 0x00081108, 0x00081508, 0x01081108, 0x01081508, 0x00081108, 0x00081508, 0x01081108, 0x01081508, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081108, 0x10081508, 0x11081108, 0x11081508, 0x10081108, 0x10081508, 0x11081108, 0x11081508 ]; $keys = []; for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { // pad the key and remove extra characters as appropriate. $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0"); // Perform the PC/1 transformation and compute C and D. $t = unpack('Nl/Nr', $key); list($l, $r) = [$t['l'], $t['r']]; $key = (self::$shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") | (self::$shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") | (self::$shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") | (self::$shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") | (self::$shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") | (self::$shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") | (self::$shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") | (self::$shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00"); $key = unpack('Nc/Nd', $key); $c = ( $key['c'] >> 4) & 0x0FFFFFFF; $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); $keys[$des_round] = [ self::ENCRYPT => [], self::DECRYPT => array_fill(0, 32, 0) ]; for ($i = 0, $ki = 31; $i < 16; ++$i, $ki -= 2) { $c <<= $shifts[$i]; $c = ($c | ($c >> 28)) & 0x0FFFFFFF; $d <<= $shifts[$i]; $d = ($d | ($d >> 28)) & 0x0FFFFFFF; // Perform the PC-2 transformation. $cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c >> 16) & 0xFF] | $pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[ $c & 0xFF]; $dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d >> 16) & 0xFF] | $pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF]; // Reorder: odd bytes/even bytes. Push the result in key schedule. $val1 = ( $cp & intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) | (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); $val2 = (($cp << 8) & intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) | (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); $keys[$des_round][self::ENCRYPT][ ] = $val1; $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; $keys[$des_round][self::ENCRYPT][ ] = $val2; $keys[$des_round][self::DECRYPT][$ki ] = $val2; } } switch ($this->des_rounds) { case 3: // 3DES keys $this->keys = [ self::ENCRYPT => array_merge( $keys[0][self::ENCRYPT], $keys[1][self::DECRYPT], $keys[2][self::ENCRYPT] ), self::DECRYPT => array_merge( $keys[2][self::DECRYPT], $keys[1][self::ENCRYPT], $keys[0][self::DECRYPT] ) ]; break; // case 1: // DES keys default: $this->keys = [ self::ENCRYPT => $keys[0][self::ENCRYPT], self::DECRYPT => $keys[0][self::DECRYPT] ]; } } /** * Setup the performance-optimized function for de/encrypt() * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() */ protected function setupInlineCrypt() { // Engine configuration for: // - DES ($des_rounds == 1) or // - 3DES ($des_rounds == 3) $des_rounds = $this->des_rounds; $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; if (!$sbox1) { $sbox1 = array_map("intval", self::$sbox1); $sbox2 = array_map("intval", self::$sbox2); $sbox3 = array_map("intval", self::$sbox3); $sbox4 = array_map("intval", self::$sbox4); $sbox5 = array_map("intval", self::$sbox5); $sbox6 = array_map("intval", self::$sbox6); $sbox7 = array_map("intval", self::$sbox7); $sbox8 = array_map("intval", self::$sbox8);' /* Merge $shuffle with $[inv]ipmap */ . ' for ($i = 0; $i < 256; ++$i) { $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; } } '; $k = [ self::ENCRYPT => $this->keys[self::ENCRYPT], self::DECRYPT => $this->keys[self::DECRYPT] ]; $init_encrypt = ''; $init_decrypt = ''; // Creating code for en- and decryption. $crypt_block = []; foreach ([self::ENCRYPT, self::DECRYPT] as $c) { /* Do the initial IP permutation. */ $crypt_block[$c] = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; $in = unpack("N*", ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01") ); ' . /* Extract L0 and R0 */ ' $l = $in[1]; $r = $in[2]; '; $l = '$l'; $r = '$r'; // Perform DES or 3DES. for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) { // Perform the 16 steps. for ($i = 0; $i < 16; ++$i) { // start of "the Feistel (F) function" - see the following URL: // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png // Merge key schedule. $crypt_block[$c] .= ' $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . '; $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' . /* S-box indexing. */ $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . '; '; // end of "the Feistel (F) function" // swap L & R list($l, $r) = [$r, $l]; } list($l, $r) = [$r, $l]; } // Perform the inverse IP permutation. $crypt_block[$c] .= '$in = ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); '; } // Creates the inline-crypt function $this->inline_crypt = $this->createInlineCryptFunction( [ 'init_crypt' => $init_crypt, 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $crypt_block[self::ENCRYPT], 'decrypt_block' => $crypt_block[self::DECRYPT] ] ); } } PK1A#]'-=HF�F�7vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.phpnu�[���<?php /** * Pure-PHP implementation of Blowfish. * * Uses mcrypt, if available, and an internal implementation, otherwise. * * PHP version 5 * * Useful resources are as follows: * * - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish} * * # An overview of bcrypt vs Blowfish * * OpenSSH private keys use a customized version of bcrypt. Specifically, instead of * encrypting OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts * OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt(). * * bcrypt is basically Blowfish but instead of performing the key expansion once it performs * the expansion 129 times for each round, with the first key expansion interleaving the salt * and password. This renders OpenSSL unusable and forces us to use a pure-PHP implementation * of blowfish. * * # phpseclib's four different _encryptBlock() implementations * * When using Blowfish as an encryption algorithm, _encryptBlock() is called 9 + 512 + * (the number of blocks in the plaintext) times. * * Each of the first 9 calls to _encryptBlock() modify the P-array. Each of the next 512 * calls modify the S-boxes. The remaining _encryptBlock() calls operate on the plaintext to * produce the ciphertext. In the pure-PHP implementation of Blowfish these remaining * _encryptBlock() calls are highly optimized through the use of eval(). Among other things, * P-array lookups are eliminated by hard-coding the key-dependent P-array values, and thus we * have explained 2 of the 4 different _encryptBlock() implementations. * * With bcrypt things are a bit different. _encryptBlock() is called 1,079,296 times, * assuming 16 rounds (which is what OpenSSH's bcrypt defaults to). The eval()-optimized * _encryptBlock() isn't as beneficial because the P-array values are not constant. Well, they * are constant, but only for, at most, 777 _encryptBlock() calls, which is equivalent to ~6KB * of data. The average length of back to back _encryptBlock() calls with a fixed P-array is * 514.12, which is ~4KB of data. Creating an eval()-optimized _encryptBlock() has an upfront * cost, which is CPU dependent and is probably not going to be worth it for just ~4KB of * data. Conseqeuently, bcrypt does not benefit from the eval()-optimized _encryptBlock(). * * The regular _encryptBlock() does unpack() and pack() on every call, as well, and that can * begin to add up after one million function calls. * * In theory, one might think that it might be beneficial to rewrite all block ciphers so * that, instead of passing strings to _encryptBlock(), you convert the string to an array of * integers and then pass successive subarrays of that array to _encryptBlock. This, however, * kills PHP's memory use. Like let's say you have a 1MB long string. After doing * $in = str_repeat('a', 1024 * 1024); PHP's memory utilization jumps up by ~1MB. After doing * $blocks = str_split($in, 4); it jumps up by an additional ~16MB. After * $blocks = array_map(fn($x) => unpack('N*', $x), $blocks); it jumps up by an additional * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that. * * This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock() * implementation can best be understood by doing Ctrl + F and searching for where * self::$use_reg_intval is defined. * * # phpseclib's three different _setupKey() implementations * * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16 * rounds by default that's ~8MB of data that's essentially being encrypted whenever * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a * fixed length whereas Blowfish keys are not of a fixed length. * * bcrypt actually has two different key expansion steps. The first one (expandstate) is * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s * being called. The second one (expand0state) is more similar to Blowfish's _setupKey() * but it can still use the fixed length key optimization discussed above and can do away with * the pack() / unpack() calls. * * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's * just a lot of work for very marginal benefits as _setupKey() is only called once for * regular Blowfish vs the 128 times it's called --per round-- with bcrypt. * * # blowfish + bcrypt in the same class * * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation. * * # Credit * * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation: * * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $blowfish = new \phpseclib3\Crypt\Blowfish('ctr'); * * $blowfish->setKey('12345678901234567890123456789012'); * * $plaintext = str_repeat('a', 1024); * * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\BlockCipher; /** * Pure-PHP implementation of Blowfish. * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> */ class Blowfish extends BlockCipher { /** * Block Length of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size * @var int */ protected $block_size = 8; /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'blowfish'; /** * Optimizing value while CFB-encrypting * * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len * @var int */ protected $cfb_init_len = 500; /** * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each * * S-Box 0 * * @var array */ private static $sbox0 = [ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a ]; /** * S-Box 1 * * @var array */ private static $sbox1 = [ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 ]; /** * S-Box 2 * * @var array */ private static $sbox2 = [ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 ]; /** * S-Box 3 * * @var array */ private static $sbox3 = [ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 ]; /** * P-Array consists of 18 32-bit subkeys * * @var array */ private static $parray = [ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b ]; /** * The BCTX-working Array * * Holds the expanded key [p] and the key-depended s-boxes [sb] * * @var array */ private $bctx; /** * Holds the last used key * * @var array */ private $kl; /** * The Key Length (in bytes) * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once.} * * @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength() * @var int */ protected $key_length = 16; /** * Default Constructor. * * @param string $mode * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ public function __construct($mode) { parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); } } /** * Sets the key length. * * Key lengths can be between 32 and 448 bits. * * @param int $length */ public function setKeyLength($length) { if ($length < 32 || $length > 448) { throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported'); } $this->key_length = $length >> 3; parent::setKeyLength($length); } /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { if ($engine == self::ENGINE_OPENSSL) { if ($this->key_length < 16) { return false; } // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { return false; } $this->cipher_name_openssl_ecb = 'bf-ecb'; $this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode(); } return parent::isValidEngineHelper($engine); } /** * Setup the key (expansion) * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() */ protected function setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key']) { // already expanded return; } $this->kl = ['key' => $this->key]; /* key-expanding p[] and S-Box building sb[] */ $this->bctx = [ 'p' => [], 'sb' => [ self::$sbox0, self::$sbox1, self::$sbox2, self::$sbox3 ] ]; // unpack binary string in unsigned chars $key = array_values(unpack('C*', $this->key)); $keyl = count($key); // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide) for ($j = 0, $i = 0; $i < 18; ++$i) { // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... for ($data = 0, $k = 0; $k < 4; ++$k) { $data = ($data << 8) | $key[$j]; if (++$j >= $keyl) { $j = 0; } } $this->bctx['p'][] = self::$parray[$i] ^ intval($data); } // encrypt the zero-string, replace P1 and P2 with the encrypted data, // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys $data = "\0\0\0\0\0\0\0\0"; for ($i = 0; $i < 18; $i += 2) { list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data))); $this->bctx['p'][$i ] = $l; $this->bctx['p'][$i + 1] = $r; } for ($i = 0; $i < 4; ++$i) { for ($j = 0; $j < 256; $j += 2) { list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data))); $this->bctx['sb'][$i][$j ] = $l; $this->bctx['sb'][$i][$j + 1] = $r; } } } /** * Initialize Static Variables */ protected static function initialize_static_variables() { if (is_float(self::$sbox2[0])) { self::$sbox0 = array_map('intval', self::$sbox0); self::$sbox1 = array_map('intval', self::$sbox1); self::$sbox2 = array_map('intval', self::$sbox2); self::$sbox3 = array_map('intval', self::$sbox3); self::$parray = array_map('intval', self::$parray); } parent::initialize_static_variables(); } /** * bcrypt * * @param string $sha2pass * @param string $sha2salt * @access private * @return string */ private static function bcrypt_hash($sha2pass, $sha2salt) { $p = self::$parray; $sbox0 = self::$sbox0; $sbox1 = self::$sbox1; $sbox2 = self::$sbox2; $sbox3 = self::$sbox3; $cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite')); $sha2pass = array_values(unpack('N*', $sha2pass)); $sha2salt = array_values(unpack('N*', $sha2salt)); self::expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 0; $i < 64; $i++) { self::expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p); self::expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p); } for ($i = 0; $i < 64; $i++) { for ($j = 0; $j < 8; $j += 2) { // count($cdata) == 8 list($cdata[$j], $cdata[$j + 1]) = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } } return pack('L*', ...$cdata); } /** * Performs OpenSSH-style bcrypt * * @param string $pass * @param string $salt * @param int $keylen * @param int $rounds * @access public * @return string */ public static function bcrypt_pbkdf($pass, $salt, $keylen, $rounds) { self::initialize_static_variables(); if (PHP_INT_SIZE == 4) { throw new \RuntimeException('bcrypt is far too slow to be practical on 32-bit versions of PHP'); } $sha2pass = hash('sha512', $pass, true); $results = []; $count = 1; while (32 * count($results) < $keylen) { $countsalt = $salt . pack('N', $count++); $sha2salt = hash('sha512', $countsalt, true); $out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt); for ($i = 1; $i < $rounds; $i++) { $sha2salt = hash('sha512', $tmpout, true); $tmpout = self::bcrypt_hash($sha2pass, $sha2salt); $out ^= $tmpout; } $results[] = $out; } $output = ''; for ($i = 0; $i < 32; $i++) { foreach ($results as $result) { $output .= $result[$i]; } } return substr($output, 0, $keylen); } /** * Key expansion without salt * * @access private * @param int[] $key * @param int[] $sbox0 * @param int[] $sbox1 * @param int[] $sbox2 * @param int[] $sbox3 * @param int[] $p * @see self::_bcrypt_hash() */ private static function expand0state(array $key, array &$sbox0, array &$sbox1, array &$sbox2, array &$sbox3, array &$p) { // expand0state is basically the same thing as this: //return self::expandstate(array_fill(0, 16, 0), $key); // but this separate function eliminates a bunch of XORs and array lookups $p = [ $p[0] ^ $key[0], $p[1] ^ $key[1], $p[2] ^ $key[2], $p[3] ^ $key[3], $p[4] ^ $key[4], $p[5] ^ $key[5], $p[6] ^ $key[6], $p[7] ^ $key[7], $p[8] ^ $key[8], $p[9] ^ $key[9], $p[10] ^ $key[10], $p[11] ^ $key[11], $p[12] ^ $key[12], $p[13] ^ $key[13], $p[14] ^ $key[14], $p[15] ^ $key[15], $p[16] ^ $key[0], $p[17] ^ $key[1] ]; // @codingStandardsIgnoreStart list( $p[0], $p[1]) = self::encryptBlockHelperFast( 0, 0, $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[2], $p[3]) = self::encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[4], $p[5]) = self::encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[6], $p[7]) = self::encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[8], $p[9]) = self::encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[10], $p[11]) = self::encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[12], $p[13]) = self::encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[14], $p[15]) = self::encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[16], $p[17]) = self::encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p); // @codingStandardsIgnoreEnd list($sbox0[0], $sbox0[1]) = self::encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2; $i < 256; $i += 2) { list($sbox0[$i], $sbox0[$i + 1]) = self::encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox1[0], $sbox1[1]) = self::encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2; $i < 256; $i += 2) { list($sbox1[$i], $sbox1[$i + 1]) = self::encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox2[0], $sbox2[1]) = self::encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2; $i < 256; $i += 2) { list($sbox2[$i], $sbox2[$i + 1]) = self::encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox3[0], $sbox3[1]) = self::encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2; $i < 256; $i += 2) { list($sbox3[$i], $sbox3[$i + 1]) = self::encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } } /** * Key expansion with salt * * @access private * @param int[] $data * @param int[] $key * @param int[] $sbox0 * @param int[] $sbox1 * @param int[] $sbox2 * @param int[] $sbox3 * @param int[] $p * @see self::_bcrypt_hash() */ private static function expandstate(array $data, array $key, array &$sbox0, array &$sbox1, array &$sbox2, array &$sbox3, array &$p) { $p = [ $p[0] ^ $key[0], $p[1] ^ $key[1], $p[2] ^ $key[2], $p[3] ^ $key[3], $p[4] ^ $key[4], $p[5] ^ $key[5], $p[6] ^ $key[6], $p[7] ^ $key[7], $p[8] ^ $key[8], $p[9] ^ $key[9], $p[10] ^ $key[10], $p[11] ^ $key[11], $p[12] ^ $key[12], $p[13] ^ $key[13], $p[14] ^ $key[14], $p[15] ^ $key[15], $p[16] ^ $key[0], $p[17] ^ $key[1] ]; // @codingStandardsIgnoreStart list( $p[0], $p[1]) = self::encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[2], $p[3]) = self::encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[4], $p[5]) = self::encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[6], $p[7]) = self::encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p); list( $p[8], $p[9]) = self::encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[10], $p[11]) = self::encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[12], $p[13]) = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[14], $p[15]) = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p); list($p[16], $p[17]) = self::encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p); // @codingStandardsIgnoreEnd list($sbox0[0], $sbox0[1]) = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better? list($sbox0[$i], $sbox0[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox1[0], $sbox1[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) { list($sbox1[$i], $sbox1[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox2[0], $sbox2[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) { list($sbox2[$i], $sbox2[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } list($sbox3[0], $sbox3[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) { list($sbox3[$i], $sbox3[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); } } /** * Encrypts a block * * @param string $in * @return string */ protected function encryptBlock($in) { $p = $this->bctx['p']; // extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower $sb_0 = $this->bctx['sb'][0]; $sb_1 = $this->bctx['sb'][1]; $sb_2 = $this->bctx['sb'][2]; $sb_3 = $this->bctx['sb'][3]; $in = unpack('N*', $in); $l = $in[1]; $r = $in[2]; list($r, $l) = PHP_INT_SIZE == 4 ? self::encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) : self::encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p); return pack("N*", $r, $l); } /** * Fast helper function for block encryption * * @access private * @param int $x0 * @param int $x1 * @param int[] $sbox0 * @param int[] $sbox1 * @param int[] $sbox2 * @param int[] $sbox3 * @param int[] $p * @return int[] */ private static function encryptBlockHelperFast($x0, $x1, array $sbox0, array $sbox1, array $sbox2, array $sbox3, array $p) { $x0 ^= $p[0]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14]; $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15]; $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16]; return [$x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF]; } /** * Slow helper function for block encryption * * @access private * @param int $x0 * @param int $x1 * @param int[] $sbox0 * @param int[] $sbox1 * @param int[] $sbox2 * @param int[] $sbox3 * @param int[] $p * @return int[] */ private static function encryptBlockHelperSlow($x0, $x1, array $sbox0, array $sbox1, array $sbox2, array $sbox3, array $p) { // -16777216 == intval(0xFF000000) on 32-bit PHP installs $x0 ^= $p[0]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14]; $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15]; $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16]; return [$x1 ^ $p[17], $x0]; } /** * Decrypts a block * * @param string $in * @return string */ protected function decryptBlock($in) { $p = $this->bctx['p']; $sb_0 = $this->bctx['sb'][0]; $sb_1 = $this->bctx['sb'][1]; $sb_2 = $this->bctx['sb'][2]; $sb_3 = $this->bctx['sb'][3]; $in = unpack('N*', $in); $l = $in[1]; $r = $in[2]; for ($i = 17; $i > 2; $i -= 2) { $l ^= $p[$i]; $r ^= self::safe_intval((self::safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]); $r ^= $p[$i - 1]; $l ^= self::safe_intval((self::safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]); } return pack('N*', $r ^ $p[0], $l ^ $p[1]); } /** * Setup the performance-optimized function for de/encrypt() * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt() */ protected function setupInlineCrypt() { $p = $this->bctx['p']; $init_crypt = ' static $sb_0, $sb_1, $sb_2, $sb_3; if (!$sb_0) { $sb_0 = $this->bctx["sb"][0]; $sb_1 = $this->bctx["sb"][1]; $sb_2 = $this->bctx["sb"][2]; $sb_3 = $this->bctx["sb"][3]; } '; $safeint = self::safe_intval_inline(); // Generating encrypt code: $encrypt_block = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; '; for ($i = 0; $i < 16; $i += 2) { $encrypt_block .= ' $l^= ' . $p[$i] . '; $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i + 1] . '; $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]') . '; '; } $encrypt_block .= ' $in = pack("N*", $r ^ ' . $p[17] . ', $l ^ ' . $p[16] . ' ); '; // Generating decrypt code: $decrypt_block = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; '; for ($i = 17; $i > 2; $i -= 2) { $decrypt_block .= ' $l^= ' . $p[$i] . '; $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i - 1] . '; $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]') . '; '; } $decrypt_block .= ' $in = pack("N*", $r ^ ' . $p[0] . ', $l ^ ' . $p[1] . ' ); '; $this->inline_crypt = $this->createInlineCryptFunction( [ 'init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ] ); } } PK1A#]ɔ��J�J1vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.phpnu�[���<?php /** * Pure-PHP (EC)DH implementation * * PHP version 5 * * Here's an example of how to compute a shared secret with this library: * <code> * <?php * include 'vendor/autoload.php'; * * $ourPrivate = \phpseclib3\Crypt\DH::createKey(); * $secret = DH::computeSecret($ourPrivate, $theirPublic); * * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\DH\Parameters; use phpseclib3\Crypt\DH\PrivateKey; use phpseclib3\Crypt\DH\PublicKey; use phpseclib3\Exception\NoKeyLoadedException; use phpseclib3\Exception\UnsupportedOperationException; use phpseclib3\Math\BigInteger; /** * Pure-PHP (EC)DH implementation * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DH extends AsymmetricKey { /** * Algorithm Name * * @var string */ const ALGORITHM = 'DH'; /** * DH prime * * @var \phpseclib3\Math\BigInteger */ protected $prime; /** * DH Base * * Prime divisor of p-1 * * @var \phpseclib3\Math\BigInteger */ protected $base; /** * Public Key * * @var \phpseclib3\Math\BigInteger */ protected $publicKey; /** * Create DH parameters * * This method is a bit polymorphic. It can take any of the following: * - two BigInteger's (prime and base) * - an integer representing the size of the prime in bits (the base is assumed to be 2) * - a string (eg. diffie-hellman-group14-sha1) * * @return Parameters */ public static function createParameters(...$args) { $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); } $params = new Parameters(); if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) { //if (!$args[0]->isPrime()) { // throw new \InvalidArgumentException('The first parameter should be a prime number'); //} $params->prime = $args[0]; $params->base = $args[1]; return $params; } elseif (count($args) == 1 && is_numeric($args[0])) { $params->prime = BigInteger::randomPrime($args[0]); $params->base = new BigInteger(2); return $params; } elseif (count($args) != 1 || !is_string($args[0])) { throw new \InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string'); } switch ($args[0]) { // see http://tools.ietf.org/html/rfc2409#section-6.2 and // http://tools.ietf.org/html/rfc2412, appendex E case 'diffie-hellman-group1-sha1': $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; break; // see http://tools.ietf.org/html/rfc3526#section-3 case 'diffie-hellman-group14-sha1': // 2048-bit MODP Group case 'diffie-hellman-group14-sha256': $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; break; // see https://tools.ietf.org/html/rfc3526#section-4 case 'diffie-hellman-group15-sha512': // 3072-bit MODP Group $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'; break; // see https://tools.ietf.org/html/rfc3526#section-5 case 'diffie-hellman-group16-sha512': // 4096-bit MODP Group $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF'; break; // see https://tools.ietf.org/html/rfc3526#section-6 case 'diffie-hellman-group17-sha512': // 6144-bit MODP Group $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' . 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' . 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' . 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' . 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' . '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' . 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' . 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' . '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF'; break; // see https://tools.ietf.org/html/rfc3526#section-7 case 'diffie-hellman-group18-sha512': // 8192-bit MODP Group $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' . 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' . 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' . 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' . 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' . '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' . 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' . 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' . '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' . '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' . '2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' . 'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' . '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' . '6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' . 'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' . '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' . '9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF'; break; default: throw new \InvalidArgumentException('Invalid named prime provided'); } $params->prime = new BigInteger($prime, 16); $params->base = new BigInteger(2); return $params; } /** * Create public / private key pair. * * The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 : * * "To increase the speed of the key exchange, both client and server may * reduce the size of their private exponents. It should be at least * twice as long as the key material that is generated from the shared * secret. For more details, see the paper by van Oorschot and Wiener * [VAN-OORSCHOT]." * * $length is in bits * * @param Parameters $params * @param int $length optional * @return DH\PrivateKey */ public static function createKey(Parameters $params, $length = 0) { $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); } $one = new BigInteger(1); if ($length) { $max = $one->bitwise_leftShift($length); $max = $max->subtract($one); } else { $max = $params->prime->subtract($one); } $key = new PrivateKey(); $key->prime = $params->prime; $key->base = $params->base; $key->privateKey = BigInteger::randomRange($one, $max); $key->publicKey = $key->base->powMod($key->privateKey, $key->prime); return $key; } /** * Compute Shared Secret * * @param PrivateKey|EC $private * @param PublicKey|BigInteger|string $public * @return mixed */ public static function computeSecret($private, $public) { if ($private instanceof PrivateKey) { // DH\PrivateKey switch (true) { case $public instanceof PublicKey: if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) { throw new \InvalidArgumentException('The public and private key do not share the same prime and / or base numbers'); } return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(true); case is_string($public): $public = new BigInteger($public, -256); // fall-through case $public instanceof BigInteger: return $public->powMod($private->privateKey, $private->prime)->toBytes(true); default: throw new \InvalidArgumentException('$public needs to be an instance of DH\PublicKey, a BigInteger or a string'); } } if ($private instanceof EC\PrivateKey) { switch (true) { case $public instanceof EC\PublicKey: $public = $public->getEncodedCoordinates(); // fall-through case is_string($public): $point = $private->multiply($public); switch ($private->getCurve()) { case 'Curve25519': case 'Curve448': $secret = $point; break; default: // according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned $secret = substr($point, 1, (strlen($point) - 1) >> 1); } /* if (($secret[0] & "\x80") === "\x80") { $secret = "\0$secret"; } */ return $secret; default: throw new \InvalidArgumentException('$public needs to be an instance of EC\PublicKey or a string (an encoded coordinate)'); } } } /** * Load the key * * @param string $key * @param string $password optional * @return AsymmetricKey */ public static function load($key, $password = false) { try { return EC::load($key, $password); } catch (NoKeyLoadedException $e) { } return parent::load($key, $password); } /** * OnLoad Handler * * @return bool */ protected static function onLoad(array $components) { if (!isset($components['privateKey']) && !isset($components['publicKey'])) { $new = new Parameters(); } else { $new = isset($components['privateKey']) ? new PrivateKey() : new PublicKey(); } $new->prime = $components['prime']; $new->base = $components['base']; if (isset($components['privateKey'])) { $new->privateKey = $components['privateKey']; } if (isset($components['publicKey'])) { $new->publicKey = $components['publicKey']; } return $new; } /** * Determines which hashing function should be used * * @param string $hash */ public function withHash($hash) { throw new UnsupportedOperationException('DH does not use a hash algorithm'); } /** * Returns the hash algorithm currently being used * */ public function getHash() { throw new UnsupportedOperationException('DH does not use a hash algorithm'); } /** * Returns the parameters * * A public / private key is only returned if the currently loaded "key" contains an x or y * value. * * @see self::getPublicKey() * @return mixed */ public function getParameters() { $type = DH::validatePlugin('Keys', 'PKCS1', 'saveParameters'); $key = $type::saveParameters($this->prime, $this->base); return DH::load($key, 'PKCS1'); } } PK1A#],� �T�T2vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.phpnu�[���<?php /** * Pure-PHP implementation of RC2. * * Uses mcrypt, if available, and an internal implementation, otherwise. * * PHP version 5 * * Useful resources are as follows: * * - {@link http://tools.ietf.org/html/rfc2268} * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $rc2 = new \phpseclib3\Crypt\RC2('ctr'); * * $rc2->setKey('abcdefgh'); * * $plaintext = str_repeat('a', 1024); * * echo $rc2->decrypt($rc2->encrypt($plaintext)); * ?> * </code> * * @author Patrick Monnerat <pm@datasphere.ch> * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\BlockCipher; use phpseclib3\Exception\BadModeException; /** * Pure-PHP implementation of RC2. * */ class RC2 extends BlockCipher { /** * Block Length of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size * @var int */ protected $block_size = 8; /** * The Key * * @see \phpseclib3\Crypt\Common\SymmetricKey::key * @see self::setKey() * @var string */ protected $key; /** * The Original (unpadded) Key * * @see \phpseclib3\Crypt\Common\SymmetricKey::key * @see self::setKey() * @see self::encrypt() * @see self::decrypt() * @var string */ private $orig_key; /** * Key Length (in bytes) * * @see \phpseclib3\Crypt\RC2::setKeyLength() * @var int */ protected $key_length = 16; // = 128 bits /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'rc2'; /** * Optimizing value while CFB-encrypting * * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len * @var int */ protected $cfb_init_len = 500; /** * The key length in bits. * * {@internal Should be in range [1..1024].} * * {@internal Changing this value after setting the key has no effect.} * * @see self::setKeyLength() * @see self::setKey() * @var int */ private $default_key_length = 1024; /** * The key length in bits. * * {@internal Should be in range [1..1024].} * * @see self::isValidEnine() * @see self::setKey() * @var int */ private $current_key_length; /** * The Key Schedule * * @see self::setupKey() * @var array */ private $keys; /** * Key expansion randomization table. * Twice the same 256-value sequence to save a modulus in key expansion. * * @see self::setKey() * @var array */ private static $pitable = [ 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD, 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD ]; /** * Inverse key expansion randomization table. * * @see self::setKey() * @var array */ private static $invpitable = [ 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, 0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53, 0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68, 0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B, 0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12, 0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB, 0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3, 0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26, 0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67, 0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB, 0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC, 0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60, 0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7, 0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD, 0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24, 0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31, 0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE, 0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99, 0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C, 0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA, 0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35, 0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61, 0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72, 0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3, 0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F, 0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9, 0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77, 0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75, 0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87, 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 ]; /** * Default Constructor. * * @param string $mode * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ public function __construct($mode) { parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new BadModeException('Block ciphers cannot be ran in stream mode'); } } /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { switch ($engine) { case self::ENGINE_OPENSSL: if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) { return false; } // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { return false; } $this->cipher_name_openssl_ecb = 'rc2-ecb'; $this->cipher_name_openssl = 'rc2-' . $this->openssl_translate_mode(); } return parent::isValidEngineHelper($engine); } /** * Sets the key length. * * Valid key lengths are 8 to 1024. * Calling this function after setting the key has no effect until the next * \phpseclib3\Crypt\RC2::setKey() call. * * @param int $length in bits * @throws \LengthException if the key length isn't supported */ public function setKeyLength($length) { if ($length < 8 || $length > 1024) { throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); } $this->default_key_length = $this->current_key_length = $length; $this->explicit_key_length = $length >> 3; } /** * Returns the current key length * * @return int */ public function getKeyLength() { return $this->current_key_length; } /** * Sets the key. * * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg. * strlen($key) <= 128), however, we only use the first 128 bytes if $key * has more then 128 bytes in it, and set $key to a single null byte if * it is empty. * * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() * @param string $key * @param int|boolean $t1 optional Effective key length in bits. * @throws \LengthException if the key length isn't supported */ public function setKey($key, $t1 = false) { $this->orig_key = $key; if ($t1 === false) { $t1 = $this->default_key_length; } if ($t1 < 1 || $t1 > 1024) { throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); } $this->current_key_length = $t1; if (strlen($key) < 1 || strlen($key) > 128) { throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported'); } $t = strlen($key); // The mcrypt RC2 implementation only supports effective key length // of 1024 bits. It is however possible to handle effective key // lengths in range 1..1024 by expanding the key and applying // inverse pitable mapping to the first byte before submitting it // to mcrypt. // Key expansion. $l = array_values(unpack('C*', $key)); $t8 = ($t1 + 7) >> 3; $tm = 0xFF >> (8 * $t8 - $t1); // Expand key. $pitable = self::$pitable; for ($i = $t; $i < 128; $i++) { $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; } $i = 128 - $t8; $l[$i] = $pitable[$l[$i] & $tm]; while ($i--) { $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; } // Prepare the key for mcrypt. $l[0] = self::$invpitable[$l[0]]; array_unshift($l, 'C*'); $this->key = pack(...$l); $this->key_length = strlen($this->key); $this->changed = $this->nonIVChanged = true; $this->setEngine(); } /** * Encrypts a message. * * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::encrypt, with some additional OpenSSL handling code * * @see self::decrypt() * @param string $plaintext * @return string $ciphertext */ public function encrypt($plaintext) { if ($this->engine == self::ENGINE_OPENSSL) { $temp = $this->key; $this->key = $this->orig_key; $result = parent::encrypt($plaintext); $this->key = $temp; return $result; } return parent::encrypt($plaintext); } /** * Decrypts a message. * * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::decrypt, with some additional OpenSSL handling code * * @see self::encrypt() * @param string $ciphertext * @return string $plaintext */ public function decrypt($ciphertext) { if ($this->engine == self::ENGINE_OPENSSL) { $temp = $this->key; $this->key = $this->orig_key; $result = parent::decrypt($ciphertext); $this->key = $temp; return $result; } return parent::decrypt($ciphertext); } /** * Encrypts a block * * @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock() * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @param string $in * @return string */ protected function encryptBlock($in) { list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); $keys = $this->keys; $limit = 20; $actions = [$limit => 44, 44 => 64]; $j = 0; for (;;) { // Mixing round. $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; $r0 |= $r0 >> 16; $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; $r1 |= $r1 >> 16; $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; $r2 |= $r2 >> 16; $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; $r3 |= $r3 >> 16; if ($j === $limit) { if ($limit === 64) { break; } // Mashing round. $r0 += $keys[$r3 & 0x3F]; $r1 += $keys[$r0 & 0x3F]; $r2 += $keys[$r1 & 0x3F]; $r3 += $keys[$r2 & 0x3F]; $limit = $actions[$limit]; } } return pack('vvvv', $r0, $r1, $r2, $r3); } /** * Decrypts a block * * @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @param string $in * @return string */ protected function decryptBlock($in) { list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); $keys = $this->keys; $limit = 44; $actions = [$limit => 20, 20 => 0]; $j = 64; for (;;) { // R-mixing round. $r3 = ($r3 | ($r3 << 16)) >> 5; $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; $r2 = ($r2 | ($r2 << 16)) >> 3; $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; $r1 = ($r1 | ($r1 << 16)) >> 2; $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; $r0 = ($r0 | ($r0 << 16)) >> 1; $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; if ($j === $limit) { if ($limit === 0) { break; } // R-mashing round. $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; $limit = $actions[$limit]; } } return pack('vvvv', $r0, $r1, $r2, $r3); } /** * Creates the key schedule * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() */ protected function setupKey() { if (!isset($this->key)) { $this->setKey(''); } // Key has already been expanded in \phpseclib3\Crypt\RC2::setKey(): // Only the first value must be altered. $l = unpack('Ca/Cb/v*', $this->key); array_unshift($l, self::$pitable[$l['a']] | ($l['b'] << 8)); unset($l['a']); unset($l['b']); $this->keys = $l; } /** * Setup the performance-optimized function for de/encrypt() * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() */ protected function setupInlineCrypt() { // Init code for both, encrypt and decrypt. $init_crypt = '$keys = $this->keys;'; $keys = $this->keys; // $in is the current 8 bytes block which has to be en/decrypt $encrypt_block = $decrypt_block = ' $in = unpack("v4", $in); $r0 = $in[1]; $r1 = $in[2]; $r2 = $in[3]; $r3 = $in[4]; '; // Create code for encryption. $limit = 20; $actions = [$limit => 44, 44 => 64]; $j = 0; for (;;) { // Mixing round. $encrypt_block .= ' $r0 = (($r0 + ' . $keys[$j++] . ' + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; $r0 |= $r0 >> 16; $r1 = (($r1 + ' . $keys[$j++] . ' + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; $r1 |= $r1 >> 16; $r2 = (($r2 + ' . $keys[$j++] . ' + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; $r2 |= $r2 >> 16; $r3 = (($r3 + ' . $keys[$j++] . ' + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; $r3 |= $r3 >> 16;'; if ($j === $limit) { if ($limit === 64) { break; } // Mashing round. $encrypt_block .= ' $r0 += $keys[$r3 & 0x3F]; $r1 += $keys[$r0 & 0x3F]; $r2 += $keys[$r1 & 0x3F]; $r3 += $keys[$r2 & 0x3F];'; $limit = $actions[$limit]; } } $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; // Create code for decryption. $limit = 44; $actions = [$limit => 20, 20 => 0]; $j = 64; for (;;) { // R-mixing round. $decrypt_block .= ' $r3 = ($r3 | ($r3 << 16)) >> 5; $r3 = ($r3 - ' . $keys[--$j] . ' - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; $r2 = ($r2 | ($r2 << 16)) >> 3; $r2 = ($r2 - ' . $keys[--$j] . ' - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; $r1 = ($r1 | ($r1 << 16)) >> 2; $r1 = ($r1 - ' . $keys[--$j] . ' - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; $r0 = ($r0 | ($r0 << 16)) >> 1; $r0 = ($r0 - ' . $keys[--$j] . ' - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; if ($j === $limit) { if ($limit === 0) { break; } // R-mashing round. $decrypt_block .= ' $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; $limit = $actions[$limit]; } } $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; // Creates the inline-crypt function $this->inline_crypt = $this->createInlineCryptFunction( [ 'init_crypt' => $init_crypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ] ); } } PK1A#]�\&~t9t96vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.phpnu�[���<?php /** * Pure-PHP implementation of Salsa20. * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\StreamCipher; use phpseclib3\Exception\BadDecryptionException; use phpseclib3\Exception\InsufficientSetupException; /** * Pure-PHP implementation of Salsa20. * * @author Jim Wigginton <terrafrost@php.net> */ class Salsa20 extends StreamCipher { /** * Part 1 of the state * * @var string|false */ protected $p1 = false; /** * Part 2 of the state * * @var string|false */ protected $p2 = false; /** * Key Length (in bytes) * * @var int */ protected $key_length = 32; // = 256 bits /** * @see \phpseclib3\Crypt\Salsa20::crypt() */ const ENCRYPT = 0; /** * @see \phpseclib3\Crypt\Salsa20::crypt() */ const DECRYPT = 1; /** * Encryption buffer for continuous mode * * @var array */ protected $enbuffer; /** * Decryption buffer for continuous mode * * @var array */ protected $debuffer; /** * Counter * * @var int */ protected $counter = 0; /** * Using Generated Poly1305 Key * * @var boolean */ protected $usingGeneratedPoly1305Key = false; /** * Salsa20 uses a nonce * * @return bool */ public function usesNonce() { return true; } /** * Sets the key. * * @param string $key * @throws \LengthException if the key length isn't supported */ public function setKey($key) { switch (strlen($key)) { case 16: case 32: break; default: throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported'); } parent::setKey($key); } /** * Sets the nonce. * * @param string $nonce */ public function setNonce($nonce) { if (strlen($nonce) != 8) { throw new \LengthException('Nonce of size ' . strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce is supported'); } $this->nonce = $nonce; $this->changed = true; $this->setEngine(); } /** * Sets the counter. * * @param int $counter */ public function setCounter($counter) { $this->counter = $counter; $this->setEngine(); } /** * Creates a Poly1305 key using the method discussed in RFC8439 * * See https://tools.ietf.org/html/rfc8439#section-2.6.1 */ protected function createPoly1305Key() { if ($this->nonce === false) { throw new InsufficientSetupException('No nonce has been defined'); } if ($this->key === false) { throw new InsufficientSetupException('No key has been defined'); } $c = clone $this; $c->setCounter(0); $c->usePoly1305 = false; $block = $c->encrypt(str_repeat("\0", 256)); $this->setPoly1305Key(substr($block, 0, 32)); if ($this->counter == 0) { $this->counter++; } } /** * Setup the self::ENGINE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: * * - setKey() * * - setNonce() * * - First run of encrypt() / decrypt() with no init-settings * * @see self::setKey() * @see self::setNonce() * @see self::disableContinuousBuffer() */ protected function setup() { if (!$this->changed) { return; } $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter]; $this->changed = $this->nonIVChanged = false; if ($this->nonce === false) { throw new InsufficientSetupException('No nonce has been defined'); } if ($this->key === false) { throw new InsufficientSetupException('No key has been defined'); } if ($this->usePoly1305 && !isset($this->poly1305Key)) { $this->usingGeneratedPoly1305Key = true; $this->createPoly1305Key(); } $key = $this->key; if (strlen($key) == 16) { $constant = 'expand 16-byte k'; $key .= $key; } else { $constant = 'expand 32-byte k'; } $this->p1 = substr($constant, 0, 4) . substr($key, 0, 16) . substr($constant, 4, 4) . $this->nonce . "\0\0\0\0"; $this->p2 = substr($constant, 8, 4) . substr($key, 16, 16) . substr($constant, 12, 4); } /** * Setup the key (expansion) */ protected function setupKey() { // Salsa20 does not utilize this method } /** * Encrypts a message. * * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @see self::crypt() * @param string $plaintext * @return string $ciphertext */ public function encrypt($plaintext) { $ciphertext = $this->crypt($plaintext, self::ENCRYPT); if (isset($this->poly1305Key)) { $this->newtag = $this->poly1305($ciphertext); } return $ciphertext; } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * At least if the continuous buffer is disabled. * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see self::crypt() * @param string $ciphertext * @return string $plaintext */ public function decrypt($ciphertext) { if (isset($this->poly1305Key)) { if ($this->oldtag === false) { throw new InsufficientSetupException('Authentication Tag has not been set'); } $newtag = $this->poly1305($ciphertext); if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) { $this->oldtag = false; throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); } $this->oldtag = false; } return $this->crypt($ciphertext, self::DECRYPT); } /** * Encrypts a block * * @param string $in */ protected function encryptBlock($in) { // Salsa20 does not utilize this method } /** * Decrypts a block * * @param string $in */ protected function decryptBlock($in) { // Salsa20 does not utilize this method } /** * Encrypts or decrypts a message. * * @see self::encrypt() * @see self::decrypt() * @param string $text * @param int $mode * @return string $text */ private function crypt($text, $mode) { $this->setup(); if (!$this->continuousBuffer) { if ($this->engine == self::ENGINE_OPENSSL) { $iv = pack('V', $this->counter) . $this->p2; return openssl_encrypt( $text, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv ); } $i = $this->counter; $blocks = str_split($text, 64); foreach ($blocks as &$block) { $block ^= static::salsa20($this->p1 . pack('V', $i++) . $this->p2); } return implode('', $blocks); } if ($mode == self::ENCRYPT) { $buffer = &$this->enbuffer; } else { $buffer = &$this->debuffer; } if (!strlen($buffer['ciphertext'])) { $ciphertext = ''; } else { $ciphertext = $text ^ Strings::shift($buffer['ciphertext'], strlen($text)); $text = substr($text, strlen($ciphertext)); if (!strlen($text)) { return $ciphertext; } } $overflow = strlen($text) % 64; // & 0x3F if ($overflow) { $text2 = Strings::pop($text, $overflow); if ($this->engine == self::ENGINE_OPENSSL) { $iv = pack('V', $buffer['counter']) . $this->p2; // at this point $text should be a multiple of 64 $buffer['counter'] += (strlen($text) >> 6) + 1; // ie. divide by 64 $encrypted = openssl_encrypt( $text . str_repeat("\0", 64), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv ); $temp = Strings::pop($encrypted, 64); } else { $blocks = str_split($text, 64); if (strlen($text)) { foreach ($blocks as &$block) { $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2); } } $encrypted = implode('', $blocks); $temp = static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2); } $ciphertext .= $encrypted . ($text2 ^ $temp); $buffer['ciphertext'] = substr($temp, $overflow); } elseif (!strlen($buffer['ciphertext'])) { if ($this->engine == self::ENGINE_OPENSSL) { $iv = pack('V', $buffer['counter']) . $this->p2; $buffer['counter'] += (strlen($text) >> 6); $ciphertext .= openssl_encrypt( $text, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv ); } else { $blocks = str_split($text, 64); foreach ($blocks as &$block) { $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2); } $ciphertext .= implode('', $blocks); } } return $ciphertext; } /** * Left Rotate * * @param int $x * @param int $n * @return int */ protected static function leftRotate($x, $n) { if (PHP_INT_SIZE == 8) { $r1 = $x << $n; $r1 &= 0xFFFFFFFF; $r2 = ($x & 0xFFFFFFFF) >> (32 - $n); } else { $x = (int) $x; $r1 = $x << $n; $r2 = $x >> (32 - $n); $r2 &= (1 << $n) - 1; } return $r1 | $r2; } /** * The quarterround function * * @param int $a * @param int $b * @param int $c * @param int $d */ protected static function quarterRound(&$a, &$b, &$c, &$d) { $b ^= self::leftRotate($a + $d, 7); $c ^= self::leftRotate($b + $a, 9); $d ^= self::leftRotate($c + $b, 13); $a ^= self::leftRotate($d + $c, 18); } /** * The doubleround function * * @param int $x0 (by reference) * @param int $x1 (by reference) * @param int $x2 (by reference) * @param int $x3 (by reference) * @param int $x4 (by reference) * @param int $x5 (by reference) * @param int $x6 (by reference) * @param int $x7 (by reference) * @param int $x8 (by reference) * @param int $x9 (by reference) * @param int $x10 (by reference) * @param int $x11 (by reference) * @param int $x12 (by reference) * @param int $x13 (by reference) * @param int $x14 (by reference) * @param int $x15 (by reference) */ protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15) { // columnRound static::quarterRound($x0, $x4, $x8, $x12); static::quarterRound($x5, $x9, $x13, $x1); static::quarterRound($x10, $x14, $x2, $x6); static::quarterRound($x15, $x3, $x7, $x11); // rowRound static::quarterRound($x0, $x1, $x2, $x3); static::quarterRound($x5, $x6, $x7, $x4); static::quarterRound($x10, $x11, $x8, $x9); static::quarterRound($x15, $x12, $x13, $x14); } /** * The Salsa20 hash function function * * @param string $x */ protected static function salsa20($x) { $z = $x = unpack('V*', $x); for ($i = 0; $i < 10; $i++) { static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]); } for ($i = 1; $i <= 16; $i++) { $x[$i] += $z[$i]; } return pack('V*', ...$x); } /** * Calculates Poly1305 MAC * * @see self::decrypt() * @see self::encrypt() * @param string $ciphertext * @return string */ protected function poly1305($ciphertext) { if (!$this->usingGeneratedPoly1305Key) { return parent::poly1305($this->aad . $ciphertext); } else { /* sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts it: $this->newtag = $this->poly1305( $this->aad . pack('V', strlen($this->aad)) . "\0\0\0\0" . $ciphertext . pack('V', strlen($ciphertext)) . "\0\0\0\0" ); phpseclib opts to use the IETF construction, even when the nonce is 64-bits instead of 96-bits */ return parent::poly1305( self::nullPad128($this->aad) . self::nullPad128($ciphertext) . pack('V', strlen($this->aad)) . "\0\0\0\0" . pack('V', strlen($ciphertext)) . "\0\0\0\0" ); } } } PK1A#]��KKCvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.phpnu�[���<?php /** * Raw RSA Key Handler * * PHP version 5 * * An array containing two \phpseclib3\Math\BigInteger objects. * * The exponent can be indexed with any of the following: * * 0, e, exponent, publicExponent * * The modulus can be indexed with any of the following: * * 1, n, modulo, modulus * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Math\BigInteger; /** * Raw RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Raw { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!is_array($key)) { throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key)); } $key = array_change_key_case($key, CASE_LOWER); $components = ['isPublicKey' => false]; foreach (['e', 'exponent', 'publicexponent', 0, 'privateexponent', 'd'] as $index) { if (isset($key[$index])) { $components['publicExponent'] = $key[$index]; break; } } foreach (['n', 'modulo', 'modulus', 1] as $index) { if (isset($key[$index])) { $components['modulus'] = $key[$index]; break; } } if (!isset($components['publicExponent']) || !isset($components['modulus'])) { throw new \UnexpectedValueException('Modulus / exponent not present'); } if (isset($key['primes'])) { $components['primes'] = $key['primes']; } elseif (isset($key['p']) && isset($key['q'])) { $indices = [ ['p', 'q'], ['prime1', 'prime2'] ]; foreach ($indices as $index) { list($i0, $i1) = $index; if (isset($key[$i0]) && isset($key[$i1])) { $components['primes'] = [1 => $key[$i0], $key[$i1]]; } } } if (isset($key['exponents'])) { $components['exponents'] = $key['exponents']; } else { $indices = [ ['dp', 'dq'], ['exponent1', 'exponent2'] ]; foreach ($indices as $index) { list($i0, $i1) = $index; if (isset($key[$i0]) && isset($key[$i1])) { $components['exponents'] = [1 => $key[$i0], $key[$i1]]; } } } if (isset($key['coefficients'])) { $components['coefficients'] = $key['coefficients']; } else { foreach (['inverseq', 'q\'', 'coefficient'] as $index) { if (isset($key[$index])) { $components['coefficients'] = [2 => $key[$index]]; } } } if (!isset($components['primes'])) { $components['isPublicKey'] = true; return $components; } if (!isset($components['exponents'])) { $one = new BigInteger(1); $temp = $components['primes'][1]->subtract($one); $exponents = [1 => $components['publicExponent']->modInverse($temp)]; $temp = $components['primes'][2]->subtract($one); $exponents[] = $components['publicExponent']->modInverse($temp); $components['exponents'] = $exponents; } if (!isset($components['coefficients'])) { $components['coefficients'] = [2 => $components['primes'][2]->modInverse($components['primes'][1])]; } foreach (['privateexponent', 'd'] as $index) { if (isset($key[$index])) { $components['privateExponent'] = $key[$index]; break; } } return $components; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return array */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('Raw private keys do not support encryption'); } return [ 'e' => clone $e, 'n' => clone $n, 'd' => clone $d, 'primes' => array_map(function ($var) { return clone $var; }, $primes), 'exponents' => array_map(function ($var) { return clone $var; }, $exponents), 'coefficients' => array_map(function ($var) { return clone $var; }, $coefficients) ]; } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @return array */ public static function savePublicKey(BigInteger $n, BigInteger $e) { return ['e' => clone $e, 'n' => clone $n]; } } PK1A#]bY�PWWEvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.phpnu�[���<?php /** * PuTTY Formatted RSA Key Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; use phpseclib3\Math\BigInteger; /** * PuTTY Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PuTTY extends Progenitor { /** * Public Handler * * @var string */ const PUBLIC_HANDLER = 'phpseclib3\Crypt\RSA\Formats\Keys\OpenSSH'; /** * Algorithm Identifier * * @var array */ protected static $types = ['ssh-rsa']; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { static $one; if (!isset($one)) { $one = new BigInteger(1); } $components = parent::load($key, $password); if (!isset($components['private'])) { return $components; } extract($components); unset($components['public'], $components['private']); $isPublicKey = false; $result = Strings::unpackSSH2('ii', $public); if ($result === false) { throw new \UnexpectedValueException('Key appears to be malformed'); } list($publicExponent, $modulus) = $result; $result = Strings::unpackSSH2('iiii', $private); if ($result === false) { throw new \UnexpectedValueException('Key appears to be malformed'); } $primes = $coefficients = []; list($privateExponent, $primes[1], $primes[2], $coefficients[2]) = $result; $temp = $primes[1]->subtract($one); $exponents = [1 => $publicExponent->modInverse($temp)]; $temp = $primes[2]->subtract($one); $exponents[] = $publicExponent->modInverse($temp); return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey'); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { if (count($primes) != 2) { throw new \InvalidArgumentException('PuTTY does not support multi-prime RSA keys'); } $public = Strings::packSSH2('ii', $e, $n); $private = Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]); return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password, $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e) { return self::wrapPublicKey(Strings::packSSH2('ii', $e, $n), 'ssh-rsa'); } } PK1A#]Z�\�++Cvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.phpnu�[���<?php /** * PKCS#8 Formatted RSA-PSS Key Handler * * PHP version 5 * * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * Analogous to "openssl genpkey -algorithm rsa-pss". * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#8 Formatted RSA-PSS Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PSS extends Progenitor { /** * OID Name * * @var string */ const OID_NAME = 'id-RSASSA-PSS'; /** * OID Value * * @var string */ const OID_VALUE = '1.2.840.113549.1.1.10'; /** * OIDs loaded * * @var bool */ private static $oidsLoaded = false; /** * Child OIDs loaded * * @var bool */ protected static $childOIDsLoaded = false; /** * Initialize static variables */ private static function initialize_static_variables() { if (!self::$oidsLoaded) { ASN1::loadOIDs([ 'md2' => '1.2.840.113549.2.2', 'md4' => '1.2.840.113549.2.4', 'md5' => '1.2.840.113549.2.5', 'id-sha1' => '1.3.14.3.2.26', 'id-sha256' => '2.16.840.1.101.3.4.2.1', 'id-sha384' => '2.16.840.1.101.3.4.2.2', 'id-sha512' => '2.16.840.1.101.3.4.2.3', 'id-sha224' => '2.16.840.1.101.3.4.2.4', 'id-sha512/224' => '2.16.840.1.101.3.4.2.5', 'id-sha512/256' => '2.16.840.1.101.3.4.2.6', 'id-mgf1' => '1.2.840.113549.1.1.8' ]); self::$oidsLoaded = true; } } /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { self::initialize_static_variables(); if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false]; $key = parent::load($key, $password); $type = isset($key['privateKey']) ? 'private' : 'public'; $result = $components + PKCS1::load($key[$type . 'Key']); if (isset($key[$type . 'KeyAlgorithm']['parameters'])) { $decoded = ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']); if ($decoded === false) { throw new \UnexpectedValueException('Unable to decode parameters'); } $params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP); } else { $params = []; } if (isset($params['maskGenAlgorithm']['parameters'])) { $decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']); if ($decoded === false) { throw new \UnexpectedValueException('Unable to decode parameters'); } $params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP); } else { $params['maskGenAlgorithm'] = [ 'algorithm' => 'id-mgf1', 'parameters' => ['algorithm' => 'id-sha1'] ]; } if (!isset($params['hashAlgorithm']['algorithm'])) { $params['hashAlgorithm']['algorithm'] = 'id-sha1'; } $result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']); $result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']); if (isset($params['saltLength'])) { $result['saltLength'] = (int) $params['saltLength']->toString(); } if (isset($key['meta'])) { $result['meta'] = $key['meta']; } return $result; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { self::initialize_static_variables(); $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients); $key = ASN1::extractBER($key); $params = self::savePSSParams($options); return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []) { self::initialize_static_variables(); $key = PKCS1::savePublicKey($n, $e); $key = ASN1::extractBER($key); $params = self::savePSSParams($options); return self::wrapPublicKey($key, $params); } /** * Encodes PSS parameters * * @param array $options * @return string */ public static function savePSSParams(array $options) { /* The trailerField field is an integer. It provides compatibility with IEEE Std 1363a-2004 [P1363A]. The value MUST be 1, which represents the trailer field with hexadecimal value 0xBC. Other trailer fields, including the trailer field composed of HashID concatenated with 0xCC that is specified in IEEE Std 1363a, are not supported. Implementations that perform signature generation MUST omit the trailerField field, indicating that the default trailer field value was used. Implementations that perform signature validation MUST recognize both a present trailerField field with value 1 and an absent trailerField field. source: https://tools.ietf.org/html/rfc4055#page-9 */ $params = [ 'trailerField' => new BigInteger(1) ]; if (isset($options['hash'])) { $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash']; } if (isset($options['MGFHash'])) { $temp = ['algorithm' => 'id-' . $options['MGFHash']]; $temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP); $params['maskGenAlgorithm'] = [ 'algorithm' => 'id-mgf1', 'parameters' => new ASN1\Element($temp) ]; } if (isset($options['saltLength'])) { $params['saltLength'] = new BigInteger($options['saltLength']); } return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP)); } } PK1A#]ߕ�kkFvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.phpnu�[���<?php /** * Miccrosoft BLOB Formatted RSA Key Handler * * More info: * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * Microsoft BLOB Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class MSBLOB { /** * Public/Private Key Pair * */ const PRIVATEKEYBLOB = 0x7; /** * Public Key * */ const PUBLICKEYBLOB = 0x6; /** * Public Key * */ const PUBLICKEYBLOBEX = 0xA; /** * RSA public key exchange algorithm * */ const CALG_RSA_KEYX = 0x0000A400; /** * RSA public key exchange algorithm * */ const CALG_RSA_SIGN = 0x00002400; /** * Public Key * */ const RSA1 = 0x31415352; /** * Private Key * */ const RSA2 = 0x32415352; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $key = Strings::base64_decode($key); if (!is_string($key)) { throw new \UnexpectedValueException('Base64 decoding produced an error'); } if (strlen($key) < 20) { throw new \UnexpectedValueException('Key appears to be malformed'); } // PUBLICKEYSTRUC publickeystruc // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx extract(unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8))); /** * @var string $type * @var string $version * @var integer $reserved * @var integer $algo */ switch (ord($type)) { case self::PUBLICKEYBLOB: case self::PUBLICKEYBLOBEX: $publickey = true; break; case self::PRIVATEKEYBLOB: $publickey = false; break; default: throw new \UnexpectedValueException('Key appears to be malformed'); } $components = ['isPublicKey' => $publickey]; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx switch ($algo) { case self::CALG_RSA_KEYX: case self::CALG_RSA_SIGN: break; default: throw new \UnexpectedValueException('Key appears to be malformed'); } // RSAPUBKEY rsapubkey // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit extract(unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12))); /** * @var integer $magic * @var integer $bitlen * @var string $pubexp */ switch ($magic) { case self::RSA2: $components['isPublicKey'] = false; // fall-through case self::RSA1: break; default: throw new \UnexpectedValueException('Key appears to be malformed'); } $baseLength = $bitlen / 16; if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) { throw new \UnexpectedValueException('Key appears to be malformed'); } $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256); // BYTE modulus[rsapubkey.bitlen/8] $components['modulus'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256); if ($publickey) { return $components; } $components['isPublicKey'] = false; // BYTE prime1[rsapubkey.bitlen/16] $components['primes'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; // BYTE prime2[rsapubkey.bitlen/16] $components['primes'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256); // BYTE exponent1[rsapubkey.bitlen/16] $components['exponents'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; // BYTE exponent2[rsapubkey.bitlen/16] $components['exponents'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256); // BYTE coefficient[rsapubkey.bitlen/16] $components['coefficients'] = [2 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)]; if (isset($components['privateExponent'])) { $components['publicExponent'] = $components['privateExponent']; } // BYTE privateExponent[rsapubkey.bitlen/8] $components['privateExponent'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256); return $components; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '') { if (count($primes) != 2) { throw new \InvalidArgumentException('MSBLOB does not support multi-prime RSA keys'); } if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('MSBLOB private keys do not support encryption'); } $n = strrev($n->toBytes()); $e = str_pad(strrev($e->toBytes()), 4, "\0"); $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); $key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e); $key .= $n; $key .= strrev($primes[1]->toBytes()); $key .= strrev($primes[2]->toBytes()); $key .= strrev($exponents[1]->toBytes()); $key .= strrev($exponents[2]->toBytes()); $key .= strrev($coefficients[2]->toBytes()); $key .= strrev($d->toBytes()); return Strings::base64_encode($key); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e) { $n = strrev($n->toBytes()); $e = str_pad(strrev($e->toBytes()), 4, "\0"); $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); $key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e); $key .= $n; return Strings::base64_encode($key); } } PK1A#]��PH��Cvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.phpnu�[���<?php /** * XML Formatted RSA Key Handler * * More info: * * http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue * http://www.w3.org/TR/xkms2/#XKMS_2_0_Paragraph_269 * http://en.wikipedia.org/wiki/XML_Signature * http://en.wikipedia.org/wiki/XKMS * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * XML Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class XML { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (!class_exists('DOMDocument')) { throw new BadConfigurationException('The dom extension is not setup correctly on this system'); } $components = [ 'isPublicKey' => false, 'primes' => [], 'exponents' => [], 'coefficients' => [] ]; $use_errors = libxml_use_internal_errors(true); $dom = new \DOMDocument(); if (substr($key, 0, 5) != '<?xml') { $key = '<xml>' . $key . '</xml>'; } if (!$dom->loadXML($key)) { libxml_use_internal_errors($use_errors); throw new \UnexpectedValueException('Key does not appear to contain XML'); } $xpath = new \DOMXPath($dom); $keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd']; foreach ($keys as $key) { // $dom->getElementsByTagName($key) is case-sensitive $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']"); if (!$temp->length) { continue; } $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256); switch ($key) { case 'modulus': $components['modulus'] = $value; break; case 'exponent': $components['publicExponent'] = $value; break; case 'p': $components['primes'][1] = $value; break; case 'q': $components['primes'][2] = $value; break; case 'dp': $components['exponents'][1] = $value; break; case 'dq': $components['exponents'][2] = $value; break; case 'inverseq': $components['coefficients'][2] = $value; break; case 'd': $components['privateExponent'] = $value; } } libxml_use_internal_errors($use_errors); foreach ($components as $key => $value) { if (is_array($value) && !count($value)) { unset($components[$key]); } } if (isset($components['modulus']) && isset($components['publicExponent'])) { if (count($components) == 3) { $components['isPublicKey'] = true; } return $components; } throw new \UnexpectedValueException('Modulus / exponent not present'); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '') { if (count($primes) != 2) { throw new \InvalidArgumentException('XML does not support multi-prime RSA keys'); } if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('XML private keys do not support encryption'); } return "<RSAKeyPair>\r\n" . ' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" . ' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" . ' <P>' . Strings::base64_encode($primes[1]->toBytes()) . "</P>\r\n" . ' <Q>' . Strings::base64_encode($primes[2]->toBytes()) . "</Q>\r\n" . ' <DP>' . Strings::base64_encode($exponents[1]->toBytes()) . "</DP>\r\n" . ' <DQ>' . Strings::base64_encode($exponents[2]->toBytes()) . "</DQ>\r\n" . ' <InverseQ>' . Strings::base64_encode($coefficients[2]->toBytes()) . "</InverseQ>\r\n" . ' <D>' . Strings::base64_encode($d->toBytes()) . "</D>\r\n" . '</RSAKeyPair>'; } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e) { return "<RSAKeyValue>\r\n" . ' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" . ' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" . '</RSAKeyValue>'; } } PK1A#](Ͼ�Cvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.phpnu�[���<?php /** * JSON Web Key (RFC7517) Formatted RSA Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor; use phpseclib3\Math\BigInteger; /** * JWK Formatted RSA Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class JWK extends Progenitor { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); if ($key->kty != 'RSA') { throw new \RuntimeException('Only RSA JWK keys are supported'); } $count = $publicCount = 0; $vars = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']; foreach ($vars as $var) { if (!isset($key->$var) || !is_string($key->$var)) { continue; } $count++; $value = new BigInteger(Strings::base64url_decode($key->$var), 256); switch ($var) { case 'n': $publicCount++; $components['modulus'] = $value; break; case 'e': $publicCount++; $components['publicExponent'] = $value; break; case 'd': $components['privateExponent'] = $value; break; case 'p': $components['primes'][1] = $value; break; case 'q': $components['primes'][2] = $value; break; case 'dp': $components['exponents'][1] = $value; break; case 'dq': $components['exponents'][2] = $value; break; case 'qi': $components['coefficients'][2] = $value; } } if ($count == count($vars)) { return $components + ['isPublicKey' => false]; } if ($count == 2 && $publicCount == 2) { return $components + ['isPublicKey' => true]; } throw new \UnexpectedValueException('Key does not have an appropriate number of RSA parameters'); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { if (count($primes) != 2) { throw new \InvalidArgumentException('JWK does not support multi-prime RSA keys'); } $key = [ 'kty' => 'RSA', 'n' => Strings::base64url_encode($n->toBytes()), 'e' => Strings::base64url_encode($e->toBytes()), 'd' => Strings::base64url_encode($d->toBytes()), 'p' => Strings::base64url_encode($primes[1]->toBytes()), 'q' => Strings::base64url_encode($primes[2]->toBytes()), 'dp' => Strings::base64url_encode($exponents[1]->toBytes()), 'dq' => Strings::base64url_encode($exponents[2]->toBytes()), 'qi' => Strings::base64url_encode($coefficients[2]->toBytes()) ]; return self::wrapKey($key, $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []) { $key = [ 'kty' => 'RSA', 'n' => Strings::base64url_encode($n->toBytes()), 'e' => Strings::base64url_encode($e->toBytes()) ]; return self::wrapKey($key, $options); } } PK1A#]��1��Gvendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.phpnu�[���<?php /** * OpenSSH Formatted RSA Key Handler * * PHP version 5 * * Place in $HOME/.ssh/authorized_keys * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; use phpseclib3\Math\BigInteger; /** * OpenSSH Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSH extends Progenitor { /** * Supported Key Types * * @var array */ protected static $types = ['ssh-rsa']; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { static $one; if (!isset($one)) { $one = new BigInteger(1); } $parsed = parent::load($key, $password); if (isset($parsed['paddedKey'])) { list($type) = Strings::unpackSSH2('s', $parsed['paddedKey']); if ($type != $parsed['type']) { throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])"); } $primes = $coefficients = []; list( $modulus, $publicExponent, $privateExponent, $coefficients[2], $primes[1], $primes[2], $comment, ) = Strings::unpackSSH2('i6s', $parsed['paddedKey']); $temp = $primes[1]->subtract($one); $exponents = [1 => $publicExponent->modInverse($temp)]; $temp = $primes[2]->subtract($one); $exponents[] = $publicExponent->modInverse($temp); $isPublicKey = false; return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey'); } list($publicExponent, $modulus) = Strings::unpackSSH2('ii', $parsed['publicKey']); return [ 'isPublicKey' => true, 'modulus' => $modulus, 'publicExponent' => $publicExponent, 'comment' => $parsed['comment'] ]; } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []) { $RSAPublicKey = Strings::packSSH2('sii', 'ssh-rsa', $e, $n); if (isset($options['binary']) ? $options['binary'] : self::$binary) { return $RSAPublicKey; } $comment = isset($options['comment']) ? $options['comment'] : self::$comment; $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $comment; return $RSAPublicKey; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { $publicKey = self::savePublicKey($n, $e, ['binary' => true]); $privateKey = Strings::packSSH2('si6', 'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]); return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); } } PK1A#]�FOn% % Evendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.phpnu�[���<?php /** * PKCS#8 Formatted RSA Key Handler * * PHP version 5 * * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 * is specific to private keys it's basically creating a DER-encoded wrapper * for keys. This just extends that same concept to public keys (much like ssh-keygen) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\Math\BigInteger; /** * PKCS#8 Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS8 extends Progenitor { /** * OID Name * * @var string */ const OID_NAME = 'rsaEncryption'; /** * OID Value * * @var string */ const OID_VALUE = '1.2.840.113549.1.1.1'; /** * Child OIDs loaded * * @var bool */ protected static $childOIDsLoaded = false; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); if (isset($key['privateKey'])) { $components['isPublicKey'] = false; $type = 'private'; } else { $components['isPublicKey'] = true; $type = 'public'; } $result = $components + PKCS1::load($key[$type . 'Key']); if (isset($key['meta'])) { $result['meta'] = $key['meta']; } return $result; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients); $key = ASN1::extractBER($key); return self::wrapPrivateKey($key, [], null, $password, null, '', $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []) { $key = PKCS1::savePublicKey($n, $e); $key = ASN1::extractBER($key); return self::wrapPublicKey($key, null); } } PK1A#]sǽi��Evendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.phpnu�[���<?php /** * PKCS#1 Formatted RSA Key Handler * * PHP version 5 * * Used by File/X509.php * * Processes keys with the following headers: * * -----BEGIN RSA PRIVATE KEY----- * -----BEGIN RSA PUBLIC KEY----- * * Analogous to ssh-keygen's pem format (as specified by -m) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#1 Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS1 extends Progenitor { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (strpos($key, 'PUBLIC') !== false) { $components = ['isPublicKey' => true]; } elseif (strpos($key, 'PRIVATE') !== false) { $components = ['isPublicKey' => false]; } else { $components = []; } $key = parent::load($key, $password); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP); if (is_array($key)) { $components += [ 'modulus' => $key['modulus'], 'publicExponent' => $key['publicExponent'], 'privateExponent' => $key['privateExponent'], 'primes' => [1 => $key['prime1'], $key['prime2']], 'exponents' => [1 => $key['exponent1'], $key['exponent2']], 'coefficients' => [2 => $key['coefficient']] ]; if ($key['version'] == 'multi') { foreach ($key['otherPrimeInfos'] as $primeInfo) { $components['primes'][] = $primeInfo['prime']; $components['exponents'][] = $primeInfo['exponent']; $components['coefficients'][] = $primeInfo['coefficient']; } } if (!isset($components['isPublicKey'])) { $components['isPublicKey'] = false; } return $components; } $key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP); if (!is_array($key)) { throw new \RuntimeException('Unable to perform ASN1 mapping'); } if (!isset($components['isPublicKey'])) { $components['isPublicKey'] = true; } return $components + $key; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @param \phpseclib3\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) { $num_primes = count($primes); $key = [ 'version' => $num_primes == 2 ? 'two-prime' : 'multi', 'modulus' => $n, 'publicExponent' => $e, 'privateExponent' => $d, 'prime1' => $primes[1], 'prime2' => $primes[2], 'exponent1' => $exponents[1], 'exponent2' => $exponents[2], 'coefficient' => $coefficients[2] ]; for ($i = 3; $i <= $num_primes; $i++) { $key['otherPrimeInfos'][] = [ 'prime' => $primes[$i], 'exponent' => $exponents[$i], 'coefficient' => $coefficients[$i] ]; } $key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP); return self::wrapPrivateKey($key, 'RSA', $password, $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $n * @param \phpseclib3\Math\BigInteger $e * @return string */ public static function savePublicKey(BigInteger $n, BigInteger $e) { $key = [ 'modulus' => $n, 'publicExponent' => $e ]; $key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP); return self::wrapPublicKey($key, 'RSA'); } } PK1A#]y�ok)?)?=vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.phpnu�[���<?php /** * RSA Private Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA\Formats\Keys\PSS; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * Raw RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ final class PrivateKey extends RSA implements Common\PrivateKey { use Common\Traits\PasswordProtected; /** * Primes for Chinese Remainder Theorem (ie. p and q) * * @var array */ protected $primes; /** * Exponents for Chinese Remainder Theorem (ie. dP and dQ) * * @var array */ protected $exponents; /** * Coefficients for Chinese Remainder Theorem (ie. qInv) * * @var array */ protected $coefficients; /** * Private Exponent * * @var \phpseclib3\Math\BigInteger */ protected $privateExponent; /** * RSADP * * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. * * @return bool|\phpseclib3\Math\BigInteger */ private function rsadp(BigInteger $c) { if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { throw new \OutOfRangeException('Ciphertext representative out of range'); } return $this->exponentiate($c); } /** * RSASP1 * * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. * * @return bool|\phpseclib3\Math\BigInteger */ private function rsasp1(BigInteger $m) { if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { throw new \OutOfRangeException('Signature representative out of range'); } return $this->exponentiate($m); } /** * Exponentiate * * @param \phpseclib3\Math\BigInteger $x * @return \phpseclib3\Math\BigInteger */ protected function exponentiate(BigInteger $x) { switch (true) { case empty($this->primes): case $this->primes[1]->equals(self::$zero): case empty($this->coefficients): case $this->coefficients[2]->equals(self::$zero): case empty($this->exponents): case $this->exponents[1]->equals(self::$zero): return $x->modPow($this->exponent, $this->modulus); } $num_primes = count($this->primes); if (!static::$enableBlinding) { $m_i = [ 1 => $x->modPow($this->exponents[1], $this->primes[1]), 2 => $x->modPow($this->exponents[2], $this->primes[2]) ]; $h = $m_i[1]->subtract($m_i[2]); $h = $h->multiply($this->coefficients[2]); list(, $h) = $h->divide($this->primes[1]); $m = $m_i[2]->add($h->multiply($this->primes[2])); $r = $this->primes[1]; for ($i = 3; $i <= $num_primes; $i++) { $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); $r = $r->multiply($this->primes[$i - 1]); $h = $m_i->subtract($m); $h = $h->multiply($this->coefficients[$i]); list(, $h) = $h->divide($this->primes[$i]); $m = $m->add($r->multiply($h)); } } else { $smallest = $this->primes[1]; for ($i = 2; $i <= $num_primes; $i++) { if ($smallest->compare($this->primes[$i]) > 0) { $smallest = $this->primes[$i]; } } $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one)); $m_i = [ 1 => $this->blind($x, $r, 1), 2 => $this->blind($x, $r, 2) ]; $h = $m_i[1]->subtract($m_i[2]); $h = $h->multiply($this->coefficients[2]); list(, $h) = $h->divide($this->primes[1]); $m = $m_i[2]->add($h->multiply($this->primes[2])); $r = $this->primes[1]; for ($i = 3; $i <= $num_primes; $i++) { $m_i = $this->blind($x, $r, $i); $r = $r->multiply($this->primes[$i - 1]); $h = $m_i->subtract($m); $h = $h->multiply($this->coefficients[$i]); list(, $h) = $h->divide($this->primes[$i]); $m = $m->add($r->multiply($h)); } } return $m; } /** * Performs RSA Blinding * * Protects against timing attacks by employing RSA Blinding. * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) * * @param \phpseclib3\Math\BigInteger $x * @param \phpseclib3\Math\BigInteger $r * @param int $i * @return \phpseclib3\Math\BigInteger */ private function blind(BigInteger $x, BigInteger $r, $i) { $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); $x = $x->modPow($this->exponents[$i], $this->primes[$i]); $r = $r->modInverse($this->primes[$i]); $x = $x->multiply($r); list(, $x) = $x->divide($this->primes[$i]); return $x; } /** * EMSA-PSS-ENCODE * * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. * * @return string * @param string $m * @throws \RuntimeException on encoding error * @param int $emBits */ private function emsa_pss_encode($m, $emBits) { // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { throw new \LengthException('RSA modulus too short'); } $salt = Random::string($sLen); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; $h = $this->hash->hash($m2); $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); $db = $ps . chr(1) . $salt; $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db) $maskedDB = $db ^ $dbMask; $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; $em = $maskedDB . $h . chr(0xBC); return $em; } /** * RSASSA-PSS-SIGN * * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. * * @param string $m * @return bool|string */ private function rsassa_pss_sign($m) { // EMSA-PSS encoding $em = $this->emsa_pss_encode($m, 8 * $this->k - 1); // RSA signature $m = $this->os2ip($em); $s = $this->rsasp1($m); $s = $this->i2osp($s, $this->k); // Output the signature S return $s; } /** * RSASSA-PKCS1-V1_5-SIGN * * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. * * @param string $m * @throws \LengthException if the RSA modulus is too short * @return bool|string */ private function rsassa_pkcs1_v1_5_sign($m) { // EMSA-PKCS1-v1_5 encoding // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus // too short" and stop. try { $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k); } catch (\LengthException $e) { throw new \LengthException('RSA modulus too short'); } // RSA signature $m = $this->os2ip($em); $s = $this->rsasp1($m); $s = $this->i2osp($s, $this->k); // Output the signature S return $s; } /** * Create a signature * * @see self::verify() * @param string $message * @return string */ public function sign($message) { switch ($this->signaturePadding) { case self::SIGNATURE_PKCS1: case self::SIGNATURE_RELAXED_PKCS1: return $this->rsassa_pkcs1_v1_5_sign($message); //case self::SIGNATURE_PSS: default: return $this->rsassa_pss_sign($message); } } /** * RSAES-PKCS1-V1_5-DECRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. * * @param string $c * @return bool|string */ private function rsaes_pkcs1_v1_5_decrypt($c) { // Length checking if (strlen($c) != $this->k) { // or if k < 11 throw new \LengthException('Ciphertext representative too long'); } // RSA decryption $c = $this->os2ip($c); $m = $this->rsadp($c); $em = $this->i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { throw new \RuntimeException('Decryption error'); } $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { throw new \RuntimeException('Decryption error'); } // Output M return $m; } /** * RSAES-OAEP-DECRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: * * Note. Care must be taken to ensure that an opponent cannot * distinguish the different error conditions in Step 3.g, whether by * error message or timing, or, more generally, learn partial * information about the encoded message EM. Otherwise an opponent may * be able to obtain useful information about the decryption of the * ciphertext C, leading to a chosen-ciphertext attack such as the one * observed by Manger [36]. * * @param string $c * @return bool|string */ private function rsaes_oaep_decrypt($c) { // Length checking // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { throw new \LengthException('Ciphertext representative too long'); } // RSA decryption $c = $this->os2ip($c); $m = $this->rsadp($c); $em = $this->i2osp($m, $this->k); // EME-OAEP decoding $lHash = $this->hash->hash($this->label); $y = ord($em[0]); $maskedSeed = substr($em, 1, $this->hLen); $maskedDB = substr($em, $this->hLen + 1); $seedMask = $this->mgf1($maskedDB, $this->hLen); $seed = $maskedSeed ^ $seedMask; $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); $db = $maskedDB ^ $dbMask; $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); $hashesMatch = hash_equals($lHash, $lHash2); $leadingZeros = 1; $patternMatch = 0; $offset = 0; for ($i = 0; $i < strlen($m); $i++) { $patternMatch |= $leadingZeros & ($m[$i] === "\1"); $leadingZeros &= $m[$i] === "\0"; $offset += $patternMatch ? 0 : 1; } // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation // to protect against timing attacks if (!$hashesMatch | !$patternMatch) { throw new \RuntimeException('Decryption error'); } // Output the message M return substr($m, $offset + 1); } /** * Raw Encryption / Decryption * * Doesn't use padding and is not recommended. * * @param string $m * @return bool|string * @throws \LengthException if strlen($m) > $this->k */ private function raw_encrypt($m) { if (strlen($m) > $this->k) { throw new \LengthException('Ciphertext representative too long'); } $temp = $this->os2ip($m); $temp = $this->rsadp($temp); return $this->i2osp($temp, $this->k); } /** * Decryption * * @see self::encrypt() * @param string $ciphertext * @return bool|string */ public function decrypt($ciphertext) { switch ($this->encryptionPadding) { case self::ENCRYPTION_NONE: return $this->raw_encrypt($ciphertext); case self::ENCRYPTION_PKCS1: return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext); //case self::ENCRYPTION_OAEP: default: return $this->rsaes_oaep_decrypt($ciphertext); } } /** * Returns the public key * * @return mixed */ public function getPublicKey() { $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); if (empty($this->modulus) || empty($this->publicExponent)) { throw new \RuntimeException('Public key components not found'); } $key = $type::savePublicKey($this->modulus, $this->publicExponent); return RSA::loadFormat('PKCS8', $key) ->withHash($this->hash->getHash()) ->withMGFHash($this->mgfHash->getHash()) ->withSaltLength($this->sLen) ->withLabel($this->label) ->withPadding($this->signaturePadding | $this->encryptionPadding); } /** * Returns the private key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin( 'Keys', $type, empty($this->primes) ? 'savePublicKey' : 'savePrivateKey' ); if ($type == PSS::class) { if ($this->signaturePadding == self::SIGNATURE_PSS) { $options += [ 'hash' => $this->hash->getHash(), 'MGFHash' => $this->mgfHash->getHash(), 'saltLength' => $this->getSaltLength() ]; } else { throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); } } if (empty($this->primes)) { return $type::savePublicKey($this->modulus, $this->exponent, $options); } return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); /* $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); if ($key !== false || count($this->primes) == 2) { return $key; } $nSize = $this->getSize() >> 1; $primes = [1 => clone self::$one, clone self::$one]; $i = 1; foreach ($this->primes as $prime) { $primes[$i] = $primes[$i]->multiply($prime); if ($primes[$i]->getLength() >= $nSize) { $i++; } } $exponents = []; $coefficients = [2 => $primes[2]->modInverse($primes[1])]; foreach ($primes as $i => $prime) { $temp = $prime->subtract(self::$one); $exponents[$i] = $this->modulus->modInverse($temp); } return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options); */ } } PK1A#]� �1<<<vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.phpnu�[���<?php /** * RSA Public Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\RSA; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA\Formats\Keys\PSS; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps\DigestInfo; use phpseclib3\Math\BigInteger; /** * Raw RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ final class PublicKey extends RSA implements Common\PublicKey { use Common\Traits\Fingerprint; /** * Exponentiate * * @param \phpseclib3\Math\BigInteger $x * @return \phpseclib3\Math\BigInteger */ private function exponentiate(BigInteger $x) { return $x->modPow($this->exponent, $this->modulus); } /** * RSAVP1 * * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. * * @param \phpseclib3\Math\BigInteger $s * @return bool|\phpseclib3\Math\BigInteger */ private function rsavp1($s) { if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { return false; } return $this->exponentiate($s); } /** * RSASSA-PKCS1-V1_5-VERIFY * * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. * * @param string $m * @param string $s * @throws \LengthException if the RSA modulus is too short * @return bool */ private function rsassa_pkcs1_v1_5_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $s = $this->os2ip($s); $m2 = $this->rsavp1($s); if ($m2 === false) { return false; } $em = $this->i2osp($m2, $this->k); if ($em === false) { return false; } // EMSA-PKCS1-v1_5 encoding $exception = false; // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus // too short" and stop. try { $em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k); $r1 = hash_equals($em, $em2); } catch (\LengthException $e) { $exception = true; } try { $em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k); $r2 = hash_equals($em, $em3); } catch (\LengthException $e) { $exception = true; } catch (UnsupportedAlgorithmException $e) { $r2 = false; } if ($exception) { throw new \LengthException('RSA modulus too short'); } // Compare return $r1 || $r2; } /** * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) * * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. * This means that under rare conditions you can have a perfectly valid v1.5 signature * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends * that if you're going to validate these types of signatures you "should indicate * whether the underlying BER encoding is a DER encoding and hence whether the signature * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of * RSA::PADDING_PKCS1... that means BER encoding was used. * * @param string $m * @param string $s * @return bool */ private function rsassa_pkcs1_v1_5_relaxed_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $s = $this->os2ip($s); $m2 = $this->rsavp1($s); if ($m2 === false) { return false; } $em = $this->i2osp($m2, $this->k); if ($em === false) { return false; } if (Strings::shift($em, 2) != "\0\1") { return false; } $em = ltrim($em, "\xFF"); if (Strings::shift($em) != "\0") { return false; } $decoded = ASN1::decodeBER($em); if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { return false; } static $oids; if (!isset($oids)) { $oids = [ 'md2' => '1.2.840.113549.2.2', 'md4' => '1.2.840.113549.2.4', // from PKCS1 v1.5 'md5' => '1.2.840.113549.2.5', 'id-sha1' => '1.3.14.3.2.26', 'id-sha256' => '2.16.840.1.101.3.4.2.1', 'id-sha384' => '2.16.840.1.101.3.4.2.2', 'id-sha512' => '2.16.840.1.101.3.4.2.3', // from PKCS1 v2.2 'id-sha224' => '2.16.840.1.101.3.4.2.4', 'id-sha512/224' => '2.16.840.1.101.3.4.2.5', 'id-sha512/256' => '2.16.840.1.101.3.4.2.6', ]; ASN1::loadOIDs($oids); } $decoded = ASN1::asn1map($decoded[0], DigestInfo::MAP); if (!isset($decoded) || $decoded === false) { return false; } if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) { return false; } if (isset($decoded['digestAlgorithm']['parameters']) && $decoded['digestAlgorithm']['parameters'] !== ['null' => '']) { return false; } $hash = $decoded['digestAlgorithm']['algorithm']; $hash = substr($hash, 0, 3) == 'id-' ? substr($hash, 3) : $hash; $hash = new Hash($hash); $em = $hash->hash($m); $em2 = $decoded['digest']; return hash_equals($em, $em2); } /** * EMSA-PSS-VERIFY * * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. * * @param string $m * @param string $em * @param int $emBits * @return string */ private function emsa_pss_verify($m, $em, $emBits) { // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // be output. $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8); $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { return false; } if ($em[strlen($em) - 1] != chr(0xBC)) { return false; } $maskedDB = substr($em, 0, -$this->hLen - 1); $h = substr($em, -$this->hLen - 1, $this->hLen); $temp = chr(0xFF << ($emBits & 7)); if ((~$maskedDB[0] & $temp) != $temp) { return false; } $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); $db = $maskedDB ^ $dbMask; $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; $temp = $emLen - $this->hLen - $sLen - 2; if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { return false; } $salt = substr($db, $temp + 1); // should be $sLen long $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; $h2 = $this->hash->hash($m2); return hash_equals($h, $h2); } /** * RSASSA-PSS-VERIFY * * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. * * @param string $m * @param string $s * @return bool|string */ private function rsassa_pss_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $modBits = strlen($this->modulus->toBits()); $s2 = $this->os2ip($s); $m2 = $this->rsavp1($s2); $em = $this->i2osp($m2, $this->k); if ($em === false) { return false; } // EMSA-PSS verification return $this->emsa_pss_verify($m, $em, $modBits - 1); } /** * Verifies a signature * * @see self::sign() * @param string $message * @param string $signature * @return bool */ public function verify($message, $signature) { switch ($this->signaturePadding) { case self::SIGNATURE_RELAXED_PKCS1: return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature); case self::SIGNATURE_PKCS1: return $this->rsassa_pkcs1_v1_5_verify($message, $signature); //case self::SIGNATURE_PSS: default: return $this->rsassa_pss_verify($message, $signature); } } /** * RSAES-PKCS1-V1_5-ENCRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. * * @param string $m * @param bool $pkcs15_compat optional * @throws \LengthException if strlen($m) > $this->k - 11 * @return bool|string */ private function rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) { $mLen = strlen($m); // Length checking if ($mLen > $this->k - 11) { throw new \LengthException('Message too long'); } // EME-PKCS1-v1_5 encoding $psLen = $this->k - $mLen - 3; $ps = ''; while (strlen($ps) != $psLen) { $temp = Random::string($psLen - strlen($ps)); $temp = str_replace("\x00", '', $temp); $ps .= $temp; } $type = 2; $em = chr(0) . chr($type) . $ps . chr(0) . $m; // RSA encryption $m = $this->os2ip($em); $c = $this->rsaep($m); $c = $this->i2osp($c, $this->k); // Output the ciphertext C return $c; } /** * RSAES-OAEP-ENCRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. * * @param string $m * @throws \LengthException if strlen($m) > $this->k - 2 * $this->hLen - 2 * @return string */ private function rsaes_oaep_encrypt($m) { $mLen = strlen($m); // Length checking // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { throw new \LengthException('Message too long'); } // EME-OAEP encoding $lHash = $this->hash->hash($this->label); $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); $db = $lHash . $ps . chr(1) . $m; $seed = Random::string($this->hLen); $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); $maskedDB = $db ^ $dbMask; $seedMask = $this->mgf1($maskedDB, $this->hLen); $maskedSeed = $seed ^ $seedMask; $em = chr(0) . $maskedSeed . $maskedDB; // RSA encryption $m = $this->os2ip($em); $c = $this->rsaep($m); $c = $this->i2osp($c, $this->k); // Output the ciphertext C return $c; } /** * RSAEP * * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. * * @param \phpseclib3\Math\BigInteger $m * @return bool|\phpseclib3\Math\BigInteger */ private function rsaep($m) { if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { throw new \OutOfRangeException('Message representative out of range'); } return $this->exponentiate($m); } /** * Raw Encryption / Decryption * * Doesn't use padding and is not recommended. * * @param string $m * @return bool|string * @throws \LengthException if strlen($m) > $this->k */ private function raw_encrypt($m) { if (strlen($m) > $this->k) { throw new \LengthException('Message too long'); } $temp = $this->os2ip($m); $temp = $this->rsaep($temp); return $this->i2osp($temp, $this->k); } /** * Encryption * * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be. * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will * be concatenated together. * * @see self::decrypt() * @param string $plaintext * @return bool|string * @throws \LengthException if the RSA modulus is too short */ public function encrypt($plaintext) { switch ($this->encryptionPadding) { case self::ENCRYPTION_NONE: return $this->raw_encrypt($plaintext); case self::ENCRYPTION_PKCS1: return $this->rsaes_pkcs1_v1_5_encrypt($plaintext); //case self::ENCRYPTION_OAEP: default: return $this->rsaes_oaep_encrypt($plaintext); } } /** * Returns the public key * * The public key is only returned under two circumstances - if the private key had the public key embedded within it * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. * * @param string $type * @param array $options optional * @return mixed */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePublicKey'); if ($type == PSS::class) { if ($this->signaturePadding == self::SIGNATURE_PSS) { $options += [ 'hash' => $this->hash->getHash(), 'MGFHash' => $this->mgfHash->getHash(), 'saltLength' => $this->getSaltLength() ]; } else { throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); } } return $type::savePublicKey($this->modulus, $this->publicExponent, $options); } /** * Converts a public key to a private key * * @return RSA */ public function asPrivateKey() { $new = new PrivateKey(); $new->exponent = $this->exponent; $new->modulus = $this->modulus; $new->k = $this->k; $new->format = $this->format; return $new ->withHash($this->hash->getHash()) ->withMGFHash($this->mgfHash->getHash()) ->withSaltLength($this->sLen) ->withLabel($this->label) ->withPadding($this->signaturePadding | $this->encryptionPadding); } } PK1A#]:�%1%1%5vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.phpnu�[���<?php /** * Random Number Generator * * PHP version 5 * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * echo bin2hex(\phpseclib3\Crypt\Random::string(8)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; /** * Pure-PHP Random Number Generator * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Random { /** * Generate a random string. * * Although microoptimizations are generally discouraged as they impair readability this function is ripe with * microoptimizations because this function has the potential of being called a huge number of times. * eg. for RSA key generation. * * @param int $length * @throws \RuntimeException if a symmetric cipher is needed but not loaded * @return string */ public static function string($length) { if (!$length) { return ''; } try { return random_bytes($length); } catch (\Exception $e) { // random_compat will throw an Exception, which in PHP 5 does not implement Throwable } catch (\Throwable $e) { // If a sufficient source of randomness is unavailable, random_bytes() will throw an // object that implements the Throwable interface (Exception, TypeError, Error). // We don't actually need to do anything here. The string() method should just continue // as normal. Note, however, that if we don't have a sufficient source of randomness for // random_bytes(), most of the other calls here will fail too, so we'll end up using // the PHP implementation. } // at this point we have no choice but to use a pure-PHP CSPRNG // cascade entropy across multiple PHP instances by fixing the session and collecting all // environmental variables, including the previous session data and the current session // data. // // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but // PHP isn't low level to be able to use those as sources and on a web server there's not likely // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use // however, a ton of people visiting the website. obviously you don't want to base your seeding // solely on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled // by the user and (2) this isn't just looking at the data sent by the current user - it's based // on the data sent by all users. one user requests the page and a hash of their info is saved. // another user visits the page and the serialization of their data is utilized along with the // server environment stuff and a hash of the previous http request data (which itself utilizes // a hash of the session data before that). certainly an attacker should be assumed to have // full control over his own http requests. he, however, is not going to have control over // everyone's http requests. static $crypto = false, $v; if ($crypto === false) { // save old session data $old_session_id = session_id(); $old_use_cookies = ini_get('session.use_cookies'); $old_session_cache_limiter = session_cache_limiter(); $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; if ($old_session_id != '') { session_write_close(); } session_id(1); ini_set('session.use_cookies', 0); session_cache_limiter(''); session_start(); $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') . (isset($_POST) ? self::safe_serialize($_POST) : '') . (isset($_GET) ? self::safe_serialize($_GET) : '') . (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') . // as of PHP 8.1 $GLOBALS can't be accessed by reference, which eliminates // the need for phpseclib_safe_serialize. see https://wiki.php.net/rfc/restrict_globals_usage // for more info (version_compare(PHP_VERSION, '8.1.0', '>=') ? serialize($GLOBALS) : self::safe_serialize($GLOBALS)) . self::safe_serialize($_SESSION) . self::safe_serialize($_OLD_SESSION); $v = $seed = $_SESSION['seed'] = sha1($v, true); if (!isset($_SESSION['count'])) { $_SESSION['count'] = 0; } $_SESSION['count']++; session_write_close(); // restore old session data if ($old_session_id != '') { session_id($old_session_id); session_start(); ini_set('session.use_cookies', $old_use_cookies); session_cache_limiter($old_session_cache_limiter); } else { if ($_OLD_SESSION !== false) { $_SESSION = $_OLD_SESSION; unset($_OLD_SESSION); } else { unset($_SESSION); } } // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the // original hash and the current hash. we'll be emulating that. for more info see the following URL: // // http://tools.ietf.org/html/rfc4253#section-7.2 // // see the is_string($crypto) part for an example of how to expand the keys $key = sha1($seed . 'A', true); $iv = sha1($seed . 'C', true); // ciphers are used as per the nist.gov link below. also, see this link: // // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives switch (true) { case class_exists('\phpseclib3\Crypt\AES'): $crypto = new AES('ctr'); break; case class_exists('\phpseclib3\Crypt\Twofish'): $crypto = new Twofish('ctr'); break; case class_exists('\phpseclib3\Crypt\Blowfish'): $crypto = new Blowfish('ctr'); break; case class_exists('\phpseclib3\Crypt\TripleDES'): $crypto = new TripleDES('ctr'); break; case class_exists('\phpseclib3\Crypt\DES'): $crypto = new DES('ctr'); break; case class_exists('\phpseclib3\Crypt\RC4'): $crypto = new RC4(); break; default: throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); } $crypto->setKey(substr($key, 0, $crypto->getKeyLength() >> 3)); $crypto->setIV(substr($iv, 0, $crypto->getBlockLength() >> 3)); $crypto->enableContinuousBuffer(); } //return $crypto->encrypt(str_repeat("\0", $length)); // the following is based off of ANSI X9.31: // // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf // // OpenSSL uses that same standard for it's random numbers: // // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c // (do a search for "ANS X9.31 A.2.4") $result = ''; while (strlen($result) < $length) { $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21 $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20 $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20 $result .= $r; } return substr($result, 0, $length); } /** * Safely serialize variables * * If a class has a private __sleep() it'll emit a warning * @return mixed * @param mixed $arr */ private static function safe_serialize(&$arr) { if (is_object($arr)) { return ''; } if (!is_array($arr)) { return serialize($arr); } // prevent circular array recursion if (isset($arr['__phpseclib_marker'])) { return ''; } $safearr = []; $arr['__phpseclib_marker'] = true; foreach (array_keys($arr) as $key) { // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage if ($key !== '__phpseclib_marker') { $safearr[$key] = self::safe_serialize($arr[$key]); } } unset($arr['__phpseclib_marker']); return serialize($safearr); } } PK1A#]�ǃ���2vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.phpnu�[���<?php /** * Pure-PHP implementation of RC4. * * Uses mcrypt, if available, and an internal implementation, otherwise. * * PHP version 5 * * Useful resources are as follows: * * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm} * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4} * * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification. * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $rc4 = new \phpseclib3\Crypt\RC4(); * * $rc4->setKey('abcdefgh'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $rc4->decrypt($rc4->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\StreamCipher; /** * Pure-PHP implementation of RC4. * * @author Jim Wigginton <terrafrost@php.net> */ class RC4 extends StreamCipher { /** * @see \phpseclib3\Crypt\RC4::_crypt() */ const ENCRYPT = 0; /** * @see \phpseclib3\Crypt\RC4::_crypt() */ const DECRYPT = 1; /** * Key Length (in bytes) * * @see \phpseclib3\Crypt\RC4::setKeyLength() * @var int */ protected $key_length = 128; // = 1024 bits /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'arcfour'; /** * The Key * * @see self::setKey() * @var string */ protected $key; /** * The Key Stream for decryption and encryption * * @see self::setKey() * @var array */ private $stream; /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { if ($engine == self::ENGINE_OPENSSL) { if ($this->continuousBuffer) { return false; } // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { return false; } $this->cipher_name_openssl = 'rc4-40'; } return parent::isValidEngineHelper($engine); } /** * Sets the key length * * Keys can be between 1 and 256 bytes long. * * @param int $length * @throws \LengthException if the key length is invalid */ public function setKeyLength($length) { if ($length < 8 || $length > 2048) { throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); } $this->key_length = $length >> 3; parent::setKeyLength($length); } /** * Sets the key length * * Keys can be between 1 and 256 bytes long. * * @param string $key */ public function setKey($key) { $length = strlen($key); if ($length < 1 || $length > 256) { throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); } parent::setKey($key); } /** * Encrypts a message. * * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @see self::crypt() * @param string $plaintext * @return string $ciphertext */ public function encrypt($plaintext) { if ($this->engine != self::ENGINE_INTERNAL) { return parent::encrypt($plaintext); } return $this->crypt($plaintext, self::ENCRYPT); } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * At least if the continuous buffer is disabled. * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see self::crypt() * @param string $ciphertext * @return string $plaintext */ public function decrypt($ciphertext) { if ($this->engine != self::ENGINE_INTERNAL) { return parent::decrypt($ciphertext); } return $this->crypt($ciphertext, self::DECRYPT); } /** * Encrypts a block * * @param string $in */ protected function encryptBlock($in) { // RC4 does not utilize this method } /** * Decrypts a block * * @param string $in */ protected function decryptBlock($in) { // RC4 does not utilize this method } /** * Setup the key (expansion) * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() */ protected function setupKey() { $key = $this->key; $keyLength = strlen($key); $keyStream = range(0, 255); $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; $temp = $keyStream[$i]; $keyStream[$i] = $keyStream[$j]; $keyStream[$j] = $temp; } $this->stream = []; $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [ 0, // index $i 0, // index $j $keyStream ]; } /** * Encrypts or decrypts a message. * * @see self::encrypt() * @see self::decrypt() * @param string $text * @param int $mode * @return string $text */ private function crypt($text, $mode) { if ($this->changed) { $this->setup(); } $stream = &$this->stream[$mode]; if ($this->continuousBuffer) { $i = &$stream[0]; $j = &$stream[1]; $keyStream = &$stream[2]; } else { $i = $stream[0]; $j = $stream[1]; $keyStream = $stream[2]; } $len = strlen($text); for ($k = 0; $k < $len; ++$k) { $i = ($i + 1) & 255; $ksi = $keyStream[$i]; $j = ($j + $ksi) & 255; $ksj = $keyStream[$j]; $keyStream[$i] = $ksj; $keyStream[$j] = $ksi; $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); } return $text; } } PK1A#]Q{*<<Dvendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.phpnu�[���<?php /** * "PKCS1" Formatted EC Key Handler * * PHP version 5 * * Processes keys with the following headers: * * -----BEGIN DH PARAMETERS----- * * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe * DSA, whose format isn't really formally described anywhere, so might as well * use it to describe this, too. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DH\Formats\Keys; use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * "PKCS1" Formatted DH Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS1 extends Progenitor { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP); if (!is_array($components)) { throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); } return $components; } /** * Convert EC parameters to the appropriate format * * @return string */ public static function saveParameters(BigInteger $prime, BigInteger $base, array $options = []) { $params = [ 'prime' => $prime, 'base' => $base ]; $params = ASN1::encodeDER($params, Maps\DHParameter::MAP); return "-----BEGIN DH PARAMETERS-----\r\n" . chunk_split(base64_encode($params), 64) . "-----END DH PARAMETERS-----\r\n"; } } PK1A#]ĤXmjjDvendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.phpnu�[���<?php /** * PKCS#8 Formatted DH Key Handler * * PHP version 5 * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DH\Formats\Keys; use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#8 Formatted DH Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS8 extends Progenitor { /** * OID Name * * @var string */ const OID_NAME = 'dhKeyAgreement'; /** * OID Value * * @var string */ const OID_VALUE = '1.2.840.113549.1.3.1'; /** * Child OIDs loaded * * @var bool */ protected static $childOIDsLoaded = false; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); if (empty($decoded)) { throw new \RuntimeException('Unable to decode BER of parameters'); } $components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP); if (!is_array($components)) { throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); } $decoded = ASN1::decodeBER($key[$type]); switch (true) { case !isset($decoded): case !isset($decoded[0]['content']): case !$decoded[0]['content'] instanceof BigInteger: throw new \RuntimeException('Unable to decode BER of parameters'); } $components[$type] = $decoded[0]['content']; return $components; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $prime * @param \phpseclib3\Math\BigInteger $base * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Math\BigInteger $publicKey * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $prime, BigInteger $base, BigInteger $privateKey, BigInteger $publicKey, $password = '', array $options = []) { $params = [ 'prime' => $prime, 'base' => $base ]; $params = ASN1::encodeDER($params, Maps\DHParameter::MAP); $params = new ASN1\Element($params); $key = ASN1::encodeDER($privateKey, ['type' => ASN1::TYPE_INTEGER]); return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Math\BigInteger $prime * @param \phpseclib3\Math\BigInteger $base * @param \phpseclib3\Math\BigInteger $publicKey * @param array $options optional * @return string */ public static function savePublicKey(BigInteger $prime, BigInteger $base, BigInteger $publicKey, array $options = []) { $params = [ 'prime' => $prime, 'base' => $base ]; $params = ASN1::encodeDER($params, Maps\DHParameter::MAP); $params = new ASN1\Element($params); $key = ASN1::encodeDER($publicKey, ['type' => ASN1::TYPE_INTEGER]); return self::wrapPublicKey($key, $params); } } PK1A#]��.<vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.phpnu�[���<?php /** * DH Parameters * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DH; use phpseclib3\Crypt\DH; /** * DH Parameters * * @author Jim Wigginton <terrafrost@php.net> */ final class Parameters extends DH { /** * Returns the parameters * * @param string $type * @param array $options optional * @return string */ public function toString($type = 'PKCS1', array $options = []) { $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); return $type::saveParameters($this->prime, $this->base, $options); } } PK1A#]�V�M;vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.phpnu�[���<?php /** * DH Public Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DH; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\DH; /** * DH Public Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PublicKey extends DH { use Common\Traits\Fingerprint; /** * Returns the public key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePublicKey'); return $type::savePublicKey($this->prime, $this->base, $this->publicKey, $options); } /** * Returns the public key as a BigInteger * * @return \phpseclib3\Math\BigInteger */ public function toBigInteger() { return $this->publicKey; } } PK1A#]B#Nh��<vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.phpnu�[���<?php /** * DH Private Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\DH; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\DH; /** * DH Private Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PrivateKey extends DH { use Common\Traits\PasswordProtected; /** * Private Key * * @var \phpseclib3\Math\BigInteger */ protected $privateKey; /** * Public Key * * @var \phpseclib3\Math\BigInteger */ protected $publicKey; /** * Returns the public key * * @return DH\PublicKey */ public function getPublicKey() { $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); if (!isset($this->publicKey)) { $this->publicKey = $this->base->powMod($this->privateKey, $this->prime); } $key = $type::savePublicKey($this->prime, $this->base, $this->publicKey); return DH::loadFormat('PKCS8', $key); } /** * Returns the private key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); if (!isset($this->publicKey)) { $this->publicKey = $this->base->powMod($this->privateKey, $this->prime); } return $type::savePrivateKey($this->prime, $this->base, $this->privateKey, $this->publicKey, $this->password, $options); } } PK1A#]�`�� � >vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.phpnu�[���<?php /** * PublicKeyLoader * * Returns a PublicKey or PrivateKey object. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\Common\PrivateKey; use phpseclib3\Crypt\Common\PublicKey; use phpseclib3\Exception\NoKeyLoadedException; use phpseclib3\File\X509; /** * PublicKeyLoader * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PublicKeyLoader { /** * Loads a public or private key * * @return AsymmetricKey * @param string|array $key * @param string $password optional */ public static function load($key, $password = false) { try { return EC::load($key, $password); } catch (NoKeyLoadedException $e) { } try { return RSA::load($key, $password); } catch (NoKeyLoadedException $e) { } try { return DSA::load($key, $password); } catch (NoKeyLoadedException $e) { } try { $x509 = new X509(); $x509->loadX509($key); $key = $x509->getPublicKey(); if ($key) { return $key; } } catch (\Exception $e) { } throw new NoKeyLoadedException('Unable to read key'); } /** * Loads a private key * * @return PrivateKey * @param string|array $key * @param string $password optional */ public static function loadPrivateKey($key, $password = false) { $key = self::load($key, $password); if (!$key instanceof PrivateKey) { throw new NoKeyLoadedException('The key that was loaded was not a private key'); } return $key; } /** * Loads a public key * * @return PublicKey * @param string|array $key */ public static function loadPublicKey($key) { $key = self::load($key); if (!$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a public key'); } return $key; } /** * Loads parameters * * @return AsymmetricKey * @param string|array $key */ public static function loadParameters($key) { $key = self::load($key); if (!$key instanceof PrivateKey && !$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a parameter'); } return $key; } } PK1A#]TĴ�h�h�6vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.phpnu�[���<?php /** * Pure-PHP implementation of Twofish. * * Uses mcrypt, if available, and an internal implementation, otherwise. * * PHP version 5 * * Useful resources are as follows: * * - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of Twofish} * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $twofish = new \phpseclib3\Crypt\Twofish('ctr'); * * $twofish->setKey('12345678901234567890123456789012'); * * $plaintext = str_repeat('a', 1024); * * echo $twofish->decrypt($twofish->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\BlockCipher; use phpseclib3\Exception\BadModeException; /** * Pure-PHP implementation of Twofish. * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> */ class Twofish extends BlockCipher { /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'twofish'; /** * Optimizing value while CFB-encrypting * * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len * @var int */ protected $cfb_init_len = 800; /** * Q-Table * * @var array */ private static $q0 = [ 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 ]; /** * Q-Table * * @var array */ private static $q1 = [ 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 ]; /** * M-Table * * @var array */ private static $m0 = [ 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3, 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, 0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17, 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, 0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5, 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, 0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 ]; /** * M-Table * * @var array */ private static $m1 = [ 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D, 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, 0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7, 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, 0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7, 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, 0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 ]; /** * M-Table * * @var array */ private static $m2 = [ 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065, 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, 0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC, 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, 0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6, 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, 0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF ]; /** * M-Table * * @var array */ private static $m3 = [ 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63, 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, 0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730, 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, 0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6, 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, 0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 ]; /** * The Key Schedule Array * * @var array */ private $K = []; /** * The Key depended S-Table 0 * * @var array */ private $S0 = []; /** * The Key depended S-Table 1 * * @var array */ private $S1 = []; /** * The Key depended S-Table 2 * * @var array */ private $S2 = []; /** * The Key depended S-Table 3 * * @var array */ private $S3 = []; /** * Holds the last used key * * @var array */ private $kl; /** * The Key Length (in bytes) * * @see Crypt_Twofish::setKeyLength() * @var int */ protected $key_length = 16; /** * Default Constructor. * * @param string $mode * @throws BadModeException if an invalid / unsupported mode is provided */ public function __construct($mode) { parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new BadModeException('Block ciphers cannot be ran in stream mode'); } } /** * Initialize Static Variables */ protected static function initialize_static_variables() { if (is_float(self::$m3[0])) { self::$m0 = array_map('intval', self::$m0); self::$m1 = array_map('intval', self::$m1); self::$m2 = array_map('intval', self::$m2); self::$m3 = array_map('intval', self::$m3); self::$q0 = array_map('intval', self::$q0); self::$q1 = array_map('intval', self::$q1); } parent::initialize_static_variables(); } /** * Sets the key length. * * Valid key lengths are 128, 192 or 256 bits * * @param int $length */ public function setKeyLength($length) { switch ($length) { case 128: case 192: case 256: break; default: throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); } parent::setKeyLength($length); } /** * Sets the key. * * Rijndael supports five different key lengths * * @see setKeyLength() * @param string $key * @throws \LengthException if the key length isn't supported */ public function setKey($key) { switch (strlen($key)) { case 16: case 24: case 32: break; default: throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); } parent::setKey($key); } /** * Setup the key (expansion) * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() */ protected function setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key']) { // already expanded return; } $this->kl = ['key' => $this->key]; /* Key expanding and generating the key-depended s-boxes */ $le_longs = unpack('V*', $this->key); $key = unpack('C*', $this->key); $m0 = self::$m0; $m1 = self::$m1; $m2 = self::$m2; $m3 = self::$m3; $q0 = self::$q0; $q1 = self::$q1; $K = $S0 = $S1 = $S2 = $S3 = []; switch (strlen($this->key)) { case 16: list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[1], $le_longs[2]); list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[3], $le_longs[4]); for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = self::safe_intval($A + $B); $K[] = $A; $A = self::safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; } break; case 24: list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[1], $le_longs[2]); list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[3], $le_longs[4]); list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[5], $le_longs[6]); for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = self::safe_intval($A + $B); $K[] = $A; $A = self::safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; } break; default: // 32 list($sf, $se, $sd, $sc) = $this->mdsrem($le_longs[1], $le_longs[2]); list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[3], $le_longs[4]); list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[5], $le_longs[6]); list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[7], $le_longs[8]); for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = self::safe_intval($A + $B); $K[] = $A; $A = self::safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; } } $this->K = $K; $this->S0 = $S0; $this->S1 = $S1; $this->S2 = $S2; $this->S3 = $S3; } /** * _mdsrem function using by the twofish cipher algorithm * * @param string $A * @param string $B * @return array */ private function mdsrem($A, $B) { // No gain by unrolling this loop. for ($i = 0; $i < 8; ++$i) { // Get most significant coefficient. $t = 0xff & ($B >> 24); // Shift the others up. $B = ($B << 8) | (0xff & ($A >> 24)); $A <<= 8; $u = $t << 1; // Subtract the modular polynomial on overflow. if ($t & 0x80) { $u ^= 0x14d; } // Remove t * (a * x^2 + 1). $B ^= $t ^ ($u << 16); // Form u = a*t + t/a = t*(a + 1/a). $u ^= 0x7fffffff & ($t >> 1); // Add the modular polynomial on underflow. if ($t & 0x01) { $u ^= 0xa6 ; } // Remove t * (a + 1/a) * (x^3 + x). $B ^= ($u << 24) | ($u << 8); } return [ 0xff & $B >> 24, 0xff & $B >> 16, 0xff & $B >> 8, 0xff & $B]; } /** * Encrypts a block * * @param string $in * @return string */ protected function encryptBlock($in) { $S0 = $this->S0; $S1 = $this->S1; $S2 = $this->S2; $S3 = $this->S3; $K = $this->K; $in = unpack("V4", $in); $R0 = $K[0] ^ $in[1]; $R1 = $K[1] ^ $in[2]; $R2 = $K[2] ^ $in[3]; $R3 = $K[3] ^ $in[4]; $ki = 7; while ($ki < 39) { $t0 = $S0[ $R0 & 0xff] ^ $S1[($R0 >> 8) & 0xff] ^ $S2[($R0 >> 16) & 0xff] ^ $S3[($R0 >> 24) & 0xff]; $t1 = $S0[($R1 >> 24) & 0xff] ^ $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; $R2 ^= self::safe_intval($t0 + $t1 + $K[++$ki]); $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]); $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ $S2[($R2 >> 16) & 0xff] ^ $S3[($R2 >> 24) & 0xff]; $t1 = $S0[($R3 >> 24) & 0xff] ^ $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; $R0 ^= self::safe_intval($t0 + $t1 + $K[++$ki]); $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]); } // @codingStandardsIgnoreStart return pack("V4", $K[4] ^ $R2, $K[5] ^ $R3, $K[6] ^ $R0, $K[7] ^ $R1); // @codingStandardsIgnoreEnd } /** * Decrypts a block * * @param string $in * @return string */ protected function decryptBlock($in) { $S0 = $this->S0; $S1 = $this->S1; $S2 = $this->S2; $S3 = $this->S3; $K = $this->K; $in = unpack("V4", $in); $R0 = $K[4] ^ $in[1]; $R1 = $K[5] ^ $in[2]; $R2 = $K[6] ^ $in[3]; $R3 = $K[7] ^ $in[4]; $ki = 40; while ($ki > 8) { $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; $R3 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]); $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; $R1 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]); } // @codingStandardsIgnoreStart return pack("V4", $K[0] ^ $R2, $K[1] ^ $R3, $K[2] ^ $R0, $K[3] ^ $R1); // @codingStandardsIgnoreEnd } /** * Setup the performance-optimized function for de/encrypt() * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt() */ protected function setupInlineCrypt() { $K = $this->K; $init_crypt = ' static $S0, $S1, $S2, $S3; if (!$S0) { for ($i = 0; $i < 256; ++$i) { $S0[] = (int)$this->S0[$i]; $S1[] = (int)$this->S1[$i]; $S2[] = (int)$this->S2[$i]; $S3[] = (int)$this->S3[$i]; } } '; $safeint = self::safe_intval_inline(); // Generating encrypt code: $encrypt_block = ' $in = unpack("V4", $in); $R0 = ' . $K[0] . ' ^ $in[1]; $R1 = ' . $K[1] . ' ^ $in[2]; $R2 = ' . $K[2] . ' ^ $in[3]; $R3 = ' . $K[3] . ' ^ $in[4]; '; for ($ki = 7, $i = 0; $i < 8; ++$i) { $encrypt_block .= ' $t0 = $S0[ $R0 & 0xff] ^ $S1[($R0 >> 8) & 0xff] ^ $S2[($R0 >> 16) & 0xff] ^ $S3[($R0 >> 24) & 0xff]; $t1 = $S0[($R1 >> 24) & 0xff] ^ $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ $S2[($R2 >> 16) & 0xff] ^ $S3[($R2 >> 24) & 0xff]; $t1 = $S0[($R3 >> 24) & 0xff] ^ $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; '; } $encrypt_block .= ' $in = pack("V4", ' . $K[4] . ' ^ $R2, ' . $K[5] . ' ^ $R3, ' . $K[6] . ' ^ $R0, ' . $K[7] . ' ^ $R1); '; // Generating decrypt code: $decrypt_block = ' $in = unpack("V4", $in); $R0 = ' . $K[4] . ' ^ $in[1]; $R1 = ' . $K[5] . ' ^ $in[2]; $R2 = ' . $K[6] . ' ^ $in[3]; $R3 = ' . $K[7] . ' ^ $in[4]; '; for ($ki = 40, $i = 0; $i < 8; ++$i) { $decrypt_block .= ' $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . '; $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . '; '; } $decrypt_block .= ' $in = pack("V4", ' . $K[0] . ' ^ $R2, ' . $K[1] . ' ^ $R3, ' . $K[2] . ' ^ $R0, ' . $K[3] . ' ^ $R1); '; $this->inline_crypt = $this->createInlineCryptFunction( [ 'init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ] ); } } PK1A#]���2vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.phpnu�[���<?php /** * Pure-PHP implementation of AES. * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * * PHP version 5 * * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of * to save one include_once(). * * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()} * is called, again, at which point, it'll be recalculated. * * Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't * make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function, * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one). * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $aes = new \phpseclib3\Crypt\AES('ctr'); * * $aes->setKey('abcdefghijklmnop'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $aes->decrypt($aes->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2008 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; /** * Pure-PHP implementation of AES. * * @author Jim Wigginton <terrafrost@php.net> */ class AES extends Rijndael { /** * Dummy function * * Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything. * * @see \phpseclib3\Crypt\Rijndael::setBlockLength() * @param int $length * @throws \BadMethodCallException anytime it's called */ public function setBlockLength($length) { throw new \BadMethodCallException('The block length cannot be set for AES.'); } /** * Sets the key length * * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length * * @see \phpseclib3\Crypt\Rijndael:setKeyLength() * @param int $length * @throws \LengthException if the key length isn't supported */ public function setKeyLength($length) { switch ($length) { case 128: case 192: case 256: break; default: throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported'); } parent::setKeyLength($length); } /** * Sets the key. * * Rijndael supports five different key lengths, AES only supports three. * * @see \phpseclib3\Crypt\Rijndael:setKey() * @see setKeyLength() * @param string $key * @throws \LengthException if the key length isn't supported */ public function setKey($key) { switch (strlen($key)) { case 16: case 24: case 32: break; default: throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); } parent::setKey($key); } } PK2A#].[-�NN?vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.phpnu�[���<?php /** * PublicKey interface * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; /** * PublicKey interface * * @author Jim Wigginton <terrafrost@php.net> */ interface PublicKey { public function verify($message, $signature); //public function encrypt($plaintext); public function toString($type, array $options = []); public function getFingerprint($algorithm); } PK2A#]b2����@vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.phpnu�[���<?php /** * PrivateKey interface * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; /** * PrivateKey interface * * @author Jim Wigginton <terrafrost@php.net> */ interface PrivateKey { public function sign($message); //public function decrypt($ciphertext); public function getPublicKey(); public function toString($type, array $options = []); /** * @param string|false $password * @return mixed */ public function withPassword($password = false); } PK2A#]-�%��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.phpnu�[���<?php /** * Base Class for all stream ciphers * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; /** * Base Class for all stream cipher classes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class StreamCipher extends SymmetricKey { /** * Block Length of the cipher * * Stream ciphers do not have a block size * * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size * @var int */ protected $block_size = 0; /** * Default Constructor. * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @return \phpseclib3\Crypt\Common\StreamCipher */ public function __construct() { parent::__construct('stream'); } /** * Stream ciphers not use an IV * * @return bool */ public function usesIV() { return false; } } PK2A#]�ɛ�**Nvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.phpnu�[���<?php /** * Password Protected Trait for Private Keys * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Traits; /** * Password Protected Trait for Private Keys * * @author Jim Wigginton <terrafrost@php.net> */ trait PasswordProtected { /** * Password * * @var string|bool */ private $password = false; /** * Sets the password * * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. * Or rather, pass in $password such that empty($password) && !is_string($password) is true. * * @see self::createKey() * @see self::load() * @param string|bool $password */ public function withPassword($password = false) { $new = clone $this; $new->password = $password; return $new; } } PK2A#]�rN���Hvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.phpnu�[���<?php /** * Fingerprint Trait for Public Keys * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Traits; use phpseclib3\Crypt\Hash; /** * Fingerprint Trait for Private Keys * * @author Jim Wigginton <terrafrost@php.net> */ trait Fingerprint { /** * Returns the public key's fingerprint * * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is * no public key currently loaded, false is returned. * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) * * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned * for invalid values. * @return mixed */ public function getFingerprint($algorithm = 'md5') { $type = self::validatePlugin('Keys', 'OpenSSH', 'savePublicKey'); if ($type === false) { return false; } $key = $this->toString('OpenSSH', ['binary' => true]); if ($key === false) { return false; } switch ($algorithm) { case 'sha256': $hash = new Hash('sha256'); $base = base64_encode($hash->hash($key)); return substr($base, 0, strlen($base) - 1); case 'md5': return substr(chunk_split(md5($key), 2, ':'), 0, -1); default: return false; } } } PK2A#]�EKP����Bvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.phpnu�[���<?php /** * Base Class for all \phpseclib3\Crypt\* cipher classes * * PHP version 5 * * Internally for phpseclib developers: * If you plan to add a new cipher class, please note following rules: * * - The new \phpseclib3\Crypt\* cipher class should extend \phpseclib3\Crypt\Common\SymmetricKey * * - Following methods are then required to be overridden/overloaded: * * - encryptBlock() * * - decryptBlock() * * - setupKey() * * - All other methods are optional to be overridden/overloaded * * - Look at the source code of the current ciphers how they extend \phpseclib3\Crypt\Common\SymmetricKey * and take one of them as a start up for the new cipher class. * * - Please read all the other comments/notes/hints here also for each class var/method * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Blowfish; use phpseclib3\Crypt\Hash; use phpseclib3\Exception\BadDecryptionException; use phpseclib3\Exception\BadModeException; use phpseclib3\Exception\InconsistentSetupException; use phpseclib3\Exception\InsufficientSetupException; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Math\BigInteger; use phpseclib3\Math\BinaryField; use phpseclib3\Math\PrimeField; /** * Base Class for all \phpseclib3\Crypt\* cipher classes * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> */ abstract class SymmetricKey { /** * Encrypt / decrypt using the Counter mode. * * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_CTR = -1; /** * Encrypt / decrypt using the Electronic Code Book mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_ECB = 1; /** * Encrypt / decrypt using the Code Book Chaining mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_CBC = 2; /** * Encrypt / decrypt using the Cipher Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_CFB = 3; /** * Encrypt / decrypt using the Cipher Feedback mode (8bit) * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_CFB8 = 7; /** * Encrypt / decrypt using the Output Feedback mode (8bit) * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_OFB8 = 8; /** * Encrypt / decrypt using the Output Feedback mode. * * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_OFB = 4; /** * Encrypt / decrypt using Galois/Counter mode. * * @link https://en.wikipedia.org/wiki/Galois/Counter_Mode * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_GCM = 5; /** * Encrypt / decrypt using streaming mode. * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() */ const MODE_STREAM = 6; /** * Mode Map * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const MODE_MAP = [ 'ctr' => self::MODE_CTR, 'ecb' => self::MODE_ECB, 'cbc' => self::MODE_CBC, 'cfb' => self::MODE_CFB, 'cfb8' => self::MODE_CFB8, 'ofb' => self::MODE_OFB, 'ofb8' => self::MODE_OFB8, 'gcm' => self::MODE_GCM, 'stream' => self::MODE_STREAM ]; /** * Base value for the internal implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_INTERNAL = 1; /** * Base value for the eval() implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_EVAL = 2; /** * Base value for the mcrypt implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_MCRYPT = 3; /** * Base value for the openssl implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_OPENSSL = 4; /** * Base value for the libsodium implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_LIBSODIUM = 5; /** * Base value for the openssl / gcm implementation $engine switch * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ const ENGINE_OPENSSL_GCM = 6; /** * Engine Reverse Map * * @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine() */ const ENGINE_MAP = [ self::ENGINE_INTERNAL => 'PHP', self::ENGINE_EVAL => 'Eval', self::ENGINE_MCRYPT => 'mcrypt', self::ENGINE_OPENSSL => 'OpenSSL', self::ENGINE_LIBSODIUM => 'libsodium', self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)' ]; /** * The Encryption Mode * * @see self::__construct() * @var int */ protected $mode; /** * The Block Length of the block cipher * * @var int */ protected $block_size = 16; /** * The Key * * @see self::setKey() * @var string */ protected $key = false; /** * HMAC Key * * @see self::setupGCM() * @var ?string */ protected $hKey = false; /** * The Initialization Vector * * @see self::setIV() * @var string */ protected $iv = false; /** * A "sliding" Initialization Vector * * @see self::enableContinuousBuffer() * @see self::clearBuffers() * @var string */ protected $encryptIV; /** * A "sliding" Initialization Vector * * @see self::enableContinuousBuffer() * @see self::clearBuffers() * @var string */ protected $decryptIV; /** * Continuous Buffer status * * @see self::enableContinuousBuffer() * @var bool */ protected $continuousBuffer = false; /** * Encryption buffer for CTR, OFB and CFB modes * * @see self::encrypt() * @see self::clearBuffers() * @var array */ protected $enbuffer; /** * Decryption buffer for CTR, OFB and CFB modes * * @see self::decrypt() * @see self::clearBuffers() * @var array */ protected $debuffer; /** * mcrypt resource for encryption * * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * * @see self::encrypt() * @var resource */ private $enmcrypt; /** * mcrypt resource for decryption * * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * * @see self::decrypt() * @var resource */ private $demcrypt; /** * Does the enmcrypt resource need to be (re)initialized? * * @see \phpseclib3\Crypt\Twofish::setKey() * @see \phpseclib3\Crypt\Twofish::setIV() * @var bool */ private $enchanged = true; /** * Does the demcrypt resource need to be (re)initialized? * * @see \phpseclib3\Crypt\Twofish::setKey() * @see \phpseclib3\Crypt\Twofish::setIV() * @var bool */ private $dechanged = true; /** * mcrypt resource for CFB mode * * mcrypt's CFB mode, in (and only in) buffered context, * is broken, so phpseclib implements the CFB mode by it self, * even when the mcrypt php extension is available. * * In order to do the CFB-mode work (fast) phpseclib * use a separate ECB-mode mcrypt resource. * * @link http://phpseclib.sourceforge.net/cfb-demo.phps * @see self::encrypt() * @see self::decrypt() * @see self::setupMcrypt() * @var resource */ private $ecb; /** * Optimizing value while CFB-encrypting * * Only relevant if $continuousBuffer enabled * and $engine == self::ENGINE_MCRYPT * * It's faster to re-init $enmcrypt if * $buffer bytes > $cfb_init_len than * using the $ecb resource furthermore. * * This value depends of the chosen cipher * and the time it would be needed for it's * initialization [by mcrypt_generic_init()] * which, typically, depends on the complexity * on its internaly Key-expanding algorithm. * * @see self::encrypt() * @var int */ protected $cfb_init_len = 600; /** * Does internal cipher state need to be (re)initialized? * * @see self::setKey() * @see self::setIV() * @see self::disableContinuousBuffer() * @var bool */ protected $changed = true; /** * Does Eval engie need to be (re)initialized? * * @see self::setup() * @var bool */ protected $nonIVChanged = true; /** * Padding status * * @see self::enablePadding() * @var bool */ private $padding = true; /** * Is the mode one that is paddable? * * @see self::__construct() * @var bool */ private $paddable = false; /** * Holds which crypt engine internaly should be use, * which will be determined automatically on __construct() * * Currently available $engines are: * - self::ENGINE_LIBSODIUM (very fast, php-extension: libsodium, extension_loaded('libsodium') required) * - self::ENGINE_OPENSSL_GCM (very fast, php-extension: openssl, extension_loaded('openssl') required) * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) * - self::ENGINE_EVAL (medium, pure php-engine, no php-extension required) * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required) * * @see self::setEngine() * @see self::encrypt() * @see self::decrypt() * @var int */ protected $engine; /** * Holds the preferred crypt engine * * @see self::setEngine() * @see self::setPreferredEngine() * @var int */ private $preferredEngine; /** * The mcrypt specific name of the cipher * * Only used if $engine == self::ENGINE_MCRYPT * * @link http://www.php.net/mcrypt_module_open * @link http://www.php.net/mcrypt_list_algorithms * @see self::setupMcrypt() * @var string */ protected $cipher_name_mcrypt; /** * The openssl specific name of the cipher * * Only used if $engine == self::ENGINE_OPENSSL * * @link http://www.php.net/openssl-get-cipher-methods * @var string */ protected $cipher_name_openssl; /** * The openssl specific name of the cipher in ECB mode * * If OpenSSL does not support the mode we're trying to use (CTR) * it can still be emulated with ECB mode. * * @link http://www.php.net/openssl-get-cipher-methods * @var string */ protected $cipher_name_openssl_ecb; /** * The default salt used by setPassword() * * @see self::setPassword() * @var string */ private $password_default_salt = 'phpseclib/salt'; /** * The name of the performance-optimized callback function * * Used by encrypt() / decrypt() * only if $engine == self::ENGINE_INTERNAL * * @see self::encrypt() * @see self::decrypt() * @see self::setupInlineCrypt() * @var Callback */ protected $inline_crypt; /** * If OpenSSL can be used in ECB but not in CTR we can emulate CTR * * @see self::openssl_ctr_process() * @var bool */ private $openssl_emulate_ctr = false; /** * Don't truncate / null pad key * * @see self::clearBuffers() * @var bool */ private $skip_key_adjustment = false; /** * Has the key length explicitly been set or should it be derived from the key, itself? * * @see self::setKeyLength() * @var bool */ protected $explicit_key_length = false; /** * Hash subkey for GHASH * * @see self::setupGCM() * @see self::ghash() * @var BinaryField\Integer */ private $h; /** * Additional authenticated data * * @var string */ protected $aad = ''; /** * Authentication Tag produced after a round of encryption * * @var string */ protected $newtag = false; /** * Authentication Tag to be verified during decryption * * @var string */ protected $oldtag = false; /** * GCM Binary Field * * @see self::__construct() * @see self::ghash() * @var BinaryField */ private static $gcmField; /** * Poly1305 Prime Field * * @see self::enablePoly1305() * @see self::poly1305() * @var PrimeField */ private static $poly1305Field; /** * Flag for using regular vs "safe" intval * * @see self::initialize_static_variables() * @var boolean */ protected static $use_reg_intval; /** * Poly1305 Key * * @see self::setPoly1305Key() * @see self::poly1305() * @var string */ protected $poly1305Key; /** * Poly1305 Flag * * @see self::setPoly1305Key() * @see self::enablePoly1305() * @var boolean */ protected $usePoly1305 = false; /** * The Original Initialization Vector * * GCM uses the nonce to build the IV but we want to be able to distinguish between nonce-derived * IV's and user-set IV's * * @see self::setIV() * @var string */ private $origIV = false; /** * Nonce * * Only used with GCM. We could re-use setIV() but nonce's can be of a different length and * toggling between GCM and other modes could be more complicated if we re-used setIV() * * @see self::setNonce() * @var string */ protected $nonce = false; /** * Default Constructor. * * $mode could be: * * - ecb * * - cbc * * - ctr * * - cfb * * - cfb8 * * - ofb * * - ofb8 * * - gcm * * @param string $mode * @throws BadModeException if an invalid / unsupported mode is provided */ public function __construct($mode) { $mode = strtolower($mode); // necessary because of 5.6 compatibility; we can't do isset(self::MODE_MAP[$mode]) in 5.6 $map = self::MODE_MAP; if (!isset($map[$mode])) { throw new BadModeException('No valid mode has been specified'); } $mode = self::MODE_MAP[$mode]; // $mode dependent settings switch ($mode) { case self::MODE_ECB: case self::MODE_CBC: $this->paddable = true; break; case self::MODE_CTR: case self::MODE_CFB: case self::MODE_CFB8: case self::MODE_OFB: case self::MODE_OFB8: case self::MODE_STREAM: $this->paddable = false; break; case self::MODE_GCM: if ($this->block_size != 16) { throw new BadModeException('GCM is only valid for block ciphers with a block size of 128 bits'); } if (!isset(self::$gcmField)) { self::$gcmField = new BinaryField(128, 7, 2, 1, 0); } $this->paddable = false; break; default: throw new BadModeException('No valid mode has been specified'); } $this->mode = $mode; static::initialize_static_variables(); } /** * Initialize static variables */ protected static function initialize_static_variables() { if (!isset(self::$use_reg_intval)) { switch (true) { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster case (PHP_OS & "\xDF\xDF\xDF") === 'WIN': case !function_exists('php_uname'): case !is_string(php_uname('m')): case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: self::$use_reg_intval = true; break; case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM': switch (true) { /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors: https://github.com/php/php-src/commit/716da71446ebbd40fa6cf2cea8a4b70f504cc3cd altho the changelogs make no mention of it, this bug was fixed with this commit: https://github.com/php/php-src/commit/c1729272b17a1fe893d1a54e423d3b71470f3ee8 affected versions of PHP are: 7.0.x, 7.1.0 - 7.1.23 and 7.2.0 - 7.2.11 */ case PHP_VERSION_ID >= 70000 && PHP_VERSION_ID <= 70123: case PHP_VERSION_ID >= 70200 && PHP_VERSION_ID <= 70211: self::$use_reg_intval = false; break; default: self::$use_reg_intval = true; } } } } /** * Sets the initialization vector. * * setIV() is not required when ecb or gcm modes are being used. * * {@internal Can be overwritten by a sub class, but does not have to be} * * @param string $iv * @throws \LengthException if the IV length isn't equal to the block size * @throws \BadMethodCallException if an IV is provided when one shouldn't be */ public function setIV($iv) { if ($this->mode == self::MODE_ECB) { throw new \BadMethodCallException('This mode does not require an IV.'); } if ($this->mode == self::MODE_GCM) { throw new \BadMethodCallException('Use setNonce instead'); } if (!$this->usesIV()) { throw new \BadMethodCallException('This algorithm does not use an IV.'); } if (strlen($iv) != $this->block_size) { throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required'); } $this->iv = $this->origIV = $iv; $this->changed = true; } /** * Enables Poly1305 mode. * * Once enabled Poly1305 cannot be disabled. * * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode */ public function enablePoly1305() { if ($this->mode == self::MODE_GCM) { throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode'); } $this->usePoly1305 = true; } /** * Enables Poly1305 mode. * * Once enabled Poly1305 cannot be disabled. If $key is not passed then an attempt to call createPoly1305Key * will be made. * * @param string $key optional * @throws \LengthException if the key isn't long enough * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode */ public function setPoly1305Key($key = null) { if ($this->mode == self::MODE_GCM) { throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode'); } if (!is_string($key) || strlen($key) != 32) { throw new \LengthException('The Poly1305 key must be 32 bytes long (256 bits)'); } if (!isset(self::$poly1305Field)) { // 2^130-5 self::$poly1305Field = new PrimeField(new BigInteger('3fffffffffffffffffffffffffffffffb', 16)); } $this->poly1305Key = $key; $this->usePoly1305 = true; } /** * Sets the nonce. * * setNonce() is only required when gcm is used * * @param string $nonce * @throws \BadMethodCallException if an nonce is provided when one shouldn't be */ public function setNonce($nonce) { if ($this->mode != self::MODE_GCM) { throw new \BadMethodCallException('Nonces are only used in GCM mode.'); } $this->nonce = $nonce; $this->setEngine(); } /** * Sets additional authenticated data * * setAAD() is only used by gcm or in poly1305 mode * * @param string $aad * @throws \BadMethodCallException if mode isn't GCM or if poly1305 isn't being utilized */ public function setAAD($aad) { if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { throw new \BadMethodCallException('Additional authenticated data is only utilized in GCM mode or with Poly1305'); } $this->aad = $aad; } /** * Returns whether or not the algorithm uses an IV * * @return bool */ public function usesIV() { return $this->mode != self::MODE_GCM && $this->mode != self::MODE_ECB; } /** * Returns whether or not the algorithm uses a nonce * * @return bool */ public function usesNonce() { return $this->mode == self::MODE_GCM; } /** * Returns the current key length in bits * * @return int */ public function getKeyLength() { return $this->key_length << 3; } /** * Returns the current block length in bits * * @return int */ public function getBlockLength() { return $this->block_size << 3; } /** * Returns the current block length in bytes * * @return int */ public function getBlockLengthInBytes() { return $this->block_size; } /** * Sets the key length. * * Keys with explicitly set lengths need to be treated accordingly * * @param int $length */ public function setKeyLength($length) { $this->explicit_key_length = $length >> 3; if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) { $this->key = false; throw new InconsistentSetupException('Key has already been set and is not ' . $this->explicit_key_length . ' bytes long'); } } /** * Sets the key. * * The min/max length(s) of the key depends on the cipher which is used. * If the key not fits the length(s) of the cipher it will paded with null bytes * up to the closest valid key length. If the key is more than max length, * we trim the excess bits. * * If the key is not explicitly set, it'll be assumed to be all null bytes. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @param string $key */ public function setKey($key) { if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { throw new InconsistentSetupException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); } $this->key = $key; $this->key_length = strlen($key); $this->setEngine(); } /** * Sets the password. * * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1: * $hash, $salt, $count, $dkLen * * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php * {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}: * $salt, $rounds, $keylen * * This is a modified version of bcrypt used by OpenSSH. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see Crypt/Hash.php * @param string $password * @param string $method * @param int|string ...$func_args * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length * @throws \RuntimeException if bcrypt is being used and a salt isn't provided * @return bool */ public function setPassword($password, $method = 'pbkdf2', ...$func_args) { $key = ''; $method = strtolower($method); switch ($method) { case 'bcrypt': if (!isset($func_args[2])) { throw new \RuntimeException('A salt must be provided for bcrypt to work'); } $salt = $func_args[0]; $rounds = isset($func_args[1]) ? $func_args[1] : 16; $keylen = isset($func_args[2]) ? $func_args[2] : $this->key_length; $key = Blowfish::bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds); $this->setKey(substr($key, 0, $keylen)); $this->setIV(substr($key, $keylen)); return true; case 'pkcs12': // from https://tools.ietf.org/html/rfc7292#appendix-B.2 case 'pbkdf1': case 'pbkdf2': // Hash function $hash = isset($func_args[0]) ? strtolower($func_args[0]) : 'sha1'; $hashObj = new Hash(); $hashObj->setHash($hash); // WPA and WPA2 use the SSID as the salt $salt = isset($func_args[1]) ? $func_args[1] : $this->password_default_salt; // RFC2898#section-4.2 uses 1,000 iterations by default // WPA and WPA2 use 4,096. $count = isset($func_args[2]) ? $func_args[2] : 1000; // Keylength if (isset($func_args[3])) { if ($func_args[3] <= 0) { throw new \LengthException('Derived key length cannot be longer 0 or less'); } $dkLen = $func_args[3]; } else { $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length; $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length; } switch (true) { case $method == 'pkcs12': /* In this specification, however, all passwords are created from BMPStrings with a NULL terminator. This means that each character in the original BMPString is encoded in 2 bytes in big-endian format (most-significant byte first). There are no Unicode byte order marks. The 2 bytes produced from the last character in the BMPString are followed by 2 additional bytes with the value 0x00. -- https://tools.ietf.org/html/rfc7292#appendix-B.1 */ $password = "\0" . chunk_split($password, 1, "\0") . "\0"; /* This standard specifies 3 different values for the ID byte mentioned above: 1. If ID=1, then the pseudorandom bits being produced are to be used as key material for performing encryption or decryption. 2. If ID=2, then the pseudorandom bits being produced are to be used as an IV (Initial Value) for encryption or decryption. 3. If ID=3, then the pseudorandom bits being produced are to be used as an integrity key for MACing. */ // Construct a string, D (the "diversifier"), by concatenating v/8 // copies of ID. $blockLength = $hashObj->getBlockLengthInBytes(); $d1 = str_repeat(chr(1), $blockLength); $d2 = str_repeat(chr(2), $blockLength); $s = ''; if (strlen($salt)) { while (strlen($s) < $blockLength) { $s .= $salt; } } $s = substr($s, 0, $blockLength); $p = ''; if (strlen($password)) { while (strlen($p) < $blockLength) { $p .= $password; } } $p = substr($p, 0, $blockLength); $i = $s . $p; $this->setKey(self::pkcs12helper($dkLen, $hashObj, $i, $d1, $count)); if ($this->usesIV()) { $this->setIV(self::pkcs12helper($this->block_size, $hashObj, $i, $d2, $count)); } return true; case $method == 'pbkdf1': if ($dkLen > $hashObj->getLengthInBytes()) { throw new \LengthException('Derived key length cannot be longer than the hash length'); } $t = $password . $salt; for ($i = 0; $i < $count; ++$i) { $t = $hashObj->hash($t); } $key = substr($t, 0, $dkLen); $this->setKey(substr($key, 0, $dkLen >> 1)); if ($this->usesIV()) { $this->setIV(substr($key, $dkLen >> 1)); } return true; case !in_array($hash, hash_algos()): $i = 1; $hashObj->setKey($password); while (strlen($key) < $dkLen) { $f = $u = $hashObj->hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { $u = $hashObj->hash($u); $f ^= $u; } $key .= $f; } $key = substr($key, 0, $dkLen); break; default: $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true); } break; default: throw new UnsupportedAlgorithmException($method . ' is not a supported password hashing method'); } $this->setKey($key); return true; } /** * PKCS#12 KDF Helper Function * * As discussed here: * * {@link https://tools.ietf.org/html/rfc7292#appendix-B} * * @see self::setPassword() * @param int $n * @param \phpseclib3\Crypt\Hash $hashObj * @param string $i * @param string $d * @param int $count * @return string $a */ private static function pkcs12helper($n, $hashObj, $i, $d, $count) { static $one; if (!isset($one)) { $one = new BigInteger(1); } $blockLength = $hashObj->getBlockLength() >> 3; $c = ceil($n / $hashObj->getLengthInBytes()); $a = ''; for ($j = 1; $j <= $c; $j++) { $ai = $d . $i; for ($k = 0; $k < $count; $k++) { $ai = $hashObj->hash($ai); } $b = ''; while (strlen($b) < $blockLength) { $b .= $ai; } $b = substr($b, 0, $blockLength); $b = new BigInteger($b, 256); $newi = ''; for ($k = 0; $k < strlen($i); $k += $blockLength) { $temp = substr($i, $k, $blockLength); $temp = new BigInteger($temp, 256); $temp->setPrecision($blockLength << 3); $temp = $temp->add($b); $temp = $temp->add($one); $newi .= $temp->toBytes(false); } $i = $newi; $a .= $ai; } return substr($a, 0, $n); } /** * Encrypts a message. * * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's * necessary are discussed in the following * URL: * * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} * * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that * length. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::decrypt() * @param string $plaintext * @return string $ciphertext */ public function encrypt($plaintext) { if ($this->paddable) { $plaintext = $this->pad($plaintext); } $this->setup(); if ($this->mode == self::MODE_GCM) { $oldIV = $this->iv; Strings::increment_str($this->iv); $cipher = new static('ctr'); $cipher->setKey($this->key); $cipher->setIV($this->iv); $ciphertext = $cipher->encrypt($plaintext); $s = $this->ghash( self::nullPad128($this->aad) . self::nullPad128($ciphertext) . self::len64($this->aad) . self::len64($ciphertext) ); $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV; $this->newtag = $cipher->encrypt($s); return $ciphertext; } if (isset($this->poly1305Key)) { $cipher = clone $this; unset($cipher->poly1305Key); $this->usePoly1305 = false; $ciphertext = $cipher->encrypt($plaintext); $this->newtag = $this->poly1305($ciphertext); return $ciphertext; } if ($this->engine === self::ENGINE_OPENSSL) { switch ($this->mode) { case self::MODE_STREAM: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); case self::MODE_ECB: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); case self::MODE_CBC: $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->encryptIV); if ($this->continuousBuffer) { $this->encryptIV = substr($result, -$this->block_size); } return $result; case self::MODE_CTR: return $this->openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); case self::MODE_CFB: // cfb loosely routines inspired by openssl's: // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} $ciphertext = ''; if ($this->continuousBuffer) { $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; } else { $iv = $this->encryptIV; $pos = 0; } $len = strlen($plaintext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $this->block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); $plaintext = substr($plaintext, $i); } $overflow = $len % $this->block_size; if ($overflow) { $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); $iv = Strings::pop($ciphertext, $this->block_size); $size = $len - $overflow; $block = $iv ^ substr($plaintext, -$overflow); $iv = substr_replace($iv, $block, 0, $overflow); $ciphertext .= $block; $pos = $overflow; } elseif ($len) { $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); $iv = substr($ciphertext, -$this->block_size); } return $ciphertext; case self::MODE_CFB8: $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->encryptIV); if ($this->continuousBuffer) { if (($len = strlen($ciphertext)) >= $this->block_size) { $this->encryptIV = substr($ciphertext, -$this->block_size); } else { $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); } } return $ciphertext; case self::MODE_OFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); $ciphertext .= $plaintext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->encryptIV = $iv; } break; case self::MODE_OFB: return $this->openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); } } if ($this->engine === self::ENGINE_MCRYPT) { set_error_handler(function () { }); if ($this->enchanged) { mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV)); $this->enchanged = false; } // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's // rewritten CFB implementation the above outputs the same thing twice. if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $block_size = $this->block_size; $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; $len = strlen($plaintext); $ciphertext = ''; $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); $this->enbuffer['enmcrypt_init'] = true; } if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } $ciphertext .= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len %= $block_size; } else { while ($len >= $block_size) { $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext .= $iv; $len -= $block_size; $i += $block_size; } } } if ($len) { $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext .= $block; $pos = $len; } restore_error_handler(); return $ciphertext; } $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV)); } restore_error_handler(); return $ciphertext; } if ($this->engine === self::ENGINE_EVAL) { $inline = $this->inline_crypt; return $inline('encrypt', $plaintext); } $buffer = &$this->enbuffer; $block_size = $this->block_size; $ciphertext = ''; switch ($this->mode) { case self::MODE_ECB: for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $ciphertext .= $this->encryptBlock(substr($plaintext, $i, $block_size)); } break; case self::MODE_CBC: $xor = $this->encryptIV; for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); $block = $this->encryptBlock($block ^ $xor); $xor = $block; $ciphertext .= $block; } if ($this->continuousBuffer) { $this->encryptIV = $xor; } break; case self::MODE_CTR: $xor = $this->encryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $buffer['ciphertext'] .= $this->encryptBlock($xor); Strings::increment_str($xor); } $key = Strings::shift($buffer['ciphertext'], $block_size); $ciphertext .= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); $key = $this->encryptBlock($xor); Strings::increment_str($xor); $ciphertext .= $block ^ $key; } } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } break; case self::MODE_CFB: // cfb loosely routines inspired by openssl's: // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} if ($this->continuousBuffer) { $iv = &$this->encryptIV; $pos = &$buffer['pos']; } else { $iv = $this->encryptIV; $pos = 0; } $len = strlen($plaintext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); } while ($len >= $block_size) { $iv = $this->encryptBlock($iv) ^ substr($plaintext, $i, $block_size); $ciphertext .= $iv; $len -= $block_size; $i += $block_size; } if ($len) { $iv = $this->encryptBlock($iv); $block = $iv ^ substr($plaintext, $i); $iv = substr_replace($iv, $block, 0, $len); $ciphertext .= $block; $pos = $len; } break; case self::MODE_CFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $ciphertext .= ($c = $plaintext[$i] ^ $this->encryptBlock($iv)); $iv = substr($iv, 1) . $c; } if ($this->continuousBuffer) { if ($len >= $block_size) { $this->encryptIV = substr($ciphertext, -$block_size); } else { $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $xor = $this->encryptBlock($iv); $ciphertext .= $plaintext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->encryptIV = $iv; } break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['xor'])) { $xor = $this->encryptBlock($xor); $buffer['xor'] .= $xor; } $key = Strings::shift($buffer['xor'], $block_size); $ciphertext .= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $xor = $this->encryptBlock($xor); $ciphertext .= substr($plaintext, $i, $block_size) ^ $xor; } $key = $xor; } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; case self::MODE_STREAM: $ciphertext = $this->encryptBlock($plaintext); break; } return $ciphertext; } /** * Decrypts a message. * * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until * it is. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::encrypt() * @param string $ciphertext * @return string $plaintext * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size */ public function decrypt($ciphertext) { if ($this->paddable && strlen($ciphertext) % $this->block_size) { throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')'); } $this->setup(); if ($this->mode == self::MODE_GCM || isset($this->poly1305Key)) { if ($this->oldtag === false) { throw new InsufficientSetupException('Authentication Tag has not been set'); } if (isset($this->poly1305Key)) { $newtag = $this->poly1305($ciphertext); } else { $oldIV = $this->iv; Strings::increment_str($this->iv); $cipher = new static('ctr'); $cipher->setKey($this->key); $cipher->setIV($this->iv); $plaintext = $cipher->decrypt($ciphertext); $s = $this->ghash( self::nullPad128($this->aad) . self::nullPad128($ciphertext) . self::len64($this->aad) . self::len64($ciphertext) ); $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV; $newtag = $cipher->encrypt($s); } if ($this->oldtag != substr($newtag, 0, strlen($newtag))) { $cipher = clone $this; unset($cipher->poly1305Key); $this->usePoly1305 = false; $plaintext = $cipher->decrypt($ciphertext); $this->oldtag = false; throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); } $this->oldtag = false; return $plaintext; } if ($this->engine === self::ENGINE_OPENSSL) { switch ($this->mode) { case self::MODE_STREAM: $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); break; case self::MODE_ECB: $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); break; case self::MODE_CBC: $offset = $this->block_size; $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV); if ($this->continuousBuffer) { $this->decryptIV = substr($ciphertext, -$offset, $this->block_size); } break; case self::MODE_CTR: $plaintext = $this->openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); break; case self::MODE_CFB: // cfb loosely routines inspired by openssl's: // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} $plaintext = ''; if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->debuffer['pos']; } else { $iv = $this->decryptIV; $pos = 0; } $len = strlen($ciphertext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $this->block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); $ciphertext = substr($ciphertext, $i); } $overflow = $len % $this->block_size; if ($overflow) { $plaintext .= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); if ($len - $overflow) { $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow); } $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); $plaintext .= $iv ^ substr($ciphertext, -$overflow); $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow); $pos = $overflow; } elseif ($len) { $plaintext .= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); $iv = substr($ciphertext, -$this->block_size); } break; case self::MODE_CFB8: $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV); if ($this->continuousBuffer) { if (($len = strlen($ciphertext)) >= $this->block_size) { $this->decryptIV = substr($ciphertext, -$this->block_size); } else { $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); $plaintext .= $ciphertext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->decryptIV = $iv; } break; case self::MODE_OFB: $plaintext = $this->openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } return $this->paddable ? $this->unpad($plaintext) : $plaintext; } if ($this->engine === self::ENGINE_MCRYPT) { set_error_handler(function () { }); $block_size = $this->block_size; if ($this->dechanged) { mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV)); $this->dechanged = false; } if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->debuffer['pos']; $len = strlen($ciphertext); $plaintext = ''; $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); $plaintext .= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len %= $block_size; } if ($len) { $iv = mcrypt_generic($this->ecb, $iv); $plaintext .= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; } restore_error_handler(); return $plaintext; } $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV)); } restore_error_handler(); return $this->paddable ? $this->unpad($plaintext) : $plaintext; } if ($this->engine === self::ENGINE_EVAL) { $inline = $this->inline_crypt; return $inline('decrypt', $ciphertext); } $block_size = $this->block_size; $buffer = &$this->debuffer; $plaintext = ''; switch ($this->mode) { case self::MODE_ECB: for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $plaintext .= $this->decryptBlock(substr($ciphertext, $i, $block_size)); } break; case self::MODE_CBC: $xor = $this->decryptIV; for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $block = substr($ciphertext, $i, $block_size); $plaintext .= $this->decryptBlock($block) ^ $xor; $xor = $block; } if ($this->continuousBuffer) { $this->decryptIV = $xor; } break; case self::MODE_CTR: $xor = $this->decryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $buffer['ciphertext'] .= $this->encryptBlock($xor); Strings::increment_str($xor); } $key = Strings::shift($buffer['ciphertext'], $block_size); $plaintext .= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $block = substr($ciphertext, $i, $block_size); $key = $this->encryptBlock($xor); Strings::increment_str($xor); $plaintext .= $block ^ $key; } } if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } break; case self::MODE_CFB: if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$buffer['pos']; } else { $iv = $this->decryptIV; $pos = 0; } $len = strlen($ciphertext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len -= $max; $pos = 0; } else { $i = $len; $pos += $len; $len = 0; } // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); } while ($len >= $block_size) { $iv = $this->encryptBlock($iv); $cb = substr($ciphertext, $i, $block_size); $plaintext .= $iv ^ $cb; $iv = $cb; $len -= $block_size; $i += $block_size; } if ($len) { $iv = $this->encryptBlock($iv); $plaintext .= $iv ^ substr($ciphertext, $i); $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); $pos = $len; } break; case self::MODE_CFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $plaintext .= $ciphertext[$i] ^ $this->encryptBlock($iv); $iv = substr($iv, 1) . $ciphertext[$i]; } if ($this->continuousBuffer) { if ($len >= $block_size) { $this->decryptIV = substr($ciphertext, -$block_size); } else { $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $xor = $this->encryptBlock($iv); $plaintext .= $ciphertext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->decryptIV = $iv; } break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['xor'])) { $xor = $this->encryptBlock($xor); $buffer['xor'] .= $xor; } $key = Strings::shift($buffer['xor'], $block_size); $plaintext .= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i += $block_size) { $xor = $this->encryptBlock($xor); $plaintext .= substr($ciphertext, $i, $block_size) ^ $xor; } $key = $xor; } if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; case self::MODE_STREAM: $plaintext = $this->decryptBlock($ciphertext); break; } return $this->paddable ? $this->unpad($plaintext) : $plaintext; } /** * Get the authentication tag * * Only used in GCM or Poly1305 mode * * @see self::encrypt() * @param int $length optional * @return string * @throws \LengthException if $length isn't of a sufficient length * @throws \RuntimeException if GCM mode isn't being used */ public function getTag($length = 16) { if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305'); } if ($this->newtag === false) { throw new \BadMethodCallException('A tag can only be returned after a round of encryption has been performed'); } // the tag is 128-bits. it can't be greater than 16 bytes because that's bigger than the tag is. if it // were 0 you might as well be doing CTR and less than 4 provides minimal security that could be trivially // easily brute forced. // see https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=36 // for more info if ($length < 4 || $length > 16) { throw new \LengthException('The authentication tag must be between 4 and 16 bytes long'); } return $length == 16 ? $this->newtag : substr($this->newtag, 0, $length); } /** * Sets the authentication tag * * Only used in GCM mode * * @see self::decrypt() * @param string $tag * @throws \LengthException if $length isn't of a sufficient length * @throws \RuntimeException if GCM mode isn't being used */ public function setTag($tag) { if ($this->usePoly1305 && !isset($this->poly1305Key) && method_exists($this, 'createPoly1305Key')) { $this->createPoly1305Key(); } if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305'); } $length = strlen($tag); if ($length < 4 || $length > 16) { throw new \LengthException('The authentication tag must be between 4 and 16 bytes long'); } $this->oldtag = $tag; } /** * Get the IV * * mcrypt requires an IV even if ECB is used * * @see self::encrypt() * @see self::decrypt() * @param string $iv * @return string */ protected function getIV($iv) { return $this->mode == self::MODE_ECB ? str_repeat("\0", $this->block_size) : $iv; } /** * OpenSSL CTR Processor * * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream * for CTR is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt() * and SymmetricKey::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this * function will emulate CTR with ECB when necessary. * * @see self::encrypt() * @see self::decrypt() * @param string $plaintext * @param string $encryptIV * @param array $buffer * @return string */ private function openssl_ctr_process($plaintext, &$encryptIV, &$buffer) { $ciphertext = ''; $block_size = $this->block_size; $key = $this->key; if ($this->openssl_emulate_ctr) { $xor = $encryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $buffer['ciphertext'] .= openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); } Strings::increment_str($xor); $otp = Strings::shift($buffer['ciphertext'], $block_size); $ciphertext .= $block ^ $otp; } } else { for ($i = 0; $i < strlen($plaintext); $i += $block_size) { $block = substr($plaintext, $i, $block_size); $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); Strings::increment_str($xor); $ciphertext .= $block ^ $otp; } } if ($this->continuousBuffer) { $encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } return $ciphertext; } if (strlen($buffer['ciphertext'])) { $ciphertext = $plaintext ^ Strings::shift($buffer['ciphertext'], strlen($plaintext)); $plaintext = substr($plaintext, strlen($ciphertext)); if (!strlen($plaintext)) { return $ciphertext; } } $overflow = strlen($plaintext) % $block_size; if ($overflow) { $plaintext2 = Strings::pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2 $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV); $temp = Strings::pop($encrypted, $block_size); $ciphertext .= $encrypted . ($plaintext2 ^ $temp); if ($this->continuousBuffer) { $buffer['ciphertext'] = substr($temp, $overflow); $encryptIV = $temp; } } elseif (!strlen($buffer['ciphertext'])) { $ciphertext .= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV); $temp = Strings::pop($ciphertext, $block_size); if ($this->continuousBuffer) { $encryptIV = $temp; } } if ($this->continuousBuffer) { $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); if ($overflow) { Strings::increment_str($encryptIV); } } return $ciphertext; } /** * OpenSSL OFB Processor * * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream * for OFB is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt() * and SymmetricKey::decrypt(). * * @see self::encrypt() * @see self::decrypt() * @param string $plaintext * @param string $encryptIV * @param array $buffer * @return string */ private function openssl_ofb_process($plaintext, &$encryptIV, &$buffer) { if (strlen($buffer['xor'])) { $ciphertext = $plaintext ^ $buffer['xor']; $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); $plaintext = substr($plaintext, strlen($ciphertext)); } else { $ciphertext = ''; } $block_size = $this->block_size; $len = strlen($plaintext); $key = $this->key; $overflow = $len % $block_size; if (strlen($plaintext)) { if ($overflow) { $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV); $xor = Strings::pop($ciphertext, $block_size); if ($this->continuousBuffer) { $encryptIV = $xor; } $ciphertext .= Strings::shift($xor, $overflow) ^ substr($plaintext, -$overflow); if ($this->continuousBuffer) { $buffer['xor'] = $xor; } } else { $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV); if ($this->continuousBuffer) { $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size); } } } return $ciphertext; } /** * phpseclib <-> OpenSSL Mode Mapper * * May need to be overwritten by classes extending this one in some cases * * @return string */ protected function openssl_translate_mode() { switch ($this->mode) { case self::MODE_ECB: return 'ecb'; case self::MODE_CBC: return 'cbc'; case self::MODE_CTR: case self::MODE_GCM: return 'ctr'; case self::MODE_CFB: return 'cfb'; case self::MODE_CFB8: return 'cfb8'; case self::MODE_OFB: return 'ofb'; } } /** * Pad "packets". * * Block ciphers working by encrypting between their specified [$this->]block_size at a time * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to * pad the input so that it is of the proper length. * * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is * transmitted separately) * * @see self::disablePadding() */ public function enablePadding() { $this->padding = true; } /** * Do not pad packets. * * @see self::enablePadding() */ public function disablePadding() { $this->padding = false; } /** * Treat consecutive "packets" as if they are a continuous buffer. * * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets * will yield different outputs: * * <code> * echo $rijndael->encrypt(substr($plaintext, 0, 16)); * echo $rijndael->encrypt(substr($plaintext, 16, 16)); * </code> * <code> * echo $rijndael->encrypt($plaintext); * </code> * * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates * another, as demonstrated with the following: * * <code> * $rijndael->encrypt(substr($plaintext, 0, 16)); * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); * </code> * <code> * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); * </code> * * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\*() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::disableContinuousBuffer() */ public function enableContinuousBuffer() { if ($this->mode == self::MODE_ECB) { return; } if ($this->mode == self::MODE_GCM) { throw new \BadMethodCallException('This mode does not run in continuous mode'); } $this->continuousBuffer = true; $this->setEngine(); } /** * Treat consecutive packets as if they are a discontinuous buffer. * * The default behavior. * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::enableContinuousBuffer() */ public function disableContinuousBuffer() { if ($this->mode == self::MODE_ECB) { return; } if (!$this->continuousBuffer) { return; } $this->continuousBuffer = false; $this->setEngine(); } /** * Test for engine validity * * @see self::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { switch ($engine) { case self::ENGINE_OPENSSL: $this->openssl_emulate_ctr = false; $result = $this->cipher_name_openssl && extension_loaded('openssl'); if (!$result) { return false; } $methods = openssl_get_cipher_methods(); if (in_array($this->cipher_name_openssl, $methods)) { return true; } // not all of openssl's symmetric cipher's support ctr. for those // that don't we'll emulate it switch ($this->mode) { case self::MODE_CTR: if (in_array($this->cipher_name_openssl_ecb, $methods)) { $this->openssl_emulate_ctr = true; return true; } } return false; case self::ENGINE_MCRYPT: set_error_handler(function () { }); $result = $this->cipher_name_mcrypt && extension_loaded('mcrypt') && in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); restore_error_handler(); return $result; case self::ENGINE_EVAL: return method_exists($this, 'setupInlineCrypt'); case self::ENGINE_INTERNAL: return true; } return false; } /** * Test for engine validity * * @see self::__construct() * @param string $engine * @return bool */ public function isValidEngine($engine) { static $reverseMap; if (!isset($reverseMap)) { $reverseMap = array_map('strtolower', self::ENGINE_MAP); $reverseMap = array_flip($reverseMap); } $engine = strtolower($engine); if (!isset($reverseMap[$engine])) { return false; } return $this->isValidEngineHelper($reverseMap[$engine]); } /** * Sets the preferred crypt engine * * Currently, $engine could be: * * - libsodium[very fast] * * - OpenSSL [very fast] * * - mcrypt [fast] * * - Eval [slow] * * - PHP [slowest] * * If the preferred crypt engine is not available the fastest available one will be used * * @see self::__construct() * @param string $engine */ public function setPreferredEngine($engine) { static $reverseMap; if (!isset($reverseMap)) { $reverseMap = array_map('strtolower', self::ENGINE_MAP); $reverseMap = array_flip($reverseMap); } $engine = is_string($engine) ? strtolower($engine) : ''; $this->preferredEngine = isset($reverseMap[$engine]) ? $reverseMap[$engine] : self::ENGINE_LIBSODIUM; $this->setEngine(); } /** * Returns the engine currently being utilized * * @see self::setEngine() */ public function getEngine() { return self::ENGINE_MAP[$this->engine]; } /** * Sets the engine as appropriate * * @see self::__construct() */ protected function setEngine() { $this->engine = null; $candidateEngines = [ self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM, self::ENGINE_OPENSSL, self::ENGINE_MCRYPT, self::ENGINE_EVAL ]; if (isset($this->preferredEngine)) { $temp = [$this->preferredEngine]; $candidateEngines = array_merge( $temp, array_diff($candidateEngines, $temp) ); } foreach ($candidateEngines as $engine) { if ($this->isValidEngineHelper($engine)) { $this->engine = $engine; break; } } if (!$this->engine) { $this->engine = self::ENGINE_INTERNAL; } if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { set_error_handler(function () { }); // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // (re)open them with the module named in $this->cipher_name_mcrypt mcrypt_module_close($this->enmcrypt); mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { mcrypt_module_close($this->ecb); $this->ecb = null; } restore_error_handler(); } $this->changed = $this->nonIVChanged = true; } /** * Encrypts a block * * Note: Must be extended by the child \phpseclib3\Crypt\* class * * @param string $in * @return string */ abstract protected function encryptBlock($in); /** * Decrypts a block * * Note: Must be extended by the child \phpseclib3\Crypt\* class * * @param string $in * @return string */ abstract protected function decryptBlock($in); /** * Setup the key (expansion) * * Only used if $engine == self::ENGINE_INTERNAL * * Note: Must extend by the child \phpseclib3\Crypt\* class * * @see self::setup() */ abstract protected function setupKey(); /** * Setup the self::ENGINE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine and flush all $buffers * Used (only) if $engine == self::ENGINE_INTERNAL * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: * * - setKey() * * - setIV() * * - disableContinuousBuffer() * * - First run of encrypt() / decrypt() with no init-settings * * {@internal setup() is always called before en/decryption.} * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::setKey() * @see self::setIV() * @see self::disableContinuousBuffer() */ protected function setup() { if (!$this->changed) { return; } $this->changed = false; if ($this->usePoly1305 && !isset($this->poly1305Key) && method_exists($this, 'createPoly1305Key')) { $this->createPoly1305Key(); } $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true]; //$this->newtag = $this->oldtag = false; if ($this->usesNonce()) { if ($this->nonce === false) { throw new InsufficientSetupException('No nonce has been defined'); } if ($this->mode == self::MODE_GCM && !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) { $this->setupGCM(); } } else { $this->iv = $this->origIV; } if ($this->iv === false && !in_array($this->mode, [self::MODE_STREAM, self::MODE_ECB])) { if ($this->mode != self::MODE_GCM || !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) { throw new InsufficientSetupException('No IV has been defined'); } } if ($this->key === false) { throw new InsufficientSetupException('No key has been defined'); } $this->encryptIV = $this->decryptIV = $this->iv; switch ($this->engine) { case self::ENGINE_MCRYPT: $this->enchanged = $this->dechanged = true; set_error_handler(function () { }); if (!isset($this->enmcrypt)) { static $mcrypt_modes = [ self::MODE_CTR => 'ctr', self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CFB => 'ncfb', self::MODE_CFB8 => MCRYPT_MODE_CFB, self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_OFB8 => MCRYPT_MODE_OFB, self::MODE_STREAM => MCRYPT_MODE_STREAM, ]; $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} if ($this->mode == self::MODE_CFB) { $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } // else should mcrypt_generic_deinit be called? if ($this->mode == self::MODE_CFB) { mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } restore_error_handler(); break; case self::ENGINE_INTERNAL: $this->setupKey(); break; case self::ENGINE_EVAL: if ($this->nonIVChanged) { $this->setupKey(); $this->setupInlineCrypt(); } } $this->nonIVChanged = false; } /** * Pads a string * * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to * chr($this->block_size - (strlen($text) % $this->block_size) * * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless * and padding will, hence forth, be enabled. * * @see self::unpad() * @param string $text * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size * @return string */ protected function pad($text) { $length = strlen($text); if (!$this->padding) { if ($length % $this->block_size == 0) { return $text; } else { throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); } } $pad = $this->block_size - ($length % $this->block_size); return str_pad($text, $length + $pad, chr($pad)); } /** * Unpads a string. * * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong * and false will be returned. * * @see self::pad() * @param string $text * @throws \LengthException if the ciphertext's length is not a multiple of the block size * @return string */ protected function unpad($text) { if (!$this->padding) { return $text; } $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { throw new BadDecryptionException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})"); } return substr($text, 0, -$length); } /** * Setup the performance-optimized function for de/encrypt() * * Stores the created (or existing) callback function-name * in $this->inline_crypt * * Internally for phpseclib developers: * * _setupInlineCrypt() would be called only if: * * - $this->engine === self::ENGINE_EVAL * * - each time on _setup(), after(!) _setupKey() * * * This ensures that _setupInlineCrypt() has always a * full ready2go initializated internal cipher $engine state * where, for example, the keys already expanded, * keys/block_size calculated and such. * * It is, each time if called, the responsibility of _setupInlineCrypt(): * * - to set $this->inline_crypt to a valid and fully working callback function * as a (faster) replacement for encrypt() / decrypt() * * - NOT to create unlimited callback functions (for memory reasons!) * no matter how often _setupInlineCrypt() would be called. At some * point of amount they must be generic re-useable. * * - the code of _setupInlineCrypt() it self, * and the generated callback code, * must be, in following order: * - 100% safe * - 100% compatible to encrypt()/decrypt() * - using only php5+ features/lang-constructs/php-extensions if * compatibility (down to php4) or fallback is provided * - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-) * - >= 10% faster than encrypt()/decrypt() [which is, by the way, * the reason for the existence of _setupInlineCrypt() :-)] * - memory-nice * - short (as good as possible) * * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib3\Crypt\* class. * - The following variable names are reserved: * - $_* (all variable names prefixed with an underscore) * - $self (object reference to it self. Do not use $this, but $self instead) * - $in (the content of $in has to en/decrypt by the generated code) * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only * * {@internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()} * * @see self::setup() * @see self::createInlineCryptFunction() * @see self::encrypt() * @see self::decrypt() */ //protected function setupInlineCrypt(); /** * Creates the performance-optimized function for en/decrypt() * * Internally for phpseclib developers: * * _createInlineCryptFunction(): * * - merge the $cipher_code [setup'ed by _setupInlineCrypt()] * with the current [$this->]mode of operation code * * - create the $inline function, which called by encrypt() / decrypt() * as its replacement to speed up the en/decryption operations. * * - return the name of the created $inline callback function * * - used to speed up en/decryption * * * * The main reason why can speed up things [up to 50%] this way are: * * - using variables more effective then regular. * (ie no use of expensive arrays but integers $k_0, $k_1 ... * or even, for example, the pure $key[] values hardcoded) * * - avoiding 1000's of function calls of ie _encryptBlock() * but inlining the crypt operations. * in the mode of operation for() loop. * * - full loop unroll the (sometimes key-dependent) rounds * avoiding this way ++$i counters and runtime-if's etc... * * The basic code architectur of the generated $inline en/decrypt() * lambda function, in pseudo php, is: * * <code> * +----------------------------------------------------------------------------------------------+ * | callback $inline = create_function: | * | lambda_function_0001_crypt_ECB($action, $text) | * | { | * | INSERT PHP CODE OF: | * | $cipher_code['init_crypt']; // general init code. | * | // ie: $sbox'es declarations used for | * | // encrypt and decrypt'ing. | * | | * | switch ($action) { | * | case 'encrypt': | * | INSERT PHP CODE OF: | * | $cipher_code['init_encrypt']; // encrypt sepcific init code. | * | ie: specified $key or $box | * | declarations for encrypt'ing. | * | | * | foreach ($ciphertext) { | * | $in = $block_size of $ciphertext; | * | | * | INSERT PHP CODE OF: | * | $cipher_code['encrypt_block']; // encrypt's (string) $in, which is always: | * | // strlen($in) == $this->block_size | * | // here comes the cipher algorithm in action | * | // for encryption. | * | // $cipher_code['encrypt_block'] has to | * | // encrypt the content of the $in variable | * | | * | $plaintext .= $in; | * | } | * | return $plaintext; | * | | * | case 'decrypt': | * | INSERT PHP CODE OF: | * | $cipher_code['init_decrypt']; // decrypt sepcific init code | * | ie: specified $key or $box | * | declarations for decrypt'ing. | * | foreach ($plaintext) { | * | $in = $block_size of $plaintext; | * | | * | INSERT PHP CODE OF: | * | $cipher_code['decrypt_block']; // decrypt's (string) $in, which is always | * | // strlen($in) == $this->block_size | * | // here comes the cipher algorithm in action | * | // for decryption. | * | // $cipher_code['decrypt_block'] has to | * | // decrypt the content of the $in variable | * | $ciphertext .= $in; | * | } | * | return $ciphertext; | * | } | * | } | * +----------------------------------------------------------------------------------------------+ * </code> * * See also the \phpseclib3\Crypt\*::_setupInlineCrypt()'s for * productive inline $cipher_code's how they works. * * Structure of: * <code> * $cipher_code = [ * 'init_crypt' => (string) '', // optional * 'init_encrypt' => (string) '', // optional * 'init_decrypt' => (string) '', // optional * 'encrypt_block' => (string) '', // required * 'decrypt_block' => (string) '' // required * ]; * </code> * * @see self::setupInlineCrypt() * @see self::encrypt() * @see self::decrypt() * @param array $cipher_code * @return string (the name of the created callback function) */ protected function createInlineCryptFunction($cipher_code) { $block_size = $this->block_size; // optional $init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : ''; $init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : ''; $init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : ''; // required $encrypt_block = $cipher_code['encrypt_block']; $decrypt_block = $cipher_code['decrypt_block']; // Generating mode of operation inline code, // merged with the $cipher_code algorithm // for encrypt- and decryption. switch ($this->mode) { case self::MODE_ECB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $in = substr($_text, $_i, ' . $block_size . '); ' . $encrypt_block . ' $_ciphertext.= $in; } return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0)); $_ciphertext_len = strlen($_text); for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $in = substr($_text, $_i, ' . $block_size . '); ' . $decrypt_block . ' $_plaintext.= $in; } return $this->unpad($_plaintext); '; break; case self::MODE_CTR: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $this->encryptIV; $_buffer = &$this->enbuffer; if (strlen($_buffer["ciphertext"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); if (strlen($_block) > strlen($_buffer["ciphertext"])) { $in = $_xor; ' . $encrypt_block . ' \phpseclib3\Common\Functions\Strings::increment_str($_xor); $_buffer["ciphertext"].= $in; } $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . '); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); $in = $_xor; ' . $encrypt_block . ' \phpseclib3\Common\Functions\Strings::increment_str($_xor); $_key = $in; $_ciphertext.= $_block ^ $_key; } } if ($this->continuousBuffer) { $this->encryptIV = $_xor; if ($_start = $_plaintext_len % ' . $block_size . ') { $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_ciphertext_len = strlen($_text); $_xor = $this->decryptIV; $_buffer = &$this->debuffer; if (strlen($_buffer["ciphertext"])) { for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); if (strlen($_block) > strlen($_buffer["ciphertext"])) { $in = $_xor; ' . $encrypt_block . ' \phpseclib3\Common\Functions\Strings::increment_str($_xor); $_buffer["ciphertext"].= $in; } $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . '); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); $in = $_xor; ' . $encrypt_block . ' \phpseclib3\Common\Functions\Strings::increment_str($_xor); $_key = $in; $_plaintext.= $_block ^ $_key; } } if ($this->continuousBuffer) { $this->decryptIV = $_xor; if ($_start = $_ciphertext_len % ' . $block_size . ') { $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; } } return $_plaintext; '; break; case self::MODE_CFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_buffer = &$this->enbuffer; if ($this->continuousBuffer) { $_iv = &$this->encryptIV; $_pos = &$_buffer["pos"]; } else { $_iv = $this->encryptIV; $_pos = 0; } $_len = strlen($_text); $_i = 0; if ($_pos) { $_orig_pos = $_pos; $_max = ' . $block_size . ' - $_pos; if ($_len >= $_max) { $_i = $_max; $_len-= $_max; $_pos = 0; } else { $_i = $_len; $_pos+= $_len; $_len = 0; } $_ciphertext = substr($_iv, $_orig_pos) ^ $_text; $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i); } while ($_len >= ' . $block_size . ') { $in = $_iv; ' . $encrypt_block . '; $_iv = $in ^ substr($_text, $_i, ' . $block_size . '); $_ciphertext.= $_iv; $_len-= ' . $block_size . '; $_i+= ' . $block_size . '; } if ($_len) { $in = $_iv; ' . $encrypt_block . ' $_iv = $in; $_block = $_iv ^ substr($_text, $_i); $_iv = substr_replace($_iv, $_block, 0, $_len); $_ciphertext.= $_block; $_pos = $_len; } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_buffer = &$this->debuffer; if ($this->continuousBuffer) { $_iv = &$this->decryptIV; $_pos = &$_buffer["pos"]; } else { $_iv = $this->decryptIV; $_pos = 0; } $_len = strlen($_text); $_i = 0; if ($_pos) { $_orig_pos = $_pos; $_max = ' . $block_size . ' - $_pos; if ($_len >= $_max) { $_i = $_max; $_len-= $_max; $_pos = 0; } else { $_i = $_len; $_pos+= $_len; $_len = 0; } $_plaintext = substr($_iv, $_orig_pos) ^ $_text; $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i); } while ($_len >= ' . $block_size . ') { $in = $_iv; ' . $encrypt_block . ' $_iv = $in; $cb = substr($_text, $_i, ' . $block_size . '); $_plaintext.= $_iv ^ $cb; $_iv = $cb; $_len-= ' . $block_size . '; $_i+= ' . $block_size . '; } if ($_len) { $in = $_iv; ' . $encrypt_block . ' $_iv = $in; $_plaintext.= $_iv ^ substr($_text, $_i); $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len); $_pos = $_len; } return $_plaintext; '; break; case self::MODE_CFB8: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_len = strlen($_text); $_iv = $this->encryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; ' . $encrypt_block . ' $_ciphertext .= ($_c = $_text[$_i] ^ $in); $_iv = substr($_iv, 1) . $_c; } if ($this->continuousBuffer) { if ($_len >= ' . $block_size . ') { $this->encryptIV = substr($_ciphertext, -' . $block_size . '); } else { $this->encryptIV = substr($this->encryptIV, $_len - ' . $block_size . ') . substr($_ciphertext, -$_len); } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_len = strlen($_text); $_iv = $this->decryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; ' . $encrypt_block . ' $_plaintext .= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $_text[$_i]; } if ($this->continuousBuffer) { if ($_len >= ' . $block_size . ') { $this->decryptIV = substr($_text, -' . $block_size . '); } else { $this->decryptIV = substr($this->decryptIV, $_len - ' . $block_size . ') . substr($_text, -$_len); } } return $_plaintext; '; break; case self::MODE_OFB8: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_len = strlen($_text); $_iv = $this->encryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; ' . $encrypt_block . ' $_ciphertext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $in[0]; } if ($this->continuousBuffer) { $this->encryptIV = $_iv; } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_len = strlen($_text); $_iv = $this->decryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; ' . $encrypt_block . ' $_plaintext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $in[0]; } if ($this->continuousBuffer) { $this->decryptIV = $_iv; } return $_plaintext; '; break; case self::MODE_OFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $this->encryptIV; $_buffer = &$this->enbuffer; if (strlen($_buffer["xor"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); if (strlen($_block) > strlen($_buffer["xor"])) { $in = $_xor; ' . $encrypt_block . ' $_xor = $in; $_buffer["xor"].= $_xor; } $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . '); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $in = $_xor; ' . $encrypt_block . ' $_xor = $in; $_ciphertext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor; } $_key = $_xor; } if ($this->continuousBuffer) { $this->encryptIV = $_xor; if ($_start = $_plaintext_len % ' . $block_size . ') { $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_ciphertext_len = strlen($_text); $_xor = $this->decryptIV; $_buffer = &$this->debuffer; if (strlen($_buffer["xor"])) { for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $_block = substr($_text, $_i, ' . $block_size . '); if (strlen($_block) > strlen($_buffer["xor"])) { $in = $_xor; ' . $encrypt_block . ' $_xor = $in; $_buffer["xor"].= $_xor; } $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . '); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $in = $_xor; ' . $encrypt_block . ' $_xor = $in; $_plaintext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor; } $_key = $_xor; } if ($this->continuousBuffer) { $this->decryptIV = $_xor; if ($_start = $_ciphertext_len % ' . $block_size . ') { $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; } } return $_plaintext; '; break; case self::MODE_STREAM: $encrypt = $init_encrypt . ' $_ciphertext = ""; ' . $encrypt_block . ' return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; ' . $decrypt_block . ' return $_plaintext; '; break; // case self::MODE_CBC: default: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $in = $this->encryptIV; for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { $in = substr($_text, $_i, ' . $block_size . ') ^ $in; ' . $encrypt_block . ' $_ciphertext.= $in; } if ($this->continuousBuffer) { $this->encryptIV = $in; } return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0)); $_ciphertext_len = strlen($_text); $_iv = $this->decryptIV; for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { $in = $_block = substr($_text, $_i, ' . $block_size . '); ' . $decrypt_block . ' $_plaintext.= $in ^ $_iv; $_iv = $_block; } if ($this->continuousBuffer) { $this->decryptIV = $_iv; } return $this->unpad($_plaintext); '; break; } // Before discrediting this, please read the following: // @see https://github.com/phpseclib/phpseclib/issues/1293 // @see https://github.com/phpseclib/phpseclib/pull/1143 eval('$func = function ($_action, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }};'); return \Closure::bind($func, $this, static::class); } /** * Convert float to int * * On ARM CPUs converting floats to ints doesn't always work * * @param string $x * @return int */ protected static function safe_intval($x) { if (is_int($x)) { return $x; } if (self::$use_reg_intval) { return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100 ? intval($x) : $x; } return (fmod($x, 0x80000000) & 0x7FFFFFFF) | ((fmod(floor($x / 0x80000000), 2) & 1) << 31); } /** * eval()'able string for in-line float to int * * @return string */ protected static function safe_intval_inline() { if (self::$use_reg_intval) { return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100 ? 'intval(%s)' : '%s'; } $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; } /** * Sets up GCM parameters * * See steps 1-2 of https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=23 * for more info * */ private function setupGCM() { // don't keep on re-calculating $this->h if (!$this->h || $this->hKey != $this->key) { $cipher = new static('ecb'); $cipher->setKey($this->key); $cipher->disablePadding(); $this->h = self::$gcmField->newInteger( Strings::switchEndianness($cipher->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")) ); $this->hKey = $this->key; } if (strlen($this->nonce) == 12) { $this->iv = $this->nonce . "\0\0\0\1"; } else { $this->iv = $this->ghash( self::nullPad128($this->nonce) . str_repeat("\0", 8) . self::len64($this->nonce) ); } } /** * Performs GHASH operation * * See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=20 * for more info * * @see self::decrypt() * @see self::encrypt() * @param string $x * @return string */ private function ghash($x) { $h = $this->h; $y = ["\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"]; $x = str_split($x, 16); $n = 0; // the switchEndianness calls are necessary because the multiplication algorithm in BinaryField/Integer // interprets strings as polynomials in big endian order whereas in GCM they're interpreted in little // endian order per https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=19. // big endian order is what binary field elliptic curves use per http://www.secg.org/sec1-v2.pdf#page=18. // we could switchEndianness here instead of in the while loop but doing so in the while loop seems like it // might be slightly more performant //$x = Strings::switchEndianness($x); foreach ($x as $xn) { $xn = Strings::switchEndianness($xn); $t = $y[$n] ^ $xn; $temp = self::$gcmField->newInteger($t); $y[++$n] = $temp->multiply($h)->toBytes(); $y[$n] = substr($y[$n], 1); } $y[$n] = Strings::switchEndianness($y[$n]); return $y[$n]; } /** * Returns the bit length of a string in a packed format * * @see self::decrypt() * @see self::encrypt() * @see self::setupGCM() * @param string $str * @return string */ private static function len64($str) { return "\0\0\0\0" . pack('N', 8 * strlen($str)); } /** * NULL pads a string to be a multiple of 128 * * @see self::decrypt() * @see self::encrypt() * @see self::setupGCM() * @param string $str * @return string */ protected static function nullPad128($str) { $len = strlen($str); return $str . str_repeat("\0", 16 * ceil($len / 16) - $len); } /** * Calculates Poly1305 MAC * * On my system ChaCha20, with libsodium, takes 0.5s. With this custom Poly1305 implementation * it takes 1.2s. * * @see self::decrypt() * @see self::encrypt() * @param string $text * @return string */ protected function poly1305($text) { $s = $this->poly1305Key; // strlen($this->poly1305Key) == 32 $r = Strings::shift($s, 16); $r = strrev($r); $r &= "\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xff"; $s = strrev($s); $r = self::$poly1305Field->newInteger(new BigInteger($r, 256)); $s = self::$poly1305Field->newInteger(new BigInteger($s, 256)); $a = self::$poly1305Field->newInteger(new BigInteger()); $blocks = str_split($text, 16); foreach ($blocks as $block) { $n = strrev($block . chr(1)); $n = self::$poly1305Field->newInteger(new BigInteger($n, 256)); $a = $a->add($n); $a = $a->multiply($r); } $r = $a->toBigInteger()->add($s->toBigInteger()); $mask = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; return strrev($r->toBytes()) & $mask; } /** * Return the mode * * You can do $obj instanceof AES or whatever to get the cipher but you can't do that to get the mode * * @return string */ public function getMode() { return array_flip(self::MODE_MAP)[$this->mode]; } /** * Is the continuous buffer enabled? * * @return boolean */ public function continuousBufferEnabled() { return $this->continuousBuffer; } } PK2A#]Н��;�;Cvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.phpnu�[���<?php /** * Base Class for all asymmetric key ciphers * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\RSA; use phpseclib3\Exception\NoKeyLoadedException; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * Base Class for all asymmetric cipher classes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AsymmetricKey { /** * Precomputed Zero * * @var \phpseclib3\Math\BigInteger */ protected static $zero; /** * Precomputed One * * @var \phpseclib3\Math\BigInteger */ protected static $one; /** * Format of the loaded key * * @var string */ protected $format; /** * Hash function * * @var \phpseclib3\Crypt\Hash */ protected $hash; /** * HMAC function * * @var \phpseclib3\Crypt\Hash */ private $hmac; /** * Supported plugins (lower case) * * @see self::initialize_static_variables() * @var array */ private static $plugins = []; /** * Invisible plugins * * @see self::initialize_static_variables() * @var array */ private static $invisiblePlugins = []; /** * Available Engines * * @var boolean[] */ protected static $engines = []; /** * Key Comment * * @var null|string */ private $comment; /** * @param string $type * @return array|string */ abstract public function toString($type, array $options = []); /** * The constructor */ protected function __construct() { self::initialize_static_variables(); $this->hash = new Hash('sha256'); $this->hmac = new Hash('sha256'); } /** * Initialize static variables */ protected static function initialize_static_variables() { if (!isset(self::$zero)) { self::$zero = new BigInteger(0); self::$one = new BigInteger(1); } self::loadPlugins('Keys'); if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') { self::loadPlugins('Signature'); } } /** * Load the key * * @param string $key * @param string $password optional * @return \phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey */ public static function load($key, $password = false) { self::initialize_static_variables(); $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')'); } $components = false; foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) { if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) { continue; } try { $components = $format::load($key, $password); } catch (\Exception $e) { $components = false; } if ($components !== false) { break; } } if ($components === false) { throw new NoKeyLoadedException('Unable to read key'); } $components['format'] = $format; $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; $comment = isset($components['comment']) ? $components['comment'] : null; $new = static::onLoad($components); $new->format = $format; $new->comment = $comment; return $new instanceof PrivateKey ? $new->withPassword($password) : $new; } /** * Loads a private key * * @return PrivateKey * @param string|array $key * @param string $password optional */ public static function loadPrivateKey($key, $password = '') { $key = self::load($key, $password); if (!$key instanceof PrivateKey) { throw new NoKeyLoadedException('The key that was loaded was not a private key'); } return $key; } /** * Loads a public key * * @return PublicKey * @param string|array $key */ public static function loadPublicKey($key) { $key = self::load($key); if (!$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a public key'); } return $key; } /** * Loads parameters * * @return AsymmetricKey * @param string|array $key */ public static function loadParameters($key) { $key = self::load($key); if (!$key instanceof PrivateKey && !$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a parameter'); } return $key; } /** * Load the key, assuming a specific format * * @param string $type * @param string $key * @param string $password optional * @return static */ public static function loadFormat($type, $key, $password = false) { self::initialize_static_variables(); $components = false; $format = strtolower($type); if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) { $format = self::$plugins[static::ALGORITHM]['Keys'][$format]; $components = $format::load($key, $password); } if ($components === false) { throw new NoKeyLoadedException('Unable to read key'); } $components['format'] = $format; $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; $new = static::onLoad($components); $new->format = $format; return $new instanceof PrivateKey ? $new->withPassword($password) : $new; } /** * Loads a private key * * @return PrivateKey * @param string $type * @param string $key * @param string $password optional */ public static function loadPrivateKeyFormat($type, $key, $password = false) { $key = self::loadFormat($type, $key, $password); if (!$key instanceof PrivateKey) { throw new NoKeyLoadedException('The key that was loaded was not a private key'); } return $key; } /** * Loads a public key * * @return PublicKey * @param string $type * @param string $key */ public static function loadPublicKeyFormat($type, $key) { $key = self::loadFormat($type, $key); if (!$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a public key'); } return $key; } /** * Loads parameters * * @return AsymmetricKey * @param string $type * @param string|array $key */ public static function loadParametersFormat($type, $key) { $key = self::loadFormat($type, $key); if (!$key instanceof PrivateKey && !$key instanceof PublicKey) { throw new NoKeyLoadedException('The key that was loaded was not a parameter'); } return $key; } /** * Validate Plugin * * @param string $format * @param string $type * @param string $method optional * @return mixed */ protected static function validatePlugin($format, $type, $method = null) { $type = strtolower($type); if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) { throw new UnsupportedFormatException("$type is not a supported format"); } $type = self::$plugins[static::ALGORITHM][$format][$type]; if (isset($method) && !method_exists($type, $method)) { throw new UnsupportedFormatException("$type does not implement $method"); } return $type; } /** * Load Plugins * * @param string $format */ private static function loadPlugins($format) { if (!isset(self::$plugins[static::ALGORITHM][$format])) { self::$plugins[static::ALGORITHM][$format] = []; foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) { if ($file->getExtension() != 'php') { continue; } $name = $file->getBasename('.php'); if ($name[0] == '.') { continue; } $type = 'phpseclib3\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name; $reflect = new \ReflectionClass($type); if ($reflect->isTrait()) { continue; } self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type; if ($reflect->hasConstant('IS_INVISIBLE')) { self::$invisiblePlugins[static::ALGORITHM][] = $type; } } } } /** * Returns a list of supported formats. * * @return array */ public static function getSupportedKeyFormats() { self::initialize_static_variables(); return self::$plugins[static::ALGORITHM]['Keys']; } /** * Add a fileformat plugin * * The plugin needs to either already be loaded or be auto-loadable. * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. * * @see self::load() * @param string $fullname * @return bool */ public static function addFileFormat($fullname) { self::initialize_static_variables(); if (class_exists($fullname)) { $meta = new \ReflectionClass($fullname); $shortname = $meta->getShortName(); self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname; if ($meta->hasConstant('IS_INVISIBLE')) { self::$invisiblePlugins[static::ALGORITHM][] = strtolower($shortname); } } } /** * Returns the format of the loaded key. * * If the key that was loaded wasn't in a valid or if the key was auto-generated * with RSA::createKey() then this will throw an exception. * * @see self::load() * @return mixed */ public function getLoadedFormat() { if (empty($this->format)) { throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"'); } $meta = new \ReflectionClass($this->format); return $meta->getShortName(); } /** * Returns the key's comment * * Not all key formats support comments. If you want to set a comment use toString() * * @return null|string */ public function getComment() { return $this->comment; } /** * Tests engine validity * */ public static function useBestEngine() { static::$engines = [ 'PHP' => true, 'OpenSSL' => extension_loaded('openssl'), // this test can be satisfied by either of the following: // http://php.net/manual/en/book.sodium.php // https://github.com/paragonie/sodium_compat 'libsodium' => function_exists('sodium_crypto_sign_keypair') ]; return static::$engines; } /** * Flag to use internal engine only (useful for unit testing) * */ public static function useInternalEngine() { static::$engines = [ 'PHP' => true, 'OpenSSL' => false, 'libsodium' => false ]; } /** * __toString() magic method * * @return string */ public function __toString() { return $this->toString('PKCS8'); } /** * Determines which hashing function should be used * * @param string $hash */ public function withHash($hash) { $new = clone $this; $new->hash = new Hash($hash); $new->hmac = new Hash($hash); return $new; } /** * Returns the hash algorithm currently being used * */ public function getHash() { return clone $this->hash; } /** * Compute the pseudorandom k for signature generation, * using the process specified for deterministic DSA. * * @param string $h1 * @return string */ protected function computek($h1) { $v = str_repeat("\1", strlen($h1)); $k = str_repeat("\0", strlen($h1)); $x = $this->int2octets($this->x); $h1 = $this->bits2octets($h1); $this->hmac->setKey($k); $k = $this->hmac->hash($v . "\0" . $x . $h1); $this->hmac->setKey($k); $v = $this->hmac->hash($v); $k = $this->hmac->hash($v . "\1" . $x . $h1); $this->hmac->setKey($k); $v = $this->hmac->hash($v); $qlen = $this->q->getLengthInBytes(); while (true) { $t = ''; while (strlen($t) < $qlen) { $v = $this->hmac->hash($v); $t = $t . $v; } $k = $this->bits2int($t); if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) { break; } $k = $this->hmac->hash($v . "\0"); $this->hmac->setKey($k); $v = $this->hmac->hash($v); } return $k; } /** * Integer to Octet String * * @param \phpseclib3\Math\BigInteger $v * @return string */ private function int2octets($v) { $out = $v->toBytes(); $rolen = $this->q->getLengthInBytes(); if (strlen($out) < $rolen) { return str_pad($out, $rolen, "\0", STR_PAD_LEFT); } elseif (strlen($out) > $rolen) { return substr($out, -$rolen); } else { return $out; } } /** * Bit String to Integer * * @param string $in * @return \phpseclib3\Math\BigInteger */ protected function bits2int($in) { $v = new BigInteger($in, 256); $vlen = strlen($in) << 3; $qlen = $this->q->getLength(); if ($vlen > $qlen) { return $v->bitwise_rightShift($vlen - $qlen); } return $v; } /** * Bit String to Octet String * * @param string $in * @return string */ private function bits2octets($in) { $z1 = $this->bits2int($in); $z2 = $z1->subtract($this->q); return $z2->compare(self::$zero) < 0 ? $this->int2octets($z1) : $this->int2octets($z2); } } PK2A#]���Avendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.phpnu�[���<?php /** * Base Class for all block ciphers * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @author Hans-Juergen Petrich <petrich@tronic-media.com> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common; /** * Base Class for all block cipher classes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BlockCipher extends SymmetricKey { } PK2A#]����xxJvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.phpnu�[���<?php /** * OpenSSH Key Handler * * PHP version 5 * * Place in $HOME/.ssh/authorized_keys * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\AES; use phpseclib3\Crypt\Random; /** * OpenSSH Formatted RSA Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSH { /** * Default comment * * @var string */ protected static $comment = 'phpseclib-generated-key'; /** * Binary key flag * * @var bool */ protected static $binary = false; /** * Sets the default comment * * @param string $comment */ public static function setComment($comment) { self::$comment = str_replace(["\r", "\n"], '', $comment); } /** * Break a public or private key down into its constituent components * * $type can be either ssh-dss or ssh-rsa * * @param string $key * @param string $password * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } // key format is described here: // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false) { $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key); $key = Strings::base64_decode($key); $magic = Strings::shift($key, 15); if ($magic != "openssh-key-v1\0") { throw new \RuntimeException('Expected openssh-key-v1'); } list($ciphername, $kdfname, $kdfoptions, $numKeys) = Strings::unpackSSH2('sssN', $key); if ($numKeys != 1) { // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass // that to the appropriate key loading parser $numKey times or something throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not'); } switch ($ciphername) { case 'none': break; case 'aes256-ctr': if ($kdfname != 'bcrypt') { throw new \RuntimeException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)'); } list($salt, $rounds) = Strings::unpackSSH2('sN', $kdfoptions); $crypto = new AES('ctr'); //$crypto->setKeyLength(256); //$crypto->disablePadding(); $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32); break; default: throw new \RuntimeException('The only supported cipherse are: none, aes256-ctr (' . $ciphername . ' is being used)'); } list($publicKey, $paddedKey) = Strings::unpackSSH2('ss', $key); list($type) = Strings::unpackSSH2('s', $publicKey); if (isset($crypto)) { $paddedKey = $crypto->decrypt($paddedKey); } list($checkint1, $checkint2) = Strings::unpackSSH2('NN', $paddedKey); // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc. if ($checkint1 != $checkint2) { throw new \RuntimeException('The two checkints do not match'); } self::checkType($type); return compact('type', 'publicKey', 'paddedKey'); } $parts = explode(' ', $key, 3); if (!isset($parts[1])) { $key = base64_decode($parts[0]); $comment = false; } else { $asciiType = $parts[0]; self::checkType($parts[0]); $key = base64_decode($parts[1]); $comment = isset($parts[2]) ? $parts[2] : false; } if ($key === false) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } list($type) = Strings::unpackSSH2('s', $key); self::checkType($type); if (isset($asciiType) && $asciiType != $type) { throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type); } if (strlen($key) <= 4) { throw new \UnexpectedValueException('Key appears to be malformed'); } $publicKey = $key; return compact('type', 'publicKey', 'comment'); } /** * Toggle between binary and printable keys * * Printable keys are what are generated by default. These are the ones that go in * $HOME/.ssh/authorized_key. * * @param bool $enabled */ public static function setBinaryOutput($enabled) { self::$binary = $enabled; } /** * Checks to see if the type is valid * * @param string $candidate */ private static function checkType($candidate) { if (!in_array($candidate, static::$types)) { throw new \RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types)); } } /** * Wrap a private key appropriately * * @param string $publicKey * @param string $privateKey * @param string $password * @param array $options * @return string */ protected static function wrapPrivateKey($publicKey, $privateKey, $password, $options) { list(, $checkint) = unpack('N', Random::string(4)); $comment = isset($options['comment']) ? $options['comment'] : self::$comment; $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) . $privateKey . Strings::packSSH2('s', $comment); $usesEncryption = !empty($password) && is_string($password); /* from http://tools.ietf.org/html/rfc4253#section-6 : Note that the length of the concatenation of 'packet_length', 'padding_length', 'payload', and 'random padding' MUST be a multiple of the cipher block size or 8, whichever is larger. */ $blockSize = $usesEncryption ? 16 : 8; $paddingLength = (($blockSize - 1) * strlen($paddedKey)) % $blockSize; for ($i = 1; $i <= $paddingLength; $i++) { $paddedKey .= chr($i); } if (!$usesEncryption) { $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey); } else { $rounds = isset($options['rounds']) ? $options['rounds'] : 16; $salt = Random::string(16); $kdfoptions = Strings::packSSH2('sN', $salt, $rounds); $crypto = new AES('ctr'); $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32); $paddedKey = $crypto->encrypt($paddedKey); $key = Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey); } $key = "openssh-key-v1\0$key"; return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . chunk_split(Strings::base64_encode($key), 70, "\n") . "-----END OPENSSH PRIVATE KEY-----\n"; } } PK2A#]UV�(H4H4Hvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.phpnu�[���<?php /** * PuTTY Formatted Key Handler * * See PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\AES; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\Random; use phpseclib3\Exception\UnsupportedAlgorithmException; /** * PuTTY Formatted Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PuTTY { /** * Default comment * * @var string */ private static $comment = 'phpseclib-generated-key'; /** * Default version * * @var int */ private static $version = 2; /** * Sets the default comment * * @param string $comment */ public static function setComment($comment) { self::$comment = str_replace(["\r", "\n"], '', $comment); } /** * Sets the default version * * @param int $version */ public static function setVersion($version) { if ($version != 2 && $version != 3) { throw new \RuntimeException('Only supported versions are 2 and 3'); } self::$version = $version; } /** * Generate a symmetric key for PuTTY v2 keys * * @param string $password * @param int $length * @return string */ private static function generateV2Key($password, $length) { $symkey = ''; $sequence = 0; while (strlen($symkey) < $length) { $temp = pack('Na*', $sequence++, $password); $symkey .= Strings::hex2bin(sha1($temp)); } return substr($symkey, 0, $length); } /** * Generate a symmetric key for PuTTY v3 keys * * @param string $password * @param string $flavour * @param int $memory * @param int $passes * @param string $salt * @return array */ private static function generateV3Key($password, $flavour, $memory, $passes, $salt) { if (!function_exists('sodium_crypto_pwhash')) { throw new \RuntimeException('sodium_crypto_pwhash needs to exist for Argon2 password hasing'); } switch ($flavour) { case 'Argon2i': $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13; break; case 'Argon2id': $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13; break; default: throw new UnsupportedAlgorithmException('Only Argon2i and Argon2id are supported'); } $length = 80; // keylen + ivlen + mac_keylen $temp = sodium_crypto_pwhash($length, $password, $salt, $passes, $memory << 10, $flavour); $symkey = substr($temp, 0, 32); $symiv = substr($temp, 32, 16); $hashkey = substr($temp, -32); return compact('symkey', 'symiv', 'hashkey'); } /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password * @return array */ public static function load($key, $password) { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== false) { $lines = preg_split('#[\r\n]+#', $key); switch (true) { case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY ----': throw new \UnexpectedValueException('Key doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----'); case $lines[count($lines) - 1] != '---- END SSH2 PUBLIC KEY ----': throw new \UnexpectedValueException('Key doesn\'t end with ---- END SSH2 PUBLIC KEY ----'); } $lines = array_splice($lines, 1, -1); $lines = array_map(function ($line) { return rtrim($line, "\r\n"); }, $lines); $data = $current = ''; $values = []; $in_value = false; foreach ($lines as $line) { switch (true) { case preg_match('#^(.*?): (.*)#', $line, $match): $in_value = $line[strlen($line) - 1] == '\\'; $current = strtolower($match[1]); $values[$current] = $in_value ? substr($match[2], 0, -1) : $match[2]; break; case $in_value: $in_value = $line[strlen($line) - 1] == '\\'; $values[$current] .= $in_value ? substr($line, 0, -1) : $line; break; default: $data .= $line; } } $components = call_user_func([static::PUBLIC_HANDLER, 'load'], $data); if ($components === false) { throw new \UnexpectedValueException('Unable to decode public key'); } $components += $values; $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $values['comment']); return $components; } $components = []; $key = preg_split('#\r\n|\r|\n#', trim($key)); if (Strings::shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') { return false; } $version = (int) Strings::shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting if ($version != 2 && $version != 3) { throw new \RuntimeException('Only v2 and v3 PuTTY private keys are supported'); } $components['type'] = $type = rtrim($key[0]); if (!in_array($type, static::$types)) { $error = count(static::$types) == 1 ? 'Only ' . static::$types[0] . ' keys are supported. ' : ''; throw new UnsupportedAlgorithmException($error . 'This is an unsupported ' . $type . ' key'); } $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); $public = Strings::base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); $source = Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public); extract(unpack('Nlength', Strings::shift($public, 4))); $newtype = Strings::shift($public, $length); if ($newtype != $type) { throw new \RuntimeException('The binary type does not match the human readable type field'); } $components['public'] = $public; switch ($version) { case 3: $hashkey = ''; break; case 2: $hashkey = 'putty-private-key-file-mac-key'; } $offset = $publicLength + 4; switch ($encryption) { case 'aes256-cbc': $crypto = new AES('cbc'); switch ($version) { case 3: $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++])); $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++])); $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++])); $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++])); $salt = Strings::hex2bin(trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++]))); extract(self::generateV3Key($password, $flavour, $memory, $passes, $salt)); break; case 2: $symkey = self::generateV2Key($password, 32); $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3); $hashkey .= $password; } } switch ($version) { case 3: $hash = new Hash('sha256'); $hash->setKey($hashkey); break; case 2: $hash = new Hash('sha1'); $hash->setKey(sha1($hashkey, true)); } $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++])); $private = Strings::base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength)))); if ($encryption != 'none') { $crypto->setKey($symkey); $crypto->setIV($symiv); $crypto->disablePadding(); $private = $crypto->decrypt($private); } $source .= Strings::packSSH2('s', $private); $hmac = trim(preg_replace('#Private-MAC: (.+)#', '$1', $key[$offset + $privateLength])); $hmac = Strings::hex2bin($hmac); if (!hash_equals($hash->hash($source), $hmac)) { throw new \UnexpectedValueException('MAC validation error'); } $components['private'] = $private; return $components; } /** * Wrap a private key appropriately * * @param string $public * @param string $private * @param string $type * @param string $password * @param array $options optional * @return string */ protected static function wrapPrivateKey($public, $private, $type, $password, array $options = []) { $encryption = (!empty($password) || is_string($password)) ? 'aes256-cbc' : 'none'; $comment = isset($options['comment']) ? $options['comment'] : self::$comment; $version = isset($options['version']) ? $options['version'] : self::$version; $key = "PuTTY-User-Key-File-$version: $type\r\n"; $key .= "Encryption: $encryption\r\n"; $key .= "Comment: $comment\r\n"; $public = Strings::packSSH2('s', $type) . $public; $source = Strings::packSSH2('ssss', $type, $encryption, $comment, $public); $public = Strings::base64_encode($public); $key .= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; $key .= chunk_split($public, 64); if (empty($password) && !is_string($password)) { $source .= Strings::packSSH2('s', $private); switch ($version) { case 3: $hash = new Hash('sha256'); $hash->setKey(''); break; case 2: $hash = new Hash('sha1'); $hash->setKey(sha1('putty-private-key-file-mac-key', true)); } } else { $private .= Random::string(16 - (strlen($private) & 15)); $source .= Strings::packSSH2('s', $private); $crypto = new AES('cbc'); switch ($version) { case 3: $salt = Random::string(16); $key .= "Key-Derivation: Argon2id\r\n"; $key .= "Argon2-Memory: 8192\r\n"; $key .= "Argon2-Passes: 13\r\n"; $key .= "Argon2-Parallelism: 1\r\n"; $key .= "Argon2-Salt: " . Strings::bin2hex($salt) . "\r\n"; extract(self::generateV3Key($password, 'Argon2id', 8192, 13, $salt)); $hash = new Hash('sha256'); $hash->setKey($hashkey); break; case 2: $symkey = self::generateV2Key($password, 32); $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3); $hashkey = 'putty-private-key-file-mac-key' . $password; $hash = new Hash('sha1'); $hash->setKey(sha1($hashkey, true)); } $crypto->setKey($symkey); $crypto->setIV($symiv); $crypto->disablePadding(); $private = $crypto->encrypt($private); $mac = $hash->hash($source); } $private = Strings::base64_encode($private); $key .= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; $key .= chunk_split($private, 64); $key .= 'Private-MAC: ' . Strings::bin2hex($hash->hash($source)) . "\r\n"; return $key; } /** * Wrap a public key appropriately * * This is basically the format described in RFC 4716 (https://tools.ietf.org/html/rfc4716) * * @param string $key * @param string $type * @return string */ protected static function wrapPublicKey($key, $type) { $key = pack('Na*a*', strlen($type), $type, $key); $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . 'Comment: "' . str_replace(['\\', '"'], ['\\\\', '\"'], self::$comment) . "\"\r\n" . chunk_split(Strings::base64_encode($key), 64) . '---- END SSH2 PUBLIC KEY ----'; return $key; } } PK2A#]5S����Fvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.phpnu�[���<?php /** * JSON Web Key (RFC7517) Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; /** * JSON Web Key Formatted Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class JWK { /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password * @return array */ public static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $key = preg_replace('#\s#', '', $key); // remove whitespace if (PHP_VERSION_ID >= 73000) { $key = json_decode($key, null, 512, JSON_THROW_ON_ERROR); } else { $key = json_decode($key); if (!$key) { throw new \RuntimeException('Unable to decode JSON'); } } if (isset($key->kty)) { return $key; } if (count($key->keys) != 1) { throw new \RuntimeException('Although the JWK key format supports multiple keys phpseclib does not'); } return $key->keys[0]; } /** * Wrap a key appropriately * * @return string */ protected static function wrapKey(array $key, array $options) { return json_encode(['keys' => [$key + $options]]); } } PK2A#]��aaGvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.phpnu�[���<?php /** * PKCS Formatted Key Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; /** * PKCS1 Formatted Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS { /** * Auto-detect the format */ const MODE_ANY = 0; /** * Require base64-encoded PEM's be supplied */ const MODE_PEM = 1; /** * Require raw DER's be supplied */ const MODE_DER = 2; /**#@-*/ /** * Is the key a base-64 encoded PEM, DER or should it be auto-detected? * * @var int */ protected static $format = self::MODE_ANY; /** * Require base64-encoded PEM's be supplied * */ public static function requirePEM() { self::$format = self::MODE_PEM; } /** * Require raw DER's be supplied * */ public static function requireDER() { self::$format = self::MODE_DER; } /** * Accept any format and auto detect the format * * This is the default setting * */ public static function requireAny() { self::$format = self::MODE_ANY; } } PK2A#]̀&O0m0mHvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.phpnu�[���<?php /** * PKCS#8 Formatted Key Handler * * PHP version 5 * * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 * is specific to private keys it's basically creating a DER-encoded wrapper * for keys. This just extends that same concept to public keys (much like ssh-keygen) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\AES; use phpseclib3\Crypt\DES; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\RC2; use phpseclib3\Crypt\RC4; use phpseclib3\Crypt\TripleDES; use phpseclib3\Exception\InsufficientSetupException; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; /** * PKCS#8 Formatted Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS8 extends PKCS { /** * Default encryption algorithm * * @var string */ private static $defaultEncryptionAlgorithm = 'id-PBES2'; /** * Default encryption scheme * * Only used when defaultEncryptionAlgorithm is id-PBES2 * * @var string */ private static $defaultEncryptionScheme = 'aes128-CBC-PAD'; /** * Default PRF * * Only used when defaultEncryptionAlgorithm is id-PBES2 * * @var string */ private static $defaultPRF = 'id-hmacWithSHA256'; /** * Default Iteration Count * * @var int */ private static $defaultIterationCount = 2048; /** * OIDs loaded * * @var bool */ private static $oidsLoaded = false; /** * Sets the default encryption algorithm * * @param string $algo */ public static function setEncryptionAlgorithm($algo) { self::$defaultEncryptionAlgorithm = $algo; } /** * Sets the default encryption algorithm for PBES2 * * @param string $algo */ public static function setEncryptionScheme($algo) { self::$defaultEncryptionScheme = $algo; } /** * Sets the iteration count * * @param int $count */ public static function setIterationCount($count) { self::$defaultIterationCount = $count; } /** * Sets the PRF for PBES2 * * @param string $algo */ public static function setPRF($algo) { self::$defaultPRF = $algo; } /** * Returns a SymmetricKey object based on a PBES1 $algo * * @return \phpseclib3\Crypt\Common\SymmetricKey * @param string $algo */ private static function getPBES1EncryptionObject($algo) { $algo = preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ? $matches[1] : substr($algo, 13); // strlen('pbeWithSHAAnd') == 13 switch ($algo) { case 'DES': $cipher = new DES('cbc'); break; case 'RC2': $cipher = new RC2('cbc'); $cipher->setKeyLength(64); break; case '3-KeyTripleDES': $cipher = new TripleDES('cbc'); break; case '2-KeyTripleDES': $cipher = new TripleDES('cbc'); $cipher->setKeyLength(128); break; case '128BitRC2': $cipher = new RC2('cbc'); $cipher->setKeyLength(128); break; case '40BitRC2': $cipher = new RC2('cbc'); $cipher->setKeyLength(40); break; case '128BitRC4': $cipher = new RC4(); $cipher->setKeyLength(128); break; case '40BitRC4': $cipher = new RC4(); $cipher->setKeyLength(40); break; default: throw new UnsupportedAlgorithmException("$algo is not a supported algorithm"); } return $cipher; } /** * Returns a hash based on a PBES1 $algo * * @return string * @param string $algo */ private static function getPBES1Hash($algo) { if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) { return $matches[1] == 'SHA' ? 'sha1' : $matches[1]; } return 'sha1'; } /** * Returns a KDF baesd on a PBES1 $algo * * @return string * @param string $algo */ private static function getPBES1KDF($algo) { switch ($algo) { case 'pbeWithMD2AndDES-CBC': case 'pbeWithMD2AndRC2-CBC': case 'pbeWithMD5AndDES-CBC': case 'pbeWithMD5AndRC2-CBC': case 'pbeWithSHA1AndDES-CBC': case 'pbeWithSHA1AndRC2-CBC': return 'pbkdf1'; } return 'pkcs12'; } /** * Returns a SymmetricKey object baesd on a PBES2 $algo * * @return SymmetricKey * @param string $algo */ private static function getPBES2EncryptionObject($algo) { switch ($algo) { case 'desCBC': $cipher = new DES('cbc'); break; case 'des-EDE3-CBC': $cipher = new TripleDES('cbc'); break; case 'rc2CBC': $cipher = new RC2('cbc'); // in theory this can be changed $cipher->setKeyLength(128); break; case 'rc5-CBC-PAD': throw new UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys'); case 'aes128-CBC-PAD': case 'aes192-CBC-PAD': case 'aes256-CBC-PAD': $cipher = new AES('cbc'); $cipher->setKeyLength(substr($algo, 3, 3)); break; default: throw new UnsupportedAlgorithmException("$algo is not supported"); } return $cipher; } /** * Initialize static variables * */ private static function initialize_static_variables() { if (!isset(static::$childOIDsLoaded)) { throw new InsufficientSetupException('This class should not be called directly'); } if (!static::$childOIDsLoaded) { ASN1::loadOIDs(is_array(static::OID_NAME) ? array_combine(static::OID_NAME, static::OID_VALUE) : [static::OID_NAME => static::OID_VALUE]); static::$childOIDsLoaded = true; } if (!self::$oidsLoaded) { // from https://tools.ietf.org/html/rfc2898 ASN1::loadOIDs([ // PBES1 encryption schemes 'pbeWithMD2AndDES-CBC' => '1.2.840.113549.1.5.1', 'pbeWithMD2AndRC2-CBC' => '1.2.840.113549.1.5.4', 'pbeWithMD5AndDES-CBC' => '1.2.840.113549.1.5.3', 'pbeWithMD5AndRC2-CBC' => '1.2.840.113549.1.5.6', 'pbeWithSHA1AndDES-CBC' => '1.2.840.113549.1.5.10', 'pbeWithSHA1AndRC2-CBC' => '1.2.840.113549.1.5.11', // from PKCS#12: // https://tools.ietf.org/html/rfc7292 'pbeWithSHAAnd128BitRC4' => '1.2.840.113549.1.12.1.1', 'pbeWithSHAAnd40BitRC4' => '1.2.840.113549.1.12.1.2', 'pbeWithSHAAnd3-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.3', 'pbeWithSHAAnd2-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.4', 'pbeWithSHAAnd128BitRC2-CBC' => '1.2.840.113549.1.12.1.5', 'pbeWithSHAAnd40BitRC2-CBC' => '1.2.840.113549.1.12.1.6', 'id-PBKDF2' => '1.2.840.113549.1.5.12', 'id-PBES2' => '1.2.840.113549.1.5.13', 'id-PBMAC1' => '1.2.840.113549.1.5.14', // from PKCS#5 v2.1: // http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf 'id-hmacWithSHA1' => '1.2.840.113549.2.7', 'id-hmacWithSHA224' => '1.2.840.113549.2.8', 'id-hmacWithSHA256' => '1.2.840.113549.2.9', 'id-hmacWithSHA384' => '1.2.840.113549.2.10', 'id-hmacWithSHA512' => '1.2.840.113549.2.11', 'id-hmacWithSHA512-224' => '1.2.840.113549.2.12', 'id-hmacWithSHA512-256' => '1.2.840.113549.2.13', 'desCBC' => '1.3.14.3.2.7', 'des-EDE3-CBC' => '1.2.840.113549.3.7', 'rc2CBC' => '1.2.840.113549.3.2', 'rc5-CBC-PAD' => '1.2.840.113549.3.9', 'aes128-CBC-PAD' => '2.16.840.1.101.3.4.1.2', 'aes192-CBC-PAD' => '2.16.840.1.101.3.4.1.22', 'aes256-CBC-PAD' => '2.16.840.1.101.3.4.1.42' ]); self::$oidsLoaded = true; } } /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ protected static function load($key, $password = '') { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $isPublic = strpos($key, 'PUBLIC') !== false; $isPrivate = strpos($key, 'PRIVATE') !== false; $decoded = self::preParse($key); $meta = []; $decrypted = ASN1::asn1map($decoded[0], Maps\EncryptedPrivateKeyInfo::MAP); if (strlen($password) && is_array($decrypted)) { $algorithm = $decrypted['encryptionAlgorithm']['algorithm']; switch ($algorithm) { // PBES1 case 'pbeWithMD2AndDES-CBC': case 'pbeWithMD2AndRC2-CBC': case 'pbeWithMD5AndDES-CBC': case 'pbeWithMD5AndRC2-CBC': case 'pbeWithSHA1AndDES-CBC': case 'pbeWithSHA1AndRC2-CBC': case 'pbeWithSHAAnd3-KeyTripleDES-CBC': case 'pbeWithSHAAnd2-KeyTripleDES-CBC': case 'pbeWithSHAAnd128BitRC2-CBC': case 'pbeWithSHAAnd40BitRC2-CBC': case 'pbeWithSHAAnd128BitRC4': case 'pbeWithSHAAnd40BitRC4': $cipher = self::getPBES1EncryptionObject($algorithm); $hash = self::getPBES1Hash($algorithm); $kdf = self::getPBES1KDF($algorithm); $meta['meta']['algorithm'] = $algorithm; $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); if (!$temp) { throw new \RuntimeException('Unable to decode BER'); } extract(ASN1::asn1map($temp[0], Maps\PBEParameter::MAP)); $iterationCount = (int) $iterationCount->toString(); $cipher->setPassword($password, $kdf, $hash, $salt, $iterationCount); $key = $cipher->decrypt($decrypted['encryptedData']); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER 2'); } break; case 'id-PBES2': $meta['meta']['algorithm'] = $algorithm; $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); if (!$temp) { throw new \RuntimeException('Unable to decode BER'); } $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP); extract($temp); $cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']); $meta['meta']['cipher'] = $encryptionScheme['algorithm']; $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); if (!$temp) { throw new \RuntimeException('Unable to decode BER'); } $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP); extract($temp); if (!$cipher instanceof RC2) { $cipher->setIV($encryptionScheme['parameters']['octetString']); } else { $temp = ASN1::decodeBER($encryptionScheme['parameters']); if (!$temp) { throw new \RuntimeException('Unable to decode BER'); } extract(ASN1::asn1map($temp[0], Maps\RC2CBCParameter::MAP)); $effectiveKeyLength = (int) $rc2ParametersVersion->toString(); switch ($effectiveKeyLength) { case 160: $effectiveKeyLength = 40; break; case 120: $effectiveKeyLength = 64; break; case 58: $effectiveKeyLength = 128; break; //default: // should be >= 256 } $cipher->setIV($iv); $cipher->setKeyLength($effectiveKeyLength); } $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm']; switch ($keyDerivationFunc['algorithm']) { case 'id-PBKDF2': $temp = ASN1::decodeBER($keyDerivationFunc['parameters']); if (!$temp) { throw new \RuntimeException('Unable to decode BER'); } $prf = ['algorithm' => 'id-hmacWithSHA1']; $params = ASN1::asn1map($temp[0], Maps\PBKDF2params::MAP); extract($params); $meta['meta']['prf'] = $prf['algorithm']; $hash = str_replace('-', '/', substr($prf['algorithm'], 11)); $params = [ $password, 'pbkdf2', $hash, $salt, (int) $iterationCount->toString() ]; if (isset($keyLength)) { $params[] = (int) $keyLength->toString(); } $cipher->setPassword(...$params); $key = $cipher->decrypt($decrypted['encryptedData']); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER 3'); } break; default: throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys'); } break; case 'id-PBMAC1': //$temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); //$value = ASN1::asn1map($temp[0], Maps\PBMAC1params::MAP); // since i can't find any implementation that does PBMAC1 it is unsupported throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.'); // at this point we'll assume that the key conforms to PublicKeyInfo } } $private = ASN1::asn1map($decoded[0], Maps\OneAsymmetricKey::MAP); if (is_array($private)) { if ($isPublic) { throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); } if (isset($private['privateKeyAlgorithm']['parameters']) && !$private['privateKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][1]['content'][1])) { $temp = $decoded[0]['content'][1]['content'][1]; $private['privateKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length'])); } if (is_array(static::OID_NAME)) { if (!in_array($private['privateKeyAlgorithm']['algorithm'], static::OID_NAME)) { throw new UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm'] . ' is not a supported key type'); } } else { if ($private['privateKeyAlgorithm']['algorithm'] != static::OID_NAME) { throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $private['privateKeyAlgorithm']['algorithm'] . ' key'); } } if (isset($private['publicKey'])) { if ($private['publicKey'][0] != "\0") { throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($private['publicKey'][0])); } $private['publicKey'] = substr($private['publicKey'], 1); } return $private + $meta; } // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference // is that the former has an octet string and the later has a bit string. the first byte of a bit // string represents the number of bits in the last byte that are to be ignored but, currently, // bit strings wanting a non-zero amount of bits trimmed are not supported $public = ASN1::asn1map($decoded[0], Maps\PublicKeyInfo::MAP); if (is_array($public)) { if ($isPrivate) { throw new \UnexpectedValueException('Human readable string claims private key but DER encoded string claims public key'); } if ($public['publicKey'][0] != "\0") { throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($public['publicKey'][0])); } if (is_array(static::OID_NAME)) { if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) { throw new UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm'] . ' is not a supported key type'); } } else { if ($public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) { throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $public['publicKeyAlgorithm']['algorithm'] . ' key'); } } if (isset($public['publicKeyAlgorithm']['parameters']) && !$public['publicKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][0]['content'][1])) { $temp = $decoded[0]['content'][0]['content'][1]; $public['publicKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length'])); } $public['publicKey'] = substr($public['publicKey'], 1); return $public; } throw new \RuntimeException('Unable to parse using either OneAsymmetricKey or PublicKeyInfo ASN1 maps'); } /** * Wrap a private key appropriately * * @param string $key * @param string $attr * @param mixed $params * @param string $password * @param string $oid optional * @param string $publicKey optional * @param array $options optional * @return string */ protected static function wrapPrivateKey($key, $attr, $params, $password, $oid = null, $publicKey = '', array $options = []) { self::initialize_static_variables(); $key = [ 'version' => 'v1', 'privateKeyAlgorithm' => [ 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid ], 'privateKey' => $key ]; if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { $key['privateKeyAlgorithm']['parameters'] = $params; } if (!empty($attr)) { $key['attributes'] = $attr; } if (!empty($publicKey)) { $key['version'] = 'v2'; $key['publicKey'] = $publicKey; } $key = ASN1::encodeDER($key, Maps\OneAsymmetricKey::MAP); if (!empty($password) && is_string($password)) { $salt = Random::string(8); $iterationCount = isset($options['iterationCount']) ? $options['iterationCount'] : self::$defaultIterationCount; $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; $encryptionScheme = isset($options['encryptionScheme']) ? $options['encryptionScheme'] : self::$defaultEncryptionScheme; $prf = isset($options['PRF']) ? $options['PRF'] : self::$defaultPRF; if ($encryptionAlgorithm == 'id-PBES2') { $crypto = self::getPBES2EncryptionObject($encryptionScheme); $hash = str_replace('-', '/', substr($prf, 11)); $kdf = 'pbkdf2'; $iv = Random::string($crypto->getBlockLength() >> 3); $PBKDF2params = [ 'salt' => $salt, 'iterationCount' => $iterationCount, 'prf' => ['algorithm' => $prf, 'parameters' => null] ]; $PBKDF2params = ASN1::encodeDER($PBKDF2params, Maps\PBKDF2params::MAP); if (!$crypto instanceof RC2) { $params = ['octetString' => $iv]; } else { $params = [ 'rc2ParametersVersion' => 58, 'iv' => $iv ]; $params = ASN1::encodeDER($params, Maps\RC2CBCParameter::MAP); $params = new ASN1\Element($params); } $params = [ 'keyDerivationFunc' => [ 'algorithm' => 'id-PBKDF2', 'parameters' => new ASN1\Element($PBKDF2params) ], 'encryptionScheme' => [ 'algorithm' => $encryptionScheme, 'parameters' => $params ] ]; $params = ASN1::encodeDER($params, Maps\PBES2params::MAP); $crypto->setIV($iv); } else { $crypto = self::getPBES1EncryptionObject($encryptionAlgorithm); $hash = self::getPBES1Hash($encryptionAlgorithm); $kdf = self::getPBES1KDF($encryptionAlgorithm); $params = [ 'salt' => $salt, 'iterationCount' => $iterationCount ]; $params = ASN1::encodeDER($params, Maps\PBEParameter::MAP); } $crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount); $key = $crypto->encrypt($key); $key = [ 'encryptionAlgorithm' => [ 'algorithm' => $encryptionAlgorithm, 'parameters' => new ASN1\Element($params) ], 'encryptedData' => $key ]; $key = ASN1::encodeDER($key, Maps\EncryptedPrivateKeyInfo::MAP); return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END ENCRYPTED PRIVATE KEY-----"; } return "-----BEGIN PRIVATE KEY-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END PRIVATE KEY-----"; } /** * Wrap a public key appropriately * * @param string $key * @param mixed $params * @param string $oid * @return string */ protected static function wrapPublicKey($key, $params, $oid = null) { self::initialize_static_variables(); $key = [ 'publicKeyAlgorithm' => [ 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid ], 'publicKey' => "\0" . $key ]; if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { $key['publicKeyAlgorithm']['parameters'] = $params; } $key = ASN1::encodeDER($key, Maps\PublicKeyInfo::MAP); return "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END PUBLIC KEY-----"; } /** * Perform some preliminary parsing of the key * * @param string $key * @return array */ private static function preParse(&$key) { self::initialize_static_variables(); if (self::$format != self::MODE_DER) { $decoded = ASN1::extractBER($key); if ($decoded !== false) { $key = $decoded; } elseif (self::$format == self::MODE_PEM) { throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); } } $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } return $decoded; } /** * Returns the encryption parameters used by the key * * @param string $key * @return array */ public static function extractEncryptionAlgorithm($key) { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } $decoded = self::preParse($key); $r = ASN1::asn1map($decoded[0], ASN1\Maps\EncryptedPrivateKeyInfo::MAP); if (!is_array($r)) { throw new \RuntimeException('Unable to parse using EncryptedPrivateKeyInfo map'); } if ($r['encryptionAlgorithm']['algorithm'] == 'id-PBES2') { $decoded = ASN1::decodeBER($r['encryptionAlgorithm']['parameters']->element); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $r['encryptionAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], ASN1\Maps\PBES2params::MAP); $kdf = &$r['encryptionAlgorithm']['parameters']['keyDerivationFunc']; switch ($kdf['algorithm']) { case 'id-PBKDF2': $decoded = ASN1::decodeBER($kdf['parameters']->element); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $kdf['parameters'] = ASN1::asn1map($decoded[0], Maps\PBKDF2params::MAP); } } return $r['encryptionAlgorithm']; } } PK2A#]I�ȥHvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.phpnu�[���<?php /** * PKCS1 Formatted Key Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\AES; use phpseclib3\Crypt\DES; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\TripleDES; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\File\ASN1; /** * PKCS1 Formatted Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS1 extends PKCS { /** * Default encryption algorithm * * @var string */ private static $defaultEncryptionAlgorithm = 'AES-128-CBC'; /** * Sets the default encryption algorithm * * @param string $algo */ public static function setEncryptionAlgorithm($algo) { self::$defaultEncryptionAlgorithm = $algo; } /** * Returns the mode constant corresponding to the mode string * * @param string $mode * @return int * @throws \UnexpectedValueException if the block cipher mode is unsupported */ private static function getEncryptionMode($mode) { switch ($mode) { case 'CBC': case 'ECB': case 'CFB': case 'OFB': case 'CTR': return $mode; } throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); } /** * Returns a cipher object corresponding to a string * * @param string $algo * @return string * @throws \UnexpectedValueException if the encryption algorithm is unsupported */ private static function getEncryptionObject($algo) { $modes = '(CBC|ECB|CFB|OFB|CTR)'; switch (true) { case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches): $cipher = new AES(self::getEncryptionMode($matches[2])); $cipher->setKeyLength($matches[1]); return $cipher; case preg_match("#^DES-EDE3-$modes$#", $algo, $matches): return new TripleDES(self::getEncryptionMode($matches[1])); case preg_match("#^DES-$modes$#", $algo, $matches): return new DES(self::getEncryptionMode($matches[1])); default: throw new UnsupportedAlgorithmException($algo . ' is not a supported algorithm'); } } /** * Generate a symmetric key for PKCS#1 keys * * @param string $password * @param string $iv * @param int $length * @return string */ private static function generateSymmetricKey($password, $iv, $length) { $symkey = ''; $iv = substr($iv, 0, 8); while (strlen($symkey) < $length) { $symkey .= md5($symkey . $password . $iv, true); } return substr($symkey, 0, $length); } /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ protected static function load($key, $password) { if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: http://tools.ietf.org/html/rfc1421#section-4.6.1.1 http://tools.ietf.org/html/rfc1421#section-4.6.1.3 DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's own implementation. ie. the implementation *is* the standard and any bugs that may exist in that implementation are part of the standard, as well. * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { $iv = Strings::hex2bin(trim($matches[2])); // remove the Proc-Type / DEK-Info sections as they're no longer needed $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); $ciphertext = ASN1::extractBER($key); if ($ciphertext === false) { $ciphertext = $key; } $crypto = self::getEncryptionObject($matches[1]); $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); $crypto->setIV($iv); $key = $crypto->decrypt($ciphertext); } else { if (self::$format != self::MODE_DER) { $decoded = ASN1::extractBER($key); if ($decoded !== false) { $key = $decoded; } elseif (self::$format == self::MODE_PEM) { throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); } } } return $key; } /** * Wrap a private key appropriately * * @param string $key * @param string $type * @param string $password * @param array $options optional * @return string */ protected static function wrapPrivateKey($key, $type, $password, array $options = []) { if (empty($password) || !is_string($password)) { return "-----BEGIN $type PRIVATE KEY-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END $type PRIVATE KEY-----"; } $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; $cipher = self::getEncryptionObject($encryptionAlgorithm); $iv = Random::string($cipher->getBlockLength() >> 3); $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); $cipher->setIV($iv); $iv = strtoupper(Strings::bin2hex($iv)); return "-----BEGIN $type PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: " . $encryptionAlgorithm . ",$iv\r\n" . "\r\n" . chunk_split(Strings::base64_encode($cipher->encrypt($key)), 64) . "-----END $type PRIVATE KEY-----"; } /** * Wrap a public key appropriately * * @param string $key * @param string $type * @return string */ protected static function wrapPublicKey($key, $type) { return "-----BEGIN $type PUBLIC KEY-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END $type PUBLIC KEY-----"; } } PK2A#]1Q7'Kvendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.phpnu�[���<?php /** * Raw Signature Handler * * PHP version 5 * * Handles signatures as arrays * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\Common\Formats\Signature; use phpseclib3\Math\BigInteger; /** * Raw Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Raw { /** * Loads a signature * * @param array $sig * @return array|bool */ public static function load($sig) { switch (true) { case !is_array($sig): case !isset($sig['r']) || !isset($sig['s']): case !$sig['r'] instanceof BigInteger: case !$sig['s'] instanceof BigInteger: return false; } return [ 'r' => $sig['r'], 's' => $sig['s'] ]; } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @return string */ public static function save(BigInteger $r, BigInteger $s) { return compact('r', 's'); } } PK2A#]��ic�$�$<vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.phpnu�[���<?php /** * EC Private Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\EC; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Curve25519; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; use phpseclib3\Crypt\Hash; use phpseclib3\Exception\UnsupportedOperationException; use phpseclib3\Math\BigInteger; /** * EC Private Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PrivateKey extends EC implements Common\PrivateKey { use Common\Traits\PasswordProtected; /** * Private Key dA * * sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of * a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by * a certain amount whereas a BigInteger isn't. * * @var object */ protected $dA; /** * @var string */ protected $secret; /** * Multiplies an encoded point by the private key * * Used by ECDH * * @param string $coordinates * @return string */ public function multiply($coordinates) { if ($this->curve instanceof MontgomeryCurve) { if ($this->curve instanceof Curve25519 && self::$engines['libsodium']) { return sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates); } $point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))]; $point = $this->curve->multiplyPoint($point, $this->dA); return strrev($point[0]->toBytes(true)); } if (!$this->curve instanceof TwistedEdwardsCurve) { $coordinates = "\0$coordinates"; } $point = PKCS1::extractPoint($coordinates, $this->curve); $point = $this->curve->multiplyPoint($point, $this->dA); if ($this->curve instanceof TwistedEdwardsCurve) { return $this->curve->encodePoint($point); } if (empty($point)) { throw new \RuntimeException('The infinity point is invalid'); } return "\4" . $point[0]->toBytes(true) . $point[1]->toBytes(true); } /** * Create a signature * * @see self::verify() * @param string $message * @return mixed */ public function sign($message) { if ($this->curve instanceof MontgomeryCurve) { throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); } $dA = $this->dA; $order = $this->curve->getOrder(); $shortFormat = $this->shortFormat; $format = $this->sigFormat; if ($format === false) { return false; } if ($this->curve instanceof TwistedEdwardsCurve) { if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { $result = sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium')); return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result) : $result; } // contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not. // quoting https://tools.ietf.org/html/rfc8032#section-8.5 , // "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used" $A = $this->curve->encodePoint($this->QA); $curve = $this->curve; $hash = new Hash($curve::HASH); $secret = substr($hash->hash($this->secret), $curve::SIZE); if ($curve instanceof Ed25519) { $dom = !isset($this->context) ? '' : 'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context; } else { $context = isset($this->context) ? $this->context : ''; $dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context; } // SHA-512(dom2(F, C) || prefix || PH(M)) $r = $hash->hash($dom . $secret . $message); $r = strrev($r); $r = new BigInteger($r, 256); list(, $r) = $r->divide($order); $R = $curve->multiplyPoint($curve->getBasePoint(), $r); $R = $curve->encodePoint($R); $k = $hash->hash($dom . $R . $A . $message); $k = strrev($k); $k = new BigInteger($k, 256); list(, $k) = $k->divide($order); $S = $k->multiply($dA)->add($r); list(, $S) = $S->divide($order); $S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0"); return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $R . $S) : $R . $S; } if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { $signature = ''; // altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long // supported signing / verification // we use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve; // doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even // has curve-specific optimizations $result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash()); if ($result) { if ($shortFormat == 'ASN1') { return $signature; } extract(ASN1Signature::load($signature)); return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s); } } $e = $this->hash->hash($message); $e = new BigInteger($e, 256); $Ln = $this->hash->getLength() - $order->getLength(); $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e; while (true) { $k = BigInteger::randomRange(self::$one, $order->subtract(self::$one)); list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); $x = $x->toBigInteger(); list(, $r) = $x->divide($order); if ($r->equals(self::$zero)) { continue; } $kinv = $k->modInverse($order); $temp = $z->add($dA->multiply($r)); $temp = $kinv->multiply($temp); list(, $s) = $temp->divide($order); if (!$s->equals(self::$zero)) { break; } } // the following is an RFC6979 compliant implementation of deterministic ECDSA // it's unused because it's mainly intended for use when a good CSPRNG isn't // available. if phpseclib's CSPRNG isn't good then even key generation is // suspect /* // if this were actually being used it'd probably be better if this lived in load() and createKey() $this->q = $this->curve->getOrder(); $dA = $this->dA->toBigInteger(); $this->x = $dA; $h1 = $this->hash->hash($message); $k = $this->computek($h1); list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); $x = $x->toBigInteger(); list(, $r) = $x->divide($this->q); $kinv = $k->modInverse($this->q); $h1 = $this->bits2int($h1); $temp = $h1->add($dA->multiply($r)); $temp = $kinv->multiply($temp); list(, $s) = $temp->divide($this->q); */ return $shortFormat == 'SSH2' ? $format::save($r, $s, $this->getCurve()) : $format::save($r, $s); } /** * Returns the private key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options); } /** * Returns the public key * * @see self::getPrivateKey() * @return mixed */ public function getPublicKey() { $format = 'PKCS8'; if ($this->curve instanceof MontgomeryCurve) { $format = 'MontgomeryPublic'; } $type = self::validatePlugin('Keys', $format, 'savePublicKey'); $key = $type::savePublicKey($this->curve, $this->QA); $key = EC::loadFormat($format, $key); if ($this->curve instanceof MontgomeryCurve) { return $key; } $key = $key ->withHash($this->hash->getHash()) ->withSignatureFormat($this->shortFormat); if ($this->curve instanceof TwistedEdwardsCurve) { $key = $key->withContext($this->context); } return $key; } } PK2A#]C��ttKvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.phpnu�[���<?php /** * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2 * * http://www.secg.org/SEC2-Ver-1.0.pdf provides for curves with custom parameters. * ie. the coefficients can be arbitrary set through specially formatted keys, etc. * As such, Prime.php is built very generically and it's not able to take full * advantage of curves with 0 coefficients to produce simplified point doubling, * point addition. Twisted Edwards curves, in contrast, do not have a way, currently, * to customize them. As such, we can omit the super generic stuff from this class * and let the named curves (Ed25519 and Ed448) define their own custom tailored * point addition and point doubling methods. * * More info: * * https://en.wikipedia.org/wiki/Twisted_Edwards_curve * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Math\BigInteger; use phpseclib3\Math\PrimeField; use phpseclib3\Math\PrimeField\Integer as PrimeInteger; /** * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2 * * @author Jim Wigginton <terrafrost@php.net> */ class TwistedEdwards extends Base { /** * The modulo * * @var BigInteger */ protected $modulo; /** * Cofficient for x^2 * * @var object */ protected $a; /** * Cofficient for x^2*y^2 * * @var object */ protected $d; /** * Base Point * * @var object[] */ protected $p; /** * The number zero over the specified finite field * * @var object */ protected $zero; /** * The number one over the specified finite field * * @var object */ protected $one; /** * The number two over the specified finite field * * @var object */ protected $two; /** * Sets the modulo */ public function setModulo(BigInteger $modulo) { $this->modulo = $modulo; $this->factory = new PrimeField($modulo); $this->zero = $this->factory->newInteger(new BigInteger(0)); $this->one = $this->factory->newInteger(new BigInteger(1)); $this->two = $this->factory->newInteger(new BigInteger(2)); } /** * Set coefficients a and b */ public function setCoefficients(BigInteger $a, BigInteger $d) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->a = $this->factory->newInteger($a); $this->d = $this->factory->newInteger($d); } /** * Set x and y coordinates for the base point */ public function setBasePoint($x, $y) { switch (true) { case !$x instanceof BigInteger && !$x instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); case !$y instanceof BigInteger && !$y instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); } if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->p = [ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof BigInteger ? $this->factory->newInteger($y) : $y ]; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getA() { return $this->a; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getD() { return $this->d; } /** * Retrieve the base point as an array * * @return array */ public function getBasePoint() { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } /* if (!isset($this->p)) { throw new \RuntimeException('setBasePoint needs to be called before this method'); } */ return $this->p; } /** * Returns the affine point * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToAffine(array $p) { if (!isset($p[2])) { return $p; } list($x, $y, $z) = $p; $z = $this->one->divide($z); return [ $x->multiply($z), $y->multiply($z) ]; } /** * Returns the modulo * * @return \phpseclib3\Math\BigInteger */ public function getModulo() { return $this->modulo; } /** * Tests whether or not the x / y values satisfy the equation * * @return boolean */ public function verifyPoint(array $p) { list($x, $y) = $p; $x2 = $x->multiply($x); $y2 = $y->multiply($y); $lhs = $this->a->multiply($x2)->add($y2); $rhs = $this->d->multiply($x2)->multiply($y2)->add($this->one); return $lhs->equals($rhs); } } PK2A#]ڟ���Gvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.phpnu�[���<?php /** * Curves over y^2 = x^3 + a*x + x * * Technically, a Montgomery curve has a coefficient for y^2 but for Curve25519 and Curve448 that * coefficient is 1. * * Curve25519 and Curve448 do not make use of the y coordinate, which makes it unsuitable for use * with ECDSA / EdDSA. A few other differences between Curve25519 and Ed25519 are discussed at * https://crypto.stackexchange.com/a/43058/4520 * * More info: * * https://en.wikipedia.org/wiki/Montgomery_curve * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Crypt\EC\Curves\Curve25519; use phpseclib3\Math\BigInteger; use phpseclib3\Math\PrimeField; use phpseclib3\Math\PrimeField\Integer as PrimeInteger; /** * Curves over y^2 = x^3 + a*x + x * * @author Jim Wigginton <terrafrost@php.net> */ class Montgomery extends Base { /** * Prime Field Integer factory * * @var \phpseclib3\Math\PrimeField */ protected $factory; /** * Cofficient for x * * @var object */ protected $a; /** * Constant used for point doubling * * @var object */ protected $a24; /** * The Number Zero * * @var object */ protected $zero; /** * The Number One * * @var object */ protected $one; /** * Base Point * * @var object */ protected $p; /** * The modulo * * @var BigInteger */ protected $modulo; /** * The Order * * @var BigInteger */ protected $order; /** * Sets the modulo */ public function setModulo(BigInteger $modulo) { $this->modulo = $modulo; $this->factory = new PrimeField($modulo); $this->zero = $this->factory->newInteger(new BigInteger()); $this->one = $this->factory->newInteger(new BigInteger(1)); } /** * Set coefficients a */ public function setCoefficients(BigInteger $a) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->a = $this->factory->newInteger($a); $two = $this->factory->newInteger(new BigInteger(2)); $four = $this->factory->newInteger(new BigInteger(4)); $this->a24 = $this->a->subtract($two)->divide($four); } /** * Set x and y coordinates for the base point * * @param BigInteger|PrimeInteger $x * @param BigInteger|PrimeInteger $y * @return PrimeInteger[] */ public function setBasePoint($x, $y) { switch (true) { case !$x instanceof BigInteger && !$x instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); case !$y instanceof BigInteger && !$y instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); } if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->p = [ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof BigInteger ? $this->factory->newInteger($y) : $y ]; } /** * Retrieve the base point as an array * * @return array */ public function getBasePoint() { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } /* if (!isset($this->p)) { throw new \RuntimeException('setBasePoint needs to be called before this method'); } */ return $this->p; } /** * Doubles and adds a point on a curve * * See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3 * * @return FiniteField[][] */ private function doubleAndAddPoint(array $p, array $q, PrimeInteger $x1) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p) || !count($q)) { return []; } if (!isset($p[1])) { throw new \RuntimeException('Affine coordinates need to be manually converted to XZ coordinates'); } list($x2, $z2) = $p; list($x3, $z3) = $q; $a = $x2->add($z2); $aa = $a->multiply($a); $b = $x2->subtract($z2); $bb = $b->multiply($b); $e = $aa->subtract($bb); $c = $x3->add($z3); $d = $x3->subtract($z3); $da = $d->multiply($a); $cb = $c->multiply($b); $temp = $da->add($cb); $x5 = $temp->multiply($temp); $temp = $da->subtract($cb); $z5 = $x1->multiply($temp->multiply($temp)); $x4 = $aa->multiply($bb); $temp = static::class == Curve25519::class ? $bb : $aa; $z4 = $e->multiply($temp->add($this->a24->multiply($e))); return [ [$x4, $z4], [$x5, $z5] ]; } /** * Multiply a point on the curve by a scalar * * Uses the montgomery ladder technique as described here: * * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772 * * @return array */ public function multiplyPoint(array $p, BigInteger $d) { $p1 = [$this->one, $this->zero]; $alreadyInternal = isset($x[1]); $p2 = $this->convertToInternal($p); $x = $p[0]; $b = $d->toBits(); $b = str_pad($b, 256, '0', STR_PAD_LEFT); for ($i = 0; $i < strlen($b); $i++) { $b_i = (int) $b[$i]; if ($b_i) { list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x); } else { list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x); } } return $alreadyInternal ? $p1 : $this->convertToAffine($p1); } /** * Converts an affine point to an XZ coordinate * * From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html * * XZ coordinates represent x y as X Z satsfying the following equations: * * x=X/Z * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToInternal(array $p) { if (empty($p)) { return [clone $this->zero, clone $this->one]; } if (isset($p[1])) { return $p; } $p[1] = clone $this->one; return $p; } /** * Returns the affine point * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToAffine(array $p) { if (!isset($p[1])) { return $p; } list($x, $z) = $p; return [$x->divide($z)]; } } PK2A#]�);�S�SBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.phpnu�[���<?php /** * Curves over y^2 = x^3 + a*x + b * * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf * The curve is a weierstrass curve with a[1], a[3] and a[2] set to 0. * * Uses Jacobian Coordinates for speed if able: * * https://en.wikipedia.org/wiki/Jacobian_curve * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BigInteger; use phpseclib3\Math\Common\FiniteField\Integer; use phpseclib3\Math\PrimeField; use phpseclib3\Math\PrimeField\Integer as PrimeInteger; /** * Curves over y^2 = x^3 + a*x + b * * @author Jim Wigginton <terrafrost@php.net> */ class Prime extends Base { /** * Prime Field Integer factory * * @var \phpseclib3\Math\PrimeFields */ protected $factory; /** * Cofficient for x^1 * * @var object */ protected $a; /** * Cofficient for x^0 * * @var object */ protected $b; /** * Base Point * * @var object */ protected $p; /** * The number one over the specified finite field * * @var object */ protected $one; /** * The number two over the specified finite field * * @var object */ protected $two; /** * The number three over the specified finite field * * @var object */ protected $three; /** * The number four over the specified finite field * * @var object */ protected $four; /** * The number eight over the specified finite field * * @var object */ protected $eight; /** * The modulo * * @var BigInteger */ protected $modulo; /** * The Order * * @var BigInteger */ protected $order; /** * Sets the modulo */ public function setModulo(BigInteger $modulo) { $this->modulo = $modulo; $this->factory = new PrimeField($modulo); $this->two = $this->factory->newInteger(new BigInteger(2)); $this->three = $this->factory->newInteger(new BigInteger(3)); // used by jacobian coordinates $this->one = $this->factory->newInteger(new BigInteger(1)); $this->four = $this->factory->newInteger(new BigInteger(4)); $this->eight = $this->factory->newInteger(new BigInteger(8)); } /** * Set coefficients a and b */ public function setCoefficients(BigInteger $a, BigInteger $b) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->a = $this->factory->newInteger($a); $this->b = $this->factory->newInteger($b); } /** * Set x and y coordinates for the base point * * @param BigInteger|PrimeInteger $x * @param BigInteger|PrimeInteger $y * @return PrimeInteger[] */ public function setBasePoint($x, $y) { switch (true) { case !$x instanceof BigInteger && !$x instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); case !$y instanceof BigInteger && !$y instanceof PrimeInteger: throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer'); } if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->p = [ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof BigInteger ? $this->factory->newInteger($y) : $y ]; } /** * Retrieve the base point as an array * * @return array */ public function getBasePoint() { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } /* if (!isset($this->p)) { throw new \RuntimeException('setBasePoint needs to be called before this method'); } */ return $this->p; } /** * Adds two "fresh" jacobian form on the curve * * @return FiniteField[] */ protected function jacobianAddPointMixedXY(array $p, array $q) { list($u1, $s1) = $p; list($u2, $s2) = $q; if ($u1->equals($u2)) { if (!$s1->equals($s2)) { return []; } else { return $this->doublePoint($p); } } $h = $u2->subtract($u1); $r = $s2->subtract($s1); $h2 = $h->multiply($h); $h3 = $h2->multiply($h); $v = $u1->multiply($h2); $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); $y3 = $r->multiply( $v->subtract($x3) )->subtract( $s1->multiply($h3) ); return [$x3, $y3, $h]; } /** * Adds one "fresh" jacobian form on the curve * * The second parameter should be the "fresh" one * * @return FiniteField[] */ protected function jacobianAddPointMixedX(array $p, array $q) { list($u1, $s1, $z1) = $p; list($x2, $y2) = $q; $z12 = $z1->multiply($z1); $u2 = $x2->multiply($z12); $s2 = $y2->multiply($z12->multiply($z1)); if ($u1->equals($u2)) { if (!$s1->equals($s2)) { return []; } else { return $this->doublePoint($p); } } $h = $u2->subtract($u1); $r = $s2->subtract($s1); $h2 = $h->multiply($h); $h3 = $h2->multiply($h); $v = $u1->multiply($h2); $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); $y3 = $r->multiply( $v->subtract($x3) )->subtract( $s1->multiply($h3) ); $z3 = $h->multiply($z1); return [$x3, $y3, $z3]; } /** * Adds two jacobian coordinates on the curve * * @return FiniteField[] */ protected function jacobianAddPoint(array $p, array $q) { list($x1, $y1, $z1) = $p; list($x2, $y2, $z2) = $q; $z12 = $z1->multiply($z1); $z22 = $z2->multiply($z2); $u1 = $x1->multiply($z22); $u2 = $x2->multiply($z12); $s1 = $y1->multiply($z22->multiply($z2)); $s2 = $y2->multiply($z12->multiply($z1)); if ($u1->equals($u2)) { if (!$s1->equals($s2)) { return []; } else { return $this->doublePoint($p); } } $h = $u2->subtract($u1); $r = $s2->subtract($s1); $h2 = $h->multiply($h); $h3 = $h2->multiply($h); $v = $u1->multiply($h2); $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); $y3 = $r->multiply( $v->subtract($x3) )->subtract( $s1->multiply($h3) ); $z3 = $h->multiply($z1)->multiply($z2); return [$x3, $y3, $z3]; } /** * Adds two points on the curve * * @return FiniteField[] */ public function addPoint(array $p, array $q) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p) || !count($q)) { if (count($q)) { return $q; } if (count($p)) { return $p; } return []; } // use jacobian coordinates if (isset($p[2]) && isset($q[2])) { if (isset($p['fresh']) && isset($q['fresh'])) { return $this->jacobianAddPointMixedXY($p, $q); } if (isset($p['fresh'])) { return $this->jacobianAddPointMixedX($q, $p); } if (isset($q['fresh'])) { return $this->jacobianAddPointMixedX($p, $q); } return $this->jacobianAddPoint($p, $q); } if (isset($p[2]) || isset($q[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to Jacobi coordinates or vice versa'); } if ($p[0]->equals($q[0])) { if (!$p[1]->equals($q[1])) { return []; } else { // eg. doublePoint list($numerator, $denominator) = $this->doublePointHelper($p); } } else { $numerator = $q[1]->subtract($p[1]); $denominator = $q[0]->subtract($p[0]); } $slope = $numerator->divide($denominator); $x = $slope->multiply($slope)->subtract($p[0])->subtract($q[0]); $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]); return [$x, $y]; } /** * Returns the numerator and denominator of the slope * * @return FiniteField[] */ protected function doublePointHelper(array $p) { $numerator = $this->three->multiply($p[0])->multiply($p[0])->add($this->a); $denominator = $this->two->multiply($p[1]); return [$numerator, $denominator]; } /** * Doubles a jacobian coordinate on the curve * * @return FiniteField[] */ protected function jacobianDoublePoint(array $p) { list($x, $y, $z) = $p; $x2 = $x->multiply($x); $y2 = $y->multiply($y); $z2 = $z->multiply($z); $s = $this->four->multiply($x)->multiply($y2); $m1 = $this->three->multiply($x2); $m2 = $this->a->multiply($z2->multiply($z2)); $m = $m1->add($m2); $x1 = $m->multiply($m)->subtract($this->two->multiply($s)); $y1 = $m->multiply($s->subtract($x1))->subtract( $this->eight->multiply($y2->multiply($y2)) ); $z1 = $this->two->multiply($y)->multiply($z); return [$x1, $y1, $z1]; } /** * Doubles a "fresh" jacobian coordinate on the curve * * @return FiniteField[] */ protected function jacobianDoublePointMixed(array $p) { list($x, $y) = $p; $x2 = $x->multiply($x); $y2 = $y->multiply($y); $s = $this->four->multiply($x)->multiply($y2); $m1 = $this->three->multiply($x2); $m = $m1->add($this->a); $x1 = $m->multiply($m)->subtract($this->two->multiply($s)); $y1 = $m->multiply($s->subtract($x1))->subtract( $this->eight->multiply($y2->multiply($y2)) ); $z1 = $this->two->multiply($y); return [$x1, $y1, $z1]; } /** * Doubles a point on a curve * * @return FiniteField[] */ public function doublePoint(array $p) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p)) { return []; } // use jacobian coordinates if (isset($p[2])) { if (isset($p['fresh'])) { return $this->jacobianDoublePointMixed($p); } return $this->jacobianDoublePoint($p); } list($numerator, $denominator) = $this->doublePointHelper($p); $slope = $numerator->divide($denominator); $x = $slope->multiply($slope)->subtract($p[0])->subtract($p[0]); $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]); return [$x, $y]; } /** * Returns the X coordinate and the derived Y coordinate * * @return array */ public function derivePoint($m) { $y = ord(Strings::shift($m)); $x = new BigInteger($m, 256); $xp = $this->convertInteger($x); switch ($y) { case 2: $ypn = false; break; case 3: $ypn = true; break; default: throw new \RuntimeException('Coordinate not in recognized format'); } $temp = $xp->multiply($this->a); $temp = $xp->multiply($xp)->multiply($xp)->add($temp); $temp = $temp->add($this->b); $b = $temp->squareRoot(); if (!$b) { throw new \RuntimeException('Unable to derive Y coordinate'); } $bn = $b->isOdd(); $yp = $ypn == $bn ? $b : $b->negate(); return [$xp, $yp]; } /** * Tests whether or not the x / y values satisfy the equation * * @return boolean */ public function verifyPoint(array $p) { list($x, $y) = $p; $lhs = $y->multiply($y); $temp = $x->multiply($this->a); $temp = $x->multiply($x)->multiply($x)->add($temp); $rhs = $temp->add($this->b); return $lhs->equals($rhs); } /** * Returns the modulo * * @return \phpseclib3\Math\BigInteger */ public function getModulo() { return $this->modulo; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getA() { return $this->a; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getB() { return $this->b; } /** * Multiply and Add Points * * Adapted from: * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L125 * * @return int[] */ public function multiplyAddPoints(array $points, array $scalars) { $length = count($points); foreach ($points as &$point) { $point = $this->convertToInternal($point); } $wnd = [$this->getNAFPoints($points[0], 7)]; $wndWidth = [isset($points[0]['nafwidth']) ? $points[0]['nafwidth'] : 7]; for ($i = 1; $i < $length; $i++) { $wnd[] = $this->getNAFPoints($points[$i], 1); $wndWidth[] = isset($points[$i]['nafwidth']) ? $points[$i]['nafwidth'] : 1; } $naf = []; // comb all window NAFs $max = 0; for ($i = $length - 1; $i >= 1; $i -= 2) { $a = $i - 1; $b = $i; if ($wndWidth[$a] != 1 || $wndWidth[$b] != 1) { $naf[$a] = $scalars[$a]->getNAF($wndWidth[$a]); $naf[$b] = $scalars[$b]->getNAF($wndWidth[$b]); $max = max(count($naf[$a]), count($naf[$b]), $max); continue; } $comb = [ $points[$a], // 1 null, // 3 null, // 5 $points[$b] // 7 ]; $comb[1] = $this->addPoint($points[$a], $points[$b]); $comb[2] = $this->addPoint($points[$a], $this->negatePoint($points[$b])); $index = [ -3, /* -1 -1 */ -1, /* -1 0 */ -5, /* -1 1 */ -7, /* 0 -1 */ 0, /* 0 -1 */ 7, /* 0 1 */ 5, /* 1 -1 */ 1, /* 1 0 */ 3 /* 1 1 */ ]; $jsf = self::getJSFPoints($scalars[$a], $scalars[$b]); $max = max(count($jsf[0]), $max); if ($max > 0) { $naf[$a] = array_fill(0, $max, 0); $naf[$b] = array_fill(0, $max, 0); } else { $naf[$a] = []; $naf[$b] = []; } for ($j = 0; $j < $max; $j++) { $ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0; $jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0; $naf[$a][$j] = $index[3 * ($ja + 1) + $jb + 1]; $naf[$b][$j] = 0; $wnd[$a] = $comb; } } $acc = []; $temp = [0, 0, 0, 0]; for ($i = $max; $i >= 0; $i--) { $k = 0; while ($i >= 0) { $zero = true; for ($j = 0; $j < $length; $j++) { $temp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0; if ($temp[$j] != 0) { $zero = false; } } if (!$zero) { break; } $k++; $i--; } if ($i >= 0) { $k++; } while ($k--) { $acc = $this->doublePoint($acc); } if ($i < 0) { break; } for ($j = 0; $j < $length; $j++) { $z = $temp[$j]; $p = null; if ($z == 0) { continue; } $p = $z > 0 ? $wnd[$j][($z - 1) >> 1] : $this->negatePoint($wnd[$j][(-$z - 1) >> 1]); $acc = $this->addPoint($acc, $p); } } return $this->convertToAffine($acc); } /** * Precomputes NAF points * * Adapted from: * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L351 * * @return int[] */ private function getNAFPoints(array $point, $wnd) { if (isset($point['naf'])) { return $point['naf']; } $res = [$point]; $max = (1 << $wnd) - 1; $dbl = $max == 1 ? null : $this->doublePoint($point); for ($i = 1; $i < $max; $i++) { $res[] = $this->addPoint($res[$i - 1], $dbl); } $point['naf'] = $res; /* $str = ''; foreach ($res as $re) { $re[0] = bin2hex($re[0]->toBytes()); $re[1] = bin2hex($re[1]->toBytes()); $str.= " ['$re[0]', '$re[1]'],\r\n"; } file_put_contents('temp.txt', $str); exit; */ return $res; } /** * Precomputes points in Joint Sparse Form * * Adapted from: * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/utils.js#L96 * * @return int[] */ private static function getJSFPoints(Integer $k1, Integer $k2) { static $three; if (!isset($three)) { $three = new BigInteger(3); } $jsf = [[], []]; $k1 = $k1->toBigInteger(); $k2 = $k2->toBigInteger(); $d1 = 0; $d2 = 0; while ($k1->compare(new BigInteger(-$d1)) > 0 || $k2->compare(new BigInteger(-$d2)) > 0) { // first phase $m14 = $k1->testBit(0) + 2 * $k1->testBit(1); $m14 += $d1; $m14 &= 3; $m24 = $k2->testBit(0) + 2 * $k2->testBit(1); $m24 += $d2; $m24 &= 3; if ($m14 == 3) { $m14 = -1; } if ($m24 == 3) { $m24 = -1; } $u1 = 0; if ($m14 & 1) { // if $m14 is odd $m8 = $k1->testBit(0) + 2 * $k1->testBit(1) + 4 * $k1->testBit(2); $m8 += $d1; $m8 &= 7; $u1 = ($m8 == 3 || $m8 == 5) && $m24 == 2 ? -$m14 : $m14; } $jsf[0][] = $u1; $u2 = 0; if ($m24 & 1) { // if $m24 is odd $m8 = $k2->testBit(0) + 2 * $k2->testBit(1) + 4 * $k2->testBit(2); $m8 += $d2; $m8 &= 7; $u2 = ($m8 == 3 || $m8 == 5) && $m14 == 2 ? -$m24 : $m24; } $jsf[1][] = $u2; // second phase if (2 * $d1 == $u1 + 1) { $d1 = 1 - $d1; } if (2 * $d2 == $u2 + 1) { $d2 = 1 - $d2; } $k1 = $k1->bitwise_rightShift(1); $k2 = $k2->bitwise_rightShift(1); } return $jsf; } /** * Returns the affine point * * A Jacobian Coordinate is of the form (x, y, z). * To convert a Jacobian Coordinate to an Affine Point * you do (x / z^2, y / z^3) * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToAffine(array $p) { if (!isset($p[2])) { return $p; } list($x, $y, $z) = $p; $z = $this->one->divide($z); $z2 = $z->multiply($z); return [ $x->multiply($z2), $y->multiply($z2)->multiply($z) ]; } /** * Converts an affine point to a jacobian coordinate * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToInternal(array $p) { if (isset($p[2])) { return $p; } $p[2] = clone $this->one; $p['fresh'] = true; return $p; } } PK2A#]��̌�Avendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.phpnu�[���<?php /** * Curve methods common to all curves * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Math\BigInteger; /** * Base * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Base { /** * The Order * * @var BigInteger */ protected $order; /** * Finite Field Integer factory * * @var \phpseclib3\Math\FiniteField\Integer */ protected $factory; /** * Returns a random integer * * @return object */ public function randomInteger() { return $this->factory->randomInteger(); } /** * Converts a BigInteger to a \phpseclib3\Math\FiniteField\Integer integer * * @return object */ public function convertInteger(BigInteger $x) { return $this->factory->newInteger($x); } /** * Returns the length, in bytes, of the modulo * * @return integer */ public function getLengthInBytes() { return $this->factory->getLengthInBytes(); } /** * Returns the length, in bits, of the modulo * * @return integer */ public function getLength() { return $this->factory->getLength(); } /** * Multiply a point on the curve by a scalar * * Uses the montgomery ladder technique as described here: * * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772 * * @return array */ public function multiplyPoint(array $p, BigInteger $d) { $alreadyInternal = isset($p[2]); $r = $alreadyInternal ? [[], $p] : [[], $this->convertToInternal($p)]; $d = $d->toBits(); for ($i = 0; $i < strlen($d); $i++) { $d_i = (int) $d[$i]; $r[1 - $d_i] = $this->addPoint($r[0], $r[1]); $r[$d_i] = $this->doublePoint($r[$d_i]); } return $alreadyInternal ? $r[0] : $this->convertToAffine($r[0]); } /** * Creates a random scalar multiplier * * @return BigInteger */ public function createRandomMultiplier() { static $one; if (!isset($one)) { $one = new BigInteger(1); } return BigInteger::randomRange($one, $this->order->subtract($one)); } /** * Performs range check */ public function rangeCheck(BigInteger $x) { static $zero; if (!isset($zero)) { $zero = new BigInteger(); } if (!isset($this->order)) { throw new \RuntimeException('setOrder needs to be called before this method'); } if ($x->compare($this->order) > 0 || $x->compare($zero) <= 0) { throw new \RangeException('x must be between 1 and the order of the curve'); } } /** * Sets the Order */ public function setOrder(BigInteger $order) { $this->order = $order; } /** * Returns the Order * * @return \phpseclib3\Math\BigInteger */ public function getOrder() { return $this->order; } /** * Use a custom defined modular reduction function * * @return object */ public function setReduction(callable $func) { $this->factory->setReduction($func); } /** * Returns the affine point * * @return object[] */ public function convertToAffine(array $p) { return $p; } /** * Converts an affine point to a jacobian coordinate * * @return object[] */ public function convertToInternal(array $p) { return $p; } /** * Negates a point * * @return object[] */ public function negatePoint(array $p) { $temp = [ $p[0], $p[1]->negate() ]; if (isset($p[2])) { $temp[] = $p[2]; } return $temp; } /** * Multiply and Add Points * * @return int[] */ public function multiplyAddPoints(array $points, array $scalars) { $p1 = $this->convertToInternal($points[0]); $p2 = $this->convertToInternal($points[1]); $p1 = $this->multiplyPoint($p1, $scalars[0]); $p2 = $this->multiplyPoint($p2, $scalars[1]); $r = $this->addPoint($p1, $p2); return $this->convertToAffine($r); } } PK2A#]�R�/L&L&Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.phpnu�[���<?php /** * Curves over y^2 + x*y = x^3 + a*x^2 + b * * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf * The curve is a weierstrass curve with a[3] and a[2] set to 0. * * Uses Jacobian Coordinates for speed if able: * * https://en.wikipedia.org/wiki/Jacobian_curve * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Math\BigInteger; use phpseclib3\Math\BinaryField; use phpseclib3\Math\BinaryField\Integer as BinaryInteger; /** * Curves over y^2 + x*y = x^3 + a*x^2 + b * * @author Jim Wigginton <terrafrost@php.net> */ class Binary extends Base { /** * Binary Field Integer factory * * @var \phpseclib3\Math\BinaryField */ protected $factory; /** * Cofficient for x^1 * * @var object */ protected $a; /** * Cofficient for x^0 * * @var object */ protected $b; /** * Base Point * * @var object */ protected $p; /** * The number one over the specified finite field * * @var object */ protected $one; /** * The modulo * * @var BigInteger */ protected $modulo; /** * The Order * * @var BigInteger */ protected $order; /** * Sets the modulo */ public function setModulo(...$modulo) { $this->modulo = $modulo; $this->factory = new BinaryField(...$modulo); $this->one = $this->factory->newInteger("\1"); } /** * Set coefficients a and b * * @param string $a * @param string $b */ public function setCoefficients($a, $b) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->a = $this->factory->newInteger(pack('H*', $a)); $this->b = $this->factory->newInteger(pack('H*', $b)); } /** * Set x and y coordinates for the base point * * @param string|BinaryInteger $x * @param string|BinaryInteger $y */ public function setBasePoint($x, $y) { switch (true) { case !is_string($x) && !$x instanceof BinaryInteger: throw new \UnexpectedValueException('Argument 1 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer'); case !is_string($y) && !$y instanceof BinaryInteger: throw new \UnexpectedValueException('Argument 2 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer'); } if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } $this->p = [ is_string($x) ? $this->factory->newInteger(pack('H*', $x)) : $x, is_string($y) ? $this->factory->newInteger(pack('H*', $y)) : $y ]; } /** * Retrieve the base point as an array * * @return array */ public function getBasePoint() { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } /* if (!isset($this->p)) { throw new \RuntimeException('setBasePoint needs to be called before this method'); } */ return $this->p; } /** * Adds two points on the curve * * @return FiniteField[] */ public function addPoint(array $p, array $q) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p) || !count($q)) { if (count($q)) { return $q; } if (count($p)) { return $p; } return []; } if (!isset($p[2]) || !isset($q[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } if ($p[0]->equals($q[0])) { return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); } // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html list($x1, $y1, $z1) = $p; list($x2, $y2, $z2) = $q; $o1 = $z1->multiply($z1); $b = $x2->multiply($o1); if ($z2->equals($this->one)) { $d = $y2->multiply($o1)->multiply($z1); $e = $x1->add($b); $f = $y1->add($d); $z3 = $e->multiply($z1); $h = $f->multiply($x2)->add($z3->multiply($y2)); $i = $f->add($z3); $g = $z3->multiply($z3); $p1 = $this->a->multiply($g); $p2 = $f->multiply($i); $p3 = $e->multiply($e)->multiply($e); $x3 = $p1->add($p2)->add($p3); $y3 = $i->multiply($x3)->add($g->multiply($h)); return [$x3, $y3, $z3]; } $o2 = $z2->multiply($z2); $a = $x1->multiply($o2); $c = $y1->multiply($o2)->multiply($z2); $d = $y2->multiply($o1)->multiply($z1); $e = $a->add($b); $f = $c->add($d); $g = $e->multiply($z1); $h = $f->multiply($x2)->add($g->multiply($y2)); $z3 = $g->multiply($z2); $i = $f->add($z3); $p1 = $this->a->multiply($z3->multiply($z3)); $p2 = $f->multiply($i); $p3 = $e->multiply($e)->multiply($e); $x3 = $p1->add($p2)->add($p3); $y3 = $i->multiply($x3)->add($g->multiply($g)->multiply($h)); return [$x3, $y3, $z3]; } /** * Doubles a point on a curve * * @return FiniteField[] */ public function doublePoint(array $p) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p)) { return []; } if (!isset($p[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html list($x1, $y1, $z1) = $p; $a = $x1->multiply($x1); $b = $a->multiply($a); if ($z1->equals($this->one)) { $x3 = $b->add($this->b); $z3 = clone $x1; $p1 = $a->add($y1)->add($z3)->multiply($this->b); $p2 = $a->add($y1)->multiply($b); $y3 = $p1->add($p2); return [$x3, $y3, $z3]; } $c = $z1->multiply($z1); $d = $c->multiply($c); $x3 = $b->add($this->b->multiply($d->multiply($d))); $z3 = $x1->multiply($c); $p1 = $b->multiply($z3); $p2 = $a->add($y1->multiply($z1))->add($z3)->multiply($x3); $y3 = $p1->add($p2); return [$x3, $y3, $z3]; } /** * Returns the X coordinate and the derived Y coordinate * * Not supported because it is covered by patents. * Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html , * * "Due to patent issues the compressed option is disabled by default for binary curves * and can be enabled by defining the preprocessor macro OPENSSL_EC_BIN_PT_COMP at * compile time." * * @return array */ public function derivePoint($m) { throw new \RuntimeException('Point compression on binary finite field elliptic curves is not supported'); } /** * Tests whether or not the x / y values satisfy the equation * * @return boolean */ public function verifyPoint(array $p) { list($x, $y) = $p; $lhs = $y->multiply($y); $lhs = $lhs->add($x->multiply($y)); $x2 = $x->multiply($x); $x3 = $x2->multiply($x); $rhs = $x3->add($this->a->multiply($x2))->add($this->b); return $lhs->equals($rhs); } /** * Returns the modulo * * @return \phpseclib3\Math\BigInteger */ public function getModulo() { return $this->modulo; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getA() { return $this->a; } /** * Returns the a coefficient * * @return \phpseclib3\Math\PrimeField\Integer */ public function getB() { return $this->b; } /** * Returns the affine point * * A Jacobian Coordinate is of the form (x, y, z). * To convert a Jacobian Coordinate to an Affine Point * you do (x / z^2, y / z^3) * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToAffine(array $p) { if (!isset($p[2])) { return $p; } list($x, $y, $z) = $p; $z = $this->one->divide($z); $z2 = $z->multiply($z); return [ $x->multiply($z2), $y->multiply($z2)->multiply($z) ]; } /** * Converts an affine point to a jacobian coordinate * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToInternal(array $p) { if (isset($p[2])) { return $p; } $p[2] = clone $this->one; $p['fresh'] = true; return $p; } } PK2A#]E��'�'Ivendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.phpnu�[���<?php /** * Generalized Koblitz Curves over y^2 = x^3 + b. * * According to http://www.secg.org/SEC2-Ver-1.0.pdf Koblitz curves are over the GF(2**m) * finite field. Both the $a$ and $b$ coefficients are either 0 or 1. However, SEC2 * generalizes the definition to include curves over GF(P) "which possess an efficiently * computable endomorphism". * * For these generalized Koblitz curves $b$ doesn't have to be 0 or 1. Whether or not $a$ * has any restrictions on it is unclear, however, for all the GF(P) Koblitz curves defined * in SEC2 v1.0 $a$ is $0$ so all of the methods defined herein will assume that it is. * * I suppose we could rename the $b$ coefficient to $a$, however, the documentation refers * to $b$ so we'll just keep it. * * If a later version of SEC2 comes out wherein some $a$ values are non-zero we can create a * new method for those. eg. KoblitzA1Prime.php or something. * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\BaseCurves; use phpseclib3\Math\BigInteger; use phpseclib3\Math\PrimeField; /** * Curves over y^2 = x^3 + b * * @author Jim Wigginton <terrafrost@php.net> */ class KoblitzPrime extends Prime { /** * Basis * * @var list<array{a: BigInteger, b: BigInteger}> */ protected $basis; /** * Beta * * @var PrimeField\Integer */ protected $beta; // don't overwrite setCoefficients() with one that only accepts one parameter so that // one might be able to switch between KoblitzPrime and Prime more easily (for benchmarking // purposes). /** * Multiply and Add Points * * Uses a efficiently computable endomorphism to achieve a slight speedup * * Adapted from: * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/short.js#L219 * * @return int[] */ public function multiplyAddPoints(array $points, array $scalars) { static $zero, $one, $two; if (!isset($two)) { $two = new BigInteger(2); $one = new BigInteger(1); } if (!isset($this->beta)) { // get roots $inv = $this->one->divide($this->two)->negate(); $s = $this->three->negate()->squareRoot()->multiply($inv); $betas = [ $inv->add($s), $inv->subtract($s) ]; $this->beta = $betas[0]->compare($betas[1]) < 0 ? $betas[0] : $betas[1]; //echo strtoupper($this->beta->toHex(true)) . "\n"; exit; } if (!isset($this->basis)) { $factory = new PrimeField($this->order); $tempOne = $factory->newInteger($one); $tempTwo = $factory->newInteger($two); $tempThree = $factory->newInteger(new BigInteger(3)); $inv = $tempOne->divide($tempTwo)->negate(); $s = $tempThree->negate()->squareRoot()->multiply($inv); $lambdas = [ $inv->add($s), $inv->subtract($s) ]; $lhs = $this->multiplyPoint($this->p, $lambdas[0])[0]; $rhs = $this->p[0]->multiply($this->beta); $lambda = $lhs->equals($rhs) ? $lambdas[0] : $lambdas[1]; $this->basis = static::extendedGCD($lambda->toBigInteger(), $this->order); ///* foreach ($this->basis as $basis) { echo strtoupper($basis['a']->toHex(true)) . "\n"; echo strtoupper($basis['b']->toHex(true)) . "\n\n"; } exit; //*/ } $npoints = $nscalars = []; for ($i = 0; $i < count($points); $i++) { $p = $points[$i]; $k = $scalars[$i]->toBigInteger(); // begin split list($v1, $v2) = $this->basis; $c1 = $v2['b']->multiply($k); list($c1, $r) = $c1->divide($this->order); if ($this->order->compare($r->multiply($two)) <= 0) { $c1 = $c1->add($one); } $c2 = $v1['b']->negate()->multiply($k); list($c2, $r) = $c2->divide($this->order); if ($this->order->compare($r->multiply($two)) <= 0) { $c2 = $c2->add($one); } $p1 = $c1->multiply($v1['a']); $p2 = $c2->multiply($v2['a']); $q1 = $c1->multiply($v1['b']); $q2 = $c2->multiply($v2['b']); $k1 = $k->subtract($p1)->subtract($p2); $k2 = $q1->add($q2)->negate(); // end split $beta = [ $p[0]->multiply($this->beta), $p[1], clone $this->one ]; if (isset($p['naf'])) { $beta['naf'] = array_map(function ($p) { return [ $p[0]->multiply($this->beta), $p[1], clone $this->one ]; }, $p['naf']); $beta['nafwidth'] = $p['nafwidth']; } if ($k1->isNegative()) { $k1 = $k1->negate(); $p = $this->negatePoint($p); } if ($k2->isNegative()) { $k2 = $k2->negate(); $beta = $this->negatePoint($beta); } $pos = 2 * $i; $npoints[$pos] = $p; $nscalars[$pos] = $this->factory->newInteger($k1); $pos++; $npoints[$pos] = $beta; $nscalars[$pos] = $this->factory->newInteger($k2); } return parent::multiplyAddPoints($npoints, $nscalars); } /** * Returns the numerator and denominator of the slope * * @return FiniteField[] */ protected function doublePointHelper(array $p) { $numerator = $this->three->multiply($p[0])->multiply($p[0]); $denominator = $this->two->multiply($p[1]); return [$numerator, $denominator]; } /** * Doubles a jacobian coordinate on the curve * * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l * * @return FiniteField[] */ protected function jacobianDoublePoint(array $p) { list($x1, $y1, $z1) = $p; $a = $x1->multiply($x1); $b = $y1->multiply($y1); $c = $b->multiply($b); $d = $x1->add($b); $d = $d->multiply($d)->subtract($a)->subtract($c)->multiply($this->two); $e = $this->three->multiply($a); $f = $e->multiply($e); $x3 = $f->subtract($this->two->multiply($d)); $y3 = $e->multiply($d->subtract($x3))->subtract( $this->eight->multiply($c) ); $z3 = $this->two->multiply($y1)->multiply($z1); return [$x3, $y3, $z3]; } /** * Doubles a "fresh" jacobian coordinate on the curve * * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl * * @return FiniteField[] */ protected function jacobianDoublePointMixed(array $p) { list($x1, $y1) = $p; $xx = $x1->multiply($x1); $yy = $y1->multiply($y1); $yyyy = $yy->multiply($yy); $s = $x1->add($yy); $s = $s->multiply($s)->subtract($xx)->subtract($yyyy)->multiply($this->two); $m = $this->three->multiply($xx); $t = $m->multiply($m)->subtract($this->two->multiply($s)); $x3 = $t; $y3 = $s->subtract($t); $y3 = $m->multiply($y3)->subtract($this->eight->multiply($yyyy)); $z3 = $this->two->multiply($y1); return [$x3, $y3, $z3]; } /** * Tests whether or not the x / y values satisfy the equation * * @return boolean */ public function verifyPoint(array $p) { list($x, $y) = $p; $lhs = $y->multiply($y); $temp = $x->multiply($x)->multiply($x); $rhs = $temp->add($this->b); return $lhs->equals($rhs); } /** * Calculates the parameters needed from the Euclidean algorithm as discussed at * http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=148 * * @param BigInteger $u * @param BigInteger $v * @return BigInteger[] */ protected static function extendedGCD(BigInteger $u, BigInteger $v) { $one = new BigInteger(1); $zero = new BigInteger(); $a = clone $one; $b = clone $zero; $c = clone $zero; $d = clone $one; $stop = $v->bitwise_rightShift($v->getLength() >> 1); $a1 = clone $zero; $b1 = clone $zero; $a2 = clone $zero; $b2 = clone $zero; $postGreatestIndex = 0; while (!$v->equals($zero)) { list($q) = $u->divide($v); $temp = $u; $u = $v; $v = $temp->subtract($v->multiply($q)); $temp = $a; $a = $c; $c = $temp->subtract($a->multiply($q)); $temp = $b; $b = $d; $d = $temp->subtract($b->multiply($q)); if ($v->compare($stop) > 0) { $a0 = $v; $b0 = $c; } else { $postGreatestIndex++; } if ($postGreatestIndex == 1) { $a1 = $v; $b1 = $c->negate(); } if ($postGreatestIndex == 2) { $rhs = $a0->multiply($a0)->add($b0->multiply($b0)); $lhs = $v->multiply($v)->add($b->multiply($b)); if ($lhs->compare($rhs) <= 0) { $a2 = $a0; $b2 = $b0->negate(); } else { $a2 = $v; $b2 = $c->negate(); } break; } } return [ ['a' => $a1, 'b' => $b1], ['a' => $a2, 'b' => $b2] ]; } } PK2A#]1�PA<vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.phpnu�[���<?php /** * EC Parameters * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC; use phpseclib3\Crypt\EC; /** * EC Parameters * * @author Jim Wigginton <terrafrost@php.net> */ final class Parameters extends EC { /** * Returns the parameters * * @param string $type * @param array $options optional * @return string */ public function toString($type = 'PKCS1', array $options = []) { $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); return $type::saveParameters($this->curve, $options); } } PK2A#]L 24BB;vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.phpnu�[���<?php /** * EC Public Key * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common; use phpseclib3\Crypt\EC; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; use phpseclib3\Crypt\Hash; use phpseclib3\Exception\UnsupportedOperationException; use phpseclib3\Math\BigInteger; /** * EC Public Key * * @author Jim Wigginton <terrafrost@php.net> */ final class PublicKey extends EC implements Common\PublicKey { use Common\Traits\Fingerprint; /** * Verify a signature * * @see self::verify() * @param string $message * @param string $signature * @return mixed */ public function verify($message, $signature) { if ($this->curve instanceof MontgomeryCurve) { throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); } $shortFormat = $this->shortFormat; $format = $this->sigFormat; if ($format === false) { return false; } $order = $this->curve->getOrder(); if ($this->curve instanceof TwistedEdwardsCurve) { if ($shortFormat == 'SSH2') { list(, $signature) = Strings::unpackSSH2('ss', $signature); } if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium')); } $curve = $this->curve; if (strlen($signature) != 2 * $curve::SIZE) { return false; } $R = substr($signature, 0, $curve::SIZE); $S = substr($signature, $curve::SIZE); try { $R = PKCS1::extractPoint($R, $curve); $R = $this->curve->convertToInternal($R); } catch (\Exception $e) { return false; } $S = strrev($S); $S = new BigInteger($S, 256); if ($S->compare($order) >= 0) { return false; } $A = $curve->encodePoint($this->QA); if ($curve instanceof Ed25519) { $dom2 = !isset($this->context) ? '' : 'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context; } else { $context = isset($this->context) ? $this->context : ''; $dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context; } $hash = new Hash($curve::HASH); $k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message); $k = strrev($k); $k = new BigInteger($k, 256); list(, $k) = $k->divide($order); $qa = $curve->convertToInternal($this->QA); $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S); $rhs = $curve->multiplyPoint($qa, $k); $rhs = $curve->addPoint($rhs, $R); $rhs = $curve->convertToAffine($rhs); return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]); } $params = $format::load($signature); if ($params === false || count($params) != 2) { return false; } extract($params); if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature; $result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash()); if ($result != -1) { return (bool) $result; } } $n_1 = $order->subtract(self::$one); if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) { return false; } $e = $this->hash->hash($message); $e = new BigInteger($e, 256); $Ln = $this->hash->getLength() - $order->getLength(); $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e; $w = $s->modInverse($order); list(, $u1) = $z->multiply($w)->divide($order); list(, $u2) = $r->multiply($w)->divide($order); $u1 = $this->curve->convertInteger($u1); $u2 = $this->curve->convertInteger($u2); list($x1, $y1) = $this->curve->multiplyAddPoints( [$this->curve->getBasePoint(), $this->QA], [$u1, $u2] ); $x1 = $x1->toBigInteger(); list(, $x1) = $x1->divide($order); return $x1->equals($r); } /** * Returns the public key * * @param string $type * @param array $options optional * @return string */ public function toString($type, array $options = []) { $type = self::validatePlugin('Keys', $type, 'savePublicKey'); return $type::savePublicKey($this->curve, $this->QA, $options); } } PK2A#]�R1��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.phpnu�[���<?php /** * ASN1 Signature Handler * * PHP version 5 * * Handles signatures in the format described in * https://tools.ietf.org/html/rfc3279#section-2.2.3 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Signature; use phpseclib3\File\ASN1 as Encoder; use phpseclib3\File\ASN1\Maps\EcdsaSigValue; use phpseclib3\Math\BigInteger; /** * ASN1 Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ASN1 { /** * Loads a signature * * @param string $sig * @return array */ public static function load($sig) { if (!is_string($sig)) { return false; } $decoded = Encoder::decodeBER($sig); if (empty($decoded)) { return false; } $components = Encoder::asn1map($decoded[0], EcdsaSigValue::MAP); return $components; } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @return string */ public static function save(BigInteger $r, BigInteger $s) { return Encoder::encodeDER(compact('r', 's'), EcdsaSigValue::MAP); } } PK2A#]|�j2Gvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.phpnu�[���<?php /** * Raw EC Signature Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Signature; use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor; /** * Raw DSA Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Raw extends Progenitor { } PK2A#]b��4Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.phpnu�[���<?php /** * IEEE P1363 Signature Handler * * PHP version 5 * * Handles signatures in the format described in * https://standards.ieee.org/ieee/1363/2049/ and * https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign#ecdsa * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Signature; use phpseclib3\Math\BigInteger; /** * ASN1 Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class IEEE { /** * Loads a signature * * @param string $sig * @return array */ public static function load($sig) { if (!is_string($sig)) { return false; } $len = strlen($sig); if ($len & 1) { return false; } $r = new BigInteger(substr($sig, 0, $len >> 1), 256); $s = new BigInteger(substr($sig, $len >> 1), 256); return compact('r', 's'); } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @return string */ public static function save(BigInteger $r, BigInteger $s) { $r = $r->toBytes(); $s = $s->toBytes(); $len = max(strlen($r), strlen($s)); return str_pad($r, $len, "\0", STR_PAD_LEFT) . str_pad($s, $len, "\0", STR_PAD_LEFT); } } PK2A#]s��0��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.phpnu�[���<?php /** * SSH2 Signature Handler * * PHP version 5 * * Handles signatures in the format used by SSH2 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Signature; use phpseclib3\Common\Functions\Strings; use phpseclib3\Math\BigInteger; /** * SSH2 Signature Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SSH2 { /** * Loads a signature * * @param string $sig * @return mixed */ public static function load($sig) { if (!is_string($sig)) { return false; } $result = Strings::unpackSSH2('ss', $sig); if ($result === false) { return false; } list($type, $blob) = $result; switch ($type) { // see https://tools.ietf.org/html/rfc5656#section-3.1.2 case 'ecdsa-sha2-nistp256': case 'ecdsa-sha2-nistp384': case 'ecdsa-sha2-nistp521': break; default: return false; } $result = Strings::unpackSSH2('ii', $blob); if ($result === false) { return false; } return [ 'r' => $result[0], 's' => $result[1] ]; } /** * Returns a signature in the appropriate format * * @param \phpseclib3\Math\BigInteger $r * @param \phpseclib3\Math\BigInteger $s * @param string $curve * @return string */ public static function save(BigInteger $r, BigInteger $s, $curve) { switch ($curve) { case 'secp256r1': $curve = 'nistp256'; break; case 'secp384r1': $curve = 'nistp384'; break; case 'secp521r1': $curve = 'nistp521'; break; default: return false; } $blob = Strings::packSSH2('ii', $r, $s); return Strings::packSSH2('ss', 'ecdsa-sha2-' . $curve, $blob); } } PK2A#]���77Ovendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.phpnu�[���<?php /** * Montgomery Public Key Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\Curves\Curve25519; use phpseclib3\Crypt\EC\Curves\Curve448; use phpseclib3\Math\BigInteger; /** * Montgomery Public Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class MontgomeryPublic { /** * Is invisible flag * */ const IS_INVISIBLE = true; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { switch (strlen($key)) { case 32: $curve = new Curve25519(); break; case 56: $curve = new Curve448(); break; default: throw new \LengthException('The only supported lengths are 32 and 56'); } $components = ['curve' => $curve]; $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), 256))]; return $components; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @return string */ public static function savePublicKey(MontgomeryCurve $curve, array $publicKey) { return strrev($publicKey[0]->toBytes()); } } PK2A#]��N��Dvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.phpnu�[���<?php /** * PKCS#8 Formatted EC Key Handler * * PHP version 5 * * Processes keys with the following headers: * * -----BEGIN ENCRYPTED PRIVATE KEY----- * -----BEGIN PRIVATE KEY----- * -----BEGIN PUBLIC KEY----- * * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 * is specific to private keys it's basically creating a DER-encoded wrapper * for keys. This just extends that same concept to public keys (much like ssh-keygen) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Crypt\EC\Curves\Ed448; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * PKCS#8 Formatted EC Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS8 extends Progenitor { use Common; /** * OID Name * * @var array */ const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448']; /** * OID Value * * @var string */ const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113']; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { // initialize_static_variables() is defined in both the trait and the parent class // when it's defined in two places it's the traits one that's called // the parent one is needed, as well, but the parent one is called by other methods // in the parent class as needed and in the context of the parent it's the parent // one that's called self::initialize_static_variables(); $key = parent::load($key, $password); $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; switch ($key[$type . 'Algorithm']['algorithm']) { case 'id-Ed25519': case 'id-Ed448': return self::loadEdDSA($key); } $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); if (!$params) { throw new \RuntimeException('Unable to decode the parameters using Maps\ECParameters'); } $components = []; $components['curve'] = self::loadCurveByParam($params); if ($type == 'publicKey') { $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']); return $components; } $decoded = ASN1::decodeBER($key['privateKey']); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP); if (isset($key['parameters']) && $params != $key['parameters']) { throw new \RuntimeException('The PKCS8 parameter field does not match the private key parameter field'); } $components['dA'] = new BigInteger($key['privateKey'], 256); $components['curve']->rangeCheck($components['dA']); $components['QA'] = isset($key['publicKey']) ? self::extractPoint($key['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); return $components; } /** * Break a public or private EdDSA key down into its constituent components * * @return array */ private static function loadEdDSA(array $key) { $components = []; if (isset($key['privateKey'])) { $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); // 0x04 == octet string // 0x20 == length (32 bytes) if (substr($key['privateKey'], 0, 2) != "\x04\x20") { throw new \RuntimeException('The first two bytes of the private key field should be 0x0420'); } $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2)); $components['dA'] = $arr['dA']; $components['secret'] = $arr['secret']; } if (isset($key['publicKey'])) { if (!isset($components['curve'])) { $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); } $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); } if (isset($key['privateKey']) && !isset($components['QA'])) { $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); } return $components; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param array $options optional * @return string */ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []) { self::initialize_static_variables(); if ($curve instanceof MontgomeryCurve) { throw new UnsupportedCurveException('Montgomery Curves are not supported'); } if ($curve instanceof TwistedEdwardsCurve) { return self::wrapPublicKey( $curve->encodePoint($publicKey), null, $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' ); } $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); return self::wrapPublicKey($key, $params, 'id-ecPublicKey'); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { self::initialize_static_variables(); if ($curve instanceof MontgomeryCurve) { throw new UnsupportedCurveException('Montgomery Curves are not supported'); } if ($curve instanceof TwistedEdwardsCurve) { return self::wrapPrivateKey( "\x04\x20" . $secret, [], null, $password, $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' ); } $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); $key = [ 'version' => 'ecPrivkeyVer1', 'privateKey' => $privateKey->toBytes(), //'parameters' => $params, 'publicKey' => "\0" . $publicKey ]; $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP); return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options); } } PK2A#]�o&�E�EBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.phpnu�[���<?php /** * XML Formatted EC Key Handler * * More info: * * https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue * http://en.wikipedia.org/wiki/XML_Signature * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Exception\BadConfigurationException; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Math\BigInteger; /** * XML Formatted EC Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class XML { use Common; /** * Default namespace * * @var string */ private static $namespace; /** * Flag for using RFC4050 syntax * * @var bool */ private static $rfc4050 = false; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { self::initialize_static_variables(); if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (!class_exists('DOMDocument')) { throw new BadConfigurationException('The dom extension is not setup correctly on this system'); } $use_errors = libxml_use_internal_errors(true); $temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#'); if ($temp) { $key = $temp; } $temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#'); if ($temp) { $key = $temp; } $dom = new \DOMDocument(); if (substr($key, 0, 5) != '<?xml') { $key = '<xml>' . $key . '</xml>'; } if (!$dom->loadXML($key)) { libxml_use_internal_errors($use_errors); throw new \UnexpectedValueException('Key does not appear to contain XML'); } $xpath = new \DOMXPath($dom); libxml_use_internal_errors($use_errors); $curve = self::loadCurveByParam($xpath); $pubkey = self::query($xpath, 'publickey', 'Public Key is not present'); $QA = self::query($xpath, 'ecdsakeyvalue')->length ? self::extractPointRFC4050($xpath, $curve) : self::extractPoint("\0" . $pubkey, $curve); libxml_use_internal_errors($use_errors); return compact('curve', 'QA'); } /** * Case-insensitive xpath query * * @param \DOMXPath $xpath * @param string $name * @param string $error optional * @param bool $decode optional * @return \DOMNodeList */ private static function query(\DOMXPath $xpath, $name, $error = null, $decode = true) { $query = '/'; $names = explode('/', $name); foreach ($names as $name) { $query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']"; } $result = $xpath->query($query); if (!isset($error)) { return $result; } if (!$result->length) { throw new \RuntimeException($error); } return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent; } /** * Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element. * * @param string $xml * @param string $ns */ private static function isolateNamespace($xml, $ns) { $dom = new \DOMDocument(); if (!$dom->loadXML($xml)) { return false; } $xpath = new \DOMXPath($dom); $nodes = $xpath->query("//*[namespace::*[.='$ns'] and not(../namespace::*[.='$ns'])]"); if (!$nodes->length) { return false; } $node = $nodes->item(0); $ns_name = $node->lookupPrefix($ns); if ($ns_name) { $node->removeAttributeNS($ns, $ns_name); } return $dom->saveXML($node); } /** * Decodes the value * * @param string $value */ private static function decodeValue($value) { return Strings::base64_decode(str_replace(["\r", "\n", ' ', "\t"], '', $value)); } /** * Extract points from an XML document * * @param \DOMXPath $xpath * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @return object[] */ private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve $curve) { $x = self::query($xpath, 'publickey/x'); $y = self::query($xpath, 'publickey/y'); if (!$x->length || !$x->item(0)->hasAttribute('Value')) { throw new \RuntimeException('Public Key / X coordinate not found'); } if (!$y->length || !$y->item(0)->hasAttribute('Value')) { throw new \RuntimeException('Public Key / Y coordinate not found'); } $point = [ $curve->convertInteger(new BigInteger($x->item(0)->getAttribute('Value'))), $curve->convertInteger(new BigInteger($y->item(0)->getAttribute('Value'))) ]; if (!$curve->verifyPoint($point)) { throw new \RuntimeException('Unable to verify that point exists on curve'); } return $point; } /** * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based * on the curve parameters * * @param \DomXPath $xpath * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false */ private static function loadCurveByParam(\DOMXPath $xpath) { $namedCurve = self::query($xpath, 'namedcurve'); if ($namedCurve->length == 1) { $oid = $namedCurve->item(0)->getAttribute('URN'); $oid = preg_replace('#[^\d.]#', '', $oid); $name = array_search($oid, self::$curveOIDs); if ($name === false) { throw new UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported'); } $curve = '\phpseclib3\Crypt\EC\Curves\\' . $name; if (!class_exists($curve)) { throw new UnsupportedCurveException('Named Curve of ' . $name . ' is not supported'); } return new $curve(); } $params = self::query($xpath, 'explicitparams'); if ($params->length) { return self::loadCurveByParamRFC4050($xpath); } $params = self::query($xpath, 'ecparameters'); if (!$params->length) { throw new \RuntimeException('No parameters are present'); } $fieldTypes = [ 'prime-field' => ['fieldid/prime/p'], 'gnb' => ['fieldid/gnb/m'], 'tnb' => ['fieldid/tnb/k'], 'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'], 'unknown' => [] ]; foreach ($fieldTypes as $type => $queries) { foreach ($queries as $query) { $result = self::query($xpath, $query); if (!$result->length) { continue 2; } $param = preg_replace('#.*/#', '', $query); $$param = self::decodeValue($result->item(0)->textContent); } break; } $a = self::query($xpath, 'curve/a', 'A coefficient is not present'); $b = self::query($xpath, 'curve/b', 'B coefficient is not present'); $base = self::query($xpath, 'base', 'Base point is not present'); $order = self::query($xpath, 'order', 'Order is not present'); switch ($type) { case 'prime-field': $curve = new PrimeCurve(); $curve->setModulo(new BigInteger($p, 256)); $curve->setCoefficients( new BigInteger($a, 256), new BigInteger($b, 256) ); $point = self::extractPoint("\0" . $base, $curve); $curve->setBasePoint(...$point); $curve->setOrder(new BigInteger($order, 256)); return $curve; case 'gnb': case 'tnb': case 'pnb': default: throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported'); } } /** * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based * on the curve parameters * * @param \DomXPath $xpath * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false */ private static function loadCurveByParamRFC4050(\DOMXPath $xpath) { $fieldTypes = [ 'prime-field' => ['primefieldparamstype/p'], 'unknown' => [] ]; foreach ($fieldTypes as $type => $queries) { foreach ($queries as $query) { $result = self::query($xpath, $query); if (!$result->length) { continue 2; } $param = preg_replace('#.*/#', '', $query); $$param = $result->item(0)->textContent; } break; } $a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', false); $b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', false); $x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', false); $y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', false); $order = self::query($xpath, 'order', 'Order is not present', false); switch ($type) { case 'prime-field': $curve = new PrimeCurve(); $p = str_replace(["\r", "\n", ' ', "\t"], '', $p); $curve->setModulo(new BigInteger($p)); $a = str_replace(["\r", "\n", ' ', "\t"], '', $a); $b = str_replace(["\r", "\n", ' ', "\t"], '', $b); $curve->setCoefficients( new BigInteger($a), new BigInteger($b) ); $x = str_replace(["\r", "\n", ' ', "\t"], '', $x); $y = str_replace(["\r", "\n", ' ', "\t"], '', $y); $curve->setBasePoint( new BigInteger($x), new BigInteger($y) ); $order = str_replace(["\r", "\n", ' ', "\t"], '', $order); $curve->setOrder(new BigInteger($order)); return $curve; default: throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported'); } } /** * Sets the namespace. dsig11 is the most common one. * * Set to null to unset. Used only for creating public keys. * * @param string $namespace */ public static function setNamespace($namespace) { self::$namespace = $namespace; } /** * Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050 */ public static function enableRFC4050Syntax() { self::$rfc4050 = true; } /** * Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters */ public static function disableRFC4050Syntax() { self::$rfc4050 = false; } /** * Convert a public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param array $options optional * @return string */ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []) { self::initialize_static_variables(); if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) { throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported'); } if (empty(static::$namespace)) { $pre = $post = ''; } else { $pre = static::$namespace . ':'; $post = ':' . static::$namespace; } if (self::$rfc4050) { return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" . self::encodeXMLParameters($curve, $pre, $options) . "\r\n" . '<' . $pre . 'PublicKey>' . "\r\n" . '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" . '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" . '</' . $pre . 'PublicKey>' . "\r\n" . '</' . $pre . 'ECDSAKeyValue>'; } $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" . self::encodeXMLParameters($curve, $pre, $options) . "\r\n" . '<' . $pre . 'PublicKey>' . Strings::base64_encode($publicKey) . '</' . $pre . 'PublicKey>' . "\r\n" . '</' . $pre . 'ECDSAKeyValue>'; } /** * Encode Parameters * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param string $pre * @param array $options optional * @return string|false */ private static function encodeXMLParameters(BaseCurve $curve, $pre, array $options = []) { $result = self::encodeParameters($curve, true, $options); if (isset($result['namedCurve'])) { $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />'; return self::$rfc4050 ? '<DomainParameters>' . str_replace('URI', 'URN', $namedCurve) . '</DomainParameters>' : $namedCurve; } if (self::$rfc4050) { $xml = '<' . $pre . 'ExplicitParams>' . "\r\n" . '<' . $pre . 'FieldParams>' . "\r\n"; $temp = $result['specifiedCurve']; switch ($temp['fieldID']['fieldType']) { case 'prime-field': $xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" . '<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '</' . $pre . 'P>' . "\r\n" . '</' . $pre . 'PrimeFieldParamsType>' . "\r\n"; $a = $curve->getA(); $b = $curve->getB(); list($x, $y) = $curve->getBasePoint(); break; default: throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported'); } $xml .= '</' . $pre . 'FieldParams>' . "\r\n" . '<' . $pre . 'CurveParamsType>' . "\r\n" . '<' . $pre . 'A>' . $a . '</' . $pre . 'A>' . "\r\n" . '<' . $pre . 'B>' . $b . '</' . $pre . 'B>' . "\r\n" . '</' . $pre . 'CurveParamsType>' . "\r\n" . '<' . $pre . 'BasePointParams>' . "\r\n" . '<' . $pre . 'BasePoint>' . "\r\n" . '<' . $pre . 'ECPointType>' . "\r\n" . '<' . $pre . 'X>' . $x . '</' . $pre . 'X>' . "\r\n" . '<' . $pre . 'Y>' . $y . '</' . $pre . 'Y>' . "\r\n" . '</' . $pre . 'ECPointType>' . "\r\n" . '</' . $pre . 'BasePoint>' . "\r\n" . '<' . $pre . 'Order>' . $curve->getOrder() . '</' . $pre . 'Order>' . "\r\n" . '</' . $pre . 'BasePointParams>' . "\r\n" . '</' . $pre . 'ExplicitParams>' . "\r\n"; return $xml; } if (isset($result['specifiedCurve'])) { $xml = '<' . $pre . 'ECParameters>' . "\r\n" . '<' . $pre . 'FieldID>' . "\r\n"; $temp = $result['specifiedCurve']; switch ($temp['fieldID']['fieldType']) { case 'prime-field': $xml .= '<' . $pre . 'Prime>' . "\r\n" . '<' . $pre . 'P>' . Strings::base64_encode($temp['fieldID']['parameters']->toBytes()) . '</' . $pre . 'P>' . "\r\n" . '</' . $pre . 'Prime>' . "\r\n" ; break; default: throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported'); } $xml .= '</' . $pre . 'FieldID>' . "\r\n" . '<' . $pre . 'Curve>' . "\r\n" . '<' . $pre . 'A>' . Strings::base64_encode($temp['curve']['a']) . '</' . $pre . 'A>' . "\r\n" . '<' . $pre . 'B>' . Strings::base64_encode($temp['curve']['b']) . '</' . $pre . 'B>' . "\r\n" . '</' . $pre . 'Curve>' . "\r\n" . '<' . $pre . 'Base>' . Strings::base64_encode($temp['base']) . '</' . $pre . 'Base>' . "\r\n" . '<' . $pre . 'Order>' . Strings::base64_encode($temp['order']) . '</' . $pre . 'Order>' . "\r\n" . '</' . $pre . 'ECParameters>'; return $xml; } } } PK2A#]���Dvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.phpnu�[���<?php /** * "PKCS1" (RFC5915) Formatted EC Key Handler * * PHP version 5 * * Used by File/X509.php * * Processes keys with the following headers: * * -----BEGIN EC PRIVATE KEY----- * -----BEGIN EC PARAMETERS----- * * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe * DSA, whose format isn't really formally described anywhere, so might as well * use it to describe this, too. PKCS1 is easier to remember than RFC5915, after * all. I suppose this could also be named IETF but idk * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * "PKCS1" (RFC5915) Formatted EC Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS1 extends Progenitor { use Common; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { self::initialize_static_variables(); if (!Strings::is_stringable($key)) { throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); } if (strpos($key, 'BEGIN EC PARAMETERS') && strpos($key, 'BEGIN EC PRIVATE KEY')) { $components = []; preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches); $decoded = parent::load($matches[0], $password); $decoded = ASN1::decodeBER($decoded); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $ecPrivate = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP); if (!is_array($ecPrivate)) { throw new \RuntimeException('Unable to perform ASN1 mapping'); } if (isset($ecPrivate['parameters'])) { $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']); } preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches); $decoded = parent::load($matches[0], ''); $decoded = ASN1::decodeBER($decoded); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); if (!is_array($ecParams)) { throw new \RuntimeException('Unable to perform ASN1 mapping'); } $ecParams = self::loadCurveByParam($ecParams); // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes // even if the modulo is the same if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) { throw new \RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY'); } if (!isset($components['curve'])) { $components['curve'] = $ecParams; } $components['dA'] = new BigInteger($ecPrivate['privateKey'], 256); $components['curve']->rangeCheck($components['dA']); $components['QA'] = isset($ecPrivate['publicKey']) ? self::extractPoint($ecPrivate['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); return $components; } $key = parent::load($key, $password); $decoded = ASN1::decodeBER($key); if (!$decoded) { throw new \RuntimeException('Unable to decode BER'); } $key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); if (is_array($key)) { return ['curve' => self::loadCurveByParam($key)]; } $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP); if (!is_array($key)) { throw new \RuntimeException('Unable to perform ASN1 mapping'); } if (!isset($key['parameters'])) { throw new \RuntimeException('Key cannot be loaded without parameters'); } $components = []; $components['curve'] = self::loadCurveByParam($key['parameters']); $components['dA'] = new BigInteger($key['privateKey'], 256); $components['QA'] = isset($ecPrivate['publicKey']) ? self::extractPoint($ecPrivate['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); return $components; } /** * Convert EC parameters to the appropriate format * * @return string */ public static function saveParameters(BaseCurve $curve, array $options = []) { self::initialize_static_variables(); if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) { throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported'); } $key = self::encodeParameters($curve, false, $options); return "-----BEGIN EC PARAMETERS-----\r\n" . chunk_split(Strings::base64_encode($key), 64) . "-----END EC PARAMETERS-----\r\n"; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { self::initialize_static_variables(); if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) { throw new UnsupportedCurveException('TwistedEdwards Curves are not supported'); } $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); $key = [ 'version' => 'ecPrivkeyVer1', 'privateKey' => $privateKey->toBytes(), 'parameters' => new ASN1\Element(self::encodeParameters($curve)), 'publicKey' => "\0" . $publicKey ]; $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP); return self::wrapPrivateKey($key, 'EC', $password, $options); } } PK2A#]�D����Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.phpnu�[���<?php /** * JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Crypt\EC\Curves\secp256k1; use phpseclib3\Crypt\EC\Curves\secp256r1; use phpseclib3\Crypt\EC\Curves\secp384r1; use phpseclib3\Crypt\EC\Curves\secp521r1; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Math\BigInteger; /** * JWK Formatted EC Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class JWK extends Progenitor { use Common; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $key = parent::load($key, $password); switch ($key->kty) { case 'EC': switch ($key->crv) { case 'P-256': case 'P-384': case 'P-521': case 'secp256k1': break; default: throw new UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)'); } break; case 'OKP': switch ($key->crv) { case 'Ed25519': case 'Ed448': break; default: throw new UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)'); } break; default: throw new \Exception('Only EC and OKP JWK keys are supported'); } $curve = '\phpseclib3\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv); $curve = new $curve(); if ($curve instanceof TwistedEdwardsCurve) { $QA = self::extractPoint(Strings::base64url_decode($key->x), $curve); if (!isset($key->d)) { return compact('curve', 'QA'); } $arr = $curve->extractSecret(Strings::base64url_decode($key->d)); return compact('curve', 'QA') + $arr; } $QA = [ $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->x), 256)), $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->y), 256)) ]; if (!$curve->verifyPoint($QA)) { throw new \RuntimeException('Unable to verify that point exists on curve'); } if (!isset($key->d)) { return compact('curve', 'QA'); } $dA = new BigInteger(Strings::base64url_decode($key->d), 256); $curve->rangeCheck($dA); return compact('curve', 'dA', 'QA'); } /** * Returns the alias that corresponds to a curve * * @return string */ private static function getAlias(BaseCurve $curve) { switch (true) { case $curve instanceof secp256r1: return 'P-256'; case $curve instanceof secp384r1: return 'P-384'; case $curve instanceof secp521r1: return 'P-521'; case $curve instanceof secp256k1: return 'secp256k1'; } $reflect = new \ReflectionClass($curve); $curveName = $reflect->isFinal() ? $reflect->getParentClass()->getShortName() : $reflect->getShortName(); throw new UnsupportedCurveException("$curveName is not a supported curve"); } /** * Return the array superstructure for an EC public key * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @return array */ private static function savePublicKeyHelper(BaseCurve $curve, array $publicKey) { if ($curve instanceof TwistedEdwardsCurve) { return [ 'kty' => 'OKP', 'crv' => $curve instanceof Ed25519 ? 'Ed25519' : 'Ed448', 'x' => Strings::base64url_encode($curve->encodePoint($publicKey)) ]; } return [ 'kty' => 'EC', 'crv' => self::getAlias($curve), 'x' => Strings::base64url_encode($publicKey[0]->toBytes()), 'y' => Strings::base64url_encode($publicKey[1]->toBytes()) ]; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param array $options optional * @return string */ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []) { $key = self::savePublicKeyHelper($curve, $publicKey); return self::wrapKey($key, $options); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { $key = self::savePublicKeyHelper($curve, $publicKey); $key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes(); $key['d'] = Strings::base64url_encode($key['d']); return self::wrapKey($key, $options); } } PK2A#]tB��Fvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.phpnu�[���<?php /** * OpenSSH Formatted EC Key Handler * * PHP version 5 * * Place in $HOME/.ssh/authorized_keys * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Math\BigInteger; /** * OpenSSH Formatted EC Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OpenSSH extends Progenitor { use Common; /** * Supported Key Types * * @var array */ protected static $types = [ 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519' ]; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $parsed = parent::load($key, $password); if (isset($parsed['paddedKey'])) { $paddedKey = $parsed['paddedKey']; list($type) = Strings::unpackSSH2('s', $paddedKey); if ($type != $parsed['type']) { throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])"); } if ($type == 'ssh-ed25519') { list(, $key, $comment) = Strings::unpackSSH2('sss', $paddedKey); $key = libsodium::load($key); $key['comment'] = $comment; return $key; } list($curveName, $publicKey, $privateKey, $comment) = Strings::unpackSSH2('ssis', $paddedKey); $curve = self::loadCurveByParam(['namedCurve' => $curveName]); $curve->rangeCheck($privateKey); return [ 'curve' => $curve, 'dA' => $privateKey, 'QA' => self::extractPoint("\0$publicKey", $curve), 'comment' => $comment ]; } if ($parsed['type'] == 'ssh-ed25519') { if (Strings::shift($parsed['publicKey'], 4) != "\0\0\0\x20") { throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); } $curve = new Ed25519(); $qa = self::extractPoint($parsed['publicKey'], $curve); } else { list($curveName, $publicKey) = Strings::unpackSSH2('ss', $parsed['publicKey']); $curveName = '\phpseclib3\Crypt\EC\Curves\\' . $curveName; $curve = new $curveName(); $qa = self::extractPoint("\0" . $publicKey, $curve); } return [ 'curve' => $curve, 'QA' => $qa, 'comment' => $parsed['comment'] ]; } /** * Returns the alias that corresponds to a curve * * @return string */ private static function getAlias(BaseCurve $curve) { self::initialize_static_variables(); $reflect = new \ReflectionClass($curve); $name = $reflect->getShortName(); $oid = self::$curveOIDs[$name]; $aliases = array_filter(self::$curveOIDs, function ($v) use ($oid) { return $v == $oid; }); $aliases = array_keys($aliases); for ($i = 0; $i < count($aliases); $i++) { if (in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) { $alias = $aliases[$i]; break; } } if (!isset($alias)) { throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports'); } return $alias; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param array $options optional * @return string */ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []) { $comment = isset($options['comment']) ? $options['comment'] : self::$comment; if ($curve instanceof Ed25519) { $key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey)); if (isset($options['binary']) ? $options['binary'] : self::$binary) { return $key; } $key = 'ssh-ed25519 ' . base64_encode($key) . ' ' . $comment; return $key; } $alias = self::getAlias($curve); $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); $key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points); if (isset($options['binary']) ? $options['binary'] : self::$binary) { return $key; } $key = 'ecdsa-sha2-' . $alias . ' ' . base64_encode($key) . ' ' . $comment; return $key; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = []) { if ($curve instanceof Ed25519) { if (!isset($secret)) { throw new \RuntimeException('Private Key does not have a secret set'); } if (strlen($secret) != 32) { throw new \RuntimeException('Private Key secret is not of the correct length'); } $pubKey = $curve->encodePoint($publicKey); $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey); $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey); return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); } $alias = self::getAlias($curve); $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); $publicKey = self::savePublicKey($curve, $publicKey, ['binary' => true]); $privateKey = Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey); return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); } } PK2A#]�� Pvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.phpnu�[���<?php /** * Montgomery Private Key Handler * * "Naked" Curve25519 private keys can pretty much be any sequence of random 32x bytes so unless * we have a "hidden" key handler pretty much every 32 byte string will be loaded as a curve25519 * private key even if it probably isn't one by PublicKeyLoader. * * "Naked" Curve25519 public keys also a string of 32 bytes so distinguishing between a "naked" * curve25519 private key and a public key is nigh impossible, hence separate plugins for each * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\Curves\Curve25519; use phpseclib3\Crypt\EC\Curves\Curve448; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * Montgomery Curve Private Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class MontgomeryPrivate { /** * Is invisible flag * */ const IS_INVISIBLE = true; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { switch (strlen($key)) { case 32: $curve = new Curve25519(); break; case 56: $curve = new Curve448(); break; default: throw new \LengthException('The only supported lengths are 32 and 56'); } $components = ['curve' => $curve]; $components['dA'] = new BigInteger($key, 256); $curve->rangeCheck($components['dA']); // note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result) $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); return $components; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @return string */ public static function savePublicKey(MontgomeryCurve $curve, array $publicKey) { return strrev($publicKey[0]->toBytes()); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, MontgomeryCurve $curve, array $publicKey, $secret = null, $password = '') { if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption'); } return $privateKey->toBytes(); } } PK2A#]V�H�[�[Evendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.phpnu�[���<?php /** * Generic EC Key Parsing Helper functions * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve; use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * Generic EC Key Parsing Helper functions * * @author Jim Wigginton <terrafrost@php.net> */ trait Common { /** * Curve OIDs * * @var array */ private static $curveOIDs = []; /** * Child OIDs loaded * * @var bool */ protected static $childOIDsLoaded = false; /** * Use Named Curves * * @var bool */ private static $useNamedCurves = true; /** * Initialize static variables */ private static function initialize_static_variables() { if (empty(self::$curveOIDs)) { // the sec* curves are from the standards for efficient cryptography group // sect* curves are curves over binary finite fields // secp* curves are curves over prime finite fields // sec*r* curves are regular curves; sec*k* curves are koblitz curves // brainpool*r* curves are regular prime finite field curves // brainpool*t* curves are twisted versions of the brainpool*r* curves self::$curveOIDs = [ 'prime192v1' => '1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1) 'prime192v2' => '1.2.840.10045.3.1.2', // J.5.1, example 2 'prime192v3' => '1.2.840.10045.3.1.3', // J.5.1, example 3 'prime239v1' => '1.2.840.10045.3.1.4', // J.5.2, example 1 'prime239v2' => '1.2.840.10045.3.1.5', // J.5.2, example 2 'prime239v3' => '1.2.840.10045.3.1.6', // J.5.2, example 3 'prime256v1' => '1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1) // https://tools.ietf.org/html/rfc5656#section-10 'nistp256' => '1.2.840.10045.3.1.7', // aka secp256r1 'nistp384' => '1.3.132.0.34', // aka secp384r1 'nistp521' => '1.3.132.0.35', // aka secp521r1 'nistk163' => '1.3.132.0.1', // aka sect163k1 'nistp192' => '1.2.840.10045.3.1.1', // aka secp192r1 'nistp224' => '1.3.132.0.33', // aka secp224r1 'nistk233' => '1.3.132.0.26', // aka sect233k1 'nistb233' => '1.3.132.0.27', // aka sect233r1 'nistk283' => '1.3.132.0.16', // aka sect283k1 'nistk409' => '1.3.132.0.36', // aka sect409k1 'nistb409' => '1.3.132.0.37', // aka sect409r1 'nistt571' => '1.3.132.0.38', // aka sect571k1 // from https://tools.ietf.org/html/rfc5915 'secp192r1' => '1.2.840.10045.3.1.1', // aka prime192v1 'sect163k1' => '1.3.132.0.1', 'sect163r2' => '1.3.132.0.15', 'secp224r1' => '1.3.132.0.33', 'sect233k1' => '1.3.132.0.26', 'sect233r1' => '1.3.132.0.27', 'secp256r1' => '1.2.840.10045.3.1.7', // aka prime256v1 'sect283k1' => '1.3.132.0.16', 'sect283r1' => '1.3.132.0.17', 'secp384r1' => '1.3.132.0.34', 'sect409k1' => '1.3.132.0.36', 'sect409r1' => '1.3.132.0.37', 'secp521r1' => '1.3.132.0.35', 'sect571k1' => '1.3.132.0.38', 'sect571r1' => '1.3.132.0.39', // from http://www.secg.org/SEC2-Ver-1.0.pdf 'secp112r1' => '1.3.132.0.6', 'secp112r2' => '1.3.132.0.7', 'secp128r1' => '1.3.132.0.28', 'secp128r2' => '1.3.132.0.29', 'secp160k1' => '1.3.132.0.9', 'secp160r1' => '1.3.132.0.8', 'secp160r2' => '1.3.132.0.30', 'secp192k1' => '1.3.132.0.31', 'secp224k1' => '1.3.132.0.32', 'secp256k1' => '1.3.132.0.10', 'sect113r1' => '1.3.132.0.4', 'sect113r2' => '1.3.132.0.5', 'sect131r1' => '1.3.132.0.22', 'sect131r2' => '1.3.132.0.23', 'sect163r1' => '1.3.132.0.2', 'sect193r1' => '1.3.132.0.24', 'sect193r2' => '1.3.132.0.25', 'sect239k1' => '1.3.132.0.3', // from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36 /* 'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1 'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2 'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3 'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1 'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1 'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2 'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3 'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4 'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5 'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1 'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1 'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2 'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3 'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4 'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5 'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1 'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1 'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1 'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1 'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1 */ // http://www.ecc-brainpool.org/download/Domain-parameters.pdf // https://tools.ietf.org/html/rfc5639 'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1', 'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2', 'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3', 'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4', 'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5', 'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6', 'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7', 'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8', 'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9', 'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10', 'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11', 'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12', 'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13', 'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14' ]; ASN1::loadOIDs([ 'prime-field' => '1.2.840.10045.1.1', 'characteristic-two-field' => '1.2.840.10045.1.2', 'characteristic-two-basis' => '1.2.840.10045.1.2.3', // per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here" 'gnBasis' => '1.2.840.10045.1.2.3.1', // NULL 'tpBasis' => '1.2.840.10045.1.2.3.2', // Trinomial 'ppBasis' => '1.2.840.10045.1.2.3.3' // Pentanomial ] + self::$curveOIDs); } } /** * Explicitly set the curve * * If the key contains an implicit curve phpseclib needs the curve * to be explicitly provided * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve */ public static function setImplicitCurve(BaseCurve $curve) { self::$implicitCurve = $curve; } /** * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based * on the curve parameters * * @param array $params * @return \phpseclib3\Crypt\EC\BaseCurves\Base|false */ protected static function loadCurveByParam(array $params) { if (count($params) > 1) { throw new \RuntimeException('No parameters are present'); } if (isset($params['namedCurve'])) { $curve = '\phpseclib3\Crypt\EC\Curves\\' . $params['namedCurve']; if (!class_exists($curve)) { throw new UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported'); } return new $curve(); } if (isset($params['implicitCurve'])) { if (!isset(self::$implicitCurve)) { throw new \RuntimeException('Implicit curves can be provided by calling setImplicitCurve'); } return self::$implicitCurve; } if (isset($params['specifiedCurve'])) { $data = $params['specifiedCurve']; switch ($data['fieldID']['fieldType']) { case 'prime-field': $curve = new PrimeCurve(); $curve->setModulo($data['fieldID']['parameters']); $curve->setCoefficients( new BigInteger($data['curve']['a'], 256), new BigInteger($data['curve']['b'], 256) ); $point = self::extractPoint("\0" . $data['base'], $curve); $curve->setBasePoint(...$point); $curve->setOrder($data['order']); return $curve; case 'characteristic-two-field': $curve = new BinaryCurve(); $params = ASN1::decodeBER($data['fieldID']['parameters']); $params = ASN1::asn1map($params[0], Maps\Characteristic_two::MAP); $modulo = [(int) $params['m']->toString()]; switch ($params['basis']) { case 'tpBasis': $modulo[] = (int) $params['parameters']->toString(); break; case 'ppBasis': $temp = ASN1::decodeBER($params['parameters']); $temp = ASN1::asn1map($temp[0], Maps\Pentanomial::MAP); $modulo[] = (int) $temp['k3']->toString(); $modulo[] = (int) $temp['k2']->toString(); $modulo[] = (int) $temp['k1']->toString(); } $modulo[] = 0; $curve->setModulo(...$modulo); $len = ceil($modulo[0] / 8); $curve->setCoefficients( Strings::bin2hex($data['curve']['a']), Strings::bin2hex($data['curve']['b']) ); $point = self::extractPoint("\0" . $data['base'], $curve); $curve->setBasePoint(...$point); $curve->setOrder($data['order']); return $curve; default: throw new UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported'); } } throw new \RuntimeException('No valid parameters are present'); } /** * Extract points from a string * * Supports both compressed and uncompressed points * * @param string $str * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @return object[] */ public static function extractPoint($str, BaseCurve $curve) { if ($curve instanceof TwistedEdwardsCurve) { // first step of point deciding as discussed at the following URL's: // https://tools.ietf.org/html/rfc8032#section-5.1.3 // https://tools.ietf.org/html/rfc8032#section-5.2.3 $y = $str; $y = strrev($y); $sign = (bool) (ord($y[0]) & 0x80); $y[0] = $y[0] & chr(0x7F); $y = new BigInteger($y, 256); if ($y->compare($curve->getModulo()) >= 0) { throw new \RuntimeException('The Y coordinate should not be >= the modulo'); } $point = $curve->recoverX($y, $sign); if (!$curve->verifyPoint($point)) { throw new \RuntimeException('Unable to verify that point exists on curve'); } return $point; } // the first byte of a bit string represents the number of bits in the last byte that are to be ignored but, // currently, bit strings wanting a non-zero amount of bits trimmed are not supported if (($val = Strings::shift($str)) != "\0") { throw new \UnexpectedValueException('extractPoint expects the first byte to be null - not ' . Strings::bin2hex($val)); } if ($str == "\0") { return []; } $keylen = strlen($str); $order = $curve->getLengthInBytes(); // point compression is being used if ($keylen == $order + 1) { return $curve->derivePoint($str); } // point compression is not being used if ($keylen == 2 * $order + 1) { preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches); list(, $w, $x, $y) = $matches; if ($w != "\4") { throw new \UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . Strings::bin2hex($val)); } $point = [ $curve->convertInteger(new BigInteger($x, 256)), $curve->convertInteger(new BigInteger($y, 256)) ]; if (!$curve->verifyPoint($point)) { throw new \RuntimeException('Unable to verify that point exists on curve'); } return $point; } throw new \UnexpectedValueException('The string representation of the points is not of an appropriate length'); } /** * Encode Parameters * * @todo Maybe at some point this could be moved to __toString() for each of the curves? * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param bool $returnArray optional * @param array $options optional * @return string|false */ private static function encodeParameters(BaseCurve $curve, $returnArray = false, array $options = []) { $useNamedCurves = isset($options['namedCurve']) ? $options['namedCurve'] : self::$useNamedCurves; $reflect = new \ReflectionClass($curve); $name = $reflect->getShortName(); if ($useNamedCurves) { if (isset(self::$curveOIDs[$name])) { if ($reflect->isFinal()) { $reflect = $reflect->getParentClass(); $name = $reflect->getShortName(); } return $returnArray ? ['namedCurve' => $name] : ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP); } foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) { if ($file->getExtension() != 'php') { continue; } $testName = $file->getBasename('.php'); $class = 'phpseclib3\Crypt\EC\Curves\\' . $testName; $reflect = new \ReflectionClass($class); if ($reflect->isFinal()) { continue; } $candidate = new $class(); switch ($name) { case 'Prime': if (!$candidate instanceof PrimeCurve) { break; } if (!$candidate->getModulo()->equals($curve->getModulo())) { break; } if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) { break; } if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) { break; } list($candidateX, $candidateY) = $candidate->getBasePoint(); list($curveX, $curveY) = $curve->getBasePoint(); if ($candidateX->toBytes() != $curveX->toBytes()) { break; } if ($candidateY->toBytes() != $curveY->toBytes()) { break; } return $returnArray ? ['namedCurve' => $testName] : ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP); case 'Binary': if (!$candidate instanceof BinaryCurve) { break; } if ($candidate->getModulo() != $curve->getModulo()) { break; } if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) { break; } if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) { break; } list($candidateX, $candidateY) = $candidate->getBasePoint(); list($curveX, $curveY) = $curve->getBasePoint(); if ($candidateX->toBytes() != $curveX->toBytes()) { break; } if ($candidateY->toBytes() != $curveY->toBytes()) { break; } return $returnArray ? ['namedCurve' => $testName] : ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP); } } } $order = $curve->getOrder(); // we could try to calculate the order thusly: // https://crypto.stackexchange.com/a/27914/4520 // https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm if (!$order) { throw new \RuntimeException('Specified Curves need the order to be specified'); } $point = $curve->getBasePoint(); $x = $point[0]->toBytes(); $y = $point[1]->toBytes(); if ($curve instanceof PrimeCurve) { /* * valid versions are: * * ecdpVer1: * - neither the curve or the base point are generated verifiably randomly. * ecdpVer2: * - curve and base point are generated verifiably at random and curve.seed is present * ecdpVer3: * - base point is generated verifiably at random but curve is not. curve.seed is present */ // other (optional) parameters can be calculated using the methods discused at // https://crypto.stackexchange.com/q/28947/4520 $data = [ 'version' => 'ecdpVer1', 'fieldID' => [ 'fieldType' => 'prime-field', 'parameters' => $curve->getModulo() ], 'curve' => [ 'a' => $curve->getA()->toBytes(), 'b' => $curve->getB()->toBytes() ], 'base' => "\4" . $x . $y, 'order' => $order ]; return $returnArray ? ['specifiedCurve' => $data] : ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP); } if ($curve instanceof BinaryCurve) { $modulo = $curve->getModulo(); $basis = count($modulo); $m = array_shift($modulo); array_pop($modulo); // the last parameter should always be 0 //rsort($modulo); switch ($basis) { case 3: $basis = 'tpBasis'; $modulo = new BigInteger($modulo[0]); break; case 5: $basis = 'ppBasis'; // these should be in strictly ascending order (hence the commented out rsort above) $modulo = [ 'k1' => new BigInteger($modulo[2]), 'k2' => new BigInteger($modulo[1]), 'k3' => new BigInteger($modulo[0]) ]; $modulo = ASN1::encodeDER($modulo, Maps\Pentanomial::MAP); $modulo = new ASN1\Element($modulo); } $params = ASN1::encodeDER([ 'm' => new BigInteger($m), 'basis' => $basis, 'parameters' => $modulo ], Maps\Characteristic_two::MAP); $params = new ASN1\Element($params); $a = ltrim($curve->getA()->toBytes(), "\0"); if (!strlen($a)) { $a = "\0"; } $b = ltrim($curve->getB()->toBytes(), "\0"); if (!strlen($b)) { $b = "\0"; } $data = [ 'version' => 'ecdpVer1', 'fieldID' => [ 'fieldType' => 'characteristic-two-field', 'parameters' => $params ], 'curve' => [ 'a' => $a, 'b' => $b ], 'base' => "\4" . $x . $y, 'order' => $order ]; return $returnArray ? ['specifiedCurve' => $data] : ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP); } throw new UnsupportedCurveException('Curve cannot be serialized'); } /** * Use Specified Curve * * A specified curve has all the coefficients, the base points, etc, explicitely included. * A specified curve is a more verbose way of representing a curve */ public static function useSpecifiedCurve() { self::$useNamedCurves = false; } /** * Use Named Curve * * A named curve does not include any parameters. It is up to the EC parameters to * know what the coefficients, the base points, etc, are from the name of the curve. * A named curve is a more concise way of representing a curve */ public static function useNamedCurve() { self::$useNamedCurves = true; } } PK2A#]���yKKHvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.phpnu�[���<?php /** * libsodium Key Handler * * Different NaCl implementations store the key differently. * https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates. * libsodium appears to use the same format as SUPERCOP. * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Exception\UnsupportedFormatException; use phpseclib3\Math\BigInteger; /** * libsodium Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class libsodium { use Common; /** * Is invisible flag * */ const IS_INVISIBLE = true; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { switch (strlen($key)) { case 32: $public = $key; break; case 64: $private = substr($key, 0, 32); $public = substr($key, -32); break; case 96: $public = substr($key, -32); if (substr($key, 32, 32) != $public) { throw new \RuntimeException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match'); } $private = substr($key, 0, 32); break; default: throw new \RuntimeException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long'); } $curve = new Ed25519(); $components = ['curve' => $curve]; if (isset($private)) { $arr = $curve->extractSecret($private); $components['dA'] = $arr['dA']; $components['secret'] = $arr['secret']; } $components['QA'] = isset($public) ? self::extractPoint($public, $curve) : $curve->multiplyPoint($curve->getBasePoint(), $components['dA']); return $components; } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @return string */ public static function savePublicKey(Ed25519 $curve, array $publicKey) { return $curve->encodePoint($publicKey); } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, $secret = null, $password = '') { if (!isset($secret)) { throw new \RuntimeException('Private Key does not have a secret set'); } if (strlen($secret) != 32) { throw new \RuntimeException('Private Key secret is not of the correct length'); } if (!empty($password) && is_string($password)) { throw new UnsupportedFormatException('libsodium private keys do not support encryption'); } return $secret . $curve->encodePoint($publicKey); } } PK2A#]�Ow$��Dvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.phpnu�[���<?php /** * PuTTY Formatted EC Key Handler * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt\EC\Formats\Keys; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Math\BigInteger; /** * PuTTY Formatted EC Key Handler * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PuTTY extends Progenitor { use Common; /** * Public Handler * * @var string */ const PUBLIC_HANDLER = 'phpseclib3\Crypt\EC\Formats\Keys\OpenSSH'; /** * Supported Key Types * * @var array */ protected static $types = [ 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519' ]; /** * Break a public or private key down into its constituent components * * @param string $key * @param string $password optional * @return array */ public static function load($key, $password = '') { $components = parent::load($key, $password); if (!isset($components['private'])) { return $components; } $private = $components['private']; $temp = Strings::base64_encode(Strings::packSSH2('s', $components['type']) . $components['public']); $components = OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']); if ($components['curve'] instanceof TwistedEdwardsCurve) { if (Strings::shift($private, 4) != "\0\0\0\x20") { throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); } $arr = $components['curve']->extractSecret($private); $components['dA'] = $arr['dA']; $components['secret'] = $arr['secret']; } else { list($components['dA']) = Strings::unpackSSH2('i', $private); $components['curve']->rangeCheck($components['dA']); } return $components; } /** * Convert a private key to the appropriate format. * * @param \phpseclib3\Math\BigInteger $privateKey * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey * @param string $secret optional * @param string $password optional * @param array $options optional * @return string */ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = false, array $options = []) { self::initialize_static_variables(); $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); $name = $public[0]; $public = Strings::base64_decode($public[1]); list(, $length) = unpack('N', Strings::shift($public, 4)); Strings::shift($public, $length); // PuTTY pads private keys with a null byte per the following: // https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926 if (!$curve instanceof TwistedEdwardsCurve) { $private = $privateKey->toBytes(); if (!(strlen($privateKey->toBits()) & 7)) { $private = "\0$private"; } } $private = $curve instanceof TwistedEdwardsCurve ? Strings::packSSH2('s', $secret) : Strings::packSSH2('s', $private); return self::wrapPrivateKey($public, $private, $name, $password, $options); } /** * Convert an EC public key to the appropriate format * * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve * @param \phpseclib3\Math\Common\FiniteField[] $publicKey * @return string */ public static function savePublicKey(BaseCurve $curve, array $publicKey) { $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); $type = $public[0]; $public = Strings::base64_decode($public[1]); list(, $length) = unpack('N', Strings::shift($public, 4)); Strings::shift($public, $length); return self::wrapPublicKey($public, $type); } } PK2A#]J�x�ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.phpnu�[���<?php /** * nistp384 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistp384 extends secp384r1 { } PK2A#]h:Ou��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.phpnu�[���<?php /** * brainpoolP512t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP512t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger( 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3', 16 )); $this->setCoefficients( new BigInteger( 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0', 16 ), // eg. -3 new BigInteger( '7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA23049' . '76540F6450085F2DAE145C22553B465763689180EA2571867423E', 16 ) ); $this->setBasePoint( new BigInteger( '640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CD' . 'B3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA', 16 ), new BigInteger( '5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEE' . 'F216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332', 16 ) ); $this->setOrder(new BigInteger( 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' . '92619418661197FAC10471DB1D381085DDADDB58796829CA90069', 16 )); } } PK2A#]V�h��Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.phpnu�[���<?php /** * prime239v3 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class prime239v3 extends Prime { public function __construct() { $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new BigInteger('255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E', 16) ); $this->setBasePoint( new BigInteger('6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A', 16), new BigInteger('1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3', 16) ); $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551', 16)); } } PK2A#]l��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.phpnu�[���<?php /** * sect163r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect163r2 extends Binary { public function __construct() { $this->setModulo(163, 7, 6, 3, 0); $this->setCoefficients( '000000000000000000000000000000000000000001', '020A601907B8C953CA1481EB10512F78744A3205FD' ); $this->setBasePoint( '03F0EBA16286A2D57EA0991168D4994637E8343E36', '00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1' ); $this->setOrder(new BigInteger('040000000000000000000292FE77E70C12A4234C33', 16)); } } PK2A#]E3���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.phpnu�[���<?php /** * sect239k1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect239k1 extends Binary { public function __construct() { $this->setModulo(239, 158, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000001' ); $this->setBasePoint( '29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC', '76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA' ); $this->setOrder(new BigInteger('2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5', 16)); } } PK2A#]��*���>vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.phpnu�[���<?php /** * Ed448 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\Random; use phpseclib3\Math\BigInteger; class Ed448 extends TwistedEdwards { const HASH = 'shake256-912'; const SIZE = 57; public function __construct() { // 2^448 - 2^224 - 1 $this->setModulo(new BigInteger( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16 )); $this->setCoefficients( new BigInteger(1), // -39081 new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756', 16) ); $this->setBasePoint( new BigInteger('4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324' . 'A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E', 16), new BigInteger('693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E' . '05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14', 16) ); $this->setOrder(new BigInteger( '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3', 16 )); } /** * Recover X from Y * * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.2.3 * * Used by EC\Keys\Common.php * * @param BigInteger $y * @param boolean $sign * @return object[] */ public function recoverX(BigInteger $y, $sign) { $y = $this->factory->newInteger($y); $y2 = $y->multiply($y); $u = $y2->subtract($this->one); $v = $this->d->multiply($y2)->subtract($this->one); $x2 = $u->divide($v); if ($x2->equals($this->zero)) { if ($sign) { throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)'); } return clone $this->zero; } // find the square root $exp = $this->getModulo()->add(new BigInteger(1)); $exp = $exp->bitwise_rightShift(2); $x = $x2->pow($exp); if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { throw new \RuntimeException('Unable to recover X coordinate'); } if ($x->isOdd() != $sign) { $x = $x->negate(); } return [$x, $y]; } /** * Extract Secret Scalar * * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.2.5 * * Used by the various key handlers * * @param string $str * @return array */ public function extractSecret($str) { if (strlen($str) != 57) { throw new \LengthException('Private Key should be 57-bytes long'); } // 1. Hash the 57-byte private key using SHAKE256(x, 114), storing the // digest in a 114-octet large buffer, denoted h. Only the lower 57 // bytes are used for generating the public key. $hash = new Hash('shake256-912'); $h = $hash->hash($str); $h = substr($h, 0, 57); // 2. Prune the buffer: The two least significant bits of the first // octet are cleared, all eight bits the last octet are cleared, and // the highest bit of the second to last octet is set. $h[0] = $h[0] & chr(0xFC); $h = strrev($h); $h[0] = "\0"; $h[1] = $h[1] | chr(0x80); // 3. Interpret the buffer as the little-endian integer, forming a // secret scalar s. $dA = new BigInteger($h, 256); return [ 'dA' => $dA, 'secret' => $str ]; $dA->secret = $str; return $dA; } /** * Encode a point as a string * * @param array $point * @return string */ public function encodePoint($point) { list($x, $y) = $point; $y = "\0" . $y->toBytes(); if ($x->isOdd()) { $y[0] = $y[0] | chr(0x80); } $y = strrev($y); return $y; } /** * Creates a random scalar multiplier * * @return \phpseclib3\Math\PrimeField\Integer */ public function createRandomMultiplier() { return $this->extractSecret(Random::string(57))['dA']; } /** * Converts an affine point to an extended homogeneous coordinate * * From https://tools.ietf.org/html/rfc8032#section-5.2.4 : * * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T), * with x = X/Z, y = Y/Z, x * y = T/Z. * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToInternal(array $p) { if (empty($p)) { return [clone $this->zero, clone $this->one, clone $this->one]; } if (isset($p[2])) { return $p; } $p[2] = clone $this->one; return $p; } /** * Doubles a point on a curve * * @return FiniteField[] */ public function doublePoint(array $p) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p)) { return []; } if (!isset($p[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } // from https://tools.ietf.org/html/rfc8032#page-18 list($x1, $y1, $z1) = $p; $b = $x1->add($y1); $b = $b->multiply($b); $c = $x1->multiply($x1); $d = $y1->multiply($y1); $e = $c->add($d); $h = $z1->multiply($z1); $j = $e->subtract($this->two->multiply($h)); $x3 = $b->subtract($e)->multiply($j); $y3 = $c->subtract($d)->multiply($e); $z3 = $e->multiply($j); return [$x3, $y3, $z3]; } /** * Adds two points on the curve * * @return FiniteField[] */ public function addPoint(array $p, array $q) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p) || !count($q)) { if (count($q)) { return $q; } if (count($p)) { return $p; } return []; } if (!isset($p[2]) || !isset($q[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } if ($p[0]->equals($q[0])) { return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); } // from https://tools.ietf.org/html/rfc8032#page-17 list($x1, $y1, $z1) = $p; list($x2, $y2, $z2) = $q; $a = $z1->multiply($z2); $b = $a->multiply($a); $c = $x1->multiply($x2); $d = $y1->multiply($y2); $e = $this->d->multiply($c)->multiply($d); $f = $b->subtract($e); $g = $b->add($e); $h = $x1->add($y1)->multiply($x2->add($y2)); $x3 = $a->multiply($f)->multiply($h->subtract($c)->subtract($d)); $y3 = $a->multiply($g)->multiply($d->subtract($c)); $z3 = $f->multiply($g); return [$x3, $y3, $z3]; } } PK2A#]& G���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.phpnu�[���<?php /** * secp521r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp521r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFF', 16)); $this->setCoefficients( new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFC', 16), new BigInteger('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF1' . '09E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B50' . '3F00', 16) ); $this->setBasePoint( new BigInteger('00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D' . '3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5' . 'BD66', 16), new BigInteger('011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E' . '662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD1' . '6650', 16) ); $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E9138' . '6409', 16)); } } PK2A#]ԋ���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.phpnu�[���<?php /** * sect409k1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect409k1 extends Binary { public function __construct() { $this->setModulo(409, 87, 0); $this->setCoefficients( '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' ); $this->setBasePoint( '0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746', '01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B' ); $this->setOrder(new BigInteger( '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F' . '83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF', 16 )); } } PK2A#]5�MppBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.phpnu�[���<?php /** * sect131r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect131r1 extends Binary { public function __construct() { $this->setModulo(131, 8, 3, 2, 0); $this->setCoefficients( '07A11B09A76B562144418FF3FF8C2570B8', '0217C05610884B63B9C6C7291678F9D341' ); $this->setBasePoint( '0081BAF91FDF9833C40F9C181343638399', '078C6E7EA38C001F73C8134B1B4EF9E150' ); $this->setOrder(new BigInteger('0400000000000000023123953A9464B54D', 16)); } } PK2A#]���9ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.phpnu�[���<?php /** * nistb233 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistb233 extends sect233r1 { } PK2A#]N��*kkBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.phpnu�[���<?php /** * secp224r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp224r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001', 16)); $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE', 16), new BigInteger('B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4', 16) ); $this->setBasePoint( new BigInteger('B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21', 16), new BigInteger('BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D', 16)); } } PK2A#]J��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.phpnu�[���<?php /** * brainpoolP160r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP160r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16)); $this->setCoefficients( new BigInteger('340E7BE2A280EB74E2BE61BADA745D97E8F7C300', 16), new BigInteger('1E589A8595423412134FAA2DBDEC95C8D8675E58', 16) ); $this->setBasePoint( new BigInteger('BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3', 16), new BigInteger('1667CB477A1A8EC338F94741669C976316DA6321', 16) ); $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16)); } } PK2A#]3�2w��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.phpnu�[���<?php /** * secp192r1 * * This is the NIST P-192 curve * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp192r1 extends Prime { public function __construct() { $modulo = new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16); $this->setModulo($modulo); // algorithm 2.27 from http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=66 /* in theory this should be faster than regular modular reductions save for one small issue. to convert to / from base-2**8 with BCMath you have to call bcmul() and bcdiv() a lot. to convert to / from base-2**8 with PHP64 you have to call base256_rshift() a lot. in short, converting to / from base-2**8 is pretty expensive and that expense is enough to offset whatever else might be gained by a simplified reduction algorithm. now, if PHP supported unsigned integers things might be different. no bit-shifting would be required for the PHP engine and it'd be a lot faster. but as is, BigInteger uses base-2**31 or base-2**26 depending on whether or not the system is has a 32-bit or a 64-bit OS. */ /* $m_length = $this->getLengthInBytes(); $this->setReduction(function($c) use ($m_length) { $cBytes = $c->toBytes(); $className = $this->className; if (strlen($cBytes) > 2 * $m_length) { list(, $r) = $c->divide($className::$modulo); return $r; } $c = str_pad($cBytes, 48, "\0", STR_PAD_LEFT); $c = array_reverse(str_split($c, 8)); $null = "\0\0\0\0\0\0\0\0"; $s1 = new BigInteger($c[2] . $c[1] . $c[0], 256); $s2 = new BigInteger($null . $c[3] . $c[3], 256); $s3 = new BigInteger($c[4] . $c[4] . $null, 256); $s4 = new BigInteger($c[5] . $c[5] . $c[5], 256); $r = $s1->add($s2)->add($s3)->add($s4); while ($r->compare($className::$modulo) >= 0) { $r = $r->subtract($className::$modulo); } return $r; }); */ $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new BigInteger('64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1', 16) ); $this->setBasePoint( new BigInteger('188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012', 16), new BigInteger('07192B95FFC8DA78631011ED6B24CDD573F977A11E794811', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831', 16)); } } PK2A#]�ؐ��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.phpnu�[���<?php /** * sect409r1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect409r1 extends Binary { public function __construct() { $this->setModulo(409, 87, 0); $this->setCoefficients( '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', '0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F' ); $this->setBasePoint( '015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7', '0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706' ); $this->setOrder(new BigInteger( '010000000000000000000000000000000000000000000000000001E2' . 'AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173', 16 )); } } PK2A#]�e>>Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.phpnu�[���<?php /** * secp192k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; use phpseclib3\Math\BigInteger; class secp192k1 extends KoblitzPrime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16)); $this->setCoefficients( new BigInteger('000000000000000000000000000000000000000000000000', 16), new BigInteger('000000000000000000000000000000000000000000000003', 16) ); $this->setBasePoint( new BigInteger('DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16), new BigInteger('9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16)); $this->basis = []; $this->basis[] = [ 'a' => new BigInteger('00B3FB3400DEC5C4ADCEB8655C', -16), 'b' => new BigInteger('8EE96418CCF4CFC7124FDA0F', -16) ]; $this->basis[] = [ 'a' => new BigInteger('01D90D03E8F096B9948B20F0A9', -16), 'b' => new BigInteger('42E49819ABBA9474E1083F6B', -16) ]; $this->beta = $this->factory->newInteger(new BigInteger('447A96E6C647963E2F7809FEAAB46947F34B0AA3CA0BBA74', -16)); } } PK2A#]��g���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.phpnu�[���<?php /** * sect193r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect193r2 extends Binary { public function __construct() { $this->setModulo(193, 15, 0); $this->setCoefficients( '0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B', '00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE' ); $this->setBasePoint( '00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F', '01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C' ); $this->setOrder(new BigInteger('010000000000000000000000015AAB561B005413CCD4EE99D5', 16)); } } PK2A#]��r���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.phpnu�[���<?php /** * secp224k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; use phpseclib3\Math\BigInteger; class secp224k1 extends KoblitzPrime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D', 16)); $this->setCoefficients( new BigInteger('00000000000000000000000000000000000000000000000000000000', 16), new BigInteger('00000000000000000000000000000000000000000000000000000005', 16) ); $this->setBasePoint( new BigInteger('A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C', 16), new BigInteger('7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5', 16) ); $this->setOrder(new BigInteger('010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7', 16)); $this->basis = []; $this->basis[] = [ 'a' => new BigInteger('00B8ADF1378A6EB73409FA6C9C637D', -16), 'b' => new BigInteger('94730F82B358A3776A826298FA6F', -16) ]; $this->basis[] = [ 'a' => new BigInteger('01DCE8D2EC6184CAF0A972769FCC8B', -16), 'b' => new BigInteger('4D2100BA3DC75AAB747CCF355DEC', -16) ]; $this->beta = $this->factory->newInteger(new BigInteger('01F178FFA4B17C89E6F73AECE2AAD57AF4C0A748B63C830947B27E04', -16)); } } PK2A#]��KjjCvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.phpnu�[���<?php /** * prime192v1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class prime192v1 extends secp192r1 { } PK2A#]<x3d��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.phpnu�[���<?php /** * brainpoolP384t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP384t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger( '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' . '1874700133107EC53', 16 )); $this->setCoefficients( new BigInteger( '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901' . 'D1A71874700133107EC50', 16 ), // eg. -3 new BigInteger( '7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B8' . '8805CED70355A33B471EE', 16 ) ); $this->setBasePoint( new BigInteger( '18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946' . 'A5F54D8D0AA2F418808CC', 16 ), new BigInteger( '25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC' . '2B2912675BF5B9E582928', 16 ) ); $this->setOrder(new BigInteger( '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' . '03B883202E9046565', 16 )); } } PK2A#]G���VVBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.phpnu�[���<?php /** * sect113r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect113r1 extends Binary { public function __construct() { $this->setModulo(113, 9, 0); $this->setCoefficients( '003088250CA6E7C7FE649CE85820F7', '00E8BEE4D3E2260744188BE0E9C723' ); $this->setBasePoint( '009D73616F35F4AB1407D73562C10F', '00A52830277958EE84D1315ED31886' ); $this->setOrder(new BigInteger('0100000000000000D9CCEC8A39E56F', 16)); } } PK2A#]*� Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.phpnu�[���<?php /** * secp160r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp160r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF', 16)); $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC', 16), new BigInteger('1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45', 16) ); $this->setBasePoint( new BigInteger('4A96B5688EF573284664698968C38BB913CBFC82', 16), new BigInteger('23A628553168947D59DCC912042351377AC5FB32', 16) ); $this->setOrder(new BigInteger('0100000000000000000001F4C8F927AED3CA752257', 16)); } } PK2A#]G��GGHvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.phpnu�[���<?php /** * brainpoolP192r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP192r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16)); $this->setCoefficients( new BigInteger('6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF', 16), new BigInteger('469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9', 16) ); $this->setBasePoint( new BigInteger('C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6', 16), new BigInteger('14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F', 16) ); $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16)); } } PK2A#]<��wwHvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.phpnu�[���<?php /** * brainpoolP224r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP224r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16)); $this->setCoefficients( new BigInteger('68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43', 16), new BigInteger('2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B', 16) ); $this->setBasePoint( new BigInteger('0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D', 16), new BigInteger('58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD', 16) ); $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16)); } } PK2A#]��qBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.phpnu�[���<?php /** * secp160k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; use phpseclib3\Math\BigInteger; class secp160k1 extends KoblitzPrime { public function __construct() { // same as secp160r2 $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16)); $this->setCoefficients( new BigInteger('0000000000000000000000000000000000000000', 16), new BigInteger('0000000000000000000000000000000000000007', 16) ); $this->setBasePoint( new BigInteger('3B4C382CE37AA192A4019E763036F4F5DD4D7EBB', 16), new BigInteger('938CF935318FDCED6BC28286531733C3F03C4FEE', 16) ); $this->setOrder(new BigInteger('0100000000000000000001B8FA16DFAB9ACA16B6B3', 16)); $this->basis = []; $this->basis[] = [ 'a' => new BigInteger('0096341F1138933BC2F505', -16), 'b' => new BigInteger('FF6E9D0418C67BB8D5F562', -16) ]; $this->basis[] = [ 'a' => new BigInteger('01BDCB3A09AAAABEAFF4A8', -16), 'b' => new BigInteger('04D12329FF0EF498EA67', -16) ]; $this->beta = $this->factory->newInteger(new BigInteger('645B7345A143464942CC46D7CF4D5D1E1E6CBB68', -16)); } } PK2A#]嬅�Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.phpnu�[���<?php /** * prime239v2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class prime239v2 extends Prime { public function __construct() { $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new BigInteger('617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C', 16) ); $this->setBasePoint( new BigInteger('38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7', 16), new BigInteger('5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA', 16) ); $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063', 16)); } } PK2A#]�i.��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.phpnu�[���<?php /** * secp112r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp112r2 extends Prime { public function __construct() { // same modulo as secp112r1 $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16)); $this->setCoefficients( new BigInteger('6127C24C05F38A0AAAF65C0EF02C', 16), new BigInteger('51DEF1815DB5ED74FCC34C85D709', 16) ); $this->setBasePoint( new BigInteger('4BA30AB5E892B4E1649DD0928643', 16), new BigInteger('ADCD46F5882E3747DEF36E956E97', 16) ); $this->setOrder(new BigInteger('36DF0AAFD8B8D7597CA10520D04B', 16)); } } PK2A#]�ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.phpnu�[���<?php /** * nistk233 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistk233 extends sect233k1 { } PK2A#]U�\���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.phpnu�[���<?php /** * secp128r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp128r2 extends Prime { public function __construct() { // same as secp128r1 $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('D6031998D1B3BBFEBF59CC9BBFF9AEE1', 16), new BigInteger('5EEEFCA380D02919DC2C6558BB6D8A5D', 16) ); $this->setBasePoint( new BigInteger('7B6AA5D85E572983E6FB32A7CDEBC140', 16), new BigInteger('27B6916A894D3AEE7106FE805FC34B44', 16) ); $this->setOrder(new BigInteger('3FFFFFFF7FFFFFFFBE0024720613B5A3', 16)); } } PK2A#]%^�XBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.phpnu�[���<?php /** * sect571r1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect571r1 extends Binary { public function __construct() { $this->setModulo(571, 10, 5, 2, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000001', '02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD' . '8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A' ); $this->setBasePoint( '0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950' . 'F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19', '037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43' . 'BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B' ); $this->setOrder(new BigInteger( '03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'E661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47', 16 )); } } PK2A#]�6���Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.phpnu�[���<?php /** * brainpoolP256t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP256t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16)); $this->setCoefficients( new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374', 16), // eg. -3 new BigInteger('662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04', 16) ); $this->setBasePoint( new BigInteger('A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4', 16), new BigInteger('2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE', 16) ); $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16)); } } PK2A#]M�I�ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.phpnu�[���<?php /** * nistk163 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistk163 extends sect163k1 { } PK2A#]�I&Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.phpnu�[���<?php /** * sect571k1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect571k1 extends Binary { public function __construct() { $this->setModulo(571, 10, 5, 2, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000001' ); $this->setBasePoint( '026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA443709584' . '93B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972', '0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0' . 'AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3' ); $this->setOrder(new BigInteger( '020000000000000000000000000000000000000000000000000000000000000000000000' . '131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001', 16 )); } } PK2A#]&����Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.phpnu�[���<?php /** * brainpoolP320t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP320t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' . '2B9EC7893EC28FCD412B1F1B32E27', 16)); $this->setCoefficients( new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28' . 'FCD412B1F1B32E24', 16), // eg. -3 new BigInteger('A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CE' . 'B5B4FEF422340353', 16) ); $this->setBasePoint( new BigInteger('925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF' . '3357F624A21BED52', 16), new BigInteger('63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B' . '1B9BC0455FB0D2C3', 16) ); $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' . '82EC7EE8658E98691555B44C59311', 16)); } } PK2A#])��ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.phpnu�[���<?php /** * nistp192 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistp192 extends secp192r1 { } PK2A#]�6D��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.phpnu�[���<?php /** * secp128r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp128r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC', 16), new BigInteger('E87579C11079F43DD824993C2CEE5ED3', 16) ); $this->setBasePoint( new BigInteger('161FF7528B899B2D0C28607CA52C5B86', 16), new BigInteger('CF5AC8395BAFEB13C02DA292DDED7A83', 16) ); $this->setOrder(new BigInteger('FFFFFFFE0000000075A30D1B9038A115', 16)); } } PK2A#]H�H��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.phpnu�[���<?php /** * brainpoolP384r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP384r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger( '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' . '1874700133107EC53', 16 )); $this->setCoefficients( new BigInteger( '7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503' . 'AD4EB04A8C7DD22CE2826', 16 ), new BigInteger( '4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DB' . 'C9943AB78696FA504C11', 16 ) ); $this->setBasePoint( new BigInteger( '1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D' . '646AAEF87B2E247D4AF1E', 16 ), new BigInteger( '8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E464621779' . '1811142820341263C5315', 16 ) ); $this->setOrder(new BigInteger( '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' . '03B883202E9046565', 16 )); } } PK2A#]Z�ǁ�Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.phpnu�[���<?php /** * brainpoolP224t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP224t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16)); $this->setCoefficients( new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC', 16), // eg. -3 new BigInteger('4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D', 16) ); $this->setBasePoint( new BigInteger('6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580', 16), new BigInteger('0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C', 16) ); $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16)); } } PK2A#]'o�lffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.phpnu�[���<?php /** * nistp521 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistp521 extends secp521r1 { } PK2A#]�s�QQHvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.phpnu�[���<?php /** * brainpoolP192t1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP192t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16)); $this->setCoefficients( new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294', 16), // eg. -3 new BigInteger('13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79', 16) ); $this->setBasePoint( new BigInteger('3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129', 16), new BigInteger('097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9', 16) ); $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16)); } } PK2A#]���0 Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.phpnu�[���<?php /** * Curve25519 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Montgomery; use phpseclib3\Math\BigInteger; class Curve25519 extends Montgomery { public function __construct() { // 2^255 - 19 $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16)); $this->a24 = $this->factory->newInteger(new BigInteger('121666')); $this->p = [$this->factory->newInteger(new BigInteger(9))]; // 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16)); /* $this->setCoefficients( new BigInteger('486662'), // a ); $this->setBasePoint( new BigInteger(9), new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401') ); */ } /** * Multiply a point on the curve by a scalar * * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8 * * @return array */ public function multiplyPoint(array $p, BigInteger $d) { //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes()))); //return [$this->factory->newInteger(new BigInteger($r, 256))]; $d = $d->toBytes(); $d &= "\xF8" . str_repeat("\xFF", 30) . "\x7F"; $d = strrev($d); $d |= "\x40"; $d = new BigInteger($d, -256); return parent::multiplyPoint($p, $d); } /** * Creates a random scalar multiplier * * @return BigInteger */ public function createRandomMultiplier() { return BigInteger::random(256); } /** * Performs range check */ public function rangeCheck(BigInteger $x) { if ($x->getLength() > 256 || $x->isNegative()) { throw new \RangeException('x must be a positive integer less than 256 bytes in length'); } } } PK2A#]��]���Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.phpnu�[���<?php /** * brainpoolP320r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP320r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' . '2B9EC7893EC28FCD412B1F1B32E27', 16)); $this->setCoefficients( new BigInteger('3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F4' . '92F375A97D860EB4', 16), new BigInteger('520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD88453981' . '6F5EB4AC8FB1F1A6', 16) ); $this->setBasePoint( new BigInteger('43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C7' . '10AF8D0D39E20611', 16), new BigInteger('14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7' . 'D35245D1692E8EE1', 16) ); $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' . '82EC7EE8658E98691555B44C59311', 16)); } } PK2A#]�v�G G Avendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.phpnu�[���<?php /** * Curve448 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Montgomery; use phpseclib3\Math\BigInteger; class Curve448 extends Montgomery { public function __construct() { // 2^448 - 2^224 - 1 $this->setModulo(new BigInteger( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16 )); $this->a24 = $this->factory->newInteger(new BigInteger('39081')); $this->p = [$this->factory->newInteger(new BigInteger(5))]; // 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d $this->setOrder(new BigInteger( '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3', 16 )); /* $this->setCoefficients( new BigInteger('156326'), // a ); $this->setBasePoint( new BigInteger(5), new BigInteger( '355293926785568175264127502063783334808976399387714271831880898' . '435169088786967410002932673765864550910142774147268105838985595290' . '606362') ); */ } /** * Multiply a point on the curve by a scalar * * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8 * * @return array */ public function multiplyPoint(array $p, BigInteger $d) { //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes()))); //return [$this->factory->newInteger(new BigInteger($r, 256))]; $d = $d->toBytes(); $d[0] = $d[0] & "\xFC"; $d = strrev($d); $d |= "\x80"; $d = new BigInteger($d, 256); return parent::multiplyPoint($p, $d); } /** * Creates a random scalar multiplier * * @return BigInteger */ public function createRandomMultiplier() { return BigInteger::random(446); } /** * Performs range check */ public function rangeCheck(BigInteger $x) { if ($x->getLength() > 448 || $x->isNegative()) { throw new \RangeException('x must be a positive integer less than 446 bytes in length'); } } } PK2A#]�yT�==Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.phpnu�[���<?php /** * prime192v2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class prime192v2 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new BigInteger('CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953', 16) ); $this->setBasePoint( new BigInteger('EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A', 16), new BigInteger('6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31', 16)); } } PK2A#]�T�"VVBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.phpnu�[���<?php /** * sect113r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect113r2 extends Binary { public function __construct() { $this->setModulo(113, 9, 0); $this->setCoefficients( '00689918DBEC7E5A0DD6DFC0AA55C7', '0095E9A9EC9B297BD4BF36E059184F' ); $this->setBasePoint( '01A57A6A7B26CA5EF52FCDB8164797', '00B3ADC94ED1FE674C06E695BABA1D' ); $this->setOrder(new BigInteger('010000000000000108789B2496AF93', 16)); } } PK2A#]���ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.phpnu�[���<?php /** * nistt571 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistt571 extends sect571k1 { } PK2A#]�(ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.phpnu�[���<?php /** * nistb409 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistb409 extends sect409r1 { } PK2A#]�c**Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.phpnu�[���<?php /** * secp160r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp160r2 extends Prime { public function __construct() { // same as secp160k1 $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16)); $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70', 16), new BigInteger('B4E134D3FB59EB8BAB57274904664D5AF50388BA', 16) ); $this->setBasePoint( new BigInteger('52DCB034293A117E1F4FF11B30F7199D3144CE6D', 16), new BigInteger('FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E', 16) ); $this->setOrder(new BigInteger('0100000000000000000000351EE786A818F3A1A16B', 16)); } } PK2A#]��E�11Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.phpnu�[���<?php /** * sect283r1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect283r1 extends Binary { public function __construct() { $this->setModulo(283, 12, 7, 5, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000000000000001', '027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5' ); $this->setBasePoint( '05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053', '03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4' ); $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307', 16)); } } PK2A#]X �u��Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.phpnu�[���<?php /** * prime239v1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class prime239v1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new BigInteger('6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A', 16) ); $this->setBasePoint( new BigInteger('0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF', 16), new BigInteger('7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE', 16) ); $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B', 16)); } } PK2A#]��g�jjCvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.phpnu�[���<?php /** * prime256v1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class prime256v1 extends secp256r1 { } PK2A#]~��11Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.phpnu�[���<?php /** * sect283k1 * * PHP version 5 and 7 * * @author Jim Wiggint on <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect283k1 extends Binary { public function __construct() { $this->setModulo(283, 12, 7, 5, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000000000000000001' ); $this->setBasePoint( '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836', '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259' ); $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61', 16)); } } PK2A#]��o��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.phpnu�[���<?php /** * brainpoolP256r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP256r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16)); $this->setCoefficients( new BigInteger('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16), new BigInteger('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) ); $this->setBasePoint( new BigInteger('8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262', 16), new BigInteger('547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997', 16) ); $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16)); } } PK2A#]�X���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.phpnu�[���<?php /** * secp112r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp112r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16)); $this->setCoefficients( new BigInteger('DB7C2ABF62E35E668076BEAD2088', 16), new BigInteger('659EF8BA043916EEDE8911702B22', 16) ); $this->setBasePoint( new BigInteger('09487239995A5EE76B55F9C2F098', 16), new BigInteger('A89CE5AF8724C0A23E0E0FF77500', 16) ); $this->setOrder(new BigInteger('DB7C2ABF62E35E7628DFAC6561C5', 16)); } } PK2A#]� ��ppBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.phpnu�[���<?php /** * sect131r2 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect131r2 extends Binary { public function __construct() { $this->setModulo(131, 8, 3, 2, 0); $this->setCoefficients( '03E5A88919D7CAFCBF415F07C2176573B2', '04B8266A46C55657AC734CE38F018F2192' ); $this->setBasePoint( '0356DCD8F2F95031AD652D23951BB366A8', '0648F06D867940A5366D9E265DE9EB240F' ); $this->setOrder(new BigInteger('0400000000000000016954A233049BA98F', 16)); } } PK2A#]v[��ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.phpnu�[���<?php /** * nistp256 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistp256 extends secp256r1 { } PK2A#]L�-AABvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.phpnu�[���<?php /** * secp256k1 * * This is the curve used in Bitcoin * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; //use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; use phpseclib3\Math\BigInteger; //class secp256k1 extends Prime class secp256k1 extends KoblitzPrime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)); $this->setCoefficients( new BigInteger('0000000000000000000000000000000000000000000000000000000000000000', 16), new BigInteger('0000000000000000000000000000000000000000000000000000000000000007', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)); $this->setBasePoint( new BigInteger('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16), new BigInteger('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16) ); $this->basis = []; $this->basis[] = [ 'a' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16), 'b' => new BigInteger('FF1BBC8129FEF177D790AB8056F5401B3D', -16) ]; $this->basis[] = [ 'a' => new BigInteger('114CA50F7A8E2F3F657C1108D9D44CFD8', -16), 'b' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16) ]; $this->beta = $this->factory->newInteger(new BigInteger('7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE', -16)); } } PK2A#]�l)OffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.phpnu�[���<?php /** * nistp224 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistp224 extends secp224r1 { } PK2A#]ش�X��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.phpnu�[���<?php /** * sect233r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect233r1 extends Binary { public function __construct() { $this->setModulo(233, 74, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000001', '0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD' ); $this->setBasePoint( '00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B', '01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052' ); $this->setOrder(new BigInteger('01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7', 16)); } } PK2A#]�(�ڛ�Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.phpnu�[���<?php /** * secp256r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp256r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16), new BigInteger('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16) ); $this->setBasePoint( new BigInteger('6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16), new BigInteger('4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16) ); $this->setOrder(new BigInteger('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16)); } } PK2A#]ę�Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.phpnu�[���<?php /** * brainpoolP512r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP512r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger( 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3', 16 )); $this->setCoefficients( new BigInteger( '7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA82' . '53AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA', 16 ), new BigInteger( '3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C' . '1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723', 16 ) ); $this->setBasePoint( new BigInteger( '81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D' . '0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822', 16 ), new BigInteger( '7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5' . 'F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892', 16 ) ); $this->setOrder(new BigInteger( 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' . '92619418661197FAC10471DB1D381085DDADDB58796829CA90069', 16 )); } } PK2A#]�"���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.phpnu�[���<?php /** * sect233k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect233k1 extends Binary { public function __construct() { $this->setModulo(233, 74, 0); $this->setCoefficients( '000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000001' ); $this->setBasePoint( '017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126', '01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3' ); $this->setOrder(new BigInteger('8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF', 16)); } } PK2A#]oۆ���Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.phpnu�[���<?php /** * sect193r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect193r1 extends Binary { public function __construct() { $this->setModulo(193, 15, 0); $this->setCoefficients( '0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01', '00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814' ); $this->setBasePoint( '01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1', '0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05' ); $this->setOrder(new BigInteger('01000000000000000000000000C7F34A778F443ACC920EBA49', 16)); } } PK2A#]�ٍ*ffAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.phpnu�[���<?php /** * nistk409 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistk409 extends sect409k1 { } PK2A#]T�y�ggAvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.phpnu�[���<?php /** * sect283k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; final class nistk283 extends sect283k1 { } PK2A#]1E��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.phpnu�[���<?php /** * sect163k1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect163k1 extends Binary { public function __construct() { $this->setModulo(163, 7, 6, 3, 0); $this->setCoefficients( '000000000000000000000000000000000000000001', '000000000000000000000000000000000000000001' ); $this->setBasePoint( '02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8', '0289070FB05D38FF58321F2E800536D538CCDAA3D9' ); $this->setOrder(new BigInteger('04000000000000000000020108A2E0CC0D99F8A5EF', 16)); } } PK2A#]���)��Hvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.phpnu�[���<?php /** * brainpoolP160t1 * * This curve is a twisted version of brainpoolP160r1 with A = -3. With brainpool, * the curves ending in r1 are the "regular" curves and the curves ending in "t1" * are the twisted version of the r1 curves. Per https://tools.ietf.org/html/rfc5639#page-7 * you can convert a point on an r1 curve to a point on a t1 curve thusly: * * F(x,y) := (x*Z^2, y*Z^3) * * The advantage of A = -3 is that some of the point doubling and point addition can be * slightly optimized. See http://hyperelliptic.org/EFD/g1p/auto-shortw-projective-3.html * vs http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html for example. * * phpseclib does not currently take advantage of this optimization opportunity * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class brainpoolP160t1 extends Prime { public function __construct() { $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16)); $this->setCoefficients( new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620C', 16), // eg. -3 new BigInteger('7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380', 16) ); $this->setBasePoint( new BigInteger('B199B13B9B34EFC1397E64BAEB05ACC265FF2378', 16), new BigInteger('ADD6718B7C7C1961F0991B842443772152C9E0AD', 16) ); $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16)); } } PK2A#]�^K��Bvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.phpnu�[���<?php /** * sect163r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Binary; use phpseclib3\Math\BigInteger; class sect163r1 extends Binary { public function __construct() { $this->setModulo(163, 7, 6, 3, 0); $this->setCoefficients( '07B6882CAAEFA84F9554FF8428BD88E246D2782AE2', '0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9' ); $this->setBasePoint( '0369979697AB43897789566789567F787A7876A654', '00435EDB42EFAFB2989D51FEFCE3C80988F41FF883' ); $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B', 16)); } } PK2A#]δ���'�'@vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.phpnu�[���<?php /** * Ed25519 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\Random; use phpseclib3\Math\BigInteger; class Ed25519 extends TwistedEdwards { const HASH = 'sha512'; /* Per https://tools.ietf.org/html/rfc8032#page-6 EdDSA has several parameters, one of which is b: 2. An integer b with 2^(b-1) > p. EdDSA public keys have exactly b bits, and EdDSA signatures have exactly 2*b bits. b is recommended to be a multiple of 8, so public key and signature lengths are an integral number of octets. SIZE corresponds to b */ const SIZE = 32; public function __construct() { // 2^255 - 19 $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16)); $this->setCoefficients( // -1 new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC', 16), // a // -121665/121666 new BigInteger('52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3', 16) // d ); $this->setBasePoint( new BigInteger('216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A', 16), new BigInteger('6666666666666666666666666666666666666666666666666666666666666658', 16) ); $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16)); // algorithm 14.47 from http://cacr.uwaterloo.ca/hac/about/chap14.pdf#page=16 /* $this->setReduction(function($x) { $parts = $x->bitwise_split(255); $className = $this->className; if (count($parts) > 2) { list(, $r) = $x->divide($className::$modulo); return $r; } $zero = new BigInteger(); $c = new BigInteger(19); switch (count($parts)) { case 2: list($qi, $ri) = $parts; break; case 1: $qi = $zero; list($ri) = $parts; break; case 0: return $zero; } $r = $ri; while ($qi->compare($zero) > 0) { $temp = $qi->multiply($c)->bitwise_split(255); if (count($temp) == 2) { list($qi, $ri) = $temp; } else { $qi = $zero; list($ri) = $temp; } $r = $r->add($ri); } while ($r->compare($className::$modulo) > 0) { $r = $r->subtract($className::$modulo); } return $r; }); */ } /** * Recover X from Y * * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.1.3 * * Used by EC\Keys\Common.php * * @param BigInteger $y * @param boolean $sign * @return object[] */ public function recoverX(BigInteger $y, $sign) { $y = $this->factory->newInteger($y); $y2 = $y->multiply($y); $u = $y2->subtract($this->one); $v = $this->d->multiply($y2)->add($this->one); $x2 = $u->divide($v); if ($x2->equals($this->zero)) { if ($sign) { throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)'); } return clone $this->zero; } // find the square root /* we don't do $x2->squareRoot() because, quoting from https://tools.ietf.org/html/rfc8032#section-5.1.1: "For point decoding or "decompression", square roots modulo p are needed. They can be computed using the Tonelli-Shanks algorithm or the special case for p = 5 (mod 8). To find a square root of a, first compute the candidate root x = a^((p+3)/8) (mod p)." */ $exp = $this->getModulo()->add(new BigInteger(3)); $exp = $exp->bitwise_rightShift(3); $x = $x2->pow($exp); // If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is a square root. if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { $temp = $this->getModulo()->subtract(new BigInteger(1)); $temp = $temp->bitwise_rightShift(2); $temp = $this->two->pow($temp); $x = $x->multiply($temp); if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { throw new \RuntimeException('Unable to recover X coordinate'); } } if ($x->isOdd() != $sign) { $x = $x->negate(); } return [$x, $y]; } /** * Extract Secret Scalar * * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.1.5 * * Used by the various key handlers * * @param string $str * @return array */ public function extractSecret($str) { if (strlen($str) != 32) { throw new \LengthException('Private Key should be 32-bytes long'); } // 1. Hash the 32-byte private key using SHA-512, storing the digest in // a 64-octet large buffer, denoted h. Only the lower 32 bytes are // used for generating the public key. $hash = new Hash('sha512'); $h = $hash->hash($str); $h = substr($h, 0, 32); // 2. Prune the buffer: The lowest three bits of the first octet are // cleared, the highest bit of the last octet is cleared, and the // second highest bit of the last octet is set. $h[0] = $h[0] & chr(0xF8); $h = strrev($h); $h[0] = ($h[0] & chr(0x3F)) | chr(0x40); // 3. Interpret the buffer as the little-endian integer, forming a // secret scalar s. $dA = new BigInteger($h, 256); return [ 'dA' => $dA, 'secret' => $str ]; } /** * Encode a point as a string * * @param array $point * @return string */ public function encodePoint($point) { list($x, $y) = $point; $y = $y->toBytes(); $y[0] = $y[0] & chr(0x7F); if ($x->isOdd()) { $y[0] = $y[0] | chr(0x80); } $y = strrev($y); return $y; } /** * Creates a random scalar multiplier * * @return \phpseclib3\Math\PrimeField\Integer */ public function createRandomMultiplier() { return $this->extractSecret(Random::string(32))['dA']; } /** * Converts an affine point to an extended homogeneous coordinate * * From https://tools.ietf.org/html/rfc8032#section-5.1.4 : * * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T), * with x = X/Z, y = Y/Z, x * y = T/Z. * * @return \phpseclib3\Math\PrimeField\Integer[] */ public function convertToInternal(array $p) { if (empty($p)) { return [clone $this->zero, clone $this->one, clone $this->one, clone $this->zero]; } if (isset($p[2])) { return $p; } $p[2] = clone $this->one; $p[3] = $p[0]->multiply($p[1]); return $p; } /** * Doubles a point on a curve * * @return FiniteField[] */ public function doublePoint(array $p) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p)) { return []; } if (!isset($p[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } // from https://tools.ietf.org/html/rfc8032#page-12 list($x1, $y1, $z1, $t1) = $p; $a = $x1->multiply($x1); $b = $y1->multiply($y1); $c = $this->two->multiply($z1)->multiply($z1); $h = $a->add($b); $temp = $x1->add($y1); $e = $h->subtract($temp->multiply($temp)); $g = $a->subtract($b); $f = $c->add($g); $x3 = $e->multiply($f); $y3 = $g->multiply($h); $t3 = $e->multiply($h); $z3 = $f->multiply($g); return [$x3, $y3, $z3, $t3]; } /** * Adds two points on the curve * * @return FiniteField[] */ public function addPoint(array $p, array $q) { if (!isset($this->factory)) { throw new \RuntimeException('setModulo needs to be called before this method'); } if (!count($p) || !count($q)) { if (count($q)) { return $q; } if (count($p)) { return $p; } return []; } if (!isset($p[2]) || !isset($q[2])) { throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); } if ($p[0]->equals($q[0])) { return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); } // from https://tools.ietf.org/html/rfc8032#page-12 list($x1, $y1, $z1, $t1) = $p; list($x2, $y2, $z2, $t2) = $q; $a = $y1->subtract($x1)->multiply($y2->subtract($x2)); $b = $y1->add($x1)->multiply($y2->add($x2)); $c = $t1->multiply($this->two)->multiply($this->d)->multiply($t2); $d = $z1->multiply($this->two)->multiply($z2); $e = $b->subtract($a); $f = $d->subtract($c); $g = $d->add($c); $h = $b->add($a); $x3 = $e->multiply($f); $y3 = $g->multiply($h); $t3 = $e->multiply($h); $z3 = $f->multiply($g); return [$x3, $y3, $z3, $t3]; } } PK2A#]���WWBvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.phpnu�[���<?php /** * secp384r1 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class secp384r1 extends Prime { public function __construct() { $this->setModulo(new BigInteger( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', 16 )); $this->setCoefficients( new BigInteger( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', 16 ), new BigInteger( 'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', 16 ) ); $this->setBasePoint( new BigInteger( 'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7', 16 ), new BigInteger( '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F', 16 ) ); $this->setOrder(new BigInteger( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16 )); } } PK2A#]K{�==Cvendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.phpnu�[���<?php /** * prime192v3 * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2017 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib3\Crypt\EC\Curves; use phpseclib3\Crypt\EC\BaseCurves\Prime; use phpseclib3\Math\BigInteger; class prime192v3 extends Prime { public function __construct() { $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16)); $this->setCoefficients( new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new BigInteger('22123DC2395A05CAA7423DAECCC94760A7D462256BD56916', 16) ); $this->setBasePoint( new BigInteger('7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896', 16), new BigInteger('38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0', 16) ); $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13', 16)); } } PK2A#]0N���7vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.phpnu�[���<?php /** * Pure-PHP implementation of ChaCha20. * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Exception\BadDecryptionException; use phpseclib3\Exception\InsufficientSetupException; /** * Pure-PHP implementation of ChaCha20. * * @author Jim Wigginton <terrafrost@php.net> */ class ChaCha20 extends Salsa20 { /** * The OpenSSL specific name of the cipher * * @var string */ protected $cipher_name_openssl = 'chacha20'; /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { switch ($engine) { case self::ENGINE_LIBSODIUM: // PHP 7.2.0 (30 Nov 2017) added support for libsodium // we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL // or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm // we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string // with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority return function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') && $this->key_length == 32 && (($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0) || $this->counter == 1) && !$this->continuousBuffer; case self::ENGINE_OPENSSL: // OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20. // PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017) // if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null // pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that // openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode // whereas libsodium does not if ($this->key_length != 32) { return false; } } return parent::isValidEngineHelper($engine); } /** * Encrypts a message. * * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @see self::crypt() * @param string $plaintext * @return string $ciphertext */ public function encrypt($plaintext) { $this->setup(); if ($this->engine == self::ENGINE_LIBSODIUM) { return $this->encrypt_with_libsodium($plaintext); } return parent::encrypt($plaintext); } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * At least if the continuous buffer is disabled. * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see self::crypt() * @param string $ciphertext * @return string $plaintext */ public function decrypt($ciphertext) { $this->setup(); if ($this->engine == self::ENGINE_LIBSODIUM) { return $this->decrypt_with_libsodium($ciphertext); } return parent::decrypt($ciphertext); } /** * Encrypts a message with libsodium * * @see self::encrypt() * @param string $plaintext * @return string $text */ private function encrypt_with_libsodium($plaintext) { $params = [$plaintext, $this->aad, $this->nonce, $this->key]; $ciphertext = strlen($this->nonce) == 8 ? sodium_crypto_aead_chacha20poly1305_encrypt(...$params) : sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params); if (!$this->usePoly1305) { return substr($ciphertext, 0, strlen($plaintext)); } $newciphertext = substr($ciphertext, 0, strlen($plaintext)); $this->newtag = $this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12 ? substr($ciphertext, strlen($plaintext)) : $this->poly1305($newciphertext); return $newciphertext; } /** * Decrypts a message with libsodium * * @see self::decrypt() * @param string $ciphertext * @return string $text */ private function decrypt_with_libsodium($ciphertext) { $params = [$ciphertext, $this->aad, $this->nonce, $this->key]; if (isset($this->poly1305Key)) { if ($this->oldtag === false) { throw new InsufficientSetupException('Authentication Tag has not been set'); } if ($this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12) { $plaintext = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params); $this->oldtag = false; if ($plaintext === false) { throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); } return $plaintext; } $newtag = $this->poly1305($ciphertext); if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) { $this->oldtag = false; throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); } $this->oldtag = false; } $plaintext = strlen($this->nonce) == 8 ? sodium_crypto_aead_chacha20poly1305_encrypt(...$params) : sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params); return substr($plaintext, 0, strlen($ciphertext)); } /** * Sets the nonce. * * @param string $nonce */ public function setNonce($nonce) { if (!is_string($nonce)) { throw new \UnexpectedValueException('The nonce should be a string'); } /* from https://tools.ietf.org/html/rfc7539#page-7 "Note also that the original ChaCha had a 64-bit nonce and 64-bit block count. We have modified this here to be more consistent with recommendations in Section 3.2 of [RFC5116]." */ switch (strlen($nonce)) { case 8: // 64 bits case 12: // 96 bits break; default: throw new \LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported'); } $this->nonce = $nonce; $this->changed = true; $this->setEngine(); } /** * Setup the self::ENGINE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: * * - setKey() * * - setNonce() * * - First run of encrypt() / decrypt() with no init-settings * * @see self::setKey() * @see self::setNonce() * @see self::disableContinuousBuffer() */ protected function setup() { if (!$this->changed) { return; } $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter]; $this->changed = $this->nonIVChanged = false; if ($this->nonce === false) { throw new InsufficientSetupException('No nonce has been defined'); } if ($this->key === false) { throw new InsufficientSetupException('No key has been defined'); } if ($this->usePoly1305 && !isset($this->poly1305Key)) { $this->usingGeneratedPoly1305Key = true; if ($this->engine == self::ENGINE_LIBSODIUM) { return; } $this->createPoly1305Key(); } $key = $this->key; if (strlen($key) == 16) { $constant = 'expand 16-byte k'; $key .= $key; } else { $constant = 'expand 32-byte k'; } $this->p1 = $constant . $key; $this->p2 = $this->nonce; if (strlen($this->nonce) == 8) { $this->p2 = "\0\0\0\0" . $this->p2; } } /** * The quarterround function * * @param int $a * @param int $b * @param int $c * @param int $d */ protected static function quarterRound(&$a, &$b, &$c, &$d) { // in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition, // xor'ing and rotation are all on the same line so i'm keeping it on the same // line here as well // @codingStandardsIgnoreStart $a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 16); $c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 12); $a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 8); $c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 7); // @codingStandardsIgnoreEnd } /** * The doubleround function * * @param int $x0 (by reference) * @param int $x1 (by reference) * @param int $x2 (by reference) * @param int $x3 (by reference) * @param int $x4 (by reference) * @param int $x5 (by reference) * @param int $x6 (by reference) * @param int $x7 (by reference) * @param int $x8 (by reference) * @param int $x9 (by reference) * @param int $x10 (by reference) * @param int $x11 (by reference) * @param int $x12 (by reference) * @param int $x13 (by reference) * @param int $x14 (by reference) * @param int $x15 (by reference) */ protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15) { // columnRound static::quarterRound($x0, $x4, $x8, $x12); static::quarterRound($x1, $x5, $x9, $x13); static::quarterRound($x2, $x6, $x10, $x14); static::quarterRound($x3, $x7, $x11, $x15); // rowRound static::quarterRound($x0, $x5, $x10, $x15); static::quarterRound($x1, $x6, $x11, $x12); static::quarterRound($x2, $x7, $x8, $x13); static::quarterRound($x3, $x4, $x9, $x14); } /** * The Salsa20 hash function function * * On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in * 0.65s vs the 0.85s that it takes with the parent method. * * If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could * be eliminated and we could knock this done to 0.60s. * * For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s. * AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval * approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc * * @param string $x */ protected static function salsa20($x) { list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) = unpack('V*', $x); $z0 = $x0; $z1 = $x1; $z2 = $x2; $z3 = $x3; $z4 = $x4; $z5 = $x5; $z6 = $x6; $z7 = $x7; $z8 = $x8; $z9 = $x9; $z10 = $x10; $z11 = $x11; $z12 = $x12; $z13 = $x13; $z14 = $x14; $z15 = $x15; // @codingStandardsIgnoreStart // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // columnRound $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12); $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8); $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12); $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8); $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12); $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8); $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12); $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8); $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7); // rowRound $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12); $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8); $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12); $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8); $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12); $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8); $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12); $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8); $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7); // @codingStandardsIgnoreEnd $x0 += $z0; $x1 += $z1; $x2 += $z2; $x3 += $z3; $x4 += $z4; $x5 += $z5; $x6 += $z6; $x7 += $z7; $x8 += $z8; $x9 += $z9; $x10 += $z10; $x11 += $z11; $x12 += $z12; $x13 += $z13; $x14 += $z14; $x15 += $z15; return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15); } } PK2A#]C�x�$�$2vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.phpnu�[���<?php /** * Pure-PHP FIPS 186-4 compliant implementation of DSA. * * PHP version 5 * * Here's an example of how to create signatures and verify signatures with this library: * <code> * <?php * include 'vendor/autoload.php'; * * $private = \phpseclib3\Crypt\DSA::createKey(); * $public = $private->getPublicKey(); * * $plaintext = 'terrafrost'; * * $signature = $private->sign($plaintext); * * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\DSA\Parameters; use phpseclib3\Crypt\DSA\PrivateKey; use phpseclib3\Crypt\DSA\PublicKey; use phpseclib3\Exception\InsufficientSetupException; use phpseclib3\Math\BigInteger; /** * Pure-PHP FIPS 186-4 compliant implementation of DSA. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DSA extends AsymmetricKey { /** * Algorithm Name * * @var string */ const ALGORITHM = 'DSA'; /** * DSA Prime P * * @var \phpseclib3\Math\BigInteger */ protected $p; /** * DSA Group Order q * * Prime divisor of p-1 * * @var \phpseclib3\Math\BigInteger */ protected $q; /** * DSA Group Generator G * * @var \phpseclib3\Math\BigInteger */ protected $g; /** * DSA public key value y * * @var \phpseclib3\Math\BigInteger */ protected $y; /** * Signature Format * * @var string */ protected $sigFormat; /** * Signature Format (Short) * * @var string */ protected $shortFormat; /** * Create DSA parameters * * @param int $L * @param int $N * @return \phpseclib3\Crypt\DSA|bool */ public static function createParameters($L = 2048, $N = 224) { self::initialize_static_variables(); $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); } if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } switch (true) { case $N == 160: /* in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024. RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most SSH DSA implementations only support keys with an N of 160. puttygen let's you set the size of L (but not the size of N) and uses 2048 as the default L value. that's not really compliant with any of the FIPS standards, however, for the purposes of maintaining compatibility with puttygen, we'll support it */ //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160: // FIPS 186-3 changed this as follows: //case $L == 1024 && $N == 160: case $L == 2048 && $N == 224: case $L == 2048 && $N == 256: case $L == 3072 && $N == 256: break; default: throw new \InvalidArgumentException('Invalid values for N and L'); } $two = new BigInteger(2); $q = BigInteger::randomPrime($N); $divisor = $q->multiply($two); do { $x = BigInteger::random($L); list(, $c) = $x->divide($divisor); $p = $x->subtract($c->subtract(self::$one)); } while ($p->getLength() != $L || !$p->isPrime()); $p_1 = $p->subtract(self::$one); list($e) = $p_1->divide($q); // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 , // "h could be obtained from a random number generator or from a counter that // changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that $h = clone $two; while (true) { $g = $h->powMod($e, $p); if (!$g->equals(self::$one)) { break; } $h = $h->add(self::$one); } $dsa = new Parameters(); $dsa->p = $p; $dsa->q = $q; $dsa->g = $g; return $dsa; } /** * Create public / private key pair. * * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or * no parameters (at which point L and N will be generated with this method) * * Returns the private key, from which the publickey can be extracted * * @param int[] ...$args * @return DSA\PrivateKey */ public static function createKey(...$args) { self::initialize_static_variables(); $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); } if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) { $params = self::createParameters($args[0], $args[1]); } elseif (count($args) == 1 && $args[0] instanceof Parameters) { $params = $args[0]; } elseif (!count($args)) { $params = self::createParameters(); } else { throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.'); } $private = new PrivateKey(); $private->p = $params->p; $private->q = $params->q; $private->g = $params->g; $private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one)); $private->y = $private->g->powMod($private->x, $private->p); //$public = clone $private; //unset($public->x); return $private ->withHash($params->hash->getHash()) ->withSignatureFormat($params->shortFormat); } /** * OnLoad Handler * * @return bool */ protected static function onLoad(array $components) { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } if (!isset($components['x']) && !isset($components['y'])) { $new = new Parameters(); } elseif (isset($components['x'])) { $new = new PrivateKey(); $new->x = $components['x']; } else { $new = new PublicKey(); } $new->p = $components['p']; $new->q = $components['q']; $new->g = $components['g']; if (isset($components['y'])) { $new->y = $components['y']; } return $new; } /** * Constructor * * PublicKey and PrivateKey objects can only be created from abstract RSA class */ protected function __construct() { $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); $this->shortFormat = 'ASN1'; parent::__construct(); } /** * Returns the key size * * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q) * * @return array */ public function getLength() { return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()]; } /** * Returns the current engine being used * * @see self::useInternalEngine() * @see self::useBestEngine() * @return string */ public function getEngine() { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ? 'OpenSSL' : 'PHP'; } /** * Returns the parameters * * A public / private key is only returned if the currently loaded "key" contains an x or y * value. * * @see self::getPublicKey() * @return mixed */ public function getParameters() { $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); $key = $type::saveParameters($this->p, $this->q, $this->g); return DSA::load($key, 'PKCS1') ->withHash($this->hash->getHash()) ->withSignatureFormat($this->shortFormat); } /** * Determines the signature padding mode * * Valid values are: ASN1, SSH2, Raw * * @param string $format */ public function withSignatureFormat($format) { $new = clone $this; $new->shortFormat = $format; $new->sigFormat = self::validatePlugin('Signature', $format); return $new; } /** * Returns the signature format currently being used * */ public function getSignatureFormat() { return $this->shortFormat; } } PK2A#]���I�I�7vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.phpnu�[���<?php /** * Pure-PHP implementation of Rijndael. * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * * PHP version 5 * * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated. * * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example, * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256. * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224 * are first defined as valid key / block lengths in * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}: * Extensions: Other block and Cipher Key lengths. * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224). * * {@internal The variable names are the same as those in * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}} * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $rijndael = new \phpseclib3\Crypt\Rijndael('ctr'); * * $rijndael->setKey('abcdefghijklmnop'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $rijndael->decrypt($rijndael->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2008 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\BlockCipher; use phpseclib3\Exception\BadDecryptionException; use phpseclib3\Exception\BadModeException; use phpseclib3\Exception\InconsistentSetupException; use phpseclib3\Exception\InsufficientSetupException; /** * Pure-PHP implementation of Rijndael. * * @author Jim Wigginton <terrafrost@php.net> */ class Rijndael extends BlockCipher { /** * The mcrypt specific name of the cipher * * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not. * \phpseclib3\Crypt\Rijndael determines automatically whether mcrypt is useable * or not for the current $block_size/$key_length. * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly. * * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @see \phpseclib3\Crypt\Common\SymmetricKey::engine * @see self::isValidEngine() * @var string */ protected $cipher_name_mcrypt = 'rijndael-128'; /** * The Key Schedule * * @see self::setup() * @var array */ private $w; /** * The Inverse Key Schedule * * @see self::setup() * @var array */ private $dw; /** * The Block Length divided by 32 * * {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once.} * * @see self::setBlockLength() * @var int */ private $Nb = 4; /** * The Key Length (in bytes) * * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once.} * * @see self::setKeyLength() * @var int */ protected $key_length = 16; /** * The Key Length divided by 32 * * @see self::setKeyLength() * @var int * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 */ private $Nk = 4; /** * The Number of Rounds * * {@internal The max value is 14, the min value is 10.} * * @var int */ private $Nr; /** * Shift offsets * * @var array */ private $c; /** * Holds the last used key- and block_size information * * @var array */ private $kl; /** * Default Constructor. * * @param string $mode * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ public function __construct($mode) { parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new BadModeException('Block ciphers cannot be ran in stream mode'); } } /** * Sets the key length. * * Valid key lengths are 128, 160, 192, 224, and 256. * * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to * 192/256 bits as, for example, mcrypt will do. * * That said, if you want be compatible with other Rijndael and AES implementations, * you should not setKeyLength(160) or setKeyLength(224). * * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use * the mcrypt php extension, even if available. * This results then in slower encryption. * * @throws \LengthException if the key length is invalid * @param int $length */ public function setKeyLength($length) { switch ($length) { case 128: case 160: case 192: case 224: case 256: $this->key_length = $length >> 3; break; default: throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } parent::setKeyLength($length); } /** * Sets the key. * * Rijndael supports five different key lengths * * @see setKeyLength() * @param string $key * @throws \LengthException if the key length isn't supported */ public function setKey($key) { switch (strlen($key)) { case 16: case 20: case 24: case 28: case 32: break; default: throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported'); } parent::setKey($key); } /** * Sets the block length * * Valid block lengths are 128, 160, 192, 224, and 256. * * @param int $length */ public function setBlockLength($length) { switch ($length) { case 128: case 160: case 192: case 224: case 256: break; default: throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } $this->Nb = $length >> 5; $this->block_size = $length >> 3; $this->changed = $this->nonIVChanged = true; $this->setEngine(); } /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { switch ($engine) { case self::ENGINE_LIBSODIUM: return function_exists('sodium_crypto_aead_aes256gcm_is_available') && sodium_crypto_aead_aes256gcm_is_available() && $this->mode == self::MODE_GCM && $this->key_length == 32 && $this->nonce && strlen($this->nonce) == 12 && $this->block_size == 16; case self::ENGINE_OPENSSL_GCM: if (!extension_loaded('openssl')) { return false; } $methods = openssl_get_cipher_methods(); return $this->mode == self::MODE_GCM && version_compare(PHP_VERSION, '7.1.0', '>=') && in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) && $this->block_size == 16; case self::ENGINE_OPENSSL: if ($this->block_size != 16) { return false; } $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode(); break; case self::ENGINE_MCRYPT: $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); if ($this->key_length % 8) { // is it a 160/224-bit key? // mcrypt is not usable for them, only for 128/192/256-bit keys return false; } } return parent::isValidEngineHelper($engine); } /** * Encrypts a block * * @param string $in * @return string */ protected function encryptBlock($in) { static $tables; if (empty($tables)) { $tables = &$this->getTables(); } $t0 = $tables[0]; $t1 = $tables[1]; $t2 = $tables[2]; $t3 = $tables[3]; $sbox = $tables[4]; $state = []; $words = unpack('N*', $in); $c = $this->c; $w = $this->w; $Nb = $this->Nb; $Nr = $this->Nr; // addRoundKey $wc = $Nb - 1; foreach ($words as $word) { $state[] = $word ^ $w[++$wc]; } // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf. // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization. // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well. // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf $temp = []; for ($round = 1; $round < $Nr; ++$round) { $i = 0; // $c[0] == 0 $j = $c[1]; $k = $c[2]; $l = $c[3]; while ($i < $Nb) { $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^ $t1[$state[$j] >> 16 & 0x000000FF] ^ $t2[$state[$k] >> 8 & 0x000000FF] ^ $t3[$state[$l] & 0x000000FF] ^ $w[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } $state = $temp; } // subWord for ($i = 0; $i < $Nb; ++$i) { $state[$i] = $sbox[$state[$i] & 0x000000FF] | ($sbox[$state[$i] >> 8 & 0x000000FF] << 8) | ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) | ($sbox[$state[$i] >> 24 & 0x000000FF] << 24); } // shiftRows + addRoundKey $i = 0; // $c[0] == 0 $j = $c[1]; $k = $c[2]; $l = $c[3]; while ($i < $Nb) { $temp[$i] = ($state[$i] & intval(0xFF000000)) ^ ($state[$j] & 0x00FF0000) ^ ($state[$k] & 0x0000FF00) ^ ($state[$l] & 0x000000FF) ^ $w[$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } return pack('N*', ...$temp); } /** * Decrypts a block * * @param string $in * @return string */ protected function decryptBlock($in) { static $invtables; if (empty($invtables)) { $invtables = &$this->getInvTables(); } $dt0 = $invtables[0]; $dt1 = $invtables[1]; $dt2 = $invtables[2]; $dt3 = $invtables[3]; $isbox = $invtables[4]; $state = []; $words = unpack('N*', $in); $c = $this->c; $dw = $this->dw; $Nb = $this->Nb; $Nr = $this->Nr; // addRoundKey $wc = $Nb - 1; foreach ($words as $word) { $state[] = $word ^ $dw[++$wc]; } $temp = []; for ($round = $Nr - 1; $round > 0; --$round) { $i = 0; // $c[0] == 0 $j = $Nb - $c[1]; $k = $Nb - $c[2]; $l = $Nb - $c[3]; while ($i < $Nb) { $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^ $dt1[$state[$j] >> 16 & 0x000000FF] ^ $dt2[$state[$k] >> 8 & 0x000000FF] ^ $dt3[$state[$l] & 0x000000FF] ^ $dw[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } $state = $temp; } // invShiftRows + invSubWord + addRoundKey $i = 0; // $c[0] == 0 $j = $Nb - $c[1]; $k = $Nb - $c[2]; $l = $Nb - $c[3]; while ($i < $Nb) { $word = ($state[$i] & intval(0xFF000000)) | ($state[$j] & 0x00FF0000) | ($state[$k] & 0x0000FF00) | ($state[$l] & 0x000000FF); $temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] | ($isbox[$word >> 8 & 0x000000FF] << 8) | ($isbox[$word >> 16 & 0x000000FF] << 16) | ($isbox[$word >> 24 & 0x000000FF] << 24)); ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } return pack('N*', ...$temp); } /** * Setup the self::ENGINE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine and flush all $buffers * Used (only) if $engine == self::ENGINE_INTERNAL * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: * * - setKey() * * - setIV() * * - disableContinuousBuffer() * * - First run of encrypt() / decrypt() with no init-settings * * {@internal setup() is always called before en/decryption.} * * {@internal Could, but not must, extend by the child Crypt_* class} * * @see self::setKey() * @see self::setIV() * @see self::disableContinuousBuffer() */ protected function setup() { if (!$this->changed) { return; } parent::setup(); if (is_string($this->iv) && strlen($this->iv) != $this->block_size) { throw new InconsistentSetupException('The IV length (' . strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')'); } } /** * Setup the key (expansion) * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() */ protected function setupKey() { // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field. // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse static $rcon; if (!isset($rcon)) { $rcon = [0, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000, 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000, 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000, 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 ]; $rcon = array_map('intval', $rcon); } if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { // already expanded return; } $this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size]; $this->Nk = $this->key_length >> 2; // see Rijndael-ammended.pdf#page=44 $this->Nr = max($this->Nk, $this->Nb) + 6; // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44, // "Table 8: Shift offsets in Shiftrow for the alternative block lengths" // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14, // "Table 2: Shift offsets for different block lengths" switch ($this->Nb) { case 4: case 5: case 6: $this->c = [0, 1, 2, 3]; break; case 7: $this->c = [0, 1, 2, 4]; break; case 8: $this->c = [0, 1, 3, 4]; } $w = array_values(unpack('N*words', $this->key)); $length = $this->Nb * ($this->Nr + 1); for ($i = $this->Nk; $i < $length; $i++) { $temp = $w[$i - 1]; if ($i % $this->Nk == 0) { // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent". // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine, // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and' // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. $temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk]; } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { $temp = $this->subWord($temp); } $w[$i] = $w[$i - $this->Nk] ^ $temp; } // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns // and generate the inverse key schedule. more specifically, // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3), // "The key expansion for the Inverse Cipher is defined as follows: // 1. Apply the Key Expansion. // 2. Apply InvMixColumn to all Round Keys except the first and the last one." // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables(); $temp = $this->w = $this->dw = []; for ($i = $row = $col = 0; $i < $length; $i++, $col++) { if ($col == $this->Nb) { if ($row == 0) { $this->dw[0] = $this->w[0]; } else { // subWord + invMixColumn + invSubWord = invMixColumn $j = 0; while ($j < $this->Nb) { $dw = $this->subWord($this->w[$row][$j]); $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^ $dt1[$dw >> 16 & 0x000000FF] ^ $dt2[$dw >> 8 & 0x000000FF] ^ $dt3[$dw & 0x000000FF]; $j++; } $this->dw[$row] = $temp; } $col = 0; $row++; } $this->w[$row][$col] = $w[$i]; } $this->dw[$row] = $this->w[$row]; // Converting to 1-dim key arrays (both ascending) $this->dw = array_reverse($this->dw); $w = array_pop($this->w); $dw = array_pop($this->dw); foreach ($this->w as $r => $wr) { foreach ($wr as $c => $wc) { $w[] = $wc; $dw[] = $this->dw[$r][$c]; } } $this->w = $w; $this->dw = $dw; } /** * Performs S-Box substitutions * * @return array * @param int $word */ private function subWord($word) { static $sbox; if (empty($sbox)) { list(, , , , $sbox) = self::getTables(); } return $sbox[$word & 0x000000FF] | ($sbox[$word >> 8 & 0x000000FF] << 8) | ($sbox[$word >> 16 & 0x000000FF] << 16) | ($sbox[$word >> 24 & 0x000000FF] << 24); } /** * Provides the mixColumns and sboxes tables * * @see self::encryptBlock() * @see self::setupInlineCrypt() * @see self::subWord() * @return array &$tables */ protected function &getTables() { static $tables; if (empty($tables)) { // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1), // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so // those are the names we'll use. $t3 = array_map('intval', [ // with array_map('intval', ...) we ensure we have only int's and not // some slower floats converted by php automatically on high values 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C ]); foreach ($t3 as $t3i) { $t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF); $t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF); $t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF); } $tables = [ // The Precomputed mixColumns tables t0 - t3 $t0, $t1, $t2, $t3, // The SubByte S-Box [ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 ] ]; } return $tables; } /** * Provides the inverse mixColumns and inverse sboxes tables * * @see self::decryptBlock() * @see self::setupInlineCrypt() * @see self::setupKey() * @return array &$tables */ protected function &getInvTables() { static $tables; if (empty($tables)) { $dt3 = array_map('intval', [ 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 ]); foreach ($dt3 as $dt3i) { $dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF); $dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF); $dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF); }; $tables = [ // The Precomputed inverse mixColumns tables dt0 - dt3 $dt0, $dt1, $dt2, $dt3, // The inverse SubByte S-Box [ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D ] ]; } return $tables; } /** * Setup the performance-optimized function for de/encrypt() * * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() */ protected function setupInlineCrypt() { $w = $this->w; $dw = $this->dw; $init_encrypt = ''; $init_decrypt = ''; $Nr = $this->Nr; $Nb = $this->Nb; $c = $this->c; // Generating encrypt code: $init_encrypt .= ' if (empty($tables)) { $tables = &$this->getTables(); } $t0 = $tables[0]; $t1 = $tables[1]; $t2 = $tables[2]; $t3 = $tables[3]; $sbox = $tables[4]; '; $s = 'e'; $e = 's'; $wc = $Nb - 1; // Preround: addRoundKey $encrypt_block = '$in = unpack("N*", $in);' . "\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n"; } // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey for ($round = 1; $round < $Nr; ++$round) { list($s, $e) = [$e, $s]; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block .= '$' . $e . $i . ' = $t0[($' . $s . $i . ' >> 24) & 0xff] ^ $t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^ $t2[($' . $s . (($i + $c[2]) % $Nb) . ' >> 8) & 0xff] ^ $t3[ $' . $s . (($i + $c[3]) % $Nb) . ' & 0xff] ^ ' . $w[++$wc] . ";\n"; } } // Finalround: subWord + shiftRows + addRoundKey for ($i = 0; $i < $Nb; ++$i) { $encrypt_block .= '$' . $e . $i . ' = $sbox[ $' . $e . $i . ' & 0xff] | ($sbox[($' . $e . $i . ' >> 8) & 0xff] << 8) | ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) | ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n"; } $encrypt_block .= '$in = pack("N*"' . "\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block .= ', ($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^ ($' . $e . (($i + $c[1]) % $Nb) . ' & 0x00FF0000 ) ^ ($' . $e . (($i + $c[2]) % $Nb) . ' & 0x0000FF00 ) ^ ($' . $e . (($i + $c[3]) % $Nb) . ' & 0x000000FF ) ^ ' . $w[$i] . "\n"; } $encrypt_block .= ');'; // Generating decrypt code: $init_decrypt .= ' if (empty($invtables)) { $invtables = &$this->getInvTables(); } $dt0 = $invtables[0]; $dt1 = $invtables[1]; $dt2 = $invtables[2]; $dt3 = $invtables[3]; $isbox = $invtables[4]; '; $s = 'e'; $e = 's'; $wc = $Nb - 1; // Preround: addRoundKey $decrypt_block = '$in = unpack("N*", $in);' . "\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n"; } // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey for ($round = 1; $round < $Nr; ++$round) { list($s, $e) = [$e, $s]; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block .= '$' . $e . $i . ' = $dt0[($' . $s . $i . ' >> 24) & 0xff] ^ $dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^ $dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >> 8) & 0xff] ^ $dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . ' & 0xff] ^ ' . $dw[++$wc] . ";\n"; } } // Finalround: subWord + shiftRows + addRoundKey for ($i = 0; $i < $Nb; ++$i) { $decrypt_block .= '$' . $e . $i . ' = $isbox[ $' . $e . $i . ' & 0xff] | ($isbox[($' . $e . $i . ' >> 8) & 0xff] << 8) | ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) | ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n"; } $decrypt_block .= '$in = pack("N*"' . "\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block .= ', ($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^ ($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' & 0x00FF0000 ) ^ ($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' & 0x0000FF00 ) ^ ($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' & 0x000000FF ) ^ ' . $dw[$i] . "\n"; } $decrypt_block .= ');'; $this->inline_crypt = $this->createInlineCryptFunction( [ 'init_crypt' => 'static $tables; static $invtables;', 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ] ); } /** * Encrypts a message. * * @see self::decrypt() * @see parent::encrypt() * @param string $plaintext * @return string */ public function encrypt($plaintext) { $this->setup(); switch ($this->engine) { case self::ENGINE_LIBSODIUM: $this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key); return Strings::shift($this->newtag, strlen($plaintext)); case self::ENGINE_OPENSSL_GCM: return openssl_encrypt( $plaintext, 'aes-' . $this->getKeyLength() . '-gcm', $this->key, OPENSSL_RAW_DATA, $this->nonce, $this->newtag, $this->aad ); } return parent::encrypt($plaintext); } /** * Decrypts a message. * * @see self::encrypt() * @see parent::decrypt() * @param string $ciphertext * @return string */ public function decrypt($ciphertext) { $this->setup(); switch ($this->engine) { case self::ENGINE_LIBSODIUM: if ($this->oldtag === false) { throw new InsufficientSetupException('Authentication Tag has not been set'); } if (strlen($this->oldtag) != 16) { break; } $plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key); if ($plaintext === false) { $this->oldtag = false; throw new BadDecryptionException('Error decrypting ciphertext with libsodium'); } return $plaintext; case self::ENGINE_OPENSSL_GCM: if ($this->oldtag === false) { throw new InsufficientSetupException('Authentication Tag has not been set'); } $plaintext = openssl_decrypt( $ciphertext, 'aes-' . $this->getKeyLength() . '-gcm', $this->key, OPENSSL_RAW_DATA, $this->nonce, $this->oldtag, $this->aad ); if ($plaintext === false) { $this->oldtag = false; throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL'); } return $plaintext; } return parent::decrypt($ciphertext); } } PK2A#]�)��4�48vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.phpnu�[���<?php /** * Pure-PHP implementation of Triple DES. * * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt). * * PHP version 5 * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $des = new \phpseclib3\Crypt\TripleDES('ctr'); * * $des->setKey('abcdefghijklmnopqrstuvwx'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $des->decrypt($des->encrypt($plaintext)); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; /** * Pure-PHP implementation of Triple DES. * * @author Jim Wigginton <terrafrost@php.net> */ class TripleDES extends DES { /** * Encrypt / decrypt using inner chaining * * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3). */ const MODE_3CBC = -2; /** * Encrypt / decrypt using outer chaining * * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib3\Crypt\Common\BlockCipher::MODE_CBC. */ const MODE_CBC3 = self::MODE_CBC; /** * Key Length (in bytes) * * @see \phpseclib3\Crypt\TripleDES::setKeyLength() * @var int */ protected $key_length = 24; /** * The mcrypt specific name of the cipher * * @see \phpseclib3\Crypt\DES::cipher_name_mcrypt * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt * @var string */ protected $cipher_name_mcrypt = 'tripledes'; /** * Optimizing value while CFB-encrypting * * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len * @var int */ protected $cfb_init_len = 750; /** * max possible size of $key * * @see self::setKey() * @see \phpseclib3\Crypt\DES::setKey() * @var string */ protected $key_length_max = 24; /** * Internal flag whether using self::MODE_3CBC or not * * @var bool */ private $mode_3cbc; /** * The \phpseclib3\Crypt\DES objects * * Used only if $mode_3cbc === true * * @var array */ private $des; /** * Default Constructor. * * Determines whether or not the mcrypt or OpenSSL extensions should be used. * * $mode could be: * * - ecb * * - cbc * * - ctr * * - cfb * * - ofb * * - 3cbc * * - cbc3 (same as cbc) * * @see \phpseclib3\Crypt\DES::__construct() * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param string $mode */ public function __construct($mode) { switch (strtolower($mode)) { // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC // and additional flag us internally as 3CBC case '3cbc': parent::__construct('cbc'); $this->mode_3cbc = true; // This three $des'es will do the 3CBC work (if $key > 64bits) $this->des = [ new DES('cbc'), new DES('cbc'), new DES('cbc'), ]; // we're going to be doing the padding, ourselves, so disable it in the \phpseclib3\Crypt\DES objects $this->des[0]->disablePadding(); $this->des[1]->disablePadding(); $this->des[2]->disablePadding(); break; case 'cbc3': $mode = 'cbc'; // fall-through // If not 3CBC, we init as usual default: parent::__construct($mode); if ($this->mode == self::MODE_STREAM) { throw new BadModeException('Block ciphers cannot be ran in stream mode'); } } } /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @param int $engine * @return bool */ protected function isValidEngineHelper($engine) { if ($engine == self::ENGINE_OPENSSL) { $this->cipher_name_openssl_ecb = 'des-ede3'; $mode = $this->openssl_translate_mode(); $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; } return parent::isValidEngineHelper($engine); } /** * Sets the initialization vector. * * SetIV is not required when \phpseclib3\Crypt\Common\SymmetricKey::MODE_ECB is being used. * * @see \phpseclib3\Crypt\Common\SymmetricKey::setIV() * @param string $iv */ public function setIV($iv) { parent::setIV($iv); if ($this->mode_3cbc) { $this->des[0]->setIV($iv); $this->des[1]->setIV($iv); $this->des[2]->setIV($iv); } } /** * Sets the key length. * * Valid key lengths are 128 and 192 bits. * * If you want to use a 64-bit key use DES.php * * @see \phpseclib3\Crypt\Common\SymmetricKey:setKeyLength() * @throws \LengthException if the key length is invalid * @param int $length */ public function setKeyLength($length) { switch ($length) { case 128: case 192: break; default: throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported'); } parent::setKeyLength($length); } /** * Sets the key. * * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * * @see \phpseclib3\Crypt\DES::setKey() * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() * @throws \LengthException if the key length is invalid * @param string $key */ public function setKey($key) { if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); } switch (strlen($key)) { case 16: $key .= substr($key, 0, 8); break; case 24: break; default: throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported'); } // copied from self::setKey() $this->key = $key; $this->key_length = strlen($key); $this->changed = $this->nonIVChanged = true; $this->setEngine(); if ($this->mode_3cbc) { $this->des[0]->setKey(substr($key, 0, 8)); $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); } } /** * Encrypts a message. * * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @param string $plaintext * @return string $cipertext */ public function encrypt($plaintext) { // parent::en/decrypt() is able to do all the work for all modes and keylengths, // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits // if the key is smaller then 8, do what we'd normally do if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->des[2]->encrypt( $this->des[1]->decrypt( $this->des[0]->encrypt( $this->pad($plaintext) ) ) ); } return parent::encrypt($plaintext); } /** * Decrypts a message. * * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @param string $ciphertext * @return string $plaintext */ public function decrypt($ciphertext) { if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->unpad( $this->des[0]->decrypt( $this->des[1]->encrypt( $this->des[2]->decrypt( str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0") ) ) ) ); } return parent::decrypt($ciphertext); } /** * Treat consecutive "packets" as if they are a continuous buffer. * * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets * will yield different outputs: * * <code> * echo $des->encrypt(substr($plaintext, 0, 8)); * echo $des->encrypt(substr($plaintext, 8, 8)); * </code> * <code> * echo $des->encrypt($plaintext); * </code> * * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates * another, as demonstrated with the following: * * <code> * $des->encrypt(substr($plaintext, 0, 8)); * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); * </code> * <code> * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); * </code> * * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\DES() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * * @see \phpseclib3\Crypt\Common\SymmetricKey::enableContinuousBuffer() * @see self::disableContinuousBuffer() */ public function enableContinuousBuffer() { parent::enableContinuousBuffer(); if ($this->mode_3cbc) { $this->des[0]->enableContinuousBuffer(); $this->des[1]->enableContinuousBuffer(); $this->des[2]->enableContinuousBuffer(); } } /** * Treat consecutive packets as if they are a discontinuous buffer. * * The default behavior. * * @see \phpseclib3\Crypt\Common\SymmetricKey::disableContinuousBuffer() * @see self::enableContinuousBuffer() */ public function disableContinuousBuffer() { parent::disableContinuousBuffer(); if ($this->mode_3cbc) { $this->des[0]->disableContinuousBuffer(); $this->des[1]->disableContinuousBuffer(); $this->des[2]->disableContinuousBuffer(); } } /** * Creates the key schedule * * @see \phpseclib3\Crypt\DES::setupKey() * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() */ protected function setupKey() { switch (true) { // if $key <= 64bits we configure our internal pure-php cipher engine // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same. case strlen($this->key) <= 8: $this->des_rounds = 1; break; // otherwise, if $key > 64bits, we configure our engine to work as 3DES. default: $this->des_rounds = 3; // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately. if ($this->mode_3cbc) { $this->des[0]->setupKey(); $this->des[1]->setupKey(); $this->des[2]->setupKey(); // because $des[0-2] will, now, do all the work we can return here // not need unnecessary stress parent::setupKey() with our, now unused, $key. return; } } // setup our key parent::setupKey(); } /** * Sets the internal crypt engine * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() * @see \phpseclib3\Crypt\Common\SymmetricKey::setPreferredEngine() * @param int $engine */ public function setPreferredEngine($engine) { if ($this->mode_3cbc) { $this->des[0]->setPreferredEngine($engine); $this->des[1]->setPreferredEngine($engine); $this->des[2]->setPreferredEngine($engine); } parent::setPreferredEngine($engine); } } PK2A#]d�$Ow4w41vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.phpnu�[���<?php /** * Pure-PHP implementation of EC. * * PHP version 5 * * Here's an example of how to create signatures and verify signatures with this library: * <code> * <?php * include 'vendor/autoload.php'; * * $private = \phpseclib3\Crypt\EC::createKey('secp256k1'); * $public = $private->getPublicKey(); * * $plaintext = 'terrafrost'; * * $signature = $private->sign($plaintext); * * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; use phpseclib3\Crypt\EC\Curves\Curve25519; use phpseclib3\Crypt\EC\Curves\Ed25519; use phpseclib3\Crypt\EC\Curves\Ed448; use phpseclib3\Crypt\EC\Formats\Keys\PKCS1; use phpseclib3\Crypt\EC\Parameters; use phpseclib3\Crypt\EC\PrivateKey; use phpseclib3\Crypt\EC\PublicKey; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Exception\UnsupportedOperationException; use phpseclib3\File\ASN1; use phpseclib3\File\ASN1\Maps\ECParameters; use phpseclib3\Math\BigInteger; /** * Pure-PHP implementation of EC. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EC extends AsymmetricKey { /** * Algorithm Name * * @var string */ const ALGORITHM = 'EC'; /** * Public Key QA * * @var object[] */ protected $QA; /** * Curve * * @var \phpseclib3\Crypt\EC\BaseCurves\Base */ protected $curve; /** * Signature Format * * @var string */ protected $format; /** * Signature Format (Short) * * @var string */ protected $shortFormat; /** * Curve Name * * @var string */ private $curveName; /** * Curve Order * * Used for deterministic ECDSA * * @var \phpseclib3\Math\BigInteger */ protected $q; /** * Alias for the private key * * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because * with x you have x * the base point yielding an (x, y)-coordinate that is the * public key. But the x is different depending on which side of the equal sign * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate. * * @var \phpseclib3\Math\BigInteger */ protected $x; /** * Context * * @var string */ protected $context; /** * Signature Format * * @var string */ protected $sigFormat; /** * Create public / private key pair. * * @param string $curve * @return \phpseclib3\Crypt\EC\PrivateKey */ public static function createKey($curve) { self::initialize_static_variables(); $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); } if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } $curve = strtolower($curve); if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) { $kp = sodium_crypto_sign_keypair(); $privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp)); //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp)); $privatekey->curveName = 'Ed25519'; //$publickey->curveName = $curve; return $privatekey; } $privatekey = new PrivateKey(); $curveName = $curve; if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) { $curveName = ucfirst($curveName); } elseif (substr($curveName, 0, 10) == 'brainpoolp') { $curveName = 'brainpoolP' . substr($curveName, 10); } $curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName; if (!class_exists($curve)) { throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported'); } $reflect = new \ReflectionClass($curve); $curveName = $reflect->isFinal() ? $reflect->getParentClass()->getShortName() : $reflect->getShortName(); $curve = new $curve(); if ($curve instanceof TwistedEdwardsCurve) { $arr = $curve->extractSecret(Random::string($curve instanceof Ed448 ? 57 : 32)); $privatekey->dA = $dA = $arr['dA']; $privatekey->secret = $arr['secret']; } else { $privatekey->dA = $dA = $curve->createRandomMultiplier(); } if ($curve instanceof Curve25519 && self::$engines['libsodium']) { //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000'); //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r); $QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes()); $privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))]; } else { $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); } $privatekey->curve = $curve; //$publickey = clone $privatekey; //unset($publickey->dA); //unset($publickey->x); $privatekey->curveName = $curveName; //$publickey->curveName = $curveName; if ($privatekey->curve instanceof TwistedEdwardsCurve) { return $privatekey->withHash($curve::HASH); } return $privatekey; } /** * OnLoad Handler * * @return bool */ protected static function onLoad(array $components) { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } if (!isset($components['dA']) && !isset($components['QA'])) { $new = new Parameters(); $new->curve = $components['curve']; return $new; } $new = isset($components['dA']) ? new PrivateKey() : new PublicKey(); $new->curve = $components['curve']; $new->QA = $components['QA']; if (isset($components['dA'])) { $new->dA = $components['dA']; $new->secret = $components['secret']; } if ($new->curve instanceof TwistedEdwardsCurve) { return $new->withHash($components['curve']::HASH); } return $new; } /** * Constructor * * PublicKey and PrivateKey objects can only be created from abstract RSA class */ protected function __construct() { $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); $this->shortFormat = 'ASN1'; parent::__construct(); } /** * Returns the curve * * Returns a string if it's a named curve, an array if not * * @return string|array */ public function getCurve() { if ($this->curveName) { return $this->curveName; } if ($this->curve instanceof MontgomeryCurve) { $this->curveName = $this->curve instanceof Curve25519 ? 'Curve25519' : 'Curve448'; return $this->curveName; } if ($this->curve instanceof TwistedEdwardsCurve) { $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448'; return $this->curveName; } $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => true]); $decoded = ASN1::extractBER($params); $decoded = ASN1::decodeBER($decoded); $decoded = ASN1::asn1map($decoded[0], ECParameters::MAP); if (isset($decoded['namedCurve'])) { $this->curveName = $decoded['namedCurve']; return $decoded['namedCurve']; } if (!$namedCurves) { PKCS1::useSpecifiedCurve(); } return $decoded; } /** * Returns the key size * * Quoting https://tools.ietf.org/html/rfc5656#section-2, * * "The size of a set of elliptic curve domain parameters on a prime * curve is defined as the number of bits in the binary representation * of the field order, commonly denoted by p. Size on a * characteristic-2 curve is defined as the number of bits in the binary * representation of the field, commonly denoted by m. A set of * elliptic curve domain parameters defines a group of order n generated * by a base point P" * * @return int */ public function getLength() { return $this->curve->getLength(); } /** * Returns the current engine being used * * @see self::useInternalEngine() * @see self::useBestEngine() * @return string */ public function getEngine() { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } if ($this->curve instanceof TwistedEdwardsCurve) { return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ? 'libsodium' : 'PHP'; } return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ? 'OpenSSL' : 'PHP'; } /** * Returns the public key coordinates as a string * * Used by ECDH * * @return string */ public function getEncodedCoordinates() { if ($this->curve instanceof MontgomeryCurve) { return strrev($this->QA[0]->toBytes(true)); } if ($this->curve instanceof TwistedEdwardsCurve) { return $this->curve->encodePoint($this->QA); } return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true); } /** * Returns the parameters * * @see self::getPublicKey() * @param string $type optional * @return mixed */ public function getParameters($type = 'PKCS1') { $type = self::validatePlugin('Keys', $type, 'saveParameters'); $key = $type::saveParameters($this->curve); return EC::load($key, 'PKCS1') ->withHash($this->hash->getHash()) ->withSignatureFormat($this->shortFormat); } /** * Determines the signature padding mode * * Valid values are: ASN1, SSH2, Raw * * @param string $format */ public function withSignatureFormat($format) { if ($this->curve instanceof MontgomeryCurve) { throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); } $new = clone $this; $new->shortFormat = $format; $new->sigFormat = self::validatePlugin('Signature', $format); return $new; } /** * Returns the signature format currently being used * */ public function getSignatureFormat() { return $this->shortFormat; } /** * Sets the context * * Used by Ed25519 / Ed448. * * @see self::sign() * @see self::verify() * @param string $context optional */ public function withContext($context = null) { if (!$this->curve instanceof TwistedEdwardsCurve) { throw new UnsupportedCurveException('Only Ed25519 and Ed448 support contexts'); } $new = clone $this; if (!isset($context)) { $new->context = null; return $new; } if (!is_string($context)) { throw new \InvalidArgumentException('setContext expects a string'); } if (strlen($context) > 255) { throw new \LengthException('The context is supposed to be, at most, 255 bytes long'); } $new->context = $context; return $new; } /** * Returns the signature format currently being used * */ public function getContext() { return $this->context; } /** * Determines which hashing function should be used * * @param string $hash */ public function withHash($hash) { if ($this->curve instanceof MontgomeryCurve) { throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); } if ($this->curve instanceof Ed25519 && $hash != 'sha512') { throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash'); } if ($this->curve instanceof Ed448 && $hash != 'shake256-912') { throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes'); } return parent::withHash($hash); } /** * __toString() magic method * * @return string */ public function __toString() { if ($this->curve instanceof MontgomeryCurve) { return ''; } return parent::__toString(); } } PK2A#]n�9xmxm2vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.phpnu�[���<?php /** * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. * * PHP version 5 * * Here's an example of how to encrypt and decrypt text with this library: * <code> * <?php * include 'vendor/autoload.php'; * * $private = \phpseclib3\Crypt\RSA::createKey(); * $public = $private->getPublicKey(); * * $plaintext = 'terrafrost'; * * $ciphertext = $public->encrypt($plaintext); * * echo $private->decrypt($ciphertext); * ?> * </code> * * Here's an example of how to create signatures and verify signatures with this library: * <code> * <?php * include 'vendor/autoload.php'; * * $private = \phpseclib3\Crypt\RSA::createKey(); * $public = $private->getPublicKey(); * * $plaintext = 'terrafrost'; * * $signature = $private->sign($plaintext); * * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * </code> * * One thing to consider when using this: so phpseclib uses PSS mode by default. * Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So * should phpseclib save to the id-RSASSA-PSS format by default or the * rsaEncryption format? For stand-alone keys I figure rsaEncryption is better * because SSH doesn't use PSS and idk how many SSH servers would be able to * decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS * format is used by default (unless you change it up to use PKCS1 instead) * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\RSA\Formats\Keys\PSS; use phpseclib3\Crypt\RSA\PrivateKey; use phpseclib3\Crypt\RSA\PublicKey; use phpseclib3\Exception\InconsistentSetupException; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Math\BigInteger; /** * Pure-PHP PKCS#1 compliant implementation of RSA. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RSA extends AsymmetricKey { /** * Algorithm Name * * @var string */ const ALGORITHM = 'RSA'; /** * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} * (OAEP) for encryption / decryption. * * Uses sha256 by default * * @see self::setHash() * @see self::setMGFHash() * @see self::encrypt() * @see self::decrypt() */ const ENCRYPTION_OAEP = 1; /** * Use PKCS#1 padding. * * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards * compatibility with protocols (like SSH-1) written before OAEP's introduction. * * @see self::encrypt() * @see self::decrypt() */ const ENCRYPTION_PKCS1 = 2; /** * Do not use any padding * * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. * * @see self::encrypt() * @see self::decrypt() */ const ENCRYPTION_NONE = 4; /** * Use the Probabilistic Signature Scheme for signing * * Uses sha256 and 0 as the salt length * * @see self::setSaltLength() * @see self::setMGFHash() * @see self::setHash() * @see self::sign() * @see self::verify() * @see self::setHash() */ const SIGNATURE_PSS = 16; /** * Use a relaxed version of PKCS#1 padding for signature verification * * @see self::sign() * @see self::verify() * @see self::setHash() */ const SIGNATURE_RELAXED_PKCS1 = 32; /** * Use PKCS#1 padding for signature verification * * @see self::sign() * @see self::verify() * @see self::setHash() */ const SIGNATURE_PKCS1 = 64; /** * Encryption padding mode * * @var int */ protected $encryptionPadding = self::ENCRYPTION_OAEP; /** * Signature padding mode * * @var int */ protected $signaturePadding = self::SIGNATURE_PSS; /** * Length of hash function output * * @var int */ protected $hLen; /** * Length of salt * * @var int */ protected $sLen; /** * Label * * @var string */ protected $label = ''; /** * Hash function for the Mask Generation Function * * @var \phpseclib3\Crypt\Hash */ protected $mgfHash; /** * Length of MGF hash function output * * @var int */ protected $mgfHLen; /** * Modulus (ie. n) * * @var \phpseclib3\Math\BigInteger */ protected $modulus; /** * Modulus length * * @var \phpseclib3\Math\BigInteger */ protected $k; /** * Exponent (ie. e or d) * * @var \phpseclib3\Math\BigInteger */ protected $exponent; /** * Default public exponent * * @var int * @link http://en.wikipedia.org/wiki/65537_%28number%29 */ private static $defaultExponent = 65537; /** * Enable Blinding? * * @var bool */ protected static $enableBlinding = true; /** * OpenSSL configuration file name. * * @see self::createKey() * @var ?string */ protected static $configFile; /** * Smallest Prime * * Per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller * than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime * to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if * engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is * ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's * a chance neither gmp nor OpenSSL are installed) * * @var int */ private static $smallestPrime = 4096; /** * Public Exponent * * @var \phpseclib3\Math\BigInteger */ protected $publicExponent; /** * Sets the public exponent for key generation * * This will be 65537 unless changed. * * @param int $val */ public static function setExponent($val) { self::$defaultExponent = $val; } /** * Sets the smallest prime number in bits. Used for key generation * * This will be 4096 unless changed. * * @param int $val */ public static function setSmallestPrime($val) { self::$smallestPrime = $val; } /** * Sets the OpenSSL config file path * * Set to the empty string to use the default config file * * @param string $val */ public static function setOpenSSLConfigPath($val) { self::$configFile = $val; } /** * Create a private key * * The public key can be extracted from the private key * * @return RSA\PrivateKey * @param int $bits */ public static function createKey($bits = 2048) { self::initialize_static_variables(); $class = new \ReflectionClass(static::class); if ($class->isFinal()) { throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); } $regSize = $bits >> 1; // divide by two to see how many bits P and Q would be if ($regSize > self::$smallestPrime) { $num_primes = floor($bits / self::$smallestPrime); $regSize = self::$smallestPrime; } else { $num_primes = 2; } if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum if (self::$engines['OpenSSL']) { $config = []; if (self::$configFile) { $config['config'] = self::$configFile; } $rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config); openssl_pkey_export($rsa, $privatekeystr, null, $config); // clear the buffer of error strings stemming from a minimalistic openssl.cnf // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up while (openssl_error_string() !== false) { } return RSA::load($privatekeystr); } } static $e; if (!isset($e)) { $e = new BigInteger(self::$defaultExponent); } $n = clone self::$one; $exponents = $coefficients = $primes = []; $lcm = [ 'top' => clone self::$one, 'bottom' => false ]; do { for ($i = 1; $i <= $num_primes; $i++) { if ($i != $num_primes) { $primes[$i] = BigInteger::randomPrime($regSize); } else { extract(BigInteger::minMaxBits($bits)); /** @var BigInteger $min * @var BigInteger $max */ list($min) = $min->divide($n); $min = $min->add(self::$one); list($max) = $max->divide($n); $primes[$i] = BigInteger::randomRangePrime($min, $max); } // the first coefficient is calculated differently from the rest // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) if ($i > 2) { $coefficients[$i] = $n->modInverse($primes[$i]); } $n = $n->multiply($primes[$i]); $temp = $primes[$i]->subtract(self::$one); // textbook RSA implementations use Euler's totient function instead of the least common multiple. // see http://en.wikipedia.org/wiki/Euler%27s_totient_function $lcm['top'] = $lcm['top']->multiply($temp); $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); } list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; } while (!$gcd->equals(self::$one)); $coefficients[2] = $primes[2]->modInverse($primes[1]); $d = $e->modInverse($temp); foreach ($primes as $i => $prime) { $temp = $prime->subtract(self::$one); $exponents[$i] = $e->modInverse($temp); } // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>: // RSAPrivateKey ::= SEQUENCE { // version Version, // modulus INTEGER, -- n // publicExponent INTEGER, -- e // privateExponent INTEGER, -- d // prime1 INTEGER, -- p // prime2 INTEGER, -- q // exponent1 INTEGER, -- d mod (p-1) // exponent2 INTEGER, -- d mod (q-1) // coefficient INTEGER, -- (inverse of q) mod p // otherPrimeInfos OtherPrimeInfos OPTIONAL // } $privatekey = new PrivateKey(); $privatekey->modulus = $n; $privatekey->k = $bits >> 3; $privatekey->publicExponent = $e; $privatekey->exponent = $d; $privatekey->primes = $primes; $privatekey->exponents = $exponents; $privatekey->coefficients = $coefficients; /* $publickey = new PublicKey; $publickey->modulus = $n; $publickey->k = $bits >> 3; $publickey->exponent = $e; $publickey->publicExponent = $e; $publickey->isPublic = true; */ return $privatekey; } /** * OnLoad Handler * * @return bool */ protected static function onLoad(array $components) { $key = $components['isPublicKey'] ? new PublicKey() : new PrivateKey(); $key->modulus = $components['modulus']; $key->publicExponent = $components['publicExponent']; $key->k = $key->modulus->getLengthInBytes(); if ($components['isPublicKey'] || !isset($components['privateExponent'])) { $key->exponent = $key->publicExponent; } else { $key->privateExponent = $components['privateExponent']; $key->exponent = $key->privateExponent; $key->primes = $components['primes']; $key->exponents = $components['exponents']; $key->coefficients = $components['coefficients']; } if ($components['format'] == PSS::class) { // in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is // explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib // uses PSS padding by default. it assumes the more secure method by default and altho it provides // for the less secure PKCS1 method you have to go out of your way to use it. this is consistent // with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that // not only does it defaults to the most secure methods - it doesn't even let you choose less // secure methods //$key = $key->withPadding(self::SIGNATURE_PSS); if (isset($components['hash'])) { $key = $key->withHash($components['hash']); } if (isset($components['MGFHash'])) { $key = $key->withMGFHash($components['MGFHash']); } if (isset($components['saltLength'])) { $key = $key->withSaltLength($components['saltLength']); } } return $key; } /** * Initialize static variables */ protected static function initialize_static_variables() { if (!isset(self::$configFile)) { self::$configFile = dirname(__FILE__) . '/../openssl.cnf'; } parent::initialize_static_variables(); } /** * Constructor * * PublicKey and PrivateKey objects can only be created from abstract RSA class */ protected function __construct() { parent::__construct(); $this->hLen = $this->hash->getLengthInBytes(); $this->mgfHash = new Hash('sha256'); $this->mgfHLen = $this->mgfHash->getLengthInBytes(); } /** * Integer-to-Octet-String primitive * * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. * * @param bool|\phpseclib3\Math\BigInteger $x * @param int $xLen * @return bool|string */ protected function i2osp($x, $xLen) { if ($x === false) { return false; } $x = $x->toBytes(); if (strlen($x) > $xLen) { throw new \OutOfRangeException('Resultant string length out of range'); } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); } /** * Octet-String-to-Integer primitive * * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @param string $x * @return \phpseclib3\Math\BigInteger */ protected function os2ip($x) { return new BigInteger($x, 256); } /** * EMSA-PKCS1-V1_5-ENCODE * * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. * * @param string $m * @param int $emLen * @throws \LengthException if the intended encoded message length is too short * @return string */ protected function emsa_pkcs1_v1_5_encode($m, $emLen) { $h = $this->hash->hash($m); // see http://tools.ietf.org/html/rfc3447#page-43 switch ($this->hash->getHash()) { case 'md2': $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"; break; case 'md5': $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; break; case 'sha1': $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; break; case 'sha256': $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; break; case 'sha384': $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"; break; case 'sha512': $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"; break; // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 case 'sha224': $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"; break; case 'sha512/224': $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c"; break; case 'sha512/256': $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20"; } $t .= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); $em = "\0\1$ps\0$t"; return $em; } /** * EMSA-PKCS1-V1_5-ENCODE (without NULL) * * Quoting https://tools.ietf.org/html/rfc8017#page-65, * * "The parameters field associated with id-sha1, id-sha224, id-sha256, * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should * generally be omitted, but if present, it shall have a value of type * NULL" * * @param string $m * @param int $emLen * @return string */ protected function emsa_pkcs1_v1_5_encode_without_null($m, $emLen) { $h = $this->hash->hash($m); // see http://tools.ietf.org/html/rfc3447#page-43 switch ($this->hash->getHash()) { case 'sha1': $t = "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14"; break; case 'sha256': $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20"; break; case 'sha384': $t = "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30"; break; case 'sha512': $t = "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40"; break; // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 case 'sha224': $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c"; break; case 'sha512/224': $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c"; break; case 'sha512/256': $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20"; break; default: throw new UnsupportedAlgorithmException('md2 and md5 require NULLs'); } $t .= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); $em = "\0\1$ps\0$t"; return $em; } /** * MGF1 * * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. * * @param string $mgfSeed * @param int $maskLen * @return string */ protected function mgf1($mgfSeed, $maskLen) { // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. $t = ''; $count = ceil($maskLen / $this->mgfHLen); for ($i = 0; $i < $count; $i++) { $c = pack('N', $i); $t .= $this->mgfHash->hash($mgfSeed . $c); } return substr($t, 0, $maskLen); } /** * Returns the key size * * More specifically, this returns the size of the modulo in bits. * * @return int */ public function getLength() { return !isset($this->modulus) ? 0 : $this->modulus->getLength(); } /** * Determines which hashing function should be used * * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and * decryption. * * @param string $hash */ public function withHash($hash) { $new = clone $this; // \phpseclib3\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch (strtolower($hash)) { case 'md2': case 'md5': case 'sha1': case 'sha256': case 'sha384': case 'sha512': case 'sha224': case 'sha512/224': case 'sha512/256': $new->hash = new Hash($hash); break; default: throw new UnsupportedAlgorithmException( 'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256' ); } $new->hLen = $new->hash->getLengthInBytes(); return $new; } /** * Determines which hashing function should be used for the mask generation function * * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's * best if Hash and MGFHash are set to the same thing this is not a requirement. * * @param string $hash */ public function withMGFHash($hash) { $new = clone $this; // \phpseclib3\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch (strtolower($hash)) { case 'md2': case 'md5': case 'sha1': case 'sha256': case 'sha384': case 'sha512': case 'sha224': case 'sha512/224': case 'sha512/256': $new->mgfHash = new Hash($hash); break; default: throw new UnsupportedAlgorithmException( 'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256' ); } $new->mgfHLen = $new->mgfHash->getLengthInBytes(); return $new; } /** * Returns the MGF hash algorithm currently being used * */ public function getMGFHash() { return clone $this->mgfHash; } /** * Determines the salt length * * Used by RSA::PADDING_PSS * * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: * * Typical salt lengths in octets are hLen (the length of the output * of the hash function Hash) and 0. * * @param int $sLen */ public function withSaltLength($sLen) { $new = clone $this; $new->sLen = $sLen; return $new; } /** * Returns the salt length currently being used * */ public function getSaltLength() { return $this->sLen !== null ? $this->sLen : $this->hLen; } /** * Determines the label * * Used by RSA::PADDING_OAEP * * To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: * * Both the encryption and the decryption operations of RSAES-OAEP take * the value of a label L as input. In this version of PKCS #1, L is * the empty string; other uses of the label are outside the scope of * this document. * * @param string $label */ public function withLabel($label) { $new = clone $this; $new->label = $label; return $new; } /** * Returns the label currently being used * */ public function getLabel() { return $this->label; } /** * Determines the padding modes * * Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1); * * @param int $padding */ public function withPadding($padding) { $masks = [ self::ENCRYPTION_OAEP, self::ENCRYPTION_PKCS1, self::ENCRYPTION_NONE ]; $encryptedCount = 0; $selected = 0; foreach ($masks as $mask) { if ($padding & $mask) { $selected = $mask; $encryptedCount++; } } if ($encryptedCount > 1) { throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected'); } $encryptionPadding = $selected; $masks = [ self::SIGNATURE_PSS, self::SIGNATURE_RELAXED_PKCS1, self::SIGNATURE_PKCS1 ]; $signatureCount = 0; $selected = 0; foreach ($masks as $mask) { if ($padding & $mask) { $selected = $mask; $signatureCount++; } } if ($signatureCount > 1) { throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected'); } $signaturePadding = $selected; $new = clone $this; if ($encryptedCount) { $new->encryptionPadding = $encryptionPadding; } if ($signatureCount) { $new->signaturePadding = $signaturePadding; } return $new; } /** * Returns the padding currently being used * */ public function getPadding() { return $this->signaturePadding | $this->encryptionPadding; } /** * Returns the current engine being used * * OpenSSL is only used in this class (and it's subclasses) for key generation * Even then it depends on the parameters you're using. It's not used for * multi-prime RSA nor is it used if the key length is outside of the range * supported by OpenSSL * * @see self::useInternalEngine() * @see self::useBestEngine() * @return string */ public function getEngine() { if (!isset(self::$engines['PHP'])) { self::useBestEngine(); } return self::$engines['OpenSSL'] && self::$defaultExponent == 65537 ? 'OpenSSL' : 'PHP'; } /** * Enable RSA Blinding * */ public static function enableBlinding() { static::$enableBlinding = true; } /** * Disable RSA Blinding * */ public static function disableBlinding() { static::$enableBlinding = false; } } PK2A#]��&�`�`�3vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.phpnu�[���<?php /** * Wrapper around hash() and hash_hmac() functions supporting truncated hashes * such as sha256-96. Any hash algorithm returned by hash_algos() (and * truncated versions thereof) are supported. * * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will * return the HMAC as opposed to the hash. * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $hash = new \phpseclib3\Crypt\Hash('sha512'); * * $hash->setKey('abcdefg'); * * echo base64_encode($hash->hash('abcdefg')); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2015 Jim Wigginton * @author Andreas Fischer <bantu@phpbb.com> * @copyright 2015 Andreas Fischer * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Crypt; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\InsufficientSetupException; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Math\BigInteger; use phpseclib3\Math\PrimeField; /** * @author Jim Wigginton <terrafrost@php.net> * @author Andreas Fischer <bantu@phpbb.com> */ class Hash { /** * Padding Types * */ const PADDING_KECCAK = 1; /** * Padding Types * */ const PADDING_SHA3 = 2; /** * Padding Types * */ const PADDING_SHAKE = 3; /** * Padding Type * * Only used by SHA3 * * @var int */ private $paddingType = 0; /** * Hash Parameter * * @see self::setHash() * @var int */ private $hashParam; /** * Byte-length of hash output (Internal HMAC) * * @see self::setHash() * @var int */ private $length; /** * Hash Algorithm * * @see self::setHash() * @var string */ private $algo; /** * Key * * @see self::setKey() * @var string */ private $key = false; /** * Nonce * * @see self::setNonce() * @var string */ private $nonce = false; /** * Hash Parameters * * @var array */ private $parameters = []; /** * Computed Key * * @see self::_computeKey() * @var string */ private $computedKey = false; /** * Outer XOR (Internal HMAC) * * Used only for sha512/* * * @see self::hash() * @var string */ private $opad; /** * Inner XOR (Internal HMAC) * * Used only for sha512/* * * @see self::hash() * @var string */ private $ipad; /** * Recompute AES Key * * Used only for umac * * @see self::hash() * @var boolean */ private $recomputeAESKey; /** * umac cipher object * * @see self::hash() * @var \phpseclib3\Crypt\AES */ private $c; /** * umac pad * * @see self::hash() * @var string */ private $pad; /** * Block Size * * @var int */ private $blockSize; /**#@+ * UMAC variables * * @var PrimeField */ private static $factory36; private static $factory64; private static $factory128; private static $offset64; private static $offset128; private static $marker64; private static $marker128; private static $maxwordrange64; private static $maxwordrange128; /**#@-*/ /** * Default Constructor. * * @param string $hash */ public function __construct($hash = 'sha256') { $this->setHash($hash); } /** * Sets the key for HMACs * * Keys can be of any length. * * @param string $key */ public function setKey($key = false) { $this->key = $key; $this->computeKey(); $this->recomputeAESKey = true; } /** * Sets the nonce for UMACs * * Keys can be of any length. * * @param string $nonce */ public function setNonce($nonce = false) { switch (true) { case !is_string($nonce): case strlen($nonce) > 0 && strlen($nonce) <= 16: $this->recomputeAESKey = true; $this->nonce = $nonce; return; } throw new \LengthException('The nonce length must be between 1 and 16 bytes, inclusive'); } /** * Pre-compute the key used by the HMAC * * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." * * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during * every call * */ private function computeKey() { if ($this->key === false) { $this->computedKey = false; return; } if (strlen($this->key) <= $this->getBlockLengthInBytes()) { $this->computedKey = $this->key; return; } $this->computedKey = is_array($this->algo) ? call_user_func($this->algo, $this->key) : hash($this->algo, $this->key, true); } /** * Gets the hash function. * * As set by the constructor or by the setHash() method. * * @return string */ public function getHash() { return $this->hashParam; } /** * Sets the hash function. * * @param string $hash */ public function setHash($hash) { $this->hashParam = $hash = strtolower($hash); switch ($hash) { case 'umac-32': case 'umac-64': case 'umac-96': case 'umac-128': $this->blockSize = 128; $this->length = abs(substr($hash, -3)) >> 3; $this->algo = 'umac'; return; case 'md2-96': case 'md5-96': case 'sha1-96': case 'sha224-96': case 'sha256-96': case 'sha384-96': case 'sha512-96': case 'sha512/224-96': case 'sha512/256-96': $hash = substr($hash, 0, -3); $this->length = 12; // 96 / 8 = 12 break; case 'md2': case 'md5': $this->length = 16; break; case 'sha1': $this->length = 20; break; case 'sha224': case 'sha512/224': case 'sha3-224': $this->length = 28; break; case 'keccak256': $this->paddingType = self::PADDING_KECCAK; // fall-through case 'sha256': case 'sha512/256': case 'sha3-256': $this->length = 32; break; case 'sha384': case 'sha3-384': $this->length = 48; break; case 'sha512': case 'sha3-512': $this->length = 64; break; default: if (preg_match('#^(shake(?:128|256))-(\d+)$#', $hash, $matches)) { $this->paddingType = self::PADDING_SHAKE; $hash = $matches[1]; $this->length = $matches[2] >> 3; } else { throw new UnsupportedAlgorithmException( "$hash is not a supported algorithm" ); } } switch ($hash) { case 'md2': case 'md2-96': $this->blockSize = 128; break; case 'md5-96': case 'sha1-96': case 'sha224-96': case 'sha256-96': case 'md5': case 'sha1': case 'sha224': case 'sha256': $this->blockSize = 512; break; case 'sha3-224': $this->blockSize = 1152; // 1600 - 2*224 break; case 'sha3-256': case 'shake256': case 'keccak256': $this->blockSize = 1088; // 1600 - 2*256 break; case 'sha3-384': $this->blockSize = 832; // 1600 - 2*384 break; case 'sha3-512': $this->blockSize = 576; // 1600 - 2*512 break; case 'shake128': $this->blockSize = 1344; // 1600 - 2*128 break; default: $this->blockSize = 1024; } if (in_array(substr($hash, 0, 5), ['sha3-', 'shake', 'kecca'])) { // PHP 7.1.0 introduced support for "SHA3 fixed mode algorithms": // http://php.net/ChangeLog-7.php#7.1.0 if (version_compare(PHP_VERSION, '7.1.0') < 0 || substr($hash, 0, 5) != 'sha3-') { //preg_match('#(\d+)$#', $hash, $matches); //$this->parameters['capacity'] = 2 * $matches[1]; // 1600 - $this->blockSize //$this->parameters['rate'] = 1600 - $this->parameters['capacity']; // == $this->blockSize if (!$this->paddingType) { $this->paddingType = self::PADDING_SHA3; } $this->parameters = [ 'capacity' => 1600 - $this->blockSize, 'rate' => $this->blockSize, 'length' => $this->length, 'padding' => $this->paddingType ]; $hash = ['phpseclib3\Crypt\Hash', PHP_INT_SIZE == 8 ? 'sha3_64' : 'sha3_32']; } } if ($hash == 'sha512/224' || $hash == 'sha512/256') { // PHP 7.1.0 introduced sha512/224 and sha512/256 support: // http://php.net/ChangeLog-7.php#7.1.0 if (version_compare(PHP_VERSION, '7.1.0') < 0) { // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 $initial = $hash == 'sha512/256' ? [ '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2' ] : [ '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1' ]; for ($i = 0; $i < 8; $i++) { $initial[$i] = new BigInteger($initial[$i], 16); $initial[$i]->setPrecision(64); } $this->parameters = compact('initial'); $hash = ['phpseclib3\Crypt\Hash', 'sha512']; } } if (is_array($hash)) { $b = $this->blockSize >> 3; $this->ipad = str_repeat(chr(0x36), $b); $this->opad = str_repeat(chr(0x5C), $b); } $this->algo = $hash; $this->computeKey(); } /** * KDF: Key-Derivation Function * * The key-derivation function generates pseudorandom bits used to key the hash functions. * * @param int $index a non-negative integer less than 2^64 * @param int $numbytes a non-negative integer less than 2^64 * @return string string of length numbytes bytes */ private function kdf($index, $numbytes) { $this->c->setIV(pack('N4', 0, $index, 0, 1)); return $this->c->encrypt(str_repeat("\0", $numbytes)); } /** * PDF Algorithm * * @return string string of length taglen bytes. */ private function pdf() { $k = $this->key; $nonce = $this->nonce; $taglen = $this->length; // // Extract and zero low bit(s) of Nonce if needed // if ($taglen <= 8) { $last = strlen($nonce) - 1; $mask = $taglen == 4 ? "\3" : "\1"; $index = $nonce[$last] & $mask; $nonce[$last] = $nonce[$last] ^ $index; } // // Make Nonce BLOCKLEN bytes by appending zeroes if needed // $nonce = str_pad($nonce, 16, "\0"); // // Generate subkey, encipher and extract indexed substring // $kp = $this->kdf(0, 16); $c = new AES('ctr'); $c->disablePadding(); $c->setKey($kp); $c->setIV($nonce); $t = $c->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); // we could use ord() but per https://paragonie.com/blog/2016/06/constant-time-encoding-boring-cryptography-rfc-4648-and-you // unpack() doesn't leak timing info return $taglen <= 8 ? substr($t, unpack('C', $index)[1] * $taglen, $taglen) : substr($t, 0, $taglen); } /** * UHASH Algorithm * * @param string $m string of length less than 2^67 bits. * @param int $taglen the integer 4, 8, 12 or 16. * @return string string of length taglen bytes. */ private function uhash($m, $taglen) { // // One internal iteration per 4 bytes of output // $iters = $taglen >> 2; // // Define total key needed for all iterations using KDF. // L1Key reuses most key material between iterations. // //$L1Key = $this->kdf(1, 1024 + ($iters - 1) * 16); $L1Key = $this->kdf(1, (1024 + ($iters - 1)) * 16); $L2Key = $this->kdf(2, $iters * 24); $L3Key1 = $this->kdf(3, $iters * 64); $L3Key2 = $this->kdf(4, $iters * 4); // // For each iteration, extract key and do three-layer hash. // If bytelength(M) <= 1024, then skip L2-HASH. // $y = ''; for ($i = 0; $i < $iters; $i++) { $L1Key_i = substr($L1Key, $i * 16, 1024); $L2Key_i = substr($L2Key, $i * 24, 24); $L3Key1_i = substr($L3Key1, $i * 64, 64); $L3Key2_i = substr($L3Key2, $i * 4, 4); $a = self::L1Hash($L1Key_i, $m); $b = strlen($m) <= 1024 ? "\0\0\0\0\0\0\0\0$a" : self::L2Hash($L2Key_i, $a); $c = self::L3Hash($L3Key1_i, $L3Key2_i, $b); $y .= $c; } return $y; } /** * L1-HASH Algorithm * * The first-layer hash breaks the message into 1024-byte chunks and * hashes each with a function called NH. Concatenating the results * forms a string, which is up to 128 times shorter than the original. * * @param string $k string of length 1024 bytes. * @param string $m string of length less than 2^67 bits. * @return string string of length (8 * ceil(bitlength(M)/8192)) bytes. */ private static function L1Hash($k, $m) { // // Break M into 1024 byte chunks (final chunk may be shorter) // $m = str_split($m, 1024); // // For each chunk, except the last: endian-adjust, NH hash // and add bit-length. Use results to build Y. // $length = new BigInteger(1024 * 8); $y = ''; for ($i = 0; $i < count($m) - 1; $i++) { $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP $y .= static::nh($k, $m[$i], $length); } // // For the last chunk: pad to 32-byte boundary, endian-adjust, // NH hash and add bit-length. Concatenate the result to Y. // $length = count($m) ? strlen($m[$i]) : 0; $pad = 32 - ($length % 32); $pad = max(32, $length + $pad % 32); $m[$i] = str_pad(isset($m[$i]) ? $m[$i] : '', $pad, "\0"); // zeropad $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP $y .= static::nh($k, $m[$i], new BigInteger($length * 8)); return $y; } /** * NH Algorithm * * @param string $k string of length 1024 bytes. * @param string $m string with length divisible by 32 bytes. * @return string string of length 8 bytes. */ private static function nh($k, $m, $length) { $toUInt32 = function ($x) { $x = new BigInteger($x, 256); $x->setPrecision(32); return $x; }; // // Break M and K into 4-byte chunks // //$t = strlen($m) >> 2; $m = str_split($m, 4); $t = count($m); $k = str_split($k, 4); $k = array_pad(array_slice($k, 0, $t), $t, 0); $m = array_map($toUInt32, $m); $k = array_map($toUInt32, $k); // // Perform NH hash on the chunks, pairing words for multiplication // which are 4 apart to accommodate vector-parallelism. // $y = new BigInteger(); $y->setPrecision(64); $i = 0; while ($i < $t) { $temp = $m[$i]->add($k[$i]); $temp->setPrecision(64); $temp = $temp->multiply($m[$i + 4]->add($k[$i + 4])); $y = $y->add($temp); $temp = $m[$i + 1]->add($k[$i + 1]); $temp->setPrecision(64); $temp = $temp->multiply($m[$i + 5]->add($k[$i + 5])); $y = $y->add($temp); $temp = $m[$i + 2]->add($k[$i + 2]); $temp->setPrecision(64); $temp = $temp->multiply($m[$i + 6]->add($k[$i + 6])); $y = $y->add($temp); $temp = $m[$i + 3]->add($k[$i + 3]); $temp->setPrecision(64); $temp = $temp->multiply($m[$i + 7]->add($k[$i + 7])); $y = $y->add($temp); $i += 8; } return $y->add($length)->toBytes(); } /** * L2-HASH: Second-Layer Hash * * The second-layer rehashes the L1-HASH output using a polynomial hash * called POLY. If the L1-HASH output is long, then POLY is called once * on a prefix of the L1-HASH output and called using different settings * on the remainder. (This two-step hashing of the L1-HASH output is * needed only if the message length is greater than 16 megabytes.) * Careful implementation of POLY is necessary to avoid a possible * timing attack (see Section 6.6 for more information). * * @param string $k string of length 24 bytes. * @param string $m string of length less than 2^64 bytes. * @return string string of length 16 bytes. */ private static function L2Hash($k, $m) { // // Extract keys and restrict to special key-sets // $k64 = $k & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF"; $k64 = new BigInteger($k64, 256); $k128 = substr($k, 8) & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF"; $k128 = new BigInteger($k128, 256); // // If M is no more than 2^17 bytes, hash under 64-bit prime, // otherwise, hash first 2^17 bytes under 64-bit prime and // remainder under 128-bit prime. // if (strlen($m) <= 0x20000) { // 2^14 64-bit words $y = self::poly(64, self::$maxwordrange64, $k64, $m); } else { $m_1 = substr($m, 0, 0x20000); // 1 << 17 $m_2 = substr($m, 0x20000) . "\x80"; $length = strlen($m_2); $pad = 16 - ($length % 16); $pad %= 16; $m_2 = str_pad($m_2, $length + $pad, "\0"); // zeropad $y = self::poly(64, self::$maxwordrange64, $k64, $m_1); $y = str_pad($y, 16, "\0", STR_PAD_LEFT); $y = self::poly(128, self::$maxwordrange128, $k128, $y . $m_2); } return str_pad($y, 16, "\0", STR_PAD_LEFT); } /** * POLY Algorithm * * @param int $wordbits the integer 64 or 128. * @param BigInteger $maxwordrange positive integer less than 2^wordbits. * @param BigInteger $k integer in the range 0 ... prime(wordbits) - 1. * @param string $m string with length divisible by (wordbits / 8) bytes. * @return integer in the range 0 ... prime(wordbits) - 1. */ private static function poly($wordbits, $maxwordrange, $k, $m) { // // Define constants used for fixing out-of-range words // $wordbytes = $wordbits >> 3; if ($wordbits == 128) { $factory = self::$factory128; $offset = self::$offset128; $marker = self::$marker128; } else { $factory = self::$factory64; $offset = self::$offset64; $marker = self::$marker64; } $k = $factory->newInteger($k); // // Break M into chunks of length wordbytes bytes // $m_i = str_split($m, $wordbytes); // // Each input word m is compared with maxwordrange. If not smaller // then 'marker' and (m - offset), both in range, are hashed. // $y = $factory->newInteger(new BigInteger(1)); foreach ($m_i as $m) { $m = $factory->newInteger(new BigInteger($m, 256)); if ($m->compare($maxwordrange) >= 0) { $y = $k->multiply($y)->add($marker); $y = $k->multiply($y)->add($m->subtract($offset)); } else { $y = $k->multiply($y)->add($m); } } return $y->toBytes(); } /** * L3-HASH: Third-Layer Hash * * The output from L2-HASH is 16 bytes long. This final hash function * hashes the 16-byte string to a fixed length of 4 bytes. * * @param string $k1 string of length 64 bytes. * @param string $k2 string of length 4 bytes. * @param string $m string of length 16 bytes. * @return string string of length 4 bytes. */ private static function L3Hash($k1, $k2, $m) { $factory = self::$factory36; $y = $factory->newInteger(new BigInteger()); for ($i = 0; $i < 8; $i++) { $m_i = $factory->newInteger(new BigInteger(substr($m, 2 * $i, 2), 256)); $k_i = $factory->newInteger(new BigInteger(substr($k1, 8 * $i, 8), 256)); $y = $y->add($m_i->multiply($k_i)); } $y = str_pad(substr($y->toBytes(), -4), 4, "\0", STR_PAD_LEFT); $y = $y ^ $k2; return $y; } /** * Compute the Hash / HMAC / UMAC. * * @param string $text * @return string */ public function hash($text) { $algo = $this->algo; if ($algo == 'umac') { if ($this->recomputeAESKey) { if (!is_string($this->nonce)) { throw new InsufficientSetupException('No nonce has been set'); } if (!is_string($this->key)) { throw new InsufficientSetupException('No key has been set'); } if (strlen($this->key) != 16) { throw new \LengthException('Key must be 16 bytes long'); } if (!isset(self::$maxwordrange64)) { $one = new BigInteger(1); $prime36 = new BigInteger("\x00\x00\x00\x0F\xFF\xFF\xFF\xFB", 256); self::$factory36 = new PrimeField($prime36); $prime64 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC5", 256); self::$factory64 = new PrimeField($prime64); $prime128 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x61", 256); self::$factory128 = new PrimeField($prime128); self::$offset64 = new BigInteger("\1\0\0\0\0\0\0\0\0", 256); self::$offset64 = self::$factory64->newInteger(self::$offset64->subtract($prime64)); self::$offset128 = new BigInteger("\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 256); self::$offset128 = self::$factory128->newInteger(self::$offset128->subtract($prime128)); self::$marker64 = self::$factory64->newInteger($prime64->subtract($one)); self::$marker128 = self::$factory128->newInteger($prime128->subtract($one)); $maxwordrange64 = $one->bitwise_leftShift(64)->subtract($one->bitwise_leftShift(32)); self::$maxwordrange64 = self::$factory64->newInteger($maxwordrange64); $maxwordrange128 = $one->bitwise_leftShift(128)->subtract($one->bitwise_leftShift(96)); self::$maxwordrange128 = self::$factory128->newInteger($maxwordrange128); } $this->c = new AES('ctr'); $this->c->disablePadding(); $this->c->setKey($this->key); $this->pad = $this->pdf(); $this->recomputeAESKey = false; } $hashedmessage = $this->uhash($text, $this->length); return $hashedmessage ^ $this->pad; } if (is_array($algo)) { if (empty($this->key) || !is_string($this->key)) { return substr($algo($text, ...array_values($this->parameters)), 0, $this->length); } // SHA3 HMACs are discussed at https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=30 $key = str_pad($this->computedKey, $b, chr(0)); $temp = $this->ipad ^ $key; $temp .= $text; $temp = substr($algo($temp, ...array_values($this->parameters)), 0, $this->length); $output = $this->opad ^ $key; $output .= $temp; $output = $algo($output, ...array_values($this->parameters)); return substr($output, 0, $this->length); } $output = !empty($this->key) || is_string($this->key) ? hash_hmac($algo, $text, $this->computedKey, true) : hash($algo, $text, true); return strlen($output) > $this->length ? substr($output, 0, $this->length) : $output; } /** * Returns the hash length (in bits) * * @return int */ public function getLength() { return $this->length << 3; } /** * Returns the hash length (in bytes) * * @return int */ public function getLengthInBytes() { return $this->length; } /** * Returns the block length (in bits) * * @return int */ public function getBlockLength() { return $this->blockSize; } /** * Returns the block length (in bytes) * * @return int */ public function getBlockLengthInBytes() { return $this->blockSize >> 3; } /** * Pads SHA3 based on the mode * * @param int $padLength * @param int $padType * @return string */ private static function sha3_pad($padLength, $padType) { switch ($padType) { case self::PADDING_KECCAK: $temp = chr(0x01) . str_repeat("\0", $padLength - 1); $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80); return $temp; case self::PADDING_SHAKE: $temp = chr(0x1F) . str_repeat("\0", $padLength - 1); $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80); return $temp; //case self::PADDING_SHA3: default: // from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=36 return $padLength == 1 ? chr(0x86) : chr(0x06) . str_repeat("\0", $padLength - 2) . chr(0x80); } } /** * Pure-PHP 32-bit implementation of SHA3 * * Whereas BigInteger.php's 32-bit engine works on PHP 64-bit this 32-bit implementation * of SHA3 will *not* work on PHP 64-bit. This is because this implementation * employees bitwise NOTs and bitwise left shifts. And the round constants only work * on 32-bit PHP. eg. dechex(-2147483648) returns 80000000 on 32-bit PHP and * FFFFFFFF80000000 on 64-bit PHP. Sure, we could do bitwise ANDs but that would slow * things down. * * SHA512 requires BigInteger to simulate 64-bit unsigned integers because SHA2 employees * addition whereas SHA3 just employees bitwise operators. PHP64 only supports signed * 64-bit integers, which complicates addition, whereas that limitation isn't an issue * for SHA3. * * In https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=919061#page=16 KECCAK[C] is * defined as "the KECCAK instance with KECCAK-f[1600] as the underlying permutation and * capacity c". This is relevant because, altho the KECCAK standard defines a mode * (KECCAK-f[800]) designed for 32-bit machines that mode is incompatible with SHA3 * * @param string $p * @param int $c * @param int $r * @param int $d * @param int $padType */ private static function sha3_32($p, $c, $r, $d, $padType) { $block_size = $r >> 3; $padLength = $block_size - (strlen($p) % $block_size); $num_ints = $block_size >> 2; $p .= static::sha3_pad($padLength, $padType); $n = strlen($p) / $r; // number of blocks $s = [ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] ]; $p = str_split($p, $block_size); foreach ($p as $pi) { $pi = unpack('V*', $pi); $x = $y = 0; for ($i = 1; $i <= $num_ints; $i += 2) { $s[$x][$y][0] ^= $pi[$i + 1]; $s[$x][$y][1] ^= $pi[$i]; if (++$y == 5) { $y = 0; $x++; } } static::processSHA3Block32($s); } $z = ''; $i = $j = 0; while (strlen($z) < $d) { $z .= pack('V2', $s[$i][$j][1], $s[$i][$j++][0]); if ($j == 5) { $j = 0; $i++; if ($i == 5) { $i = 0; static::processSHA3Block32($s); } } } return $z; } /** * 32-bit block processing method for SHA3 * * @param array $s */ private static function processSHA3Block32(&$s) { static $rotationOffsets = [ [ 0, 1, 62, 28, 27], [36, 44, 6, 55, 20], [ 3, 10, 43, 25, 39], [41, 45, 15, 21, 8], [18, 2, 61, 56, 14] ]; // the standards give these constants in hexadecimal notation. it's tempting to want to use // that same notation, here, however, we can't, because 0x80000000, on PHP32, is a positive // float - not the negative int that we need to be in PHP32. so we use -2147483648 instead static $roundConstants = [ [0, 1], [0, 32898], [-2147483648, 32906], [-2147483648, -2147450880], [0, 32907], [0, -2147483647], [-2147483648, -2147450751], [-2147483648, 32777], [0, 138], [0, 136], [0, -2147450871], [0, -2147483638], [0, -2147450741], [-2147483648, 139], [-2147483648, 32905], [-2147483648, 32771], [-2147483648, 32770], [-2147483648, 128], [0, 32778], [-2147483648, -2147483638], [-2147483648, -2147450751], [-2147483648, 32896], [0, -2147483647], [-2147483648, -2147450872] ]; for ($round = 0; $round < 24; $round++) { // theta step $parity = $rotated = []; for ($i = 0; $i < 5; $i++) { $parity[] = [ $s[0][$i][0] ^ $s[1][$i][0] ^ $s[2][$i][0] ^ $s[3][$i][0] ^ $s[4][$i][0], $s[0][$i][1] ^ $s[1][$i][1] ^ $s[2][$i][1] ^ $s[3][$i][1] ^ $s[4][$i][1] ]; $rotated[] = static::rotateLeft32($parity[$i], 1); } $temp = [ [$parity[4][0] ^ $rotated[1][0], $parity[4][1] ^ $rotated[1][1]], [$parity[0][0] ^ $rotated[2][0], $parity[0][1] ^ $rotated[2][1]], [$parity[1][0] ^ $rotated[3][0], $parity[1][1] ^ $rotated[3][1]], [$parity[2][0] ^ $rotated[4][0], $parity[2][1] ^ $rotated[4][1]], [$parity[3][0] ^ $rotated[0][0], $parity[3][1] ^ $rotated[0][1]] ]; for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { $s[$i][$j][0] ^= $temp[$j][0]; $s[$i][$j][1] ^= $temp[$j][1]; } } $st = $s; // rho and pi steps for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft32($s[$j][$i], $rotationOffsets[$j][$i]); } } // chi step for ($i = 0; $i < 5; $i++) { $s[$i][0] = [ $st[$i][0][0] ^ (~$st[$i][1][0] & $st[$i][2][0]), $st[$i][0][1] ^ (~$st[$i][1][1] & $st[$i][2][1]) ]; $s[$i][1] = [ $st[$i][1][0] ^ (~$st[$i][2][0] & $st[$i][3][0]), $st[$i][1][1] ^ (~$st[$i][2][1] & $st[$i][3][1]) ]; $s[$i][2] = [ $st[$i][2][0] ^ (~$st[$i][3][0] & $st[$i][4][0]), $st[$i][2][1] ^ (~$st[$i][3][1] & $st[$i][4][1]) ]; $s[$i][3] = [ $st[$i][3][0] ^ (~$st[$i][4][0] & $st[$i][0][0]), $st[$i][3][1] ^ (~$st[$i][4][1] & $st[$i][0][1]) ]; $s[$i][4] = [ $st[$i][4][0] ^ (~$st[$i][0][0] & $st[$i][1][0]), $st[$i][4][1] ^ (~$st[$i][0][1] & $st[$i][1][1]) ]; } // iota step $s[0][0][0] ^= $roundConstants[$round][0]; $s[0][0][1] ^= $roundConstants[$round][1]; } } /** * Rotate 32-bit int * * @param array $x * @param int $shift */ private static function rotateLeft32($x, $shift) { if ($shift < 32) { list($hi, $lo) = $x; } else { $shift -= 32; list($lo, $hi) = $x; } return [ ($hi << $shift) | (($lo >> (32 - $shift)) & (1 << $shift) - 1), ($lo << $shift) | (($hi >> (32 - $shift)) & (1 << $shift) - 1) ]; } /** * Pure-PHP 64-bit implementation of SHA3 * * @param string $p * @param int $c * @param int $r * @param int $d * @param int $padType */ private static function sha3_64($p, $c, $r, $d, $padType) { $block_size = $r >> 3; $padLength = $block_size - (strlen($p) % $block_size); $num_ints = $block_size >> 2; $p .= static::sha3_pad($padLength, $padType); $n = strlen($p) / $r; // number of blocks $s = [ [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0] ]; $p = str_split($p, $block_size); foreach ($p as $pi) { $pi = unpack('P*', $pi); $x = $y = 0; foreach ($pi as $subpi) { $s[$x][$y++] ^= $subpi; if ($y == 5) { $y = 0; $x++; } } static::processSHA3Block64($s); } $z = ''; $i = $j = 0; while (strlen($z) < $d) { $z .= pack('P', $s[$i][$j++]); if ($j == 5) { $j = 0; $i++; if ($i == 5) { $i = 0; static::processSHA3Block64($s); } } } return $z; } /** * 64-bit block processing method for SHA3 * * @param array $s */ private static function processSHA3Block64(&$s) { static $rotationOffsets = [ [ 0, 1, 62, 28, 27], [36, 44, 6, 55, 20], [ 3, 10, 43, 25, 39], [41, 45, 15, 21, 8], [18, 2, 61, 56, 14] ]; static $roundConstants = [ 1, 32898, -9223372036854742902, -9223372034707259392, 32907, 2147483649, -9223372034707259263, -9223372036854743031, 138, 136, 2147516425, 2147483658, 2147516555, -9223372036854775669, -9223372036854742903, -9223372036854743037, -9223372036854743038, -9223372036854775680, 32778, -9223372034707292150, -9223372034707259263, -9223372036854742912, 2147483649, -9223372034707259384 ]; for ($round = 0; $round < 24; $round++) { // theta step $parity = []; for ($i = 0; $i < 5; $i++) { $parity[] = $s[0][$i] ^ $s[1][$i] ^ $s[2][$i] ^ $s[3][$i] ^ $s[4][$i]; } $temp = [ $parity[4] ^ static::rotateLeft64($parity[1], 1), $parity[0] ^ static::rotateLeft64($parity[2], 1), $parity[1] ^ static::rotateLeft64($parity[3], 1), $parity[2] ^ static::rotateLeft64($parity[4], 1), $parity[3] ^ static::rotateLeft64($parity[0], 1) ]; for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { $s[$i][$j] ^= $temp[$j]; } } $st = $s; // rho and pi steps for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft64($s[$j][$i], $rotationOffsets[$j][$i]); } } // chi step for ($i = 0; $i < 5; $i++) { $s[$i] = [ $st[$i][0] ^ (~$st[$i][1] & $st[$i][2]), $st[$i][1] ^ (~$st[$i][2] & $st[$i][3]), $st[$i][2] ^ (~$st[$i][3] & $st[$i][4]), $st[$i][3] ^ (~$st[$i][4] & $st[$i][0]), $st[$i][4] ^ (~$st[$i][0] & $st[$i][1]) ]; } // iota step $s[0][0] ^= $roundConstants[$round]; } } /** * Rotate 64-bit int * * @param int $x * @param int $shift */ private static function rotateLeft64($x, $shift) { return ($x << $shift) | (($x >> (64 - $shift)) & ((1 << $shift) - 1)); } /** * Pure-PHP implementation of SHA512 * * @param string $m * @param array $hash * @return string */ private static function sha512($m, $hash) { static $k; if (!isset($k)) { // Initialize table of round constants // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) $k = [ '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' ]; for ($i = 0; $i < 80; $i++) { $k[$i] = new BigInteger($k[$i], 16); } } // Pre-processing $length = strlen($m); // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 $m .= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); $m[$length] = chr(0x80); // we don't support hashing strings 512MB long $m .= pack('N4', 0, 0, 0, $length << 3); // Process the message in successive 1024-bit chunks $chunks = str_split($m, 128); foreach ($chunks as $chunk) { $w = []; for ($i = 0; $i < 16; $i++) { $temp = new BigInteger(Strings::shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } // Extend the sixteen 32-bit words into eighty 32-bit words for ($i = 16; $i < 80; $i++) { $temp = [ $w[$i - 15]->bitwise_rightRotate(1), $w[$i - 15]->bitwise_rightRotate(8), $w[$i - 15]->bitwise_rightShift(7) ]; $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = [ $w[$i - 2]->bitwise_rightRotate(19), $w[$i - 2]->bitwise_rightRotate(61), $w[$i - 2]->bitwise_rightShift(6) ]; $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $w[$i] = clone $w[$i - 16]; $w[$i] = $w[$i]->add($s0); $w[$i] = $w[$i]->add($w[$i - 7]); $w[$i] = $w[$i]->add($s1); } // Initialize hash value for this chunk $a = clone $hash[0]; $b = clone $hash[1]; $c = clone $hash[2]; $d = clone $hash[3]; $e = clone $hash[4]; $f = clone $hash[5]; $g = clone $hash[6]; $h = clone $hash[7]; // Main loop for ($i = 0; $i < 80; $i++) { $temp = [ $a->bitwise_rightRotate(28), $a->bitwise_rightRotate(34), $a->bitwise_rightRotate(39) ]; $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = [ $a->bitwise_and($b), $a->bitwise_and($c), $b->bitwise_and($c) ]; $maj = $temp[0]->bitwise_xor($temp[1]); $maj = $maj->bitwise_xor($temp[2]); $t2 = $s0->add($maj); $temp = [ $e->bitwise_rightRotate(14), $e->bitwise_rightRotate(18), $e->bitwise_rightRotate(41) ]; $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $temp = [ $e->bitwise_and($f), $g->bitwise_and($e->bitwise_not()) ]; $ch = $temp[0]->bitwise_xor($temp[1]); $t1 = $h->add($s1); $t1 = $t1->add($ch); $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); $h = clone $g; $g = clone $f; $f = clone $e; $e = $d->add($t1); $d = clone $c; $c = clone $b; $b = clone $a; $a = $t1->add($t2); } // Add this chunk's hash to result so far $hash = [ $hash[0]->add($a), $hash[1]->add($b), $hash[2]->add($c), $hash[3]->add($d), $hash[4]->add($e), $hash[5]->add($f), $hash[6]->add($g), $hash[7]->add($h) ]; } // Produce the final hash value (big-endian) // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes(); return $temp; } /** * __toString() magic method */ public function __toString() { return $this->getHash(); } } PK2A#]>�B�r?r?Avendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.phpnu�[���<?php /** * Common String Functions * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Common\Functions; use ParagonIE\ConstantTime\Base64; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\ConstantTime\Hex; use phpseclib3\Math\BigInteger; use phpseclib3\Math\Common\FiniteField; /** * Common String Functions * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Strings { /** * String Shift * * Inspired by array_shift * * @param string $string * @param int $index * @return string */ public static function shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } /** * String Pop * * Inspired by array_pop * * @param string $string * @param int $index * @return string */ public static function pop(&$string, $index = 1) { $substr = substr($string, -$index); $string = substr($string, 0, -$index); return $substr; } /** * Parse SSH2-style string * * Returns either an array or a boolean if $data is malformed. * * Valid characters for $format are as follows: * * C = byte * b = boolean (true/false) * N = uint32 * Q = uint64 * s = string * i = mpint * L = name-list * * uint64 is not supported. * * @param string $format * @param string $data * @return mixed */ public static function unpackSSH2($format, &$data) { $format = self::formatPack($format); $result = []; for ($i = 0; $i < strlen($format); $i++) { switch ($format[$i]) { case 'C': case 'b': if (!strlen($data)) { throw new \LengthException('At least one byte needs to be present for successful C / b decodes'); } break; case 'N': case 'i': case 's': case 'L': if (strlen($data) < 4) { throw new \LengthException('At least four byte needs to be present for successful N / i / s / L decodes'); } break; case 'Q': if (strlen($data) < 8) { throw new \LengthException('At least eight byte needs to be present for successful N / i / s / L decodes'); } break; default: throw new \InvalidArgumentException('$format contains an invalid character'); } switch ($format[$i]) { case 'C': $result[] = ord(self::shift($data)); continue 2; case 'b': $result[] = ord(self::shift($data)) != 0; continue 2; case 'N': list(, $temp) = unpack('N', self::shift($data, 4)); $result[] = $temp; continue 2; case 'Q': // pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version // so in theory we could support this BUT, "64-bit format codes are not available for // 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs // 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow // for. sure, you're not gonna get the full precision of 64-bit numbers but just because // you need > 32-bit precision doesn't mean you need the full 64-bit precision extract(unpack('Nupper/Nlower', self::shift($data, 8))); $temp = $upper ? 4294967296 * $upper : 0; $temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower; // $temp = hexdec(bin2hex(self::shift($data, 8))); $result[] = $temp; continue 2; } list(, $length) = unpack('N', self::shift($data, 4)); if (strlen($data) < $length) { throw new \LengthException("$length bytes needed; " . strlen($data) . ' bytes available'); } $temp = self::shift($data, $length); switch ($format[$i]) { case 'i': $result[] = new BigInteger($temp, -256); break; case 's': $result[] = $temp; break; case 'L': $result[] = explode(',', $temp); } } return $result; } /** * Create SSH2-style string * * @param string $format * @param string|int|float|array|bool ...$elements * @return string */ public static function packSSH2($format, ...$elements) { $format = self::formatPack($format); if (strlen($format) != count($elements)) { throw new \InvalidArgumentException('There must be as many arguments as there are characters in the $format string'); } $result = ''; for ($i = 0; $i < strlen($format); $i++) { $element = $elements[$i]; switch ($format[$i]) { case 'C': if (!is_int($element)) { throw new \InvalidArgumentException('Bytes must be represented as an integer between 0 and 255, inclusive.'); } $result .= pack('C', $element); break; case 'b': if (!is_bool($element)) { throw new \InvalidArgumentException('A boolean parameter was expected.'); } $result .= $element ? "\1" : "\0"; break; case 'Q': if (!is_int($element) && !is_float($element)) { throw new \InvalidArgumentException('An integer was expected.'); } // 4294967296 == 1 << 32 $result .= pack('NN', $element / 4294967296, $element); break; case 'N': if (is_float($element)) { $element = (int) $element; } if (!is_int($element)) { throw new \InvalidArgumentException('An integer was expected.'); } $result .= pack('N', $element); break; case 's': if (!self::is_stringable($element)) { throw new \InvalidArgumentException('A string was expected.'); } $result .= pack('Na*', strlen($element), $element); break; case 'i': if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) { throw new \InvalidArgumentException('A phpseclib3\Math\BigInteger or phpseclib3\Math\Common\FiniteField\Integer object was expected.'); } $element = $element->toBytes(true); $result .= pack('Na*', strlen($element), $element); break; case 'L': if (!is_array($element)) { throw new \InvalidArgumentException('An array was expected.'); } $element = implode(',', $element); $result .= pack('Na*', strlen($element), $element); break; default: throw new \InvalidArgumentException('$format contains an invalid character'); } } return $result; } /** * Expand a pack string * * Converts C5 to CCCCC, for example. * * @param string $format * @return string */ private static function formatPack($format) { $parts = preg_split('#(\d+)#', $format, -1, PREG_SPLIT_DELIM_CAPTURE); $format = ''; for ($i = 1; $i < count($parts); $i += 2) { $format .= substr($parts[$i - 1], 0, -1) . str_repeat(substr($parts[$i - 1], -1), $parts[$i]); } $format .= $parts[$i - 1]; return $format; } /** * Convert binary data into bits * * bin2hex / hex2bin refer to base-256 encoded data as binary, whilst * decbin / bindec refer to base-2 encoded data as binary. For the purposes * of this function, bin refers to base-256 encoded data whilst bits refers * to base-2 encoded data * * @param string $x * @return string */ public static function bits2bin($x) { /* // the pure-PHP approach is faster than the GMP approach if (function_exists('gmp_export')) { return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0); } */ if (preg_match('#[^01]#', $x)) { throw new \RuntimeException('The only valid characters are 0 and 1'); } if (!defined('PHP_INT_MIN')) { define('PHP_INT_MIN', ~PHP_INT_MAX); } $length = strlen($x); if (!$length) { return ''; } $block_size = PHP_INT_SIZE << 3; $pad = $block_size - ($length % $block_size); if ($pad != $block_size) { $x = str_repeat('0', $pad) . $x; } $parts = str_split($x, $block_size); $str = ''; foreach ($parts as $part) { $xor = $part[0] == '1' ? PHP_INT_MIN : 0; $part[0] = '0'; $str .= pack( PHP_INT_SIZE == 4 ? 'N' : 'J', $xor ^ eval('return 0b' . $part . ';') ); } return ltrim($str, "\0"); } /** * Convert bits to binary data * * @param string $x * @return string */ public static function bin2bits($x, $trim = true) { /* // the pure-PHP approach is slower than the GMP approach BUT // i want to the pure-PHP version to be easily unit tested as well if (function_exists('gmp_import')) { return gmp_strval(gmp_import($x), 2); } */ $len = strlen($x); $mod = $len % PHP_INT_SIZE; if ($mod) { $x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0", STR_PAD_LEFT); } $bits = ''; if (PHP_INT_SIZE == 4) { $digits = unpack('N*', $x); foreach ($digits as $digit) { $bits .= sprintf('%032b', $digit); } } else { $digits = unpack('J*', $x); foreach ($digits as $digit) { $bits .= sprintf('%064b', $digit); } } return $trim ? ltrim($bits, '0') : $bits; } /** * Switch Endianness Bit Order * * @param string $x * @return string */ public static function switchEndianness($x) { $r = ''; for ($i = strlen($x) - 1; $i >= 0; $i--) { $b = ord($x[$i]); if (PHP_INT_SIZE === 8) { // 3 operations // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv $r .= chr((($b * 0x0202020202) & 0x010884422010) % 1023); } else { // 7 operations // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits $p1 = ($b * 0x0802) & 0x22110; $p2 = ($b * 0x8020) & 0x88440; $r .= chr( (($p1 | $p2) * 0x10101) >> 16 ); } } return $r; } /** * Increment the current string * * @param string $var * @return string */ public static function increment_str(&$var) { if (function_exists('sodium_increment')) { $var = strrev($var); sodium_increment($var); $var = strrev($var); return $var; } for ($i = 4; $i <= strlen($var); $i += 4) { $temp = substr($var, -$i, 4); switch ($temp) { case "\xFF\xFF\xFF\xFF": $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4); break; case "\x7F\xFF\xFF\xFF": $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4); return $var; default: $temp = unpack('Nnum', $temp); $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4); return $var; } } $remainder = strlen($var) % 4; if ($remainder == 0) { return $var; } $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT)); $temp = substr(pack('N', $temp['num'] + 1), -$remainder); $var = substr_replace($var, $temp, 0, $remainder); return $var; } /** * Find whether the type of a variable is string (or could be converted to one) * * @param mixed $var * @return bool * @psalm-assert-if-true string|\Stringable $var */ public static function is_stringable($var) { return is_string($var) || (is_object($var) && method_exists($var, '__toString')); } /** * Constant Time Base64-decoding * * ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so * ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39 * * @param string $data * @return string */ public static function base64_decode($data) { return function_exists('sodium_base642bin') ? sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') : Base64::decode($data); } /** * Constant Time Base64-decoding (URL safe) * * @param string $data * @return string */ public static function base64url_decode($data) { // return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data)); return function_exists('sodium_base642bin') ? sodium_base642bin($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') : Base64UrlSafe::decode($data); } /** * Constant Time Base64-encoding * * @param string $data * @return string */ public static function base64_encode($data) { return function_exists('sodium_bin2base64') ? sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) : Base64::encode($data); } /** * Constant Time Base64-encoding (URL safe) * * @param string $data * @return string */ public static function base64url_encode($data) { // return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data)); return function_exists('sodium_bin2base64') ? sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE) : Base64UrlSafe::encode($data); } /** * Constant Time Hex Decoder * * @param string $data * @return string */ public static function hex2bin($data) { return function_exists('sodium_hex2bin') ? sodium_hex2bin($data) : Hex::decode($data); } /** * Constant Time Hex Encoder * * @param string $data * @return string */ public static function bin2hex($data) { return function_exists('sodium_bin2hex') ? sodium_bin2hex($data) : Hex::encode($data); } } PK2A#]������2vendor/phpseclib/phpseclib/phpseclib/File/ASN1.phpnu�[���<?php /** * Pure-PHP ASN.1 Parser * * PHP version 5 * * ASN.1 provides the semantics for data encoded using various schemes. The most commonly * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded * DER blobs. * * \phpseclib3\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context. * * Uses the 1988 ASN.1 syntax. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File; use phpseclib3\Common\Functions\Strings; use phpseclib3\File\ASN1\Element; use phpseclib3\Math\BigInteger; /** * Pure-PHP ASN.1 Parser * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ASN1 { // Tag Classes // http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 const CLASS_UNIVERSAL = 0; const CLASS_APPLICATION = 1; const CLASS_CONTEXT_SPECIFIC = 2; const CLASS_PRIVATE = 3; // Tag Classes // http://www.obj-sys.com/asn1tutorial/node124.html const TYPE_BOOLEAN = 1; const TYPE_INTEGER = 2; const TYPE_BIT_STRING = 3; const TYPE_OCTET_STRING = 4; const TYPE_NULL = 5; const TYPE_OBJECT_IDENTIFIER = 6; //const TYPE_OBJECT_DESCRIPTOR = 7; //const TYPE_INSTANCE_OF = 8; // EXTERNAL const TYPE_REAL = 9; const TYPE_ENUMERATED = 10; //const TYPE_EMBEDDED = 11; const TYPE_UTF8_STRING = 12; //const TYPE_RELATIVE_OID = 13; const TYPE_SEQUENCE = 16; // SEQUENCE OF const TYPE_SET = 17; // SET OF // More Tag Classes // http://www.obj-sys.com/asn1tutorial/node10.html const TYPE_NUMERIC_STRING = 18; const TYPE_PRINTABLE_STRING = 19; const TYPE_TELETEX_STRING = 20; // T61String const TYPE_VIDEOTEX_STRING = 21; const TYPE_IA5_STRING = 22; const TYPE_UTC_TIME = 23; const TYPE_GENERALIZED_TIME = 24; const TYPE_GRAPHIC_STRING = 25; const TYPE_VISIBLE_STRING = 26; // ISO646String const TYPE_GENERAL_STRING = 27; const TYPE_UNIVERSAL_STRING = 28; //const TYPE_CHARACTER_STRING = 29; const TYPE_BMP_STRING = 30; // Tag Aliases // These tags are kinda place holders for other tags. const TYPE_CHOICE = -1; const TYPE_ANY = -2; /** * ASN.1 object identifiers * * @var array * @link http://en.wikipedia.org/wiki/Object_identifier */ private static $oids = []; /** * ASN.1 object identifier reverse mapping * * @var array */ private static $reverseOIDs = []; /** * Default date format * * @var string * @link http://php.net/class.datetime */ private static $format = 'D, d M Y H:i:s O'; /** * Filters * * If the mapping type is self::TYPE_ANY what do we actually encode it as? * * @var array * @see self::encode_der() */ private static $filters; /** * Current Location of most recent ASN.1 encode process * * Useful for debug purposes * * @var array * @see self::encode_der() */ private static $location; /** * DER Encoded String * * In case we need to create ASN1\Element object's.. * * @var string * @see self::decodeDER() */ private static $encoded; /** * Type mapping table for the ANY type. * * Structured or unknown types are mapped to a \phpseclib3\File\ASN1\Element. * Unambiguous types get the direct mapping (int/real/bool). * Others are mapped as a choice, with an extra indexing level. * * @var array */ const ANY_MAP = [ self::TYPE_BOOLEAN => true, self::TYPE_INTEGER => true, self::TYPE_BIT_STRING => 'bitString', self::TYPE_OCTET_STRING => 'octetString', self::TYPE_NULL => 'null', self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', self::TYPE_REAL => true, self::TYPE_ENUMERATED => 'enumerated', self::TYPE_UTF8_STRING => 'utf8String', self::TYPE_NUMERIC_STRING => 'numericString', self::TYPE_PRINTABLE_STRING => 'printableString', self::TYPE_TELETEX_STRING => 'teletexString', self::TYPE_VIDEOTEX_STRING => 'videotexString', self::TYPE_IA5_STRING => 'ia5String', self::TYPE_UTC_TIME => 'utcTime', self::TYPE_GENERALIZED_TIME => 'generalTime', self::TYPE_GRAPHIC_STRING => 'graphicString', self::TYPE_VISIBLE_STRING => 'visibleString', self::TYPE_GENERAL_STRING => 'generalString', self::TYPE_UNIVERSAL_STRING => 'universalString', //self::TYPE_CHARACTER_STRING => 'characterString', self::TYPE_BMP_STRING => 'bmpString' ]; /** * String type to character size mapping table. * * Non-convertable types are absent from this table. * size == 0 indicates variable length encoding. * * @var array */ const STRING_TYPE_SIZE = [ self::TYPE_UTF8_STRING => 0, self::TYPE_BMP_STRING => 2, self::TYPE_UNIVERSAL_STRING => 4, self::TYPE_PRINTABLE_STRING => 1, self::TYPE_TELETEX_STRING => 1, self::TYPE_IA5_STRING => 1, self::TYPE_VISIBLE_STRING => 1, ]; /** * Parse BER-encoding * * Serves a similar purpose to openssl's asn1parse * * @param Element|string $encoded * @return ?array */ public static function decodeBER($encoded) { if ($encoded instanceof Element) { $encoded = $encoded->element; } self::$encoded = $encoded; $decoded = self::decode_ber($encoded); if ($decoded === false) { return null; } return [$decoded]; } /** * Parse BER-encoding (Helper function) * * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used. * * @param string $encoded * @param int $start * @param int $encoded_pos * @return array|bool */ private static function decode_ber($encoded, $start = 0, $encoded_pos = 0) { $current = ['start' => $start]; if (!isset($encoded[$encoded_pos])) { return false; } $type = ord($encoded[$encoded_pos++]); $startOffset = 1; $constructed = ($type >> 5) & 1; $tag = $type & 0x1F; if ($tag == 0x1F) { $tag = 0; // process septets (since the eighth bit is ignored, it's not an octet) do { if (!isset($encoded[$encoded_pos])) { return false; } $temp = ord($encoded[$encoded_pos++]); $startOffset++; $loop = $temp >> 7; $tag <<= 7; $temp &= 0x7F; // "bits 7 to 1 of the first subsequent octet shall not all be zero" if ($startOffset == 2 && $temp == 0) { return false; } $tag |= $temp; } while ($loop); } $start += $startOffset; // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 if (!isset($encoded[$encoded_pos])) { return false; } $length = ord($encoded[$encoded_pos++]); $start++; if ($length == 0x80) { // indefinite length // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all // immediately available." -- paragraph 8.1.3.2.c $length = strlen($encoded) - $encoded_pos; } elseif ($length & 0x80) { // definite length, long form // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only // support it up to four. $length &= 0x7F; $temp = substr($encoded, $encoded_pos, $length); $encoded_pos += $length; // tags of indefinte length don't really have a header length; this length includes the tag $current += ['headerlength' => $length + 2]; $start += $length; extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); /** @var integer $length */ } else { $current += ['headerlength' => 2]; } if ($length > (strlen($encoded) - $encoded_pos)) { return false; } $content = substr($encoded, $encoded_pos, $length); $content_pos = 0; // at this point $length can be overwritten. it's only accurate for definite length things as is /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 built-in types. It defines an application-independent data type that must be distinguishable from all other data types. The other three classes are user defined. The APPLICATION class distinguishes data types that have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this data type; the term CONTEXT-SPECIFIC does not appear. -- http://www.obj-sys.com/asn1tutorial/node12.html */ $class = ($type >> 6) & 3; switch ($class) { case self::CLASS_APPLICATION: case self::CLASS_PRIVATE: case self::CLASS_CONTEXT_SPECIFIC: if (!$constructed) { return [ 'type' => $class, 'constant' => $tag, 'content' => $content, 'length' => $length + $start - $current['start'] ] + $current; } $newcontent = []; $remainingLength = $length; while ($remainingLength > 0) { $temp = self::decode_ber($content, $start, $content_pos); if ($temp === false) { break; } $length = $temp['length']; // end-of-content octets - see paragraph 8.1.5 if (substr($content, $content_pos + $length, 2) == "\0\0") { $length += 2; $start += $length; $newcontent[] = $temp; break; } $start += $length; $remainingLength -= $length; $newcontent[] = $temp; $content_pos += $length; } return [ 'type' => $class, 'constant' => $tag, // the array encapsulation is for BC with the old format 'content' => $newcontent, // the only time when $content['headerlength'] isn't defined is when the length is indefinite. // the absence of $content['headerlength'] is how we know if something is indefinite or not. // technically, it could be defined to be 2 and then another indicator could be used but whatever. 'length' => $start - $current['start'] ] + $current; } $current += ['type' => $tag]; // decode UNIVERSAL tags switch ($tag) { case self::TYPE_BOOLEAN: // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 if ($constructed || strlen($content) != 1) { return false; } $current['content'] = (bool) ord($content[$content_pos]); break; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if ($constructed) { return false; } $current['content'] = new BigInteger(substr($content, $content_pos), -256); break; case self::TYPE_REAL: // not currently supported return false; case self::TYPE_BIT_STRING: // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, // the number of unused bits in the final subsequent octet. The number shall be in the range zero to // seven. if (!$constructed) { $current['content'] = substr($content, $content_pos); } else { $temp = self::decode_ber($content, $start, $content_pos); if ($temp === false) { return false; } $length -= (strlen($content) - $content_pos); $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { return false; } $current['content'] .= substr($temp[$i]['content'], 1); } // all subtags should be bit strings if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { return false; } $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } break; case self::TYPE_OCTET_STRING: if (!$constructed) { $current['content'] = substr($content, $content_pos); } else { $current['content'] = ''; $length = 0; while (substr($content, $content_pos, 2) != "\0\0") { $temp = self::decode_ber($content, $length + $start, $content_pos); if ($temp === false) { return false; } $content_pos += $temp['length']; // all subtags should be octet strings if ($temp['type'] != self::TYPE_OCTET_STRING) { return false; } $current['content'] .= $temp['content']; $length += $temp['length']; } if (substr($content, $content_pos, 2) == "\0\0") { $length += 2; // +2 for the EOC } } break; case self::TYPE_NULL: // "The contents octets shall not contain any octets." -- paragraph 8.8.2 if ($constructed || strlen($content)) { return false; } break; case self::TYPE_SEQUENCE: case self::TYPE_SET: if (!$constructed) { return false; } $offset = 0; $current['content'] = []; $content_len = strlen($content); while ($content_pos < $content_len) { // if indefinite length construction was used and we have an end-of-content string next // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { $length = $offset + 2; // +2 for the EOC break 2; } $temp = self::decode_ber($content, $start + $offset, $content_pos); if ($temp === false) { return false; } $content_pos += $temp['length']; $current['content'][] = $temp; $offset += $temp['length']; } break; case self::TYPE_OBJECT_IDENTIFIER: if ($constructed) { return false; } $current['content'] = self::decodeOID(substr($content, $content_pos)); if ($current['content'] === false) { return false; } break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING -- X.690-0207.pdf#page=23 (paragraph 8.21.3) Per that, we're not going to do any validation. If there are any illegal characters in the string, we don't really care */ case self::TYPE_NUMERIC_STRING: // 0,1,2,3,4,5,6,7,8,9, and space case self::TYPE_PRINTABLE_STRING: // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, // hyphen, full stop, solidus, colon, equal sign, question mark case self::TYPE_TELETEX_STRING: // The Teletex character set in CCITT's T61, space, and delete // see http://en.wikipedia.org/wiki/Teletex#Character_sets case self::TYPE_VIDEOTEX_STRING: // The Videotex character set in CCITT's T.100 and T.101, space, and delete case self::TYPE_VISIBLE_STRING: // Printing character sets of international ASCII, and space case self::TYPE_IA5_STRING: // International Alphabet 5 (International ASCII) case self::TYPE_GRAPHIC_STRING: // All registered G sets, and space case self::TYPE_GENERAL_STRING: // All registered C and G sets, space and delete case self::TYPE_UTF8_STRING: // ???? case self::TYPE_BMP_STRING: if ($constructed) { return false; } $current['content'] = substr($content, $content_pos); break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: if ($constructed) { return false; } $current['content'] = self::decodeTime(substr($content, $content_pos), $tag); break; default: return false; } $start += $length; // ie. length is the length of the full TLV encoding - it's not just the length of the value return $current + ['length' => $start - $current['start']]; } /** * ASN.1 Map * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * * "Special" mappings may be applied on a per tag-name basis via $special. * * @param array $decoded * @param array $mapping * @param array $special * @return array|bool|Element|string|null */ public static function asn1map(array $decoded, $mapping, $special = []) { if (isset($mapping['explicit']) && is_array($decoded['content'])) { $decoded = $decoded['content'][0]; } switch (true) { case $mapping['type'] == self::TYPE_ANY: $intype = $decoded['type']; // !isset(self::ANY_MAP[$intype]) produces a fatal error on PHP 5.6 if (isset($decoded['constant']) || !array_key_exists($intype, self::ANY_MAP) || (ord(self::$encoded[$decoded['start']]) & 0x20)) { return new Element(substr(self::$encoded, $decoded['start'], $decoded['length'])); } $inmap = self::ANY_MAP[$intype]; if (is_string($inmap)) { return [$inmap => self::asn1map($decoded, ['type' => $intype] + $mapping, $special)]; } break; case $mapping['type'] == self::TYPE_CHOICE: foreach ($mapping['children'] as $key => $option) { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: $value = self::asn1map($decoded, $option, $special); break; case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: $v = self::asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; } } if (isset($value)) { if (isset($special[$key])) { $value = $special[$key]($value); } return [$key => $value]; } } return null; case isset($mapping['implicit']): case isset($mapping['explicit']): case $decoded['type'] == $mapping['type']: break; default: // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings, // let it through switch (true) { case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18 case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30 case $mapping['type'] < 18: case $mapping['type'] > 30: return null; } } if (isset($mapping['implicit'])) { $decoded['type'] = $mapping['type']; } switch ($decoded['type']) { case self::TYPE_SEQUENCE: $map = []; // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { if (($map[] = self::asn1map($content, $child, $special)) === null) { return null; } } return $map; } $n = count($decoded['content']); $i = 0; foreach ($mapping['children'] as $key => $child) { $maymatch = $i < $n; // Match only existing input. if ($maymatch) { $temp = $decoded['content'][$i]; if ($child['type'] != self::TYPE_CHOICE) { // Get the mapping and input class & constant. $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { $tempClass = $temp['type']; } if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } if (isset($constant) && isset($temp['constant'])) { // Can only match if constants and class match. $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. $maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false; } } } if ($maymatch) { // Attempt submapping. $candidate = self::asn1map($temp, $child, $special); $maymatch = $candidate !== null; } if ($maymatch) { // Got the match: use it. if (isset($special[$key])) { $candidate = $special[$key]($candidate); } $map[$key] = $candidate; $i++; } elseif (isset($child['default'])) { $map[$key] = $child['default']; } elseif (!isset($child['optional'])) { return null; // Syntax error. } } // Fail mapping if all input items have not been consumed. return $i < $n ? null : $map; // the main diff between sets and sequences is the encapsulation of the foreach in another for loop case self::TYPE_SET: $map = []; // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { if (($map[] = self::asn1map($content, $child, $special)) === null) { return null; } } return $map; } for ($i = 0; $i < count($decoded['content']); $i++) { $temp = $decoded['content'][$i]; $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { $tempClass = $temp['type']; } foreach ($mapping['children'] as $key => $child) { if (isset($map[$key])) { continue; } $maymatch = true; if ($child['type'] != self::TYPE_CHOICE) { $childClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } if (isset($constant) && isset($temp['constant'])) { // Can only match if constants and class match. $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. $maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false; } } if ($maymatch) { // Attempt submapping. $candidate = self::asn1map($temp, $child, $special); $maymatch = $candidate !== null; } if (!$maymatch) { break; } // Got the match: use it. if (isset($special[$key])) { $candidate = $special[$key]($candidate); } $map[$key] = $candidate; break; } } foreach ($mapping['children'] as $key => $child) { if (!isset($map[$key])) { if (isset($child['default'])) { $map[$key] = $child['default']; } elseif (!isset($child['optional'])) { return null; } } } return $map; case self::TYPE_OBJECT_IDENTIFIER: return isset(self::$oids[$decoded['content']]) ? self::$oids[$decoded['content']] : $decoded['content']; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: // for explicitly tagged optional stuff if (is_array($decoded['content'])) { $decoded['content'] = $decoded['content'][0]['content']; } // for implicitly tagged optional stuff // in theory, doing isset($mapping['implicit']) would work but malformed certs do exist // in the wild that OpenSSL decodes without issue so we'll support them as well if (!is_object($decoded['content'])) { $decoded['content'] = self::decodeTime($decoded['content'], $decoded['type']); } return $decoded['content'] ? $decoded['content']->format(self::$format) : false; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); $size = (strlen($decoded['content']) - 1) * 8 - $offset; /* From X.680-0207.pdf#page=46 (21.7): "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove) arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should therefore ensure that different semantics are not associated with such values which differ only in the number of trailing 0 bits." */ $bits = count($mapping['mapping']) == $size ? [] : array_fill(0, count($mapping['mapping']) - $size, false); for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) { $current = ord($decoded['content'][$i]); for ($j = $offset; $j < 8; $j++) { $bits[] = (bool) ($current & (1 << $j)); } $offset = 0; } $values = []; $map = array_reverse($mapping['mapping']); foreach ($map as $i => $value) { if ($bits[$i]) { $values[] = $value; } } return $values; } // fall-through case self::TYPE_OCTET_STRING: return $decoded['content']; case self::TYPE_NULL: return ''; case self::TYPE_BOOLEAN: case self::TYPE_NUMERIC_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_IA5_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_GENERAL_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: return $decoded['content']; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: $temp = $decoded['content']; if (isset($mapping['implicit'])) { $temp = new BigInteger($decoded['content'], -256); } if (isset($mapping['mapping'])) { $temp = (int) $temp->toString(); return isset($mapping['mapping'][$temp]) ? $mapping['mapping'][$temp] : false; } return $temp; } } /** * DER-decode the length * * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @param string $string * @return int */ public static function decodeLength(&$string) { $length = ord(Strings::shift($string)); if ($length & 0x80) { // definite length, long form $length &= 0x7F; $temp = Strings::shift($string, $length); list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); } return $length; } /** * ASN.1 Encode * * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function * an ASN.1 compiler. * * "Special" mappings can be applied via $special. * * @param Element|string|array $source * @param array $mapping * @param array $special * @return string */ public static function encodeDER($source, $mapping, $special = []) { self::$location = []; return self::encode_der($source, $mapping, null, $special); } /** * ASN.1 Encode (Helper function) * * @param Element|string|array|null $source * @param array $mapping * @param int $idx * @param array $special * @return string */ private static function encode_der($source, array $mapping, $idx = null, array $special = []) { if ($source instanceof Element) { return $source->element; } // do not encode (implicitly optional) fields with value set to default if (isset($mapping['default']) && $source === $mapping['default']) { return ''; } if (isset($idx)) { if (isset($special[$idx])) { $source = $special[$idx]($source); } self::$location[] = $idx; } $tag = $mapping['type']; switch ($tag) { case self::TYPE_SET: // Children order is not important, thus process in sequence. case self::TYPE_SEQUENCE: $tag |= 0x20; // set the constructed bit // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { $value = []; $child = $mapping['children']; foreach ($source as $content) { $temp = self::encode_der($content, $child, null, $special); if ($temp === false) { return false; } $value[] = $temp; } /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets. NOTE - The padding octets are for comparison purposes only and do not appear in the encodings." -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ if ($mapping['type'] == self::TYPE_SET) { sort($value); } $value = implode('', $value); break; } $value = ''; foreach ($mapping['children'] as $key => $child) { if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { return false; } continue; } $temp = self::encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { /* From X.680-0207.pdf#page=58 (30.6): "The tagging construction specifies explicit tagging if any of the following holds: ... c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." */ if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); $temp = $subtag . self::encodeLength(strlen($temp)) . $temp; } else { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value .= $temp; } break; case self::TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { if (!isset($source[$key])) { continue; } $temp = self::encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } $tag = ord($temp[0]); // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); $temp = $subtag . self::encodeLength(strlen($temp)) . $temp; } else { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } } if (isset($idx)) { array_pop(self::$location); } if ($temp && isset($mapping['cast'])) { $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']); } return $temp; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if (!isset($mapping['mapping'])) { if (is_numeric($source)) { $source = new BigInteger($source); } $value = $source->toBytes(true); } else { $value = array_search($source, $mapping['mapping']); if ($value === false) { return false; } $value = new BigInteger($value); $value = $value->toBytes(true); } if (!strlen($value)) { $value = chr(0); } break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format .= 'mdHis'; // if $source does _not_ include timezone information within it then assume that the timezone is GMT $date = new \DateTime($source, new \DateTimeZone('GMT')); // if $source _does_ include timezone information within it then convert the time to GMT $date->setTimezone(new \DateTimeZone('GMT')); $value = $date->format($format) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; for ($i = 0; $i < count($mapping['mapping']); $i++) { if (in_array($mapping['mapping'][$i], $source)) { $bits[$i] = 1; $size = $i; } } if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { $size = $mapping['min'] - 1; } $offset = 8 - (($size + 1) & 7); $offset = $offset !== 8 ? $offset : 0; $value = chr($offset); for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { unset($bits[$i]); } $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); foreach ($bytes as $byte) { $value .= chr(bindec($byte)); } break; } // fall-through case self::TYPE_OCTET_STRING: /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ $value = $source; break; case self::TYPE_OBJECT_IDENTIFIER: $value = self::encodeOID($source); break; case self::TYPE_ANY: $loc = self::$location; if (isset($idx)) { array_pop(self::$location); } switch (true) { case !isset($source): return self::encode_der(null, ['type' => self::TYPE_NULL] + $mapping, null, $special); case is_int($source): case $source instanceof BigInteger: return self::encode_der($source, ['type' => self::TYPE_INTEGER] + $mapping, null, $special); case is_float($source): return self::encode_der($source, ['type' => self::TYPE_REAL] + $mapping, null, $special); case is_bool($source): return self::encode_der($source, ['type' => self::TYPE_BOOLEAN] + $mapping, null, $special); case is_array($source) && count($source) == 1: $typename = implode('', array_keys($source)); $outtype = array_search($typename, self::ANY_MAP, true); if ($outtype !== false) { return self::encode_der($source[$typename], ['type' => $outtype] + $mapping, null, $special); } } $filters = self::$filters; foreach ($loc as $part) { if (!isset($filters[$part])) { $filters = false; break; } $filters = $filters[$part]; } if ($filters === false) { throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); } return self::encode_der($source, $filters + $mapping, null, $special); case self::TYPE_NULL: $value = ''; break; case self::TYPE_NUMERIC_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: case self::TYPE_IA5_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_GENERAL_STRING: $value = $source; break; case self::TYPE_BOOLEAN: $value = $source ? "\xFF" : "\x00"; break; default: throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', self::$location)); } if (isset($idx)) { array_pop(self::$location); } if (isset($mapping['cast'])) { if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { $value = chr($tag) . self::encodeLength(strlen($value)) . $value; $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast']; } else { $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast']; } } return chr($tag) . self::encodeLength(strlen($value)) . $value; } /** * BER-decode the OID * * Called by _decode_ber() * * @param string $content * @return string */ public static function decodeOID($content) { static $eighty; if (!$eighty) { $eighty = new BigInteger(80); } $oid = []; $pos = 0; $len = strlen($content); // see https://github.com/openjdk/jdk/blob/2deb318c9f047ec5a4b160d66a4b52f93688ec42/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java#L55 if ($len > 4096) { //throw new \RuntimeException("Object identifier size is limited to 4096 bytes ($len bytes present)"); return false; } if (ord($content[$len - 1]) & 0x80) { return false; } $n = new BigInteger(); while ($pos < $len) { $temp = ord($content[$pos++]); $n = $n->bitwise_leftShift(7); $n = $n->bitwise_or(new BigInteger($temp & 0x7F)); if (~$temp & 0x80) { $oid[] = $n; $n = new BigInteger(); } } $part1 = array_shift($oid); $first = floor(ord($content[0]) / 40); /* "This packing of the first two object identifier components recognizes that only three values are allocated from the root node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1." -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22 */ if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78) array_unshift($oid, ord($content[0]) % 40); array_unshift($oid, $first); } else { array_unshift($oid, $part1->subtract($eighty)); array_unshift($oid, 2); } return implode('.', $oid); } /** * DER-encode the OID * * Called by _encode_der() * * @param string $source * @return string */ public static function encodeOID($source) { static $mask, $zero, $forty; if (!$mask) { $mask = new BigInteger(0x7F); $zero = new BigInteger(); $forty = new BigInteger(40); } if (!preg_match('#(?:\d+\.)+#', $source)) { $oid = isset(self::$reverseOIDs[$source]) ? self::$reverseOIDs[$source] : false; } else { $oid = $source; } if ($oid === false) { throw new \RuntimeException('Invalid OID'); } $parts = explode('.', $oid); $part1 = array_shift($parts); $part2 = array_shift($parts); $first = new BigInteger($part1); $first = $first->multiply($forty); $first = $first->add(new BigInteger($part2)); array_unshift($parts, $first->toString()); $value = ''; foreach ($parts as $part) { if (!$part) { $temp = "\0"; } else { $temp = ''; $part = new BigInteger($part); while (!$part->equals($zero)) { $submask = $part->bitwise_and($mask); $submask->setPrecision(8); $temp = (chr(0x80) | $submask->toBytes()) . $temp; $part = $part->bitwise_rightShift(7); } $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); } $value .= $temp; } return $value; } /** * BER-decode the time * * Called by _decode_ber() and in the case of implicit tags asn1map(). * * @param string $content * @param int $tag * @return \DateTime|false */ private static function decodeTime($content, $tag) { /* UTCTime: http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 http://www.obj-sys.com/asn1tutorial/node15.html GeneralizedTime: http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://www.obj-sys.com/asn1tutorial/node14.html */ $format = 'YmdHis'; if ($tag == self::TYPE_UTC_TIME) { // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the // browsers parse it phpseclib ought to too if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) { $content = $matches[1] . '00' . $matches[2]; } $prefix = substr($content, 0, 2) >= 50 ? '19' : '20'; $content = $prefix . $content; } elseif (strpos($content, '.') !== false) { $format .= '.u'; } if ($content[strlen($content) - 1] == 'Z') { $content = substr($content, 0, -1) . '+0000'; } if (strpos($content, '-') !== false || strpos($content, '+') !== false) { $format .= 'O'; } // error supression isn't necessary as of PHP 7.0: // http://php.net/manual/en/migration70.other-changes.php return @\DateTime::createFromFormat($format, $content); } /** * Set the time format * * Sets the time / date format for asn1map(). * * @param string $format */ public static function setTimeFormat($format) { self::$format = $format; } /** * Load OIDs * * Load the relevant OIDs for a particular ASN.1 semantic mapping. * Previously loaded OIDs are retained. * * @param array $oids */ public static function loadOIDs(array $oids) { self::$reverseOIDs += $oids; self::$oids = array_flip(self::$reverseOIDs); } /** * Set filters * * See \phpseclib3\File\X509, etc, for an example. * Previously loaded filters are not retained. * * @param array $filters */ public static function setFilters(array $filters) { self::$filters = $filters; } /** * String type conversion * * This is a lazy conversion, dealing only with character size. * No real conversion table is used. * * @param string $in * @param int $from * @param int $to * @return string */ public static function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) { // isset(self::STRING_TYPE_SIZE[$from] returns a fatal error on PHP 5.6 if (!array_key_exists($from, self::STRING_TYPE_SIZE) || !array_key_exists($to, self::STRING_TYPE_SIZE)) { return false; } $insize = self::STRING_TYPE_SIZE[$from]; $outsize = self::STRING_TYPE_SIZE[$to]; $inlength = strlen($in); $out = ''; for ($i = 0; $i < $inlength;) { if ($inlength - $i < $insize) { return false; } // Get an input character as a 32-bit value. $c = ord($in[$i++]); switch (true) { case $insize == 4: $c = ($c << 8) | ord($in[$i++]); $c = ($c << 8) | ord($in[$i++]); // fall-through case $insize == 2: $c = ($c << 8) | ord($in[$i++]); // fall-through case $insize == 1: break; case ($c & 0x80) == 0x00: break; case ($c & 0x40) == 0x00: return false; default: $bit = 6; do { if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) { return false; } $c = ($c << 6) | (ord($in[$i++]) & 0x3F); $bit += 5; $mask = 1 << $bit; } while ($c & $bit); $c &= $mask - 1; break; } // Convert and append the character to output string. $v = ''; switch (true) { case $outsize == 4: $v .= chr($c & 0xFF); $c >>= 8; $v .= chr($c & 0xFF); $c >>= 8; // fall-through case $outsize == 2: $v .= chr($c & 0xFF); $c >>= 8; // fall-through case $outsize == 1: $v .= chr($c & 0xFF); $c >>= 8; if ($c) { return false; } break; case ($c & (PHP_INT_SIZE == 8 ? 0x80000000 : (1 << 31))) != 0: return false; case $c >= 0x04000000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x04000000; // fall-through case $c >= 0x00200000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00200000; // fall-through case $c >= 0x00010000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00010000; // fall-through case $c >= 0x00000800: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00000800; // fall-through case $c >= 0x00000080: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x000000C0; // fall-through default: $v .= chr($c); break; } $out .= strrev($v); } return $out; } /** * Extract raw BER from Base64 encoding * * @param string $str * @return string */ public static function extractBER($str) { /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them * above and beyond the ceritificate. * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: * * Bag Attributes * localKeyID: 01 00 00 00 * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ if (strlen($str) > ini_get('pcre.backtrack_limit')) { $temp = $str; } else { $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); } // remove new lines $temp = str_replace(["\r", "\n", ' '], '', $temp); // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : false; return $temp != false ? $temp : $str; } /** * DER-encode the length * * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @param int $length * @return string */ public static function encodeLength($length) { if ($length <= 0x7F) { return chr($length); } $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } /** * Returns the OID corresponding to a name * * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able * to work from version to version. * * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that * what's being passed to it already is an OID and return that instead. A few examples. * * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1' * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1' * getOID('zzz') == 'zzz' * * @param string $name * @return string */ public static function getOID($name) { return isset(self::$reverseOIDs[$name]) ? self::$reverseOIDs[$name] : $name; } } PK2A#]��5G.G.2vendor/phpseclib/phpseclib/phpseclib/File/X509.phpnu�[���<?php /** * Pure-PHP X.509 Parser * * PHP version 5 * * Encode and decode X.509 certificates. * * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}. * * Note that loading an X.509 certificate and resaving it may invalidate the signature. The reason being that the signature is based on a * portion of the certificate that contains optional parameters with default values. ie. if the parameter isn't there the default value is * used. Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the * the certificate all together unless the certificate is re-signed. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Common\PrivateKey; use phpseclib3\Crypt\Common\PublicKey; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\EC; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA\Formats\Keys\PSS; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\File\ASN1\Element; use phpseclib3\File\ASN1\Maps; use phpseclib3\Math\BigInteger; /** * Pure-PHP X.509 Parser * * @author Jim Wigginton <terrafrost@php.net> */ class X509 { /** * Flag to only accept signatures signed by certificate authorities * * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs * */ const VALIDATE_SIGNATURE_BY_CA = 1; /** * Return internal array representation * * @see \phpseclib3\File\X509::getDN() */ const DN_ARRAY = 0; /** * Return string * * @see \phpseclib3\File\X509::getDN() */ const DN_STRING = 1; /** * Return ASN.1 name string * * @see \phpseclib3\File\X509::getDN() */ const DN_ASN1 = 2; /** * Return OpenSSL compatible array * * @see \phpseclib3\File\X509::getDN() */ const DN_OPENSSL = 3; /** * Return canonical ASN.1 RDNs string * * @see \phpseclib3\File\X509::getDN() */ const DN_CANON = 4; /** * Return name hash for file indexing * * @see \phpseclib3\File\X509::getDN() */ const DN_HASH = 5; /** * Save as PEM * * ie. a base64-encoded PEM with a header and a footer * * @see \phpseclib3\File\X509::saveX509() * @see \phpseclib3\File\X509::saveCSR() * @see \phpseclib3\File\X509::saveCRL() */ const FORMAT_PEM = 0; /** * Save as DER * * @see \phpseclib3\File\X509::saveX509() * @see \phpseclib3\File\X509::saveCSR() * @see \phpseclib3\File\X509::saveCRL() */ const FORMAT_DER = 1; /** * Save as a SPKAC * * @see \phpseclib3\File\X509::saveX509() * @see \phpseclib3\File\X509::saveCSR() * @see \phpseclib3\File\X509::saveCRL() * * Only works on CSRs. Not currently supported. */ const FORMAT_SPKAC = 2; /** * Auto-detect the format * * Used only by the load*() functions * * @see \phpseclib3\File\X509::saveX509() * @see \phpseclib3\File\X509::saveCSR() * @see \phpseclib3\File\X509::saveCRL() */ const FORMAT_AUTO_DETECT = 3; /** * Attribute value disposition. * If disposition is >= 0, this is the index of the target value. */ const ATTR_ALL = -1; // All attribute values (array). const ATTR_APPEND = -2; // Add a value. const ATTR_REPLACE = -3; // Clear first, then add a value. /** * Distinguished Name * * @var array */ private $dn; /** * Public key * * @var string|PublicKey */ private $publicKey; /** * Private key * * @var string|PrivateKey */ private $privateKey; /** * The certificate authorities * * @var array */ private $CAs = []; /** * The currently loaded certificate * * @var array */ private $currentCert; /** * The signature subject * * There's no guarantee \phpseclib3\File\X509 is going to re-encode an X.509 cert in the same way it was originally * encoded so we take save the portion of the original cert that the signature would have made for. * * @var string */ private $signatureSubject; /** * Certificate Start Date * * @var string */ private $startDate; /** * Certificate End Date * * @var string|Element */ private $endDate; /** * Serial Number * * @var string */ private $serialNumber; /** * Key Identifier * * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. * * @var string */ private $currentKeyIdentifier; /** * CA Flag * * @var bool */ private $caFlag = false; /** * SPKAC Challenge * * @var string */ private $challenge; /** * @var array */ private $extensionValues = []; /** * OIDs loaded * * @var bool */ private static $oidsLoaded = false; /** * Recursion Limit * * @var int */ private static $recur_limit = 5; /** * URL fetch flag * * @var bool */ private static $disable_url_fetch = false; /** * @var array */ private static $extensions = []; /** * @var ?array */ private $ipAddresses = null; /** * @var ?array */ private $domains = null; /** * Default Constructor. * * @return \phpseclib3\File\X509 */ public function __construct() { // Explicitly Tagged Module, 1988 Syntax // http://tools.ietf.org/html/rfc5280#appendix-A.1 if (!self::$oidsLoaded) { // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 ASN1::loadOIDs([ //'id-pkix' => '1.3.6.1.5.5.7', //'id-pe' => '1.3.6.1.5.5.7.1', //'id-qt' => '1.3.6.1.5.5.7.2', //'id-kp' => '1.3.6.1.5.5.7.3', //'id-ad' => '1.3.6.1.5.5.7.48', 'id-qt-cps' => '1.3.6.1.5.5.7.2.1', 'id-qt-unotice' => '1.3.6.1.5.5.7.2.2', 'id-ad-ocsp' => '1.3.6.1.5.5.7.48.1', 'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2', 'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3', 'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5', //'id-at' => '2.5.4', 'id-at-name' => '2.5.4.41', 'id-at-surname' => '2.5.4.4', 'id-at-givenName' => '2.5.4.42', 'id-at-initials' => '2.5.4.43', 'id-at-generationQualifier' => '2.5.4.44', 'id-at-commonName' => '2.5.4.3', 'id-at-localityName' => '2.5.4.7', 'id-at-stateOrProvinceName' => '2.5.4.8', 'id-at-organizationName' => '2.5.4.10', 'id-at-organizationalUnitName' => '2.5.4.11', 'id-at-title' => '2.5.4.12', 'id-at-description' => '2.5.4.13', 'id-at-dnQualifier' => '2.5.4.46', 'id-at-countryName' => '2.5.4.6', 'id-at-serialNumber' => '2.5.4.5', 'id-at-pseudonym' => '2.5.4.65', 'id-at-postalCode' => '2.5.4.17', 'id-at-streetAddress' => '2.5.4.9', 'id-at-uniqueIdentifier' => '2.5.4.45', 'id-at-role' => '2.5.4.72', 'id-at-postalAddress' => '2.5.4.16', 'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3', 'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2', 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1', 'id-at-businessCategory' => '2.5.4.15', //'id-domainComponent' => '0.9.2342.19200300.100.1.25', //'pkcs-9' => '1.2.840.113549.1.9', 'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1', //'id-ce' => '2.5.29', 'id-ce-authorityKeyIdentifier' => '2.5.29.35', 'id-ce-subjectKeyIdentifier' => '2.5.29.14', 'id-ce-keyUsage' => '2.5.29.15', 'id-ce-privateKeyUsagePeriod' => '2.5.29.16', 'id-ce-certificatePolicies' => '2.5.29.32', //'anyPolicy' => '2.5.29.32.0', 'id-ce-policyMappings' => '2.5.29.33', 'id-ce-subjectAltName' => '2.5.29.17', 'id-ce-issuerAltName' => '2.5.29.18', 'id-ce-subjectDirectoryAttributes' => '2.5.29.9', 'id-ce-basicConstraints' => '2.5.29.19', 'id-ce-nameConstraints' => '2.5.29.30', 'id-ce-policyConstraints' => '2.5.29.36', 'id-ce-cRLDistributionPoints' => '2.5.29.31', 'id-ce-extKeyUsage' => '2.5.29.37', //'anyExtendedKeyUsage' => '2.5.29.37.0', 'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1', 'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2', 'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3', 'id-kp-emailProtection' => '1.3.6.1.5.5.7.3.4', 'id-kp-timeStamping' => '1.3.6.1.5.5.7.3.8', 'id-kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9', 'id-ce-inhibitAnyPolicy' => '2.5.29.54', 'id-ce-freshestCRL' => '2.5.29.46', 'id-pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1', 'id-pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11', 'id-ce-cRLNumber' => '2.5.29.20', 'id-ce-issuingDistributionPoint' => '2.5.29.28', 'id-ce-deltaCRLIndicator' => '2.5.29.27', 'id-ce-cRLReasons' => '2.5.29.21', 'id-ce-certificateIssuer' => '2.5.29.29', 'id-ce-holdInstructionCode' => '2.5.29.23', //'holdInstruction' => '1.2.840.10040.2', 'id-holdinstruction-none' => '1.2.840.10040.2.1', 'id-holdinstruction-callissuer' => '1.2.840.10040.2.2', 'id-holdinstruction-reject' => '1.2.840.10040.2.3', 'id-ce-invalidityDate' => '2.5.29.24', 'rsaEncryption' => '1.2.840.113549.1.1.1', 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2', 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4', 'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5', 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14', 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11', 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12', 'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13', 'id-ecPublicKey' => '1.2.840.10045.2.1', 'ecdsa-with-SHA1' => '1.2.840.10045.4.1', // from https://tools.ietf.org/html/rfc5758#section-3.2 'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1', 'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2', 'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3', 'ecdsa-with-SHA512' => '1.2.840.10045.4.3.4', 'id-dsa' => '1.2.840.10040.4.1', 'id-dsa-with-sha1' => '1.2.840.10040.4.3', // from https://tools.ietf.org/html/rfc5758#section-3.1 'id-dsa-with-sha224' => '2.16.840.1.101.3.4.3.1', 'id-dsa-with-sha256' => '2.16.840.1.101.3.4.3.2', // from https://tools.ietf.org/html/rfc8410: 'id-Ed25519' => '1.3.101.112', 'id-Ed448' => '1.3.101.113', 'id-RSASSA-PSS' => '1.2.840.113549.1.1.10', //'id-sha224' => '2.16.840.1.101.3.4.2.4', //'id-sha256' => '2.16.840.1.101.3.4.2.1', //'id-sha384' => '2.16.840.1.101.3.4.2.2', //'id-sha512' => '2.16.840.1.101.3.4.2.3', //'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4', //'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3', //'id-GostR3410-2001' => '1.2.643.2.2.20', //'id-GostR3410-94' => '1.2.643.2.2.19', // Netscape Object Identifiers from "Netscape Certificate Extensions" 'netscape' => '2.16.840.1.113730', 'netscape-cert-extension' => '2.16.840.1.113730.1', 'netscape-cert-type' => '2.16.840.1.113730.1.1', 'netscape-comment' => '2.16.840.1.113730.1.13', 'netscape-ca-policy-url' => '2.16.840.1.113730.1.8', // the following are X.509 extensions not supported by phpseclib 'id-pe-logotype' => '1.3.6.1.5.5.7.1.12', 'entrustVersInfo' => '1.2.840.113533.7.65.0', 'verisignPrivate' => '2.16.840.1.113733.1.6.9', // for Certificate Signing Requests // see http://tools.ietf.org/html/rfc2985 'pkcs-9-at-unstructuredName' => '1.2.840.113549.1.9.2', // PKCS #9 unstructured name 'pkcs-9-at-challengePassword' => '1.2.840.113549.1.9.7', // Challenge password for certificate revocations 'pkcs-9-at-extensionRequest' => '1.2.840.113549.1.9.14' // Certificate extension request ]); } } /** * Load X.509 certificate * * Returns an associative array describing the X.509 cert or a false if the cert failed to load * * @param array|string $cert * @param int $mode * @return mixed */ public function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($cert) && isset($cert['tbsCertificate'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); $this->dn = $cert['tbsCertificate']['subject']; if (!isset($this->dn)) { return false; } $this->currentCert = $cert; $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; unset($this->signatureSubject); return $cert; } if ($mode != self::FORMAT_DER) { $newcert = ASN1::extractBER($cert); if ($mode == self::FORMAT_PEM && $cert == $newcert) { return false; } $cert = $newcert; } if ($cert === false) { $this->currentCert = false; return false; } $decoded = ASN1::decodeBER($cert); if ($decoded) { $x509 = ASN1::asn1map($decoded[0], Maps\Certificate::MAP); } if (!isset($x509) || $x509 === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); if ($this->isSubArrayValid($x509, 'tbsCertificate/extensions')) { $this->mapInExtensions($x509, 'tbsCertificate/extensions'); } $this->mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence'); $this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence'); $key = $x509['tbsCertificate']['subjectPublicKeyInfo']; $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP); $x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(base64_encode($key), 64) . "-----END PUBLIC KEY-----"; $this->currentCert = $x509; $this->dn = $x509['tbsCertificate']['subject']; $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; return $x509; } /** * Save X.509 certificate * * @param array $cert * @param int $format optional * @return string */ public function saveX509(array $cert, $format = self::FORMAT_PEM) { if (!is_array($cert) || !isset($cert['tbsCertificate'])) { return false; } switch (true) { // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()" case !($algorithm = $this->subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')): case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): break; default: $cert['tbsCertificate']['subjectPublicKeyInfo'] = new Element( base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])) ); } if ($algorithm == 'rsaEncryption') { $cert['signatureAlgorithm']['parameters'] = null; $cert['tbsCertificate']['signature']['parameters'] = null; } $filters = []; $type_utf8_string = ['type' => ASN1::TYPE_UTF8_STRING]; $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string; $filters['signatureAlgorithm']['parameters'] = $type_utf8_string; $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string; //$filters['policyQualifiers']['qualifier'] = $type_utf8_string; $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; foreach (self::$extensions as $extension) { $filters['tbsCertificate']['extensions'][] = $extension; } /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib3\File\ASN1::TYPE_IA5_STRING. \phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random characters. */ $filters['policyQualifiers']['qualifier'] = ['type' => ASN1::TYPE_IA5_STRING]; ASN1::setFilters($filters); $this->mapOutExtensions($cert, 'tbsCertificate/extensions'); $this->mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence'); $this->mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence'); $cert = ASN1::encodeDER($cert, Maps\Certificate::MAP); switch ($format) { case self::FORMAT_DER: return $cert; // case self::FORMAT_PEM: default: return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Strings::base64_encode($cert), 64) . '-----END CERTIFICATE-----'; } } /** * Map extension values from octet string to extension-specific internal * format. * * @param array $root (by reference) * @param string $path */ private function mapInExtensions(array &$root, $path) { $extensions = &$this->subArrayUnchecked($root, $path); if ($extensions) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ $map = $this->getMapping($id); if (!is_bool($map)) { $decoder = $id == 'id-ce-nameConstraints' ? [static::class, 'decodeNameConstraintIP'] : [static::class, 'decodeIP']; $decoded = ASN1::decodeBER($value); if (!$decoded) { continue; } $mapped = ASN1::asn1map($decoded[0], $map, ['iPAddress' => $decoder]); $value = $mapped === false ? $decoded[0] : $mapped; if ($id == 'id-ce-certificatePolicies') { for ($j = 0; $j < count($value); $j++) { if (!isset($value[$j]['policyQualifiers'])) { continue; } for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; $map = $this->getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { $decoded = ASN1::decodeBER($subvalue); if (!$decoded) { continue; } $mapped = ASN1::asn1map($decoded[0], $map); $subvalue = $mapped === false ? $decoded[0] : $mapped; } } } } } } } } /** * Map extension values from extension-specific internal format to * octet string. * * @param array $root (by reference) * @param string $path */ private function mapOutExtensions(array &$root, $path) { $extensions = &$this->subArray($root, $path, !empty($this->extensionValues)); foreach ($this->extensionValues as $id => $data) { extract($data); $newext = [ 'extnId' => $id, 'extnValue' => $value, 'critical' => $critical ]; if ($replace) { foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { $extensions[$key] = $newext; continue 2; } } } $extensions[] = $newext; } if (is_array($extensions)) { $size = count($extensions); for ($i = 0; $i < $size; $i++) { if ($extensions[$i] instanceof Element) { continue; } $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; switch ($id) { case 'id-ce-certificatePolicies': for ($j = 0; $j < count($value); $j++) { if (!isset($value[$j]['policyQualifiers'])) { continue; } for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; $map = $this->getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { // by default \phpseclib3\File\ASN1 will try to render qualifier as a \phpseclib3\File\ASN1::TYPE_IA5_STRING since it's // actual type is \phpseclib3\File\ASN1::TYPE_ANY $subvalue = new Element(ASN1::encodeDER($subvalue, $map)); } } } break; case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string if (isset($value['authorityCertSerialNumber'])) { if ($value['authorityCertSerialNumber']->toBytes() == '') { $temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; $value['authorityCertSerialNumber'] = new Element($temp); } } } /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ $map = $this->getMapping($id); if (is_bool($map)) { if (!$map) { //user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { $value = ASN1::encodeDER($value, $map, ['iPAddress' => [static::class, 'encodeIP']]); } } } } /** * Map attribute values from ANY type to attribute-specific internal * format. * * @param array $root (by reference) * @param string $path */ private function mapInAttributes(&$root, $path) { $attributes = &$this->subArray($root, $path); if (is_array($attributes)) { for ($i = 0; $i < count($attributes); $i++) { $id = $attributes[$i]['type']; /* $value contains the DER encoding of an ASN.1 value corresponding to the attribute type identified by type */ $map = $this->getMapping($id); if (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; for ($j = 0; $j < count($values); $j++) { $value = ASN1::encodeDER($values[$j], Maps\AttributeValue::MAP); $decoded = ASN1::decodeBER($value); if (!is_bool($map)) { if (!$decoded) { continue; } $mapped = ASN1::asn1map($decoded[0], $map); if ($mapped !== false) { $values[$j] = $mapped; } if ($id == 'pkcs-9-at-extensionRequest' && $this->isSubArrayValid($values, $j)) { $this->mapInExtensions($values, $j); } } elseif ($map) { $values[$j] = $value; } } } } } } /** * Map attribute values from attribute-specific internal format to * ANY type. * * @param array $root (by reference) * @param string $path */ private function mapOutAttributes(&$root, $path) { $attributes = &$this->subArray($root, $path); if (is_array($attributes)) { $size = count($attributes); for ($i = 0; $i < $size; $i++) { /* [value] contains the DER encoding of an ASN.1 value corresponding to the attribute type identified by type */ $id = $attributes[$i]['type']; $map = $this->getMapping($id); if ($map === false) { //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; for ($j = 0; $j < count($values); $j++) { switch ($id) { case 'pkcs-9-at-extensionRequest': $this->mapOutExtensions($values, $j); break; } if (!is_bool($map)) { $temp = ASN1::encodeDER($values[$j], $map); $decoded = ASN1::decodeBER($temp); if (!$decoded) { continue; } $values[$j] = ASN1::asn1map($decoded[0], Maps\AttributeValue::MAP); } } } } } } /** * Map DN values from ANY type to DN-specific internal * format. * * @param array $root (by reference) * @param string $path */ private function mapInDNs(array &$root, $path) { $dns = &$this->subArray($root, $path); if (is_array($dns)) { for ($i = 0; $i < count($dns); $i++) { for ($j = 0; $j < count($dns[$i]); $j++) { $type = $dns[$i][$j]['type']; $value = &$dns[$i][$j]['value']; if (is_object($value) && $value instanceof Element) { $map = $this->getMapping($type); if (!is_bool($map)) { $decoded = ASN1::decodeBER($value); if (!$decoded) { continue; } $value = ASN1::asn1map($decoded[0], $map); } } } } } } /** * Map DN values from DN-specific internal format to * ANY type. * * @param array $root (by reference) * @param string $path */ private function mapOutDNs(array &$root, $path) { $dns = &$this->subArray($root, $path); if (is_array($dns)) { $size = count($dns); for ($i = 0; $i < $size; $i++) { for ($j = 0; $j < count($dns[$i]); $j++) { $type = $dns[$i][$j]['type']; $value = &$dns[$i][$j]['value']; if (is_object($value) && $value instanceof Element) { continue; } $map = $this->getMapping($type); if (!is_bool($map)) { $value = new Element(ASN1::encodeDER($value, $map)); } } } } } /** * Associate an extension ID to an extension mapping * * @param string $extnId * @return mixed */ private function getMapping($extnId) { if (!is_string($extnId)) { // eg. if it's a \phpseclib3\File\ASN1\Element object return true; } if (isset(self::$extensions[$extnId])) { return self::$extensions[$extnId]; } switch ($extnId) { case 'id-ce-keyUsage': return Maps\KeyUsage::MAP; case 'id-ce-basicConstraints': return Maps\BasicConstraints::MAP; case 'id-ce-subjectKeyIdentifier': return Maps\KeyIdentifier::MAP; case 'id-ce-cRLDistributionPoints': return Maps\CRLDistributionPoints::MAP; case 'id-ce-authorityKeyIdentifier': return Maps\AuthorityKeyIdentifier::MAP; case 'id-ce-certificatePolicies': return Maps\CertificatePolicies::MAP; case 'id-ce-extKeyUsage': return Maps\ExtKeyUsageSyntax::MAP; case 'id-pe-authorityInfoAccess': return Maps\AuthorityInfoAccessSyntax::MAP; case 'id-ce-subjectAltName': return Maps\SubjectAltName::MAP; case 'id-ce-subjectDirectoryAttributes': return Maps\SubjectDirectoryAttributes::MAP; case 'id-ce-privateKeyUsagePeriod': return Maps\PrivateKeyUsagePeriod::MAP; case 'id-ce-issuerAltName': return Maps\IssuerAltName::MAP; case 'id-ce-policyMappings': return Maps\PolicyMappings::MAP; case 'id-ce-nameConstraints': return Maps\NameConstraints::MAP; case 'netscape-cert-type': return Maps\netscape_cert_type::MAP; case 'netscape-comment': return Maps\netscape_comment::MAP; case 'netscape-ca-policy-url': return Maps\netscape_ca_policy_url::MAP; // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets // back around to asn1map() and we don't want it decoded again. //case 'id-qt-cps': // return Maps\CPSuri::MAP; case 'id-qt-unotice': return Maps\UserNotice::MAP; // the following OIDs are unsupported but we don't want them to give notices when calling saveX509(). case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt case 'entrustVersInfo': // http://support.microsoft.com/kb/287547 case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION // "SET Secure Electronic Transaction Specification" // http://www.maithean.com/docs/set_bk3.pdf case '2.23.42.7.0': // id-set-hashedRootKey // "Certificate Transparency" // https://tools.ietf.org/html/rfc6962 case '1.3.6.1.4.1.11129.2.4.2': // "Qualified Certificate statements" // https://tools.ietf.org/html/rfc3739#section-3.2.6 case '1.3.6.1.5.5.7.1.3': return true; // CSR attributes case 'pkcs-9-at-unstructuredName': return Maps\PKCS9String::MAP; case 'pkcs-9-at-challengePassword': return Maps\DirectoryString::MAP; case 'pkcs-9-at-extensionRequest': return Maps\Extensions::MAP; // CRL extensions. case 'id-ce-cRLNumber': return Maps\CRLNumber::MAP; case 'id-ce-deltaCRLIndicator': return Maps\CRLNumber::MAP; case 'id-ce-issuingDistributionPoint': return Maps\IssuingDistributionPoint::MAP; case 'id-ce-freshestCRL': return Maps\CRLDistributionPoints::MAP; case 'id-ce-cRLReasons': return Maps\CRLReason::MAP; case 'id-ce-invalidityDate': return Maps\InvalidityDate::MAP; case 'id-ce-certificateIssuer': return Maps\CertificateIssuer::MAP; case 'id-ce-holdInstructionCode': return Maps\HoldInstructionCode::MAP; case 'id-at-postalAddress': return Maps\PostalAddress::MAP; } return false; } /** * Load an X.509 certificate as a certificate authority * * @param string $cert * @return bool */ public function loadCA($cert) { $olddn = $this->dn; $oldcert = $this->currentCert; $oldsigsubj = $this->signatureSubject; $oldkeyid = $this->currentKeyIdentifier; $cert = $this->loadX509($cert); if (!$cert) { $this->dn = $olddn; $this->currentCert = $oldcert; $this->signatureSubject = $oldsigsubj; $this->currentKeyIdentifier = $oldkeyid; return false; } /* From RFC5280 "PKIX Certificate and CRL Profile": If the keyUsage extension is present, then the subject public key MUST NOT be used to verify signatures on certificates or CRLs unless the corresponding keyCertSign or cRLSign bit is set. */ //$keyUsage = $this->getExtension('id-ce-keyUsage'); //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) { // return false; //} /* From RFC5280 "PKIX Certificate and CRL Profile": The cA boolean indicates whether the certified public key may be used to verify certificate signatures. If the cA boolean is not asserted, then the keyCertSign bit in the key usage extension MUST NOT be asserted. If the basic constraints extension is not present in a version 3 certificate, or the extension is present but the cA boolean is not asserted, then the certified public key MUST NOT be used to verify certificate signatures. */ //$basicConstraints = $this->getExtension('id-ce-basicConstraints'); //if (!$basicConstraints || !$basicConstraints['cA']) { // return false; //} $this->CAs[] = $cert; $this->dn = $olddn; $this->currentCert = $oldcert; $this->signatureSubject = $oldsigsubj; return true; } /** * Validate an X.509 certificate against a URL * * From RFC2818 "HTTP over TLS": * * Matching is performed using the matching rules specified by * [RFC2459]. If more than one identity of a given type is present in * the certificate (e.g., more than one dNSName name, a match in any one * of the set is considered acceptable.) Names may contain the wildcard * character * which is considered to match any single domain name * component or component fragment. E.g., *.a.com matches foo.a.com but * not bar.foo.a.com. f*.com matches foo.com but not bar.com. * * @param string $url * @return bool */ public function validateURL($url) { if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } $components = parse_url($url); if (!isset($components['host'])) { return false; } if ($names = $this->getExtension('id-ce-subjectAltName')) { foreach ($names as $name) { foreach ($name as $key => $value) { $value = preg_quote($value); $value = str_replace('\*', '[^.]*', $value); switch ($key) { case 'dNSName': /* From RFC2818 "HTTP over TLS": If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. */ if (preg_match('#^' . $value . '$#', $components['host'])) { return true; } break; case 'iPAddress': /* From RFC2818 "HTTP over TLS": In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI. */ if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { return true; } } } } return false; } if ($value = $this->getDNProp('id-at-commonName')) { $value = str_replace(['.', '*'], ['\.', '[^.]*'], $value[0]); return preg_match('#^' . $value . '$#', $components['host']) === 1; } return false; } /** * Validate a date * * If $date isn't defined it is assumed to be the current date. * * @param \DateTimeInterface|string $date optional * @return bool */ public function validateDate($date = null) { if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } if (!isset($date)) { $date = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get())); } $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime']; $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; if (is_string($date)) { $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get())); } $notBefore = new \DateTimeImmutable($notBefore, new \DateTimeZone(@date_default_timezone_get())); $notAfter = new \DateTimeImmutable($notAfter, new \DateTimeZone(@date_default_timezone_get())); return $date >= $notBefore && $date <= $notAfter; } /** * Fetches a URL * * @param string $url * @return bool|string */ private static function fetchURL($url) { if (self::$disable_url_fetch) { return false; } $parts = parse_url($url); $data = ''; switch ($parts['scheme']) { case 'http': $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); if (!$fsock) { return false; } $path = $parts['path']; if (isset($parts['query'])) { $path .= '?' . $parts['query']; } fputs($fsock, "GET $path HTTP/1.0\r\n"); fputs($fsock, "Host: $parts[host]\r\n\r\n"); $line = fgets($fsock, 1024); if (strlen($line) < 3) { return false; } preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); if ($temp[1] != '200') { return false; } // skip the rest of the headers in the http response while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { } while (!feof($fsock)) { $temp = fread($fsock, 1024); if ($temp === false) { return false; } $data .= $temp; } break; //case 'ftp': //case 'ldap': //default: } return $data; } /** * Validates an intermediate cert as identified via authority info access extension * * See https://tools.ietf.org/html/rfc4325 for more info * * @param bool $caonly * @param int $count * @return bool */ private function testForIntermediate($caonly, $count) { $opts = $this->getExtension('id-pe-authorityInfoAccess'); if (!is_array($opts)) { return false; } foreach ($opts as $opt) { if ($opt['accessMethod'] == 'id-ad-caIssuers') { // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP, // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325 // discusses if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { $url = $opt['accessLocation']['uniformResourceIdentifier']; break; } } } if (!isset($url)) { return false; } $cert = static::fetchURL($url); if (!is_string($cert)) { return false; } $parent = new static(); $parent->CAs = $this->CAs; /* "Conforming applications that support HTTP or FTP for accessing certificates MUST be able to accept .cer files and SHOULD be able to accept .p7c files." -- https://tools.ietf.org/html/rfc4325 A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797" These are currently unsupported */ if (!is_array($parent->loadX509($cert))) { return false; } if (!$parent->validateSignatureCountable($caonly, ++$count)) { return false; } $this->CAs[] = $parent->currentCert; //$this->loadCA($cert); return true; } /** * Validate a signature * * Works on X.509 certs, CSR's and CRL's. * Returns true if the signature is verified, false if it is not correct or null on error * * By default returns false for self-signed certs. Call validateSignature(false) to make this support * self-signed. * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * * @param bool $caonly optional * @return mixed */ public function validateSignature($caonly = true) { return $this->validateSignatureCountable($caonly, 0); } /** * Validate a signature * * Performs said validation whilst keeping track of how many times validation method is called * * @param bool $caonly * @param int $count * @return mixed */ private function validateSignatureCountable($caonly, $count) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return null; } if ($count == self::$recur_limit) { return false; } /* TODO: "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 implement pathLenConstraint in the id-ce-basicConstraints extension */ switch (true) { case isset($this->currentCert['tbsCertificate']): // self-signed cert switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $this->currentCert; // working cert } } if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { // even if the cert is a self-signed one we still want to see if it's a CA; // if not, we'll conditionally return an error $ca = $this->CAs[$i]; switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { break 2; // serial mismatch - check other ca } $signingCert = $ca; // working cert break 3; } } } if (count($this->CAs) == $i && $caonly) { return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } } elseif (!isset($signingCert) || $caonly) { return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } return $this->validateSignatureHelper( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr($this->currentCert['signature'], 1), $this->signatureSubject ); case isset($this->currentCert['certificationRequestInfo']): return $this->validateSignatureHelper( $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr($this->currentCert['signature'], 1), $this->signatureSubject ); case isset($this->currentCert['publicKeyAndChallenge']): return $this->validateSignatureHelper( $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr($this->currentCert['signature'], 1), $this->signatureSubject ); case isset($this->currentCert['tbsCertList']): if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { break 2; // serial mismatch - check other ca } $signingCert = $ca; // working cert break 3; } } } } if (!isset($signingCert)) { return false; } return $this->validateSignatureHelper( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr($this->currentCert['signature'], 1), $this->signatureSubject ); default: return false; } } /** * Validates a signature * * Returns true if the signature is verified and false if it is not correct. * If the algorithms are unsupposed an exception is thrown. * * @param string $publicKeyAlgorithm * @param string $publicKey * @param string $signatureAlgorithm * @param string $signature * @param string $signatureSubject * @throws \phpseclib3\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @return bool */ private function validateSignatureHelper($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { switch ($publicKeyAlgorithm) { case 'id-RSASSA-PSS': $key = RSA::loadFormat('PSS', $publicKey); break; case 'rsaEncryption': $key = RSA::loadFormat('PKCS8', $publicKey); switch ($signatureAlgorithm) { case 'id-RSASSA-PSS': break; case 'md2WithRSAEncryption': case 'md5WithRSAEncryption': case 'sha1WithRSAEncryption': case 'sha224WithRSAEncryption': case 'sha256WithRSAEncryption': case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $key = $key ->withHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)) ->withPadding(RSA::SIGNATURE_PKCS1); break; default: throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; case 'id-Ed25519': case 'id-Ed448': $key = EC::loadFormat('PKCS8', $publicKey); break; case 'id-ecPublicKey': $key = EC::loadFormat('PKCS8', $publicKey); switch ($signatureAlgorithm) { case 'ecdsa-with-SHA1': case 'ecdsa-with-SHA224': case 'ecdsa-with-SHA256': case 'ecdsa-with-SHA384': case 'ecdsa-with-SHA512': $key = $key ->withHash(preg_replace('#^ecdsa-with-#', '', strtolower($signatureAlgorithm))); break; default: throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; case 'id-dsa': $key = DSA::loadFormat('PKCS8', $publicKey); switch ($signatureAlgorithm) { case 'id-dsa-with-sha1': case 'id-dsa-with-sha224': case 'id-dsa-with-sha256': $key = $key ->withHash(preg_replace('#^id-dsa-with-#', '', strtolower($signatureAlgorithm))); break; default: throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; default: throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); } return $key->verify($signatureSubject, $signature); } /** * Sets the recursion limit * * When validating a signature it may be necessary to download intermediate certs from URI's. * An intermediate cert that linked to itself would result in an infinite loop so to prevent * that we set a recursion limit. A negative number means that there is no recursion limit. * * @param int $count */ public static function setRecurLimit($count) { self::$recur_limit = $count; } /** * Prevents URIs from being automatically retrieved * */ public static function disableURLFetch() { self::$disable_url_fetch = true; } /** * Allows URIs to be automatically retrieved * */ public static function enableURLFetch() { self::$disable_url_fetch = false; } /** * Decodes an IP address * * Takes in a base64 encoded "blob" and returns a human readable IP address * * @param string $ip * @return string */ public static function decodeIP($ip) { return inet_ntop($ip); } /** * Decodes an IP address in a name constraints extension * * Takes in a base64 encoded "blob" and returns a human readable IP address / mask * * @param string $ip * @return array */ public static function decodeNameConstraintIP($ip) { $size = strlen($ip) >> 1; $mask = substr($ip, $size); $ip = substr($ip, 0, $size); return [inet_ntop($ip), inet_ntop($mask)]; } /** * Encodes an IP address * * Takes a human readable IP address into a base64-encoded "blob" * * @param string|array $ip * @return string */ public static function encodeIP($ip) { return is_string($ip) ? inet_pton($ip) : inet_pton($ip[0]) . inet_pton($ip[1]); } /** * "Normalizes" a Distinguished Name property * * @param string $propName * @return mixed */ private function translateDNProp($propName) { switch (strtolower($propName)) { case 'jurisdictionofincorporationcountryname': case 'jurisdictioncountryname': case 'jurisdictionc': return 'jurisdictionOfIncorporationCountryName'; case 'jurisdictionofincorporationstateorprovincename': case 'jurisdictionstateorprovincename': case 'jurisdictionst': return 'jurisdictionOfIncorporationStateOrProvinceName'; case 'jurisdictionlocalityname': case 'jurisdictionl': return 'jurisdictionLocalityName'; case 'id-at-businesscategory': case 'businesscategory': return 'id-at-businessCategory'; case 'id-at-countryname': case 'countryname': case 'c': return 'id-at-countryName'; case 'id-at-organizationname': case 'organizationname': case 'o': return 'id-at-organizationName'; case 'id-at-dnqualifier': case 'dnqualifier': return 'id-at-dnQualifier'; case 'id-at-commonname': case 'commonname': case 'cn': return 'id-at-commonName'; case 'id-at-stateorprovincename': case 'stateorprovincename': case 'state': case 'province': case 'provincename': case 'st': return 'id-at-stateOrProvinceName'; case 'id-at-localityname': case 'localityname': case 'l': return 'id-at-localityName'; case 'id-emailaddress': case 'emailaddress': return 'pkcs-9-at-emailAddress'; case 'id-at-serialnumber': case 'serialnumber': return 'id-at-serialNumber'; case 'id-at-postalcode': case 'postalcode': return 'id-at-postalCode'; case 'id-at-streetaddress': case 'streetaddress': return 'id-at-streetAddress'; case 'id-at-name': case 'name': return 'id-at-name'; case 'id-at-givenname': case 'givenname': return 'id-at-givenName'; case 'id-at-surname': case 'surname': case 'sn': return 'id-at-surname'; case 'id-at-initials': case 'initials': return 'id-at-initials'; case 'id-at-generationqualifier': case 'generationqualifier': return 'id-at-generationQualifier'; case 'id-at-organizationalunitname': case 'organizationalunitname': case 'ou': return 'id-at-organizationalUnitName'; case 'id-at-pseudonym': case 'pseudonym': return 'id-at-pseudonym'; case 'id-at-title': case 'title': return 'id-at-title'; case 'id-at-description': case 'description': return 'id-at-description'; case 'id-at-role': case 'role': return 'id-at-role'; case 'id-at-uniqueidentifier': case 'uniqueidentifier': case 'x500uniqueidentifier': return 'id-at-uniqueIdentifier'; case 'postaladdress': case 'id-at-postaladdress': return 'id-at-postalAddress'; default: return false; } } /** * Set a Distinguished Name property * * @param string $propName * @param mixed $propValue * @param string $type optional * @return bool */ public function setDNProp($propName, $propValue, $type = 'utf8String') { if (empty($this->dn)) { $this->dn = ['rdnSequence' => []]; } if (($propName = $this->translateDNProp($propName)) === false) { return false; } foreach ((array) $propValue as $v) { if (!is_array($v) && isset($type)) { $v = [$type => $v]; } $this->dn['rdnSequence'][] = [ [ 'type' => $propName, 'value' => $v ] ]; } return true; } /** * Remove Distinguished Name properties * * @param string $propName */ public function removeDNProp($propName) { if (empty($this->dn)) { return; } if (($propName = $this->translateDNProp($propName)) === false) { return; } $dn = &$this->dn['rdnSequence']; $size = count($dn); for ($i = 0; $i < $size; $i++) { if ($dn[$i][0]['type'] == $propName) { unset($dn[$i]); } } $dn = array_values($dn); // fix for https://bugs.php.net/75433 affecting PHP 7.2 if (!isset($dn[0])) { $dn = array_splice($dn, 0, 0); } } /** * Get Distinguished Name properties * * @param string $propName * @param array $dn optional * @param bool $withType optional * @return mixed */ public function getDNProp($propName, array $dn = null, $withType = false) { if (!isset($dn)) { $dn = $this->dn; } if (empty($dn)) { return false; } if (($propName = $this->translateDNProp($propName)) === false) { return false; } $filters = []; $filters['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; ASN1::setFilters($filters); $this->mapOutDNs($dn, 'rdnSequence'); $dn = $dn['rdnSequence']; $result = []; for ($i = 0; $i < count($dn); $i++) { if ($dn[$i][0]['type'] == $propName) { $v = $dn[$i][0]['value']; if (!$withType) { if (is_array($v)) { foreach ($v as $type => $s) { $type = array_search($type, ASN1::ANY_MAP); if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) { $s = ASN1::convert($s, $type); if ($s !== false) { $v = $s; break; } } } if (is_array($v)) { $v = array_pop($v); // Always strip data type. } } elseif (is_object($v) && $v instanceof Element) { $map = $this->getMapping($propName); if (!is_bool($map)) { $decoded = ASN1::decodeBER($v); if (!$decoded) { return false; } $v = ASN1::asn1map($decoded[0], $map); } } } $result[] = $v; } } return $result; } /** * Set a Distinguished Name * * @param mixed $dn * @param bool $merge optional * @param string $type optional * @return bool */ public function setDN($dn, $merge = false, $type = 'utf8String') { if (!$merge) { $this->dn = null; } if (is_array($dn)) { if (isset($dn['rdnSequence'])) { $this->dn = $dn; // No merge here. return true; } // handles stuff generated by openssl_x509_parse() foreach ($dn as $prop => $value) { if (!$this->setDNProp($prop, $value, $type)) { return false; } } return true; } // handles everything else $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); for ($i = 1; $i < count($results); $i += 2) { $prop = trim($results[$i], ', =/'); $value = $results[$i + 1]; if (!$this->setDNProp($prop, $value, $type)) { return false; } } return true; } /** * Get the Distinguished Name for a certificates subject * * @param mixed $format optional * @param array $dn optional * @return array|bool|string */ public function getDN($format = self::DN_ARRAY, array $dn = null) { if (!isset($dn)) { $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; } switch ((int) $format) { case self::DN_ARRAY: return $dn; case self::DN_ASN1: $filters = []; $filters['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; ASN1::setFilters($filters); $this->mapOutDNs($dn, 'rdnSequence'); return ASN1::encodeDER($dn, Maps\Name::MAP); case self::DN_CANON: // No SEQUENCE around RDNs and all string values normalized as // trimmed lowercase UTF-8 with all spacing as one blank. // constructed RDNs will not be canonicalized $filters = []; $filters['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; ASN1::setFilters($filters); $result = ''; $this->mapOutDNs($dn, 'rdnSequence'); foreach ($dn['rdnSequence'] as $rdn) { foreach ($rdn as $i => $attr) { $attr = &$rdn[$i]; if (is_array($attr['value'])) { foreach ($attr['value'] as $type => $v) { $type = array_search($type, ASN1::ANY_MAP, true); if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) { $v = ASN1::convert($v, $type); if ($v !== false) { $v = preg_replace('/\s+/', ' ', $v); $attr['value'] = strtolower(trim($v)); break; } } } } } $result .= ASN1::encodeDER($rdn, Maps\RelativeDistinguishedName::MAP); } return $result; case self::DN_HASH: $dn = $this->getDN(self::DN_CANON, $dn); $hash = new Hash('sha1'); $hash = $hash->hash($dn); extract(unpack('Vhash', $hash)); return strtolower(Strings::bin2hex(pack('N', $hash))); } // Default is to return a string. $start = true; $output = ''; $result = []; $filters = []; $filters['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; ASN1::setFilters($filters); $this->mapOutDNs($dn, 'rdnSequence'); foreach ($dn['rdnSequence'] as $field) { $prop = $field[0]['type']; $value = $field[0]['value']; $delim = ', '; switch ($prop) { case 'id-at-countryName': $desc = 'C'; break; case 'id-at-stateOrProvinceName': $desc = 'ST'; break; case 'id-at-organizationName': $desc = 'O'; break; case 'id-at-organizationalUnitName': $desc = 'OU'; break; case 'id-at-commonName': $desc = 'CN'; break; case 'id-at-localityName': $desc = 'L'; break; case 'id-at-surname': $desc = 'SN'; break; case 'id-at-uniqueIdentifier': $delim = '/'; $desc = 'x500UniqueIdentifier'; break; case 'id-at-postalAddress': $delim = '/'; $desc = 'postalAddress'; break; default: $delim = '/'; $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop); } if (!$start) { $output .= $delim; } if (is_array($value)) { foreach ($value as $type => $v) { $type = array_search($type, ASN1::ANY_MAP, true); if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) { $v = ASN1::convert($v, $type); if ($v !== false) { $value = $v; break; } } } if (is_array($value)) { $value = array_pop($value); // Always strip data type. } } elseif (is_object($value) && $value instanceof Element) { $callback = function ($x) { return '\x' . bin2hex($x[0]); }; $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } $output .= $desc . '=' . $value; $result[$desc] = isset($result[$desc]) ? array_merge((array) $result[$desc], [$value]) : $value; $start = false; } return $format == self::DN_OPENSSL ? $result : $output; } /** * Get the Distinguished Name for a certificate/crl issuer * * @param int $format optional * @return mixed */ public function getIssuerDN($format = self::DN_ARRAY) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']); case isset($this->currentCert['tbsCertList']): return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']); } return false; } /** * Get the Distinguished Name for a certificate/csr subject * Alias of getDN() * * @param int $format optional * @return mixed */ public function getSubjectDN($format = self::DN_ARRAY) { switch (true) { case !empty($this->dn): return $this->getDN($format); case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']); case isset($this->currentCert['certificationRequestInfo']): return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']); } return false; } /** * Get an individual Distinguished Name property for a certificate/crl issuer * * @param string $propName * @param bool $withType optional * @return mixed */ public function getIssuerDNProp($propName, $withType = false) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); case isset($this->currentCert['tbsCertList']): return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); } return false; } /** * Get an individual Distinguished Name property for a certificate/csr subject * * @param string $propName * @param bool $withType optional * @return mixed */ public function getSubjectDNProp($propName, $withType = false) { switch (true) { case !empty($this->dn): return $this->getDNProp($propName, null, $withType); case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); case isset($this->currentCert['certificationRequestInfo']): return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); } return false; } /** * Get the certificate chain for the current cert * * @return mixed */ public function getChain() { $chain = [$this->currentCert]; if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } while (true) { $currentCert = $chain[count($chain) - 1]; for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if ($currentCert === $ca) { break 3; } $chain[] = $ca; break 2; } } } if ($i == count($this->CAs)) { break; } } foreach ($chain as $key => $value) { $chain[$key] = new X509(); $chain[$key]->loadX509($value); } return $chain; } /** * Returns the current cert * * @return array|bool */ public function &getCurrentCert() { return $this->currentCert; } /** * Set public key * * Key needs to be a \phpseclib3\Crypt\RSA object * * @param PublicKey $key * @return void */ public function setPublicKey(PublicKey $key) { $this->publicKey = $key; } /** * Set private key * * Key needs to be a \phpseclib3\Crypt\RSA object * * @param PrivateKey $key */ public function setPrivateKey(PrivateKey $key) { $this->privateKey = $key; } /** * Set challenge * * Used for SPKAC CSR's * * @param string $challenge */ public function setChallenge($challenge) { $this->challenge = $challenge; } /** * Gets the public key * * Returns a \phpseclib3\Crypt\RSA object or a false. * * @return mixed */ public function getPublicKey() { if (isset($this->publicKey)) { return $this->publicKey; } if (isset($this->currentCert) && is_array($this->currentCert)) { $paths = [ 'tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo', 'publicKeyAndChallenge/spki' ]; foreach ($paths as $path) { $keyinfo = $this->subArray($this->currentCert, $path); if (!empty($keyinfo)) { break; } } } if (empty($keyinfo)) { return false; } $key = $keyinfo['subjectPublicKey']; switch ($keyinfo['algorithm']['algorithm']) { case 'id-RSASSA-PSS': return RSA::loadFormat('PSS', $key); case 'rsaEncryption': return RSA::loadFormat('PKCS8', $key)->withPadding(RSA::SIGNATURE_PKCS1); case 'id-ecPublicKey': case 'id-Ed25519': case 'id-Ed448': return EC::loadFormat('PKCS8', $key); case 'id-dsa': return DSA::loadFormat('PKCS8', $key); } return false; } /** * Load a Certificate Signing Request * * @param string $csr * @param int $mode * @return mixed */ public function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($csr) && isset($csr['certificationRequestInfo'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); $this->dn = $csr['certificationRequestInfo']['subject']; if (!isset($this->dn)) { return false; } $this->currentCert = $csr; return $csr; } // see http://tools.ietf.org/html/rfc2986 if ($mode != self::FORMAT_DER) { $newcsr = ASN1::extractBER($csr); if ($mode == self::FORMAT_PEM && $csr == $newcsr) { return false; } $csr = $newcsr; } $orig = $csr; if ($csr === false) { $this->currentCert = false; return false; } $decoded = ASN1::decodeBER($csr); if (!$decoded) { $this->currentCert = false; return false; } $csr = ASN1::asn1map($decoded[0], Maps\CertificationRequest::MAP); if (!isset($csr) || $csr === false) { $this->currentCert = false; return false; } $this->mapInAttributes($csr, 'certificationRequestInfo/attributes'); $this->mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence'); $this->dn = $csr['certificationRequestInfo']['subject']; $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $key = $csr['certificationRequestInfo']['subjectPKInfo']; $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP); $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(base64_encode($key), 64) . "-----END PUBLIC KEY-----"; $this->currentKeyIdentifier = null; $this->currentCert = $csr; $this->publicKey = null; $this->publicKey = $this->getPublicKey(); return $csr; } /** * Save CSR request * * @param array $csr * @param int $format optional * @return string */ public function saveCSR(array $csr, $format = self::FORMAT_PEM) { if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { return false; } switch (true) { case !($algorithm = $this->subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): break; default: $csr['certificationRequestInfo']['subjectPKInfo'] = new Element( base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])) ); } $filters = []; $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; ASN1::setFilters($filters); $this->mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence'); $this->mapOutAttributes($csr, 'certificationRequestInfo/attributes'); $csr = ASN1::encodeDER($csr, Maps\CertificationRequest::MAP); switch ($format) { case self::FORMAT_DER: return $csr; // case self::FORMAT_PEM: default: return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(Strings::base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; } } /** * Load a SPKAC CSR * * SPKAC's are produced by the HTML5 keygen element: * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * * @param string $spkac * @return mixed */ public function loadSPKAC($spkac) { if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); $this->currentCert = $spkac; return $spkac; } // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge // OpenSSL produces SPKAC's that are preceded by the string SPKAC= $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : false; if ($temp != false) { $spkac = $temp; } $orig = $spkac; if ($spkac === false) { $this->currentCert = false; return false; } $decoded = ASN1::decodeBER($spkac); if (!$decoded) { $this->currentCert = false; return false; } $spkac = ASN1::asn1map($decoded[0], Maps\SignedPublicKeyAndChallenge::MAP); if (!isset($spkac) || !is_array($spkac)) { $this->currentCert = false; return false; } $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $key = $spkac['publicKeyAndChallenge']['spki']; $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP); $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(base64_encode($key), 64) . "-----END PUBLIC KEY-----"; $this->currentKeyIdentifier = null; $this->currentCert = $spkac; $this->publicKey = null; $this->publicKey = $this->getPublicKey(); return $spkac; } /** * Save a SPKAC CSR request * * @param array $spkac * @param int $format optional * @return string */ public function saveSPKAC(array $spkac, $format = self::FORMAT_PEM) { if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { return false; } $algorithm = $this->subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); switch (true) { case !$algorithm: case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): break; default: $spkac['publicKeyAndChallenge']['spki'] = new Element( base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])) ); } $spkac = ASN1::encodeDER($spkac, Maps\SignedPublicKeyAndChallenge::MAP); switch ($format) { case self::FORMAT_DER: return $spkac; // case self::FORMAT_PEM: default: // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much // no other SPKAC decoders phpseclib will use that same format return 'SPKAC=' . Strings::base64_encode($spkac); } } /** * Load a Certificate Revocation List * * @param string $crl * @param int $mode * @return mixed */ public function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($crl) && isset($crl['tbsCertList'])) { $this->currentCert = $crl; unset($this->signatureSubject); return $crl; } if ($mode != self::FORMAT_DER) { $newcrl = ASN1::extractBER($crl); if ($mode == self::FORMAT_PEM && $crl == $newcrl) { return false; } $crl = $newcrl; } $orig = $crl; if ($crl === false) { $this->currentCert = false; return false; } $decoded = ASN1::decodeBER($crl); if (!$decoded) { $this->currentCert = false; return false; } $crl = ASN1::asn1map($decoded[0], Maps\CertificateList::MAP); if (!isset($crl) || $crl === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->mapInDNs($crl, 'tbsCertList/issuer/rdnSequence'); if ($this->isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { $this->mapInExtensions($crl, 'tbsCertList/crlExtensions'); } if ($this->isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { $rclist_ref = &$this->subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); if ($rclist_ref) { $rclist = $crl['tbsCertList']['revokedCertificates']; foreach ($rclist as $i => $extension) { if ($this->isSubArrayValid($rclist, "$i/crlEntryExtensions")) { $this->mapInExtensions($rclist_ref, "$i/crlEntryExtensions"); } } } } $this->currentKeyIdentifier = null; $this->currentCert = $crl; return $crl; } /** * Save Certificate Revocation List. * * @param array $crl * @param int $format optional * @return string */ public function saveCRL(array $crl, $format = self::FORMAT_PEM) { if (!is_array($crl) || !isset($crl['tbsCertList'])) { return false; } $filters = []; $filters['tbsCertList']['issuer']['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING]; $filters['tbsCertList']['signature']['parameters'] = ['type' => ASN1::TYPE_UTF8_STRING]; $filters['signatureAlgorithm']['parameters'] = ['type' => ASN1::TYPE_UTF8_STRING]; if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] = ['type' => ASN1::TYPE_NULL]; } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] = ['type' => ASN1::TYPE_NULL]; } ASN1::setFilters($filters); $this->mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence'); $this->mapOutExtensions($crl, 'tbsCertList/crlExtensions'); $rclist = &$this->subArray($crl, 'tbsCertList/revokedCertificates'); if (is_array($rclist)) { foreach ($rclist as $i => $extension) { $this->mapOutExtensions($rclist, "$i/crlEntryExtensions"); } } $crl = ASN1::encodeDER($crl, Maps\CertificateList::MAP); switch ($format) { case self::FORMAT_DER: return $crl; // case self::FORMAT_PEM: default: return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Strings::base64_encode($crl), 64) . '-----END X509 CRL-----'; } } /** * Helper function to build a time field according to RFC 3280 section * - 4.1.2.5 Validity * - 5.1.2.4 This Update * - 5.1.2.5 Next Update * - 5.1.2.6 Revoked Certificates * by choosing utcTime iff year of date given is before 2050 and generalTime else. * * @param string $date in format date('D, d M Y H:i:s O') * @return array|Element */ private function timeField($date) { if ($date instanceof Element) { return $date; } $dateObj = new \DateTimeImmutable($date, new \DateTimeZone('GMT')); $year = $dateObj->format('Y'); // the same way ASN1.php parses this if ($year < 2050) { return ['utcTime' => $date]; } else { return ['generalTime' => $date]; } } /** * Sign an X.509 certificate * * $issuer's private key needs to be loaded. * $subject can be either an existing X.509 cert (if you want to resign it), * a CSR or something with the DN and public key explicitly set. * * @return mixed */ public function sign(X509 $issuer, X509 $subject) { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; } if (isset($subject->publicKey) && !($subjectPublicKey = $subject->formatSubjectPublicKey())) { return false; } $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey); if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { $this->currentCert = $subject->currentCert; $this->currentCert['tbsCertificate']['signature'] = $signatureAlgorithm; $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; if (!empty($this->startDate)) { $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->timeField($this->startDate); } if (!empty($this->endDate)) { $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->timeField($this->endDate); } if (!empty($this->serialNumber)) { $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; } if (!empty($subject->dn)) { $this->currentCert['tbsCertificate']['subject'] = $subject->dn; } if (!empty($subject->publicKey)) { $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey; } $this->removeExtension('id-ce-authorityKeyIdentifier'); if (isset($subject->domains)) { $this->removeExtension('id-ce-subjectAltName'); } } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { return false; } else { if (!isset($subject->publicKey)) { return false; } $startDate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get())); $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); $endDate = new \DateTimeImmutable('+1 year', new \DateTimeZone(@date_default_timezone_get())); $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); /* "The serial number MUST be a positive integer" "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 for the integer to be positive the leading bit needs to be 0 hence the application of a bitmap */ $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256); $this->currentCert = [ 'tbsCertificate' => [ 'version' => 'v3', 'serialNumber' => $serialNumber, // $this->setSerialNumber() 'signature' => $signatureAlgorithm, 'issuer' => false, // this is going to be overwritten later 'validity' => [ 'notBefore' => $this->timeField($startDate), // $this->setStartDate() 'notAfter' => $this->timeField($endDate) // $this->setEndDate() ], 'subject' => $subject->dn, 'subjectPublicKeyInfo' => $subjectPublicKey ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => false // this is going to be overwritten later ]; // Copy extensions from CSR. $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0); if (!empty($csrexts)) { $this->currentCert['tbsCertificate']['extensions'] = $csrexts; } } $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; if (isset($issuer->currentKeyIdentifier)) { $this->setExtension('id-ce-authorityKeyIdentifier', [ //'authorityCertIssuer' => array( // array( // 'directoryName' => $issuer->dn // ) //), 'keyIdentifier' => $issuer->currentKeyIdentifier ]); //$extensions = &$this->currentCert['tbsCertificate']['extensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; //} //unset($extensions); } if (isset($subject->currentKeyIdentifier)) { $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); } $altName = []; if (isset($subject->domains) && count($subject->domains)) { $altName = array_map(['\phpseclib3\File\X509', 'dnsName'], $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { // should an IP address appear as the CN if no domain name is specified? idk //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1); $ipAddresses = []; foreach ($subject->ipAddresses as $ipAddress) { $encoded = $subject->ipAddress($ipAddress); if ($encoded !== false) { $ipAddresses[] = $encoded; } } if (count($ipAddresses)) { $altName = array_merge($altName, $ipAddresses); } } if (!empty($altName)) { $this->setExtension('id-ce-subjectAltName', $altName); } if ($this->caFlag) { $keyUsage = $this->getExtension('id-ce-keyUsage'); if (!$keyUsage) { $keyUsage = []; } $this->setExtension( 'id-ce-keyUsage', array_values(array_unique(array_merge($keyUsage, ['cRLSign', 'keyCertSign']))) ); $basicConstraints = $this->getExtension('id-ce-basicConstraints'); if (!$basicConstraints) { $basicConstraints = []; } $this->setExtension( 'id-ce-basicConstraints', array_merge(['cA' => true], $basicConstraints), true ); if (!isset($subject->currentKeyIdentifier)) { $this->setExtension('id-ce-subjectKeyIdentifier', $this->computeKeyIdentifier($this->currentCert), false, false); } } // resync $this->signatureSubject // save $tbsCertificate in case there are any \phpseclib3\File\ASN1\Element objects in it $tbsCertificate = $this->currentCert['tbsCertificate']; $this->loadX509($this->saveX509($this->currentCert)); $result = $this->currentCert; $this->currentCert['signature'] = $result['signature'] = "\0" . $issuer->privateKey->sign($this->signatureSubject); $result['tbsCertificate'] = $tbsCertificate; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } /** * Sign a CSR * * @return mixed */ public function signCSR() { if (!is_object($this->privateKey) || empty($this->dn)) { return false; } $origPublicKey = $this->publicKey; $this->publicKey = $this->privateKey->getPublicKey(); $publicKey = $this->formatSubjectPublicKey(); $this->publicKey = $origPublicKey; $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey); if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; if (!empty($this->dn)) { $this->currentCert['certificationRequestInfo']['subject'] = $this->dn; } $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; } else { $this->currentCert = [ 'certificationRequestInfo' => [ 'version' => 'v1', 'subject' => $this->dn, 'subjectPKInfo' => $publicKey ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => false // this is going to be overwritten later ]; } // resync $this->signatureSubject // save $certificationRequestInfo in case there are any \phpseclib3\File\ASN1\Element objects in it $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; $this->loadCSR($this->saveCSR($this->currentCert)); $result = $this->currentCert; $this->currentCert['signature'] = $result['signature'] = "\0" . $this->privateKey->sign($this->signatureSubject); $result['certificationRequestInfo'] = $certificationRequestInfo; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } /** * Sign a SPKAC * * @return mixed */ public function signSPKAC() { if (!is_object($this->privateKey)) { return false; } $origPublicKey = $this->publicKey; $this->publicKey = $this->privateKey->getPublicKey(); $publicKey = $this->formatSubjectPublicKey(); $this->publicKey = $origPublicKey; $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey); // re-signing a SPKAC seems silly but since everything else supports re-signing why not? if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; if (!empty($this->challenge)) { // the bitwise AND ensures that the output is a valid IA5String $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge)); } } else { $this->currentCert = [ 'publicKeyAndChallenge' => [ 'spki' => $publicKey, // quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>, // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified." // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way // we could alternatively do this instead if we ignored the specs: // Random::string(8) & str_repeat("\x7F", 8) 'challenge' => !empty($this->challenge) ? $this->challenge : '' ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => false // this is going to be overwritten later ]; } // resync $this->signatureSubject // save $publicKeyAndChallenge in case there are any \phpseclib3\File\ASN1\Element objects in it $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; $this->loadSPKAC($this->saveSPKAC($this->currentCert)); $result = $this->currentCert; $this->currentCert['signature'] = $result['signature'] = "\0" . $this->privateKey->sign($this->signatureSubject); $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } /** * Sign a CRL * * $issuer's private key needs to be loaded. * * @return mixed */ public function signCRL(X509 $issuer, X509 $crl) { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; } $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey); $thisUpdate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get())); $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; $this->currentCert['tbsCertList']['signature'] = $signatureAlgorithm; $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; } else { $this->currentCert = [ 'tbsCertList' => [ 'version' => 'v2', 'signature' => $signatureAlgorithm, 'issuer' => false, // this is going to be overwritten later 'thisUpdate' => $this->timeField($thisUpdate) // $this->setStartDate() ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => false // this is going to be overwritten later ]; } $tbsCertList = &$this->currentCert['tbsCertList']; $tbsCertList['issuer'] = $issuer->dn; $tbsCertList['thisUpdate'] = $this->timeField($thisUpdate); if (!empty($this->endDate)) { $tbsCertList['nextUpdate'] = $this->timeField($this->endDate); // $this->setEndDate() } else { unset($tbsCertList['nextUpdate']); } if (!empty($this->serialNumber)) { $crlNumber = $this->serialNumber; } else { $crlNumber = $this->getExtension('id-ce-cRLNumber'); // "The CRL number is a non-critical CRL extension that conveys a // monotonically increasing sequence number for a given CRL scope and // CRL issuer. This extension allows users to easily determine when a // particular CRL supersedes another CRL." // -- https://tools.ietf.org/html/rfc5280#section-5.2.3 $crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null; } $this->removeExtension('id-ce-authorityKeyIdentifier'); $this->removeExtension('id-ce-issuerAltName'); // Be sure version >= v2 if some extension found. $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; if (!$version) { if (!empty($tbsCertList['crlExtensions'])) { $version = 1; // v2. } elseif (!empty($tbsCertList['revokedCertificates'])) { foreach ($tbsCertList['revokedCertificates'] as $cert) { if (!empty($cert['crlEntryExtensions'])) { $version = 1; // v2. } } } if ($version) { $tbsCertList['version'] = $version; } } // Store additional extensions. if (!empty($tbsCertList['version'])) { // At least v2. if (!empty($crlNumber)) { $this->setExtension('id-ce-cRLNumber', $crlNumber); } if (isset($issuer->currentKeyIdentifier)) { $this->setExtension('id-ce-authorityKeyIdentifier', [ //'authorityCertIssuer' => array( // ] // 'directoryName' => $issuer->dn // ] //), 'keyIdentifier' => $issuer->currentKeyIdentifier ]); //$extensions = &$tbsCertList['crlExtensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; //} //unset($extensions); } $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); if ($issuerAltName !== false) { $this->setExtension('id-ce-issuerAltName', $issuerAltName); } } if (empty($tbsCertList['revokedCertificates'])) { unset($tbsCertList['revokedCertificates']); } unset($tbsCertList); // resync $this->signatureSubject // save $tbsCertList in case there are any \phpseclib3\File\ASN1\Element objects in it $tbsCertList = $this->currentCert['tbsCertList']; $this->loadCRL($this->saveCRL($this->currentCert)); $result = $this->currentCert; $this->currentCert['signature'] = $result['signature'] = "\0" . $issuer->privateKey->sign($this->signatureSubject); $result['tbsCertList'] = $tbsCertList; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } /** * Identify signature algorithm from key settings * * @param PrivateKey $key * @throws \phpseclib3\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @return array */ private static function identifySignatureAlgorithm(PrivateKey $key) { if ($key instanceof RSA) { if ($key->getPadding() & RSA::SIGNATURE_PSS) { $r = PSS::load($key->withPassword()->toString('PSS')); return [ 'algorithm' => 'id-RSASSA-PSS', 'parameters' => PSS::savePSSParams($r) ]; } switch ($key->getHash()) { case 'md2': case 'md5': case 'sha1': case 'sha224': case 'sha256': case 'sha384': case 'sha512': return ['algorithm' => $key->getHash() . 'WithRSAEncryption']; } throw new UnsupportedAlgorithmException('The only supported hash algorithms for RSA are: md2, md5, sha1, sha224, sha256, sha384, sha512'); } if ($key instanceof DSA) { switch ($key->getHash()) { case 'sha1': case 'sha224': case 'sha256': return ['algorithm' => 'id-dsa-with-' . $key->getHash()]; } throw new UnsupportedAlgorithmException('The only supported hash algorithms for DSA are: sha1, sha224, sha256'); } if ($key instanceof EC) { switch ($key->getCurve()) { case 'Ed25519': case 'Ed448': return ['algorithm' => 'id-' . $key->getCurve()]; } switch ($key->getHash()) { case 'sha1': case 'sha224': case 'sha256': case 'sha384': case 'sha512': return ['algorithm' => 'ecdsa-with-' . strtoupper($key->getHash())]; } throw new UnsupportedAlgorithmException('The only supported hash algorithms for EC are: sha1, sha224, sha256, sha384, sha512'); } throw new UnsupportedAlgorithmException('The only supported public key classes are: RSA, DSA, EC'); } /** * Set certificate start date * * @param \DateTimeInterface|string $date */ public function setStartDate($date) { if (!is_object($date) || !($date instanceof \DateTimeInterface)) { $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get())); } $this->startDate = $date->format('D, d M Y H:i:s O'); } /** * Set certificate end date * * @param \DateTimeInterface|string $date */ public function setEndDate($date) { /* To indicate that a certificate has no well-defined expiration date, the notAfter SHOULD be assigned the GeneralizedTime value of 99991231235959Z. -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5 */ if (is_string($date) && strtolower($date) === 'lifetime') { $temp = '99991231235959Z'; $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . ASN1::encodeLength(strlen($temp)) . $temp; $this->endDate = new Element($temp); } else { if (!is_object($date) || !($date instanceof \DateTimeInterface)) { $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get())); } $this->endDate = $date->format('D, d M Y H:i:s O'); } } /** * Set Serial Number * * @param string $serial * @param int $base optional */ public function setSerialNumber($serial, $base = -256) { $this->serialNumber = new BigInteger($serial, $base); } /** * Turns the certificate into a certificate authority * */ public function makeCA() { $this->caFlag = true; } /** * Check for validity of subarray * * This is intended for use in conjunction with _subArrayUnchecked(), * implementing the checks included in _subArray() but without copying * a potentially large array by passing its reference by-value to is_array(). * * @param array $root * @param string $path * @return boolean */ private function isSubArrayValid(array $root, $path) { if (!is_array($root)) { return false; } foreach (explode('/', $path) as $i) { if (!is_array($root)) { return false; } if (!isset($root[$i])) { return true; } $root = $root[$i]; } return true; } /** * Get a reference to a subarray * * This variant of _subArray() does no is_array() checking, * so $root should be checked with _isSubArrayValid() first. * * This is here for performance reasons: * Passing a reference (i.e. $root) by-value (i.e. to is_array()) * creates a copy. If $root is an especially large array, this is expensive. * * @param array $root * @param string $path absolute path with / as component separator * @param bool $create optional * @return array|false */ private function &subArrayUnchecked(array &$root, $path, $create = false) { $false = false; foreach (explode('/', $path) as $i) { if (!isset($root[$i])) { if (!$create) { return $false; } $root[$i] = []; } $root = &$root[$i]; } return $root; } /** * Get a reference to a subarray * * @param array $root * @param string $path absolute path with / as component separator * @param bool $create optional * @return array|false */ private function &subArray(array &$root = null, $path, $create = false) { $false = false; if (!is_array($root)) { return $false; } foreach (explode('/', $path) as $i) { if (!is_array($root)) { return $false; } if (!isset($root[$i])) { if (!$create) { return $false; } $root[$i] = []; } $root = &$root[$i]; } return $root; } /** * Get a reference to an extension subarray * * @param array $root * @param string $path optional absolute path with / as component separator * @param bool $create optional * @return array|false */ private function &extensions(array &$root = null, $path = null, $create = false) { if (!isset($root)) { $root = $this->currentCert; } switch (true) { case !empty($path): case !is_array($root): break; case isset($root['tbsCertificate']): $path = 'tbsCertificate/extensions'; break; case isset($root['tbsCertList']): $path = 'tbsCertList/crlExtensions'; break; case isset($root['certificationRequestInfo']): $pth = 'certificationRequestInfo/attributes'; $attributes = &$this->subArray($root, $pth, $create); if (is_array($attributes)) { foreach ($attributes as $key => $value) { if ($value['type'] == 'pkcs-9-at-extensionRequest') { $path = "$pth/$key/value/0"; break 2; } } if ($create) { $key = count($attributes); $attributes[] = ['type' => 'pkcs-9-at-extensionRequest', 'value' => []]; $path = "$pth/$key/value/0"; } } break; } $extensions = &$this->subArray($root, $path, $create); if (!is_array($extensions)) { $false = false; return $false; } return $extensions; } /** * Remove an Extension * * @param string $id * @param string $path optional * @return bool */ private function removeExtensionHelper($id, $path = null) { $extensions = &$this->extensions($this->currentCert, $path); if (!is_array($extensions)) { return false; } $result = false; foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { unset($extensions[$key]); $result = true; } } $extensions = array_values($extensions); // fix for https://bugs.php.net/75433 affecting PHP 7.2 if (!isset($extensions[0])) { $extensions = array_splice($extensions, 0, 0); } return $result; } /** * Get an Extension * * Returns the extension if it exists and false if not * * @param string $id * @param array $cert optional * @param string $path optional * @return mixed */ private function getExtensionHelper($id, array $cert = null, $path = null) { $extensions = $this->extensions($cert, $path); if (!is_array($extensions)) { return false; } foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { return $value['extnValue']; } } return false; } /** * Returns a list of all extensions in use * * @param array $cert optional * @param string $path optional * @return array */ private function getExtensionsHelper(array $cert = null, $path = null) { $exts = $this->extensions($cert, $path); $extensions = []; if (is_array($exts)) { foreach ($exts as $extension) { $extensions[] = $extension['extnId']; } } return $extensions; } /** * Set an Extension * * @param string $id * @param mixed $value * @param bool $critical optional * @param bool $replace optional * @param string $path optional * @return bool */ private function setExtensionHelper($id, $value, $critical = false, $replace = true, $path = null) { $extensions = &$this->extensions($this->currentCert, $path, true); if (!is_array($extensions)) { return false; } $newext = ['extnId' => $id, 'critical' => $critical, 'extnValue' => $value]; foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { if (!$replace) { return false; } $extensions[$key] = $newext; return true; } } $extensions[] = $newext; return true; } /** * Remove a certificate, CSR or CRL Extension * * @param string $id * @return bool */ public function removeExtension($id) { return $this->removeExtensionHelper($id); } /** * Get a certificate, CSR or CRL Extension * * Returns the extension if it exists and false if not * * @param string $id * @param array $cert optional * @param string $path * @return mixed */ public function getExtension($id, array $cert = null, $path = null) { return $this->getExtensionHelper($id, $cert, $path); } /** * Returns a list of all extensions in use in certificate, CSR or CRL * * @param array $cert optional * @param string $path optional * @return array */ public function getExtensions(array $cert = null, $path = null) { return $this->getExtensionsHelper($cert, $path); } /** * Set a certificate, CSR or CRL Extension * * @param string $id * @param mixed $value * @param bool $critical optional * @param bool $replace optional * @return bool */ public function setExtension($id, $value, $critical = false, $replace = true) { return $this->setExtensionHelper($id, $value, $critical, $replace); } /** * Remove a CSR attribute. * * @param string $id * @param int $disposition optional * @return bool */ public function removeAttribute($id, $disposition = self::ATTR_ALL) { $attributes = &$this->subArray($this->currentCert, 'certificationRequestInfo/attributes'); if (!is_array($attributes)) { return false; } $result = false; foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: case $disposition == self::ATTR_REPLACE: return false; case $disposition >= $n: $disposition -= $n; break; case $disposition == self::ATTR_ALL: case $n == 1: unset($attributes[$key]); $result = true; break; default: unset($attributes[$key]['value'][$disposition]); $attributes[$key]['value'] = array_values($attributes[$key]['value']); $result = true; break; } if ($result && $disposition != self::ATTR_ALL) { break; } } } $attributes = array_values($attributes); return $result; } /** * Get a CSR attribute * * Returns the attribute if it exists and false if not * * @param string $id * @param int $disposition optional * @param array $csr optional * @return mixed */ public function getAttribute($id, $disposition = self::ATTR_ALL, array $csr = null) { if (empty($csr)) { $csr = $this->currentCert; } $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes'); if (!is_array($attributes)) { return false; } foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: case $disposition == self::ATTR_REPLACE: return false; case $disposition == self::ATTR_ALL: return $attribute['value']; case $disposition >= $n: $disposition -= $n; break; default: return $attribute['value'][$disposition]; } } } return false; } /** * Returns a list of all CSR attributes in use * * @param array $csr optional * @return array */ public function getAttributes(array $csr = null) { if (empty($csr)) { $csr = $this->currentCert; } $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes'); $attrs = []; if (is_array($attributes)) { foreach ($attributes as $attribute) { $attrs[] = $attribute['type']; } } return $attrs; } /** * Set a CSR attribute * * @param string $id * @param mixed $value * @param int $disposition optional * @return bool */ public function setAttribute($id, $value, $disposition = self::ATTR_ALL) { $attributes = &$this->subArray($this->currentCert, 'certificationRequestInfo/attributes', true); if (!is_array($attributes)) { return false; } switch ($disposition) { case self::ATTR_REPLACE: $disposition = self::ATTR_APPEND; // fall-through case self::ATTR_ALL: $this->removeAttribute($id); break; } foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: $last = $key; break; case $disposition >= $n: $disposition -= $n; break; default: $attributes[$key]['value'][$disposition] = $value; return true; } } } switch (true) { case $disposition >= 0: return false; case isset($last): $attributes[$last]['value'][] = $value; break; default: $attributes[] = ['type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value : [$value]]; break; } return true; } /** * Sets the subject key identifier * * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. * * @param string $value */ public function setKeyIdentifier($value) { if (empty($value)) { unset($this->currentKeyIdentifier); } else { $this->currentKeyIdentifier = $value; } } /** * Compute a public key identifier. * * Although key identifiers may be set to any unique value, this function * computes key identifiers from public key according to the two * recommended methods (4.2.1.2 RFC 3280). * Highly polymorphic: try to accept all possible forms of key: * - Key object * - \phpseclib3\File\X509 object with public or private key defined * - Certificate or CSR array * - \phpseclib3\File\ASN1\Element object * - PEM or DER string * * @param mixed $key optional * @param int $method optional * @return string binary key identifier */ public function computeKeyIdentifier($key = null, $method = 1) { if (is_null($key)) { $key = $this; } switch (true) { case is_string($key): break; case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); case !is_object($key): return false; case $key instanceof Element: // Assume the element is a bitstring-packed key. $decoded = ASN1::decodeBER($key->element); if (!$decoded) { return false; } $raw = ASN1::asn1map($decoded[0], ['type' => ASN1::TYPE_BIT_STRING]); if (empty($raw)) { return false; } // If the key is private, compute identifier from its corresponding public key. $key = PublicKeyLoader::load($raw); if ($key instanceof PrivateKey) { // If private. return $this->computeKeyIdentifier($key, $method); } $key = $raw; // Is a public key. break; case $key instanceof X509: if (isset($key->publicKey)) { return $this->computeKeyIdentifier($key->publicKey, $method); } if (isset($key->privateKey)) { return $this->computeKeyIdentifier($key->privateKey, $method); } if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { return $this->computeKeyIdentifier($key->currentCert, $method); } return false; default: // Should be a key object (i.e.: \phpseclib3\Crypt\RSA). $key = $key->getPublicKey(); break; } // If in PEM format, convert to binary. $key = ASN1::extractBER($key); // Now we have the key string: compute its sha-1 sum. $hash = new Hash('sha1'); $hash = $hash->hash($key); if ($method == 2) { $hash = substr($hash, -8); $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); } return $hash; } /** * Format a public key as appropriate * * @return array|false */ private function formatSubjectPublicKey() { $format = $this->publicKey instanceof RSA && ($this->publicKey->getPadding() & RSA::SIGNATURE_PSS) ? 'PSS' : 'PKCS8'; $publicKey = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->toString($format))); $decoded = ASN1::decodeBER($publicKey); if (!$decoded) { return false; } $mapped = ASN1::asn1map($decoded[0], Maps\SubjectPublicKeyInfo::MAP); if (!is_array($mapped)) { return false; } $mapped['subjectPublicKey'] = $this->publicKey->toString($format); return $mapped; } /** * Set the domain name's which the cert is to be valid for * * @param mixed ...$domains * @return void */ public function setDomain(...$domains) { $this->domains = $domains; $this->removeDNProp('id-at-commonName'); $this->setDNProp('id-at-commonName', $this->domains[0]); } /** * Set the IP Addresses's which the cert is to be valid for * * @param mixed[] ...$ipAddresses */ public function setIPAddress(...$ipAddresses) { $this->ipAddresses = $ipAddresses; /* if (!isset($this->domains)) { $this->removeDNProp('id-at-commonName'); $this->setDNProp('id-at-commonName', $this->ipAddresses[0]); } */ } /** * Helper function to build domain array * * @param string $domain * @return array */ private static function dnsName($domain) { return ['dNSName' => $domain]; } /** * Helper function to build IP Address array * * (IPv6 is not currently supported) * * @param string $address * @return array */ private function iPAddress($address) { return ['iPAddress' => $address]; } /** * Get the index of a revoked certificate. * * @param array $rclist * @param string $serial * @param bool $create optional * @return int|false */ private function revokedCertificate(array &$rclist, $serial, $create = false) { $serial = new BigInteger($serial); foreach ($rclist as $i => $rc) { if (!($serial->compare($rc['userCertificate']))) { return $i; } } if (!$create) { return false; } $i = count($rclist); $revocationDate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get())); $rclist[] = ['userCertificate' => $serial, 'revocationDate' => $this->timeField($revocationDate->format('D, d M Y H:i:s O'))]; return $i; } /** * Revoke a certificate. * * @param string $serial * @param string $date optional * @return bool */ public function revoke($serial, $date = null) { if (isset($this->currentCert['tbsCertList'])) { if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if ($this->revokedCertificate($rclist, $serial) === false) { // If not yet revoked if (($i = $this->revokedCertificate($rclist, $serial, true)) !== false) { if (!empty($date)) { $rclist[$i]['revocationDate'] = $this->timeField($date); } return true; } } } } return false; } /** * Unrevoke a certificate. * * @param string $serial * @return bool */ public function unrevoke($serial) { if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->revokedCertificate($rclist, $serial)) !== false) { unset($rclist[$i]); $rclist = array_values($rclist); return true; } } return false; } /** * Get a revoked certificate. * * @param string $serial * @return mixed */ public function getRevoked($serial) { if (is_array($rclist = $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->revokedCertificate($rclist, $serial)) !== false) { return $rclist[$i]; } } return false; } /** * List revoked certificates * * @param array $crl optional * @return array|bool */ public function listRevoked(array $crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (!isset($crl['tbsCertList'])) { return false; } $result = []; if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { foreach ($rclist as $rc) { $result[] = $rc['userCertificate']->toString(); } } return $result; } /** * Remove a Revoked Certificate Extension * * @param string $serial * @param string $id * @return bool */ public function removeRevokedCertificateExtension($serial, $id) { if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->revokedCertificate($rclist, $serial)) !== false) { return $this->removeExtensionHelper($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } /** * Get a Revoked Certificate Extension * * Returns the extension if it exists and false if not * * @param string $serial * @param string $id * @param array $crl optional * @return mixed */ public function getRevokedCertificateExtension($serial, $id, array $crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->revokedCertificate($rclist, $serial)) !== false) { return $this->getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } /** * Returns a list of all extensions in use for a given revoked certificate * * @param string $serial * @param array $crl optional * @return array|bool */ public function getRevokedCertificateExtensions($serial, array $crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->revokedCertificate($rclist, $serial)) !== false) { return $this->getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } /** * Set a Revoked Certificate Extension * * @param string $serial * @param string $id * @param mixed $value * @param bool $critical optional * @param bool $replace optional * @return bool */ public function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) { if (isset($this->currentCert['tbsCertList'])) { if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if (($i = $this->revokedCertificate($rclist, $serial, true)) !== false) { return $this->setExtensionHelper($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } } return false; } /** * Register the mapping for a custom/unsupported extension. * * @param string $id * @param array $mapping */ public static function registerExtension($id, array $mapping) { if (isset(self::$extensions[$id]) && self::$extensions[$id] !== $mapping) { throw new \RuntimeException( 'Extension ' . $id . ' has already been defined with a different mapping.' ); } self::$extensions[$id] = $mapping; } /** * Register the mapping for a custom/unsupported extension. * * @param string $id * * @return array|null */ public static function getRegisteredExtension($id) { return isset(self::$extensions[$id]) ? self::$extensions[$id] : null; } /** * Register the mapping for a custom/unsupported extension. * * @param string $id * @param mixed $value * @param bool $critical * @param bool $replace */ public function setExtensionValue($id, $value, $critical = false, $replace = false) { $this->extensionValues[$id] = compact('critical', 'replace', 'value'); } } PK2A#]O��@JJ:vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.phpnu�[���<?php /** * ASN.1 Raw Element * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1; /** * ASN.1 Raw Element * * An ASN.1 ANY mapping will return an ASN1\Element object. Use of this object * will also bypass the normal encoding rules in ASN1::encodeDER() * * @author Jim Wigginton <terrafrost@php.net> */ class Element { /** * Raw element value * * @var string */ public $element; /** * Constructor * * @param string $encoded * @return \phpseclib3\File\ASN1\Element */ public function __construct($encoded) { $this->element = $encoded; } } PK2A#]�M0eeEvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.phpnu�[���<?php /** * PostalAddress * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PostalAddress * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PostalAddress { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'optional' => true, 'min' => 1, 'max' => -1, 'children' => DirectoryString::MAP ]; } PK2A#]�>�uKvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.phpnu�[���<?php /** * HoldInstructionCode * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * HoldInstructionCode * * @author Jim Wigginton <terrafrost@php.net> */ abstract class HoldInstructionCode { const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]; } PK2A#]�贗��Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.phpnu�[���<?php /** * Certificate * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Certificate * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Certificate { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'tbsCertificate' => TBSCertificate::MAP, 'signatureAlgorithm' => AlgorithmIdentifier::MAP, 'signature' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]��҅]]Kvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.phpnu�[���<?php /** * CertificatePolicies * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertificatePolicies * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificatePolicies { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => PolicyInformation::MAP ]; } PK2A#]�n����Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.phpnu�[���<?php /** * BaseDistance * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * BaseDistance * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BaseDistance { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]qt���Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.phpnu�[���<?php /** * NetworkAddress * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * NetworkAddress * * @author Jim Wigginton <terrafrost@php.net> */ abstract class NetworkAddress { const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING]; } PK2A#]��u�\\Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.phpnu�[���<?php /** * TBSCertList * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * TBSCertList * * @author Jim Wigginton <terrafrost@php.net> */ abstract class TBSCertList { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => ['v1', 'v2', 'v3'], 'optional' => true, 'default' => 'v2' ], 'signature' => AlgorithmIdentifier::MAP, 'issuer' => Name::MAP, 'thisUpdate' => Time::MAP, 'nextUpdate' => [ 'optional' => true ] + Time::MAP, 'revokedCertificates' => [ 'type' => ASN1::TYPE_SEQUENCE, 'optional' => true, 'min' => 0, 'max' => -1, 'children' => RevokedCertificate::MAP ], 'crlExtensions' => [ 'constant' => 0, 'optional' => true, 'explicit' => true ] + Extensions::MAP ] ]; } PK2A#]|�[���Pvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.phpnu�[���<?php /** * AdministrationDomainName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AdministrationDomainName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AdministrationDomainName { const MAP = [ 'type' => ASN1::TYPE_CHOICE, // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC 'class' => ASN1::CLASS_APPLICATION, 'cast' => 2, 'children' => [ 'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING], 'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING] ] ]; } PK2A#]�P�nRR@vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.phpnu�[���<?php /** * Validity * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Validity * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Validity { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'notBefore' => Time::MAP, 'notAfter' => Time::MAP ] ]; } PK2A#]�ϒ��Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.phpnu�[���<?php /** * PrivateKeyInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PrivateKeyInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PrivateKeyInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => ['v1'] ], 'privateKeyAlgorithm' => AlgorithmIdentifier::MAP, 'privateKey' => PrivateKey::MAP, 'attributes' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + Attributes::MAP ] ]; } PK2A#]�W��Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.phpnu�[���<?php /** * OneAsymmetricKey * * See https://tools.ietf.org/html/rfc5958 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * OneAsymmetricKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OneAsymmetricKey { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => ['v1', 'v2'] ], 'privateKeyAlgorithm' => AlgorithmIdentifier::MAP, 'privateKey' => PrivateKey::MAP, 'attributes' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + Attributes::MAP, 'publicKey' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + PublicKey::MAP ] ]; } PK2A#]��0���Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.phpnu�[���<?php /** * DSAParams * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DSAParams * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DSAParams { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'p' => ['type' => ASN1::TYPE_INTEGER], 'q' => ['type' => ASN1::TYPE_INTEGER], 'g' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]� �$>>Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.phpnu�[���<?php /** * BasicConstraints * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * BasicConstraints * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BasicConstraints { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'cA' => [ 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ], 'pathLenConstraint' => [ 'type' => ASN1::TYPE_INTEGER, 'optional' => true ] ] ]; } PK2A#]�ZP��Svendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.phpnu�[���<?php /** * SignedPublicKeyAndChallenge * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * SignedPublicKeyAndChallenge * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SignedPublicKeyAndChallenge { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'publicKeyAndChallenge' => PublicKeyAndChallenge::MAP, 'signatureAlgorithm' => AlgorithmIdentifier::MAP, 'signature' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]�ȴ��Jvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.phpnu�[���<?php /** * TerminalIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * TerminalIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class TerminalIdentifier { const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING]; } PK2A#]ǔ4iiOvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.phpnu�[���<?php /** * SubjectInfoAccessSyntax * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * SubjectInfoAccessSyntax * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SubjectInfoAccessSyntax { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => AccessDescription::MAP ]; } PK2A#]xR�wEvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.phpnu�[���<?php /** * RSAPrivateKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RSAPrivateKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RSAPrivateKey { // version must be multi if otherPrimeInfos present const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => ['two-prime', 'multi'] ], 'modulus' => ['type' => ASN1::TYPE_INTEGER], // n 'publicExponent' => ['type' => ASN1::TYPE_INTEGER], // e 'privateExponent' => ['type' => ASN1::TYPE_INTEGER], // d 'prime1' => ['type' => ASN1::TYPE_INTEGER], // p 'prime2' => ['type' => ASN1::TYPE_INTEGER], // q 'exponent1' => ['type' => ASN1::TYPE_INTEGER], // d mod (p-1) 'exponent2' => ['type' => ASN1::TYPE_INTEGER], // d mod (q-1) 'coefficient' => ['type' => ASN1::TYPE_INTEGER], // (inverse of q) mod p 'otherPrimeInfos' => OtherPrimeInfos::MAP + ['optional' => true] ] ]; } PK2A#]�Z��WWFvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.phpnu�[���<?php /** * TBSCertificate * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * TBSCertificate * * @author Jim Wigginton <terrafrost@php.net> */ abstract class TBSCertificate { // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ // technically, default implies optional, but we'll define it as being optional, none-the-less, just to // reenforce that fact 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'constant' => 0, 'optional' => true, 'explicit' => true, 'mapping' => ['v1', 'v2', 'v3'], 'default' => 'v1' ], 'serialNumber' => CertificateSerialNumber::MAP, 'signature' => AlgorithmIdentifier::MAP, 'issuer' => Name::MAP, 'validity' => Validity::MAP, 'subject' => Name::MAP, 'subjectPublicKeyInfo' => SubjectPublicKeyInfo::MAP, // implicit means that the T in the TLV structure is to be rewritten, regardless of the type 'issuerUniqueID' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + UniqueIdentifier::MAP, 'subjectUniqueID' => [ 'constant' => 2, 'optional' => true, 'implicit' => true ] + UniqueIdentifier::MAP, // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if // it's not IMPLICIT, it's EXPLICIT 'extensions' => [ 'constant' => 3, 'optional' => true, 'explicit' => true ] + Extensions::MAP ] ]; } PK2A#]$s}��Mvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.phpnu�[���<?php /** * PrivateKeyUsagePeriod * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PrivateKeyUsagePeriod * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PrivateKeyUsagePeriod { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'notBefore' => [ 'constant' => 0, 'optional' => true, 'implicit' => true, 'type' => ASN1::TYPE_GENERALIZED_TIME], 'notAfter' => [ 'constant' => 1, 'optional' => true, 'implicit' => true, 'type' => ASN1::TYPE_GENERALIZED_TIME] ] ]; } PK2A#]��Ovendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.phpnu�[���<?php /** * CertificateSerialNumber * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertificateSerialNumber * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificateSerialNumber { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]�����Uvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.phpnu�[���<?php /** * BuiltInDomainDefinedAttribute * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * BuiltInDomainDefinedAttribute * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BuiltInDomainDefinedAttribute { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'type' => ['type' => ASN1::TYPE_PRINTABLE_STRING], 'value' => ['type' => ASN1::TYPE_PRINTABLE_STRING] ] ]; } PK2A#]�U��=vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.phpnu�[���<?php /** * Curve * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Curve * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Curve { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'a' => FieldElement::MAP, 'b' => FieldElement::MAP, 'seed' => [ 'type' => ASN1::TYPE_BIT_STRING, 'optional' => true ] ] ]; } PK2A#]���yy<vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.phpnu�[���<?php /** * Time * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Time * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Time { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'utcTime' => ['type' => ASN1::TYPE_UTC_TIME], 'generalTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME] ] ]; } PK2A#]�O���Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.phpnu�[���<?php /** * SubjectAltName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; /** * SubjectAltName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SubjectAltName { const MAP = GeneralNames::MAP; } PK2A#]��0���Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.phpnu�[���<?php /** * Trinomial * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Trinomial * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Trinomial { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]mX�A Bvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.phpnu�[���<?php /** * UserNotice * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * UserNotice * * @author Jim Wigginton <terrafrost@php.net> */ abstract class UserNotice { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'noticeRef' => [ 'optional' => true, 'implicit' => true ] + NoticeReference::MAP, 'explicitText' => [ 'optional' => true, 'implicit' => true ] + DisplayText::MAP ] ]; } PK2A#]qƃ���?vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.phpnu�[���<?php /** * FieldID * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * FieldID * * @author Jim Wigginton <terrafrost@php.net> */ abstract class FieldID { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'fieldType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'parameters' => [ 'type' => ASN1::TYPE_ANY, 'optional' => true ] ] ]; } PK2A#]���{JJJvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.phpnu�[���<?php /** * netscape_cert_type * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * netscape_cert_type * * mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html> * * @author Jim Wigginton <terrafrost@php.net> */ abstract class netscape_cert_type { const MAP = [ 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => [ 'SSLClient', 'SSLServer', 'Email', 'ObjectSigning', 'Reserved', 'SSLCA', 'EmailCA', 'ObjectSigningCA' ] ]; } PK2A#]�P*���Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.phpnu�[���<?php /** * CertPolicyId * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertPolicyId * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertPolicyId { const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]; } PK2A#]��0צ�Ovendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.phpnu�[���<?php /** * EncryptedPrivateKeyInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * EncryptedPrivateKeyInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EncryptedPrivateKeyInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'encryptionAlgorithm' => AlgorithmIdentifier::MAP, 'encryptedData' => EncryptedData::MAP ] ]; } PK2A#]_�����Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.phpnu�[���<?php /** * MaskGenAglorithm * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; /** * MaskGenAglorithm * * @author Jim Wigginton <terrafrost@php.net> */ abstract class MaskGenAlgorithm { const MAP = AlgorithmIdentifier::MAP; } PK2A#]�[:g��Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.phpnu�[���<?php /** * FieldElement * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * FieldElement * * @author Jim Wigginton <terrafrost@php.net> */ abstract class FieldElement { const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; } PK2A#] ,�ooQvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.phpnu�[���<?php /** * AuthorityInfoAccessSyntax * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AuthorityInfoAccessSyntax * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AuthorityInfoAccessSyntax { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => AccessDescription::MAP ]; } PK2A#]����Mvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.phpnu�[���<?php /** * PublicKeyAndChallenge * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PublicKeyAndChallenge * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PublicKeyAndChallenge { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'spki' => SubjectPublicKeyInfo::MAP, 'challenge' => ['type' => ASN1::TYPE_IA5_STRING] ] ]; } PK2A#]��)�Gvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.phpnu�[���<?php /** * RC2CBCParameter * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RC2CBCParameter * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RC2CBCParameter { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'rc2ParametersVersion' => [ 'type' => ASN1::TYPE_INTEGER, 'optional' => true ], 'iv' => ['type' => ASN1::TYPE_OCTET_STRING] ] ]; } PK2A#]|ZkMvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.phpnu�[���<?php /** * NumericUserIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * NumericUserIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class NumericUserIdentifier { const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING]; } PK2A#]��a���Mvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.phpnu�[���<?php /** * DistributionPointName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DistributionPointName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DistributionPointName { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'fullName' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + GeneralNames::MAP, 'nameRelativeToCRLIssuer' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + RelativeDistinguishedName::MAP ] ]; } PK2A#]��"P��Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.phpnu�[���<?php /** * RSAPublicKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RSAPublicKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RSAPublicKey { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'modulus' => ['type' => ASN1::TYPE_INTEGER], 'publicExponent' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]h����Qvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.phpnu�[���<?php /** * RelativeDistinguishedName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RelativeDistinguishedName * * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, * but they can be useful at times when either there is no unique attribute in the entry or you * want to ensure that the entry's DN contains some useful identifying information. * * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RelativeDistinguishedName { const MAP = [ 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => AttributeTypeAndValue::MAP ]; } PK2A#]ߖg%||Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.phpnu�[���<?php /** * EcdsaSigValue * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * EcdsaSigValue * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EcdsaSigValue { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'r' => ['type' => ASN1::TYPE_INTEGER], 's' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]�*<��Mvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.phpnu�[���<?php /** * AttributeTypeAndValue * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AttributeTypeAndValue * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AttributeTypeAndValue { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'type' => AttributeType::MAP, 'value' => AttributeValue::MAP ] ]; } PK2A#]S���Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.phpnu�[���<?php /** * PBMAC1params * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PBMAC1params * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PBMAC1params { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'keyDerivationFunc' => AlgorithmIdentifier::MAP, 'messageAuthScheme' => AlgorithmIdentifier::MAP ] ]; } PK2A#]�CMMIvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.phpnu�[���<?php /** * PolicyInformation * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PolicyInformation * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PolicyInformation { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'policyIdentifier' => CertPolicyId::MAP, 'policyQualifiers' => [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'optional' => true, 'children' => PolicyQualifierInfo::MAP ] ] ]; } PK2A#]���f��>vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.phpnu�[���<?php /** * CPSuri * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CPSuri * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CPSuri { const MAP = ['type' => ASN1::TYPE_IA5_STRING]; } PK2A#]��p���Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.phpnu�[���<?php /** * EncryptedData * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * EncryptedData * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EncryptedData { const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; } PK2A#]��6��Gvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.phpnu�[���<?php /** * CertificateList * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertificateList * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificateList { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'tbsCertList' => TBSCertList::MAP, 'signatureAlgorithm' => AlgorithmIdentifier::MAP, 'signature' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]np����Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.phpnu�[���<?php /** * KeyPurposeId * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * KeyPurposeId * * @author Jim Wigginton <terrafrost@php.net> */ abstract class KeyPurposeId { const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]; } PK2A#]�o�o��Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.phpnu�[���<?php /** * GeneralSubtree * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * GeneralSubtree * * @author Jim Wigginton <terrafrost@php.net> */ abstract class GeneralSubtree { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'base' => GeneralName::MAP, 'minimum' => [ 'constant' => 0, 'optional' => true, 'implicit' => true, 'default' => '0' ] + BaseDistance::MAP, 'maximum' => [ 'constant' => 1, 'optional' => true, 'implicit' => true, ] + BaseDistance::MAP ] ]; } PK2A#]�/�w��Qvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.phpnu�[���<?php /** * BuiltInStandardAttributes * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * BuiltInStandardAttributes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BuiltInStandardAttributes { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'country-name' => ['optional' => true] + CountryName::MAP, 'administration-domain-name' => ['optional' => true] + AdministrationDomainName::MAP, 'network-address' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + NetworkAddress::MAP, 'terminal-identifier' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + TerminalIdentifier::MAP, 'private-domain-name' => [ 'constant' => 2, 'optional' => true, 'explicit' => true ] + PrivateDomainName::MAP, 'organization-name' => [ 'constant' => 3, 'optional' => true, 'implicit' => true ] + OrganizationName::MAP, 'numeric-user-identifier' => [ 'constant' => 4, 'optional' => true, 'implicit' => true ] + NumericUserIdentifier::MAP, 'personal-name' => [ 'constant' => 5, 'optional' => true, 'implicit' => true ] + PersonalName::MAP, 'organizational-unit-names' => [ 'constant' => 6, 'optional' => true, 'implicit' => true ] + OrganizationalUnitNames::MAP ] ]; } PK2A#]�!A���Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.phpnu�[���<?php /** * AccessDescription * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AccessDescription * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AccessDescription { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'accessMethod' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'accessLocation' => GeneralName::MAP ] ]; } PK2A#]��զ��Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.phpnu�[���<?php /** * CountryName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CountryName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CountryName { const MAP = [ 'type' => ASN1::TYPE_CHOICE, // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC 'class' => ASN1::CLASS_APPLICATION, 'cast' => 1, 'children' => [ 'x121-dcc-code' => ['type' => ASN1::TYPE_NUMERIC_STRING], 'iso-3166-alpha2-code' => ['type' => ASN1::TYPE_PRINTABLE_STRING] ] ]; } PK2A#]7hܧrrGvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.phpnu�[���<?php /** * DirectoryString * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DirectoryString * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DirectoryString { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'teletexString' => ['type' => ASN1::TYPE_TELETEX_STRING], 'printableString' => ['type' => ASN1::TYPE_PRINTABLE_STRING], 'universalString' => ['type' => ASN1::TYPE_UNIVERSAL_STRING], 'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING], 'bmpString' => ['type' => ASN1::TYPE_BMP_STRING] ] ]; } PK2A#]ߣ�A Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.phpnu�[���<?php /** * AnotherName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AnotherName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AnotherName { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'type-id' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'value' => [ 'type' => ASN1::TYPE_ANY, 'constant' => 0, 'optional' => true, 'explicit' => true ] ] ]; } PK2A#]��7���Bvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.phpnu�[���<?php /** * Extensions * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Extensions * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Extensions { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, // technically, it's MAX, but we'll assume anything < 0 is MAX 'max' => -1, // if 'children' isn't an array then 'min' and 'max' must be defined 'children' => Extension::MAP ]; } PK2A#]w�8u��Ovendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.phpnu�[���<?php /** * OrganizationalUnitNames * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * OrganizationalUnitNames * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OrganizationalUnitNames { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-organizational-units 'children' => ['type' => ASN1::TYPE_PRINTABLE_STRING] ]; } PK2A#]n����Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.phpnu�[���<?php /** * PublicKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PublicKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PublicKey { const MAP = ['type' => ASN1::TYPE_BIT_STRING]; } PK2A#]O��5��Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.phpnu�[���<?php /** * IssuerAltName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; /** * IssuerAltName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class IssuerAltName { const MAP = GeneralNames::MAP; } PK2A#]s�މ��Kvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.phpnu�[���<?php /** * PolicyQualifierInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PolicyQualifierInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PolicyQualifierInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'policyQualifierId' => PolicyQualifierId::MAP, 'qualifier' => ['type' => ASN1::TYPE_ANY] ] ]; } PK2A#]"5�,��Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.phpnu�[���<?php /** * DSAPublicKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DSAPublicKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DSAPublicKey { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]ǓR��Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.phpnu�[���<?php /** * OrganizationName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * OrganizationName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OrganizationName { const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING]; } PK2A#]��(���Kvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.phpnu�[���<?php /** * AlgorithmIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AlgorithmIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AlgorithmIdentifier { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'algorithm' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'parameters' => [ 'type' => ASN1::TYPE_ANY, 'optional' => true ] ] ]; } PK2A#]��8]SSNvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.phpnu�[���<?php /** * AuthorityKeyIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AuthorityKeyIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AuthorityKeyIdentifier { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'keyIdentifier' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + KeyIdentifier::MAP, 'authorityCertIssuer' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + GeneralNames::MAP, 'authorityCertSerialNumber' => [ 'constant' => 2, 'optional' => true, 'implicit' => true ] + CertificateSerialNumber::MAP ] ]; } PK2A#]ߠq�NNGvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.phpnu�[���<?php /** * GeneralSubtrees * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * GeneralSubtrees * * @author Jim Wigginton <terrafrost@php.net> */ abstract class GeneralSubtrees { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => GeneralSubtree::MAP ]; } PK2A#]Z�?���Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.phpnu�[���<?php /** * netscape_comment * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * netscape_comment * * @author Jim Wigginton <terrafrost@php.net> */ abstract class netscape_comment { const MAP = ['type' => ASN1::TYPE_IA5_STRING]; } PK2A#]S.y�^^Pvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.phpnu�[���<?php /** * IssuingDistributionPoint * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * IssuingDistributionPoint * * @author Jim Wigginton <terrafrost@php.net> */ abstract class IssuingDistributionPoint { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'distributionPoint' => [ 'constant' => 0, 'optional' => true, 'explicit' => true ] + DistributionPointName::MAP, 'onlyContainsUserCerts' => [ 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 1, 'optional' => true, 'default' => false, 'implicit' => true ], 'onlyContainsCACerts' => [ 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 2, 'optional' => true, 'default' => false, 'implicit' => true ], 'onlySomeReasons' => [ 'constant' => 3, 'optional' => true, 'implicit' => true ] + ReasonFlags::MAP, 'indirectCRL' => [ 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 4, 'optional' => true, 'default' => false, 'implicit' => true ], 'onlyContainsAttributeCerts' => [ 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 5, 'optional' => true, 'default' => false, 'implicit' => true ] ] ]; } PK2A#]'J��Bvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.phpnu�[���<?php /** * PrivateKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PrivateKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PrivateKey { const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; } PK2A#]L����Gvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.phpnu�[���<?php /** * NameConstraints * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * NameConstraints * * @author Jim Wigginton <terrafrost@php.net> */ abstract class NameConstraints { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'permittedSubtrees' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + GeneralSubtrees::MAP, 'excludedSubtrees' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + GeneralSubtrees::MAP ] ]; } PK2A#]�����Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.phpnu�[���<?php /** * GeneralName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * GeneralName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class GeneralName { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'otherName' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + AnotherName::MAP, 'rfc822Name' => [ 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ], 'dNSName' => [ 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ], 'x400Address' => [ 'constant' => 3, 'optional' => true, 'implicit' => true ] + ORAddress::MAP, 'directoryName' => [ 'constant' => 4, 'optional' => true, 'explicit' => true ] + Name::MAP, 'ediPartyName' => [ 'constant' => 5, 'optional' => true, 'implicit' => true ] + EDIPartyName::MAP, 'uniformResourceIdentifier' => [ 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 6, 'optional' => true, 'implicit' => true ], 'iPAddress' => [ 'type' => ASN1::TYPE_OCTET_STRING, 'constant' => 7, 'optional' => true, 'implicit' => true ], 'registeredID' => [ 'type' => ASN1::TYPE_OBJECT_IDENTIFIER, 'constant' => 8, 'optional' => true, 'implicit' => true ] ] ]; } PK2A#]�݈Y++Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.phpnu�[���<?php /** * Extension * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Extension * * A certificate using system MUST reject the certificate if it encounters * a critical extension it does not recognize; however, a non-critical * extension may be ignored if it is not recognized. * * http://tools.ietf.org/html/rfc5280#section-4.2 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Extension { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'extnId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'critical' => [ 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ], 'extnValue' => ['type' => ASN1::TYPE_OCTET_STRING] ] ]; } PK2A#]��c5��Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.phpnu�[���<?php /** * PolicyQualifierId * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PolicyQualifierId * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PolicyQualifierId { const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]; } PK2A#]�$I���Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.phpnu�[���<?php /** * HashAglorithm * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; /** * HashAglorithm * * @author Jim Wigginton <terrafrost@php.net> */ abstract class HashAlgorithm { const MAP = AlgorithmIdentifier::MAP; } PK2A#]z��7bbAvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.phpnu�[���<?php /** * CRLReason * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CRLReason * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CRLReason { const MAP = [ 'type' => ASN1::TYPE_ENUMERATED, 'mapping' => [ 'unspecified', 'keyCompromise', 'cACompromise', 'affiliationChanged', 'superseded', 'cessationOfOperation', 'certificateHold', // Value 7 is not used. 8 => 'removeFromCRL', 'privilegeWithdrawn', 'aACompromise' ] ]; } PK2A#](��&&Jvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.phpnu�[���<?php /** * Characteristic_two * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Characteristic_two * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Characteristic_two { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'm' => ['type' => ASN1::TYPE_INTEGER], // field size 2**m 'basis' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'parameters' => [ 'type' => ASN1::TYPE_ANY, 'optional' => true ] ] ]; } PK2A#]Z��Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.phpnu�[���<?php /** * PBKDF2params * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PBKDF2params * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PBKDF2params { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ // technically, this is a CHOICE in RFC2898 but the other "choice" is, currently, more of a placeholder // in the RFC 'salt' => ['type' => ASN1::TYPE_OCTET_STRING], 'iterationCount' => ['type' => ASN1::TYPE_INTEGER], 'keyLength' => [ 'type' => ASN1::TYPE_INTEGER, 'optional' => true ], 'prf' => AlgorithmIdentifier::MAP + ['optional' => true] ] ]; } PK2A#]���5��Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.phpnu�[���<?php /** * CertificateIssuer * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; /** * CertificateIssuer * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificateIssuer { const MAP = GeneralNames::MAP; } PK2A#]6�nj��Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.phpnu�[���<?php /** * InvalidityDate * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * InvalidityDate * * @author Jim Wigginton <terrafrost@php.net> */ abstract class InvalidityDate { const MAP = ['type' => ASN1::TYPE_GENERALIZED_TIME]; } PK2A#]��In((<vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.phpnu�[���<?php /** * Name * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Name * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Name { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'rdnSequence' => RDNSequence::MAP ] ]; } PK2A#]�1�NNEvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.phpnu�[���<?php /** * DSAPrivateKey * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DSAPrivateKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DSAPrivateKey { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => ['type' => ASN1::TYPE_INTEGER], 'p' => ['type' => ASN1::TYPE_INTEGER], 'q' => ['type' => ASN1::TYPE_INTEGER], 'g' => ['type' => ASN1::TYPE_INTEGER], 'y' => ['type' => ASN1::TYPE_INTEGER], 'x' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]ܥX<��Jvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.phpnu�[���<?php /** * ExtensionAttribute * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ExtensionAttribute * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ExtensionAttribute { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'extension-attribute-type' => [ 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ], 'extension-attribute-value' => [ 'type' => ASN1::TYPE_ANY, 'constant' => 1, 'optional' => true, 'explicit' => true ] ] ]; } PK2A#]�>a��Fvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.phpnu�[���<?php /** * AttributeValue * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AttributeValue * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AttributeValue { const MAP = ['type' => ASN1::TYPE_ANY]; } PK2A#]�͖""Gvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.phpnu�[���<?php /** * NoticeReference * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * NoticeReference * * @author Jim Wigginton <terrafrost@php.net> */ abstract class NoticeReference { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'organization' => DisplayText::MAP, 'noticeNumbers' => [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 200, 'children' => ['type' => ASN1::TYPE_INTEGER] ] ] ]; } PK2A#]�5��vvCvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.phpnu�[���<?php /** * DssSigValue * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DssSigValue * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DssSigValue { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'r' => ['type' => ASN1::TYPE_INTEGER], 's' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]�A�v��Pvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.phpnu�[���<?php /** * CertificationRequestInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertificationRequestInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificationRequestInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => ['v1'] ], 'subject' => Name::MAP, 'subjectPKInfo' => SubjectPublicKeyInfo::MAP, 'attributes' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + Attributes::MAP, ] ]; } PK2A#][iD��Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.phpnu�[���<?php /** * PBES2params * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PBES2params * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PBES2params { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'keyDerivationFunc' => AlgorithmIdentifier::MAP, 'encryptionScheme' => AlgorithmIdentifier::MAP ] ]; } PK2A#]�R�|��Gvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.phpnu�[���<?php /** * OtherPrimeInfos * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * OtherPrimeInfos * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OtherPrimeInfos { // version must be multi if otherPrimeInfos present const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => OtherPrimeInfo::MAP ]; } PK2A#]��_�ttDvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.phpnu�[���<?php /** * ECParameters * * From: https://tools.ietf.org/html/rfc5915 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ECParameters * * ECParameters ::= CHOICE { * namedCurve OBJECT IDENTIFIER * -- implicitCurve NULL * -- specifiedCurve SpecifiedECDomain * } * -- implicitCurve and specifiedCurve MUST NOT be used in PKIX. * -- Details for SpecifiedECDomain can be found in [X9.62]. * -- Any future additions to this CHOICE should be coordinated * -- with ANSI X9. * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ECParameters { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'namedCurve' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'implicitCurve' => ['type' => ASN1::TYPE_NULL], 'specifiedCurve' => SpecifiedECDomain::MAP ] ]; } PK2A#]�:�VVCvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.phpnu�[���<?php /** * DHParameter * * From: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf#page=6 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DHParameter * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DHParameter { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'prime' => ['type' => ASN1::TYPE_INTEGER], 'base' => ['type' => ASN1::TYPE_INTEGER], 'privateValueLength' => [ 'type' => ASN1::TYPE_INTEGER, 'optional' => true ] ] ]; } PK2A#]�����Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.phpnu�[���<?php /** * CRLNumber * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CRLNumber * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CRLNumber { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]�$U@vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.phpnu�[���<?php /** * KeyUsage * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * KeyUsage * * @author Jim Wigginton <terrafrost@php.net> */ abstract class KeyUsage { const MAP = [ 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => [ 'digitalSignature', 'nonRepudiation', 'keyEncipherment', 'dataEncipherment', 'keyAgreement', 'keyCertSign', 'cRLSign', 'encipherOnly', 'decipherOnly' ] ]; } PK2A#]�e��Nvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.phpnu�[���<?php /** * netscape_ca_policy_url * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * netscape_ca_policy_url * * @author Jim Wigginton <terrafrost@php.net> */ abstract class netscape_ca_policy_url { const MAP = ['type' => ASN1::TYPE_IA5_STRING]; } PK2A#]%��q��Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.phpnu�[���<?php /** * PBEParameter * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PBEParameter * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PBEParameter { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'salt' => ['type' => ASN1::TYPE_OCTET_STRING], 'iterationCount' => ['type' => ASN1::TYPE_INTEGER] ] ]; } PK2A#]�]�9��Lvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.phpnu�[���<?php /** * SubjectPublicKeyInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * SubjectPublicKeyInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SubjectPublicKeyInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'algorithm' => AlgorithmIdentifier::MAP, 'subjectPublicKey' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]�5~��Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.phpnu�[���<?php /** * Attribute * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Attribute * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Attribute { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'type' => AttributeType::MAP, 'value' => [ 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => AttributeValue::MAP ] ] ]; } PK2A#]A2Z:��Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.phpnu�[���<?php /** * PKCS9String * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PKCS9String * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PKCS9String { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'ia5String' => ['type' => ASN1::TYPE_IA5_STRING], 'directoryString' => DirectoryString::MAP ] ]; } PK2A#]��j��Bvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.phpnu�[���<?php /** * DigestInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DigestInfo * * from https://tools.ietf.org/html/rfc2898#appendix-A.3 * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DigestInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'digestAlgorithm' => AlgorithmIdentifier::MAP, 'digest' => ['type' => ASN1::TYPE_OCTET_STRING] ] ]; } PK2A#]b78���Jvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.phpnu�[���<?php /** * RevokedCertificate * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RevokedCertificate * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RevokedCertificate { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'userCertificate' => CertificateSerialNumber::MAP, 'revocationDate' => Time::MAP, 'crlEntryExtensions' => [ 'optional' => true ] + Extensions::MAP ] ]; } PK2A#]F�]ũ�Vvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.phpnu�[���<?php /** * BuiltInDomainDefinedAttributes * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * BuiltInDomainDefinedAttributes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class BuiltInDomainDefinedAttributes { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-domain-defined-attributes 'children' => BuiltInDomainDefinedAttribute::MAP ]; } PK2A#]XQ���Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.phpnu�[���<?php /** * RSASSA_PSS_params * * As defined in https://tools.ietf.org/html/rfc4055#section-3.1 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RSASSA_PSS_params * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RSASSA_PSS_params { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'hashAlgorithm' => [ 'constant' => 0, 'optional' => true, 'explicit' => true, //'default' => 'sha1Identifier' ] + HashAlgorithm::MAP, 'maskGenAlgorithm' => [ 'constant' => 1, 'optional' => true, 'explicit' => true, //'default' => 'mgf1SHA1Identifier' ] + MaskGenAlgorithm::MAP, 'saltLength' => [ 'type' => ASN1::TYPE_INTEGER, 'constant' => 2, 'optional' => true, 'explicit' => true, 'default' => 20 ], 'trailerField' => [ 'type' => ASN1::TYPE_INTEGER, 'constant' => 3, 'optional' => true, 'explicit' => true, 'default' => 1 ] ] ]; } PK2A#]����Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.phpnu�[���<?php /** * AttributeType * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * AttributeType * * @author Jim Wigginton <terrafrost@php.net> */ abstract class AttributeType { const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]; } PK2A#]��[jjRvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.phpnu�[���<?php /** * SubjectDirectoryAttributes * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * SubjectDirectoryAttributes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SubjectDirectoryAttributes { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => Attribute::MAP ]; } PK2A#]r�i--Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.phpnu�[���<?php /** * PublicKeyInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PublicKeyInfo * * this format is not formally defined anywhere but is none-the-less the form you * get when you do "openssl rsa -in private.pem -outform PEM -pubout" * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PublicKeyInfo { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'publicKeyAlgorithm' => AlgorithmIdentifier::MAP, 'publicKey' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]��CBBDvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.phpnu�[���<?php /** * GeneralNames * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * GeneralNames * * @author Jim Wigginton <terrafrost@php.net> */ abstract class GeneralNames { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => GeneralName::MAP ]; } PK2A#]Bʑ7��Hvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.phpnu�[���<?php /** * UniqueIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * UniqueIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class UniqueIdentifier { const MAP = ['type' => ASN1::TYPE_BIT_STRING]; } PK2A#]f�{Avendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.phpnu�[���<?php /** * ORAddress * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ORAddress * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ORAddress { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'built-in-standard-attributes' => BuiltInStandardAttributes::MAP, 'built-in-domain-defined-attributes' => ['optional' => true] + BuiltInDomainDefinedAttributes::MAP, 'extension-attributes' => ['optional' => true] + ExtensionAttributes::MAP ] ]; } PK2A#]��CCDvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.phpnu�[���<?php /** * PersonalName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PersonalName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PersonalName { const MAP = [ 'type' => ASN1::TYPE_SET, 'children' => [ 'surname' => [ 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ], 'given-name' => [ 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ], 'initials' => [ 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ], 'generation-qualifier' => [ 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 3, 'optional' => true, 'implicit' => true ] ] ]; } PK2A#]Og�~~Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.phpnu�[���<?php /** * ECPrivateKey * * From: https://tools.ietf.org/html/rfc5915 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ECPrivateKey * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ECPrivateKey { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => [1 => 'ecPrivkeyVer1'] ], 'privateKey' => ['type' => ASN1::TYPE_OCTET_STRING], 'parameters' => [ 'constant' => 0, 'optional' => true, 'explicit' => true ] + ECParameters::MAP, 'publicKey' => [ 'type' => ASN1::TYPE_BIT_STRING, 'constant' => 1, 'optional' => true, 'explicit' => true ] ] ]; } PK2A#]��J*Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.phpnu�[���<?php /** * ReasonFlags * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ReasonFlags * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ReasonFlags { const MAP = [ 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => [ 'unused', 'keyCompromise', 'cACompromise', 'affiliationChanged', 'superseded', 'cessationOfOperation', 'certificateHold', 'privilegeWithdrawn', 'aACompromise' ] ]; } PK2A#]V:22Dvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.phpnu�[���<?php /** * EDIPartyName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * EDIPartyName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class EDIPartyName { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'nameAssigner' => [ 'constant' => 0, 'optional' => true, 'implicit' => true ] + DirectoryString::MAP, // partyName is technically required but \phpseclib3\File\ASN1 doesn't currently support non-optional constants and // setting it to optional gets the job done in any event. 'partyName' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + DirectoryString::MAP ] ]; } PK2A#]��D��Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.phpnu�[���<?php /** * PrivateDomainName * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PrivateDomainName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PrivateDomainName { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING], 'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING] ] ]; } PK2A#]��6RFvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.phpnu�[���<?php /** * OtherPrimeInfo * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * OtherPrimeInfo * * @author Jim Wigginton <terrafrost@php.net> */ abstract class OtherPrimeInfo { // version must be multi if otherPrimeInfos present const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'prime' => ['type' => ASN1::TYPE_INTEGER], // ri 'exponent' => ['type' => ASN1::TYPE_INTEGER], // di 'coefficient' => ['type' => ASN1::TYPE_INTEGER] // ti ] ]; } PK2A#]RL�OXXIvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.phpnu�[���<?php /** * SpecifiedECDomain * * From: http://www.secg.org/sec1-v2.pdf#page=109 * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * SpecifiedECDomain * * @author Jim Wigginton <terrafrost@php.net> */ abstract class SpecifiedECDomain { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'version' => [ 'type' => ASN1::TYPE_INTEGER, 'mapping' => [1 => 'ecdpVer1', 'ecdpVer2', 'ecdpVer3'] ], 'fieldID' => FieldID::MAP, 'curve' => Curve::MAP, 'base' => ECPoint::MAP, 'order' => ['type' => ASN1::TYPE_INTEGER], 'cofactor' => [ 'type' => ASN1::TYPE_INTEGER, 'optional' => true ], 'hash' => ['optional' => true] + HashAlgorithm::MAP ] ]; } PK2A#]��d��?vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.phpnu�[���<?php /** * ECPoint * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ECPoint * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ECPoint { const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; } PK2A#]�S+Y55Bvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.phpnu�[���<?php /** * Attributes * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Attributes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Attributes { const MAP = [ 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => Attribute::MAP ]; } PK2A#])�����Lvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.phpnu�[���<?php /** * CertificationRequest * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CertificationRequest * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CertificationRequest { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'certificationRequestInfo' => CertificationRequestInfo::MAP, 'signatureAlgorithm' => AlgorithmIdentifier::MAP, 'signature' => ['type' => ASN1::TYPE_BIT_STRING] ] ]; } PK2A#]r��Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.phpnu�[���<?php /** * DisplayText * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DisplayText * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DisplayText { const MAP = [ 'type' => ASN1::TYPE_CHOICE, 'children' => [ 'ia5String' => ['type' => ASN1::TYPE_IA5_STRING], 'visibleString' => ['type' => ASN1::TYPE_VISIBLE_STRING], 'bmpString' => ['type' => ASN1::TYPE_BMP_STRING], 'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING] ] ]; } PK2A#]�^j��?vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.phpnu�[���<?php /** * Prime_p * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Prime_p * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Prime_p { const MAP = ['type' => ASN1::TYPE_INTEGER]; } PK2A#]�ǀ���Cvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.phpnu�[���<?php /** * Pentanomial * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * Pentanomial * * @author Jim Wigginton <terrafrost@php.net> */ abstract class Pentanomial { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'k1' => ['type' => ASN1::TYPE_INTEGER], // k1 > 0 'k2' => ['type' => ASN1::TYPE_INTEGER], // k2 > k1 'k3' => ['type' => ASN1::TYPE_INTEGER], // k3 > h2 ] ]; } PK2A#]��dRRIvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.phpnu�[���<?php /** * ExtKeyUsageSyntax * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ExtKeyUsageSyntax * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ExtKeyUsageSyntax { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => KeyPurposeId::MAP ]; } PK2A#]�;[���Evendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.phpnu�[���<?php /** * KeyIdentifier * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * KeyIdentifier * * @author Jim Wigginton <terrafrost@php.net> */ abstract class KeyIdentifier { const MAP = ['type' => ASN1::TYPE_OCTET_STRING]; } PK2A#]����((Ivendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.phpnu�[���<?php /** * DistributionPoint * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * DistributionPoint * * @author Jim Wigginton <terrafrost@php.net> */ abstract class DistributionPoint { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'distributionPoint' => [ 'constant' => 0, 'optional' => true, 'explicit' => true ] + DistributionPointName::MAP, 'reasons' => [ 'constant' => 1, 'optional' => true, 'implicit' => true ] + ReasonFlags::MAP, 'cRLIssuer' => [ 'constant' => 2, 'optional' => true, 'implicit' => true ] + GeneralNames::MAP ] ]; } PK2A#]Ыy`ccMvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.phpnu�[���<?php /** * CRLDistributionPoints * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * CRLDistributionPoints * * @author Jim Wigginton <terrafrost@php.net> */ abstract class CRLDistributionPoints { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => DistributionPoint::MAP ]; } PK2A#]>p�aCvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.phpnu�[���<?php /** * RDNSequence * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * RDNSequence * * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, * but they can be useful at times when either there is no unique attribute in the entry or you * want to ensure that the entry's DN contains some useful identifying information. * * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName * * @author Jim Wigginton <terrafrost@php.net> */ abstract class RDNSequence { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, // RDNSequence does not define a min or a max, which means it doesn't have one 'min' => 0, 'max' => -1, 'children' => RelativeDistinguishedName::MAP ]; } PK2A#] ��YuuKvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.phpnu�[���<?php /** * ExtensionAttributes * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * ExtensionAttributes * * @author Jim Wigginton <terrafrost@php.net> */ abstract class ExtensionAttributes { const MAP = [ 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => 256, // ub-extension-attributes 'children' => ExtensionAttribute::MAP ]; } PK2A#]��^wFvendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.phpnu�[���<?php /** * PolicyMappings * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2016 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File\ASN1\Maps; use phpseclib3\File\ASN1; /** * PolicyMappings * * @author Jim Wigginton <terrafrost@php.net> */ abstract class PolicyMappings { const MAP = [ 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => [ 'type' => ASN1::TYPE_SEQUENCE, 'children' => [ 'issuerDomainPolicy' => CertPolicyId::MAP, 'subjectDomainPolicy' => CertPolicyId::MAP ] ] ]; } PK2A#]֟a�NN2vendor/phpseclib/phpseclib/phpseclib/File/ANSI.phpnu�[���<?php /** * Pure-PHP ANSI Decoder * * PHP version 5 * * If you call read() in \phpseclib3\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back. * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC). They tell a * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what * color to display them in, etc. \phpseclib3\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\File; /** * Pure-PHP ANSI Decoder * * @author Jim Wigginton <terrafrost@php.net> */ class ANSI { /** * Max Width * * @var int */ private $max_x; /** * Max Height * * @var int */ private $max_y; /** * Max History * * @var int */ private $max_history; /** * History * * @var array */ private $history; /** * History Attributes * * @var array */ private $history_attrs; /** * Current Column * * @var int */ private $x; /** * Current Row * * @var int */ private $y; /** * Old Column * * @var int */ private $old_x; /** * Old Row * * @var int */ private $old_y; /** * An empty attribute cell * * @var object */ private $base_attr_cell; /** * The current attribute cell * * @var object */ private $attr_cell; /** * An empty attribute row * * @var array */ private $attr_row; /** * The current screen text * * @var list<string> */ private $screen; /** * The current screen attributes * * @var array */ private $attrs; /** * Current ANSI code * * @var string */ private $ansi; /** * Tokenization * * @var array */ private $tokenization; /** * Default Constructor. * * @return \phpseclib3\File\ANSI */ public function __construct() { $attr_cell = new \stdClass(); $attr_cell->bold = false; $attr_cell->underline = false; $attr_cell->blink = false; $attr_cell->background = 'black'; $attr_cell->foreground = 'white'; $attr_cell->reverse = false; $this->base_attr_cell = clone $attr_cell; $this->attr_cell = clone $attr_cell; $this->setHistory(200); $this->setDimensions(80, 24); } /** * Set terminal width and height * * Resets the screen as well * * @param int $x * @param int $y */ public function setDimensions($x, $y) { $this->max_x = $x - 1; $this->max_y = $y - 1; $this->x = $this->y = 0; $this->history = $this->history_attrs = []; $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell); $this->screen = array_fill(0, $this->max_y + 1, ''); $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); $this->ansi = ''; } /** * Set the number of lines that should be logged past the terminal height * * @param int $history */ public function setHistory($history) { $this->max_history = $history; } /** * Load a string * * @param string $source */ public function loadString($source) { $this->setDimensions($this->max_x + 1, $this->max_y + 1); $this->appendString($source); } /** * Appdend a string * * @param string $source */ public function appendString($source) { $this->tokenization = ['']; for ($i = 0; $i < strlen($source); $i++) { if (strlen($this->ansi)) { $this->ansi .= $source[$i]; $chr = ord($source[$i]); // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements // single character CSI's not currently supported switch (true) { case $this->ansi == "\x1B=": $this->ansi = ''; continue 2; case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: break; default: continue 2; } $this->tokenization[] = $this->ansi; $this->tokenization[] = ''; // http://ascii-table.com/ansi-escape-sequences-vt-100.php switch ($this->ansi) { case "\x1B[H": // Move cursor to upper left corner $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $this->y = 0; break; case "\x1B[J": // Clear screen from cursor down $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); if (count($this->history) == $this->max_history) { array_shift($this->history); array_shift($this->history_attrs); } // fall-through case "\x1B[K": // Clear screen from cursor right $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell)); break; case "\x1B[2K": // Clear entire line $this->screen[$this->y] = str_repeat(' ', $this->x); $this->attrs[$this->y] = $this->attr_row; break; case "\x1B[?1h": // set cursor key to application case "\x1B[?25h": // show the cursor case "\x1B(B": // set united states g0 character set break; case "\x1BE": // Move to next line $this->newLine(); $this->x = 0; break; default: switch (true) { case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines $this->old_y = $this->y; $this->y += (int) $match[1]; break; case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $match[2] - 1; $this->y = (int) $match[1] - 1; break; case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines $this->old_x = $this->x; $this->x += $match[1]; break; case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines $this->old_x = $this->x; $this->x -= $match[1]; if ($this->x < 0) { $this->x = 0; } break; case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window break; case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes $attr_cell = &$this->attr_cell; $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { case '': case '0': // Turn off character attributes $attr_cell = clone $this->base_attr_cell; break; case '1': // Turn bold mode on $attr_cell->bold = true; break; case '4': // Turn underline mode on $attr_cell->underline = true; break; case '5': // Turn blinking mode on $attr_cell->blink = true; break; case '7': // Turn reverse video on $attr_cell->reverse = !$attr_cell->reverse; $temp = $attr_cell->background; $attr_cell->background = $attr_cell->foreground; $attr_cell->foreground = $temp; break; default: // set colors //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground; $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' }; //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background; $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; switch ($mod) { // @codingStandardsIgnoreStart case '30': $front = 'black'; break; case '31': $front = 'red'; break; case '32': $front = 'green'; break; case '33': $front = 'yellow'; break; case '34': $front = 'blue'; break; case '35': $front = 'magenta'; break; case '36': $front = 'cyan'; break; case '37': $front = 'white'; break; case '40': $back = 'black'; break; case '41': $back = 'red'; break; case '42': $back = 'green'; break; case '43': $back = 'yellow'; break; case '44': $back = 'blue'; break; case '45': $back = 'magenta'; break; case '46': $back = 'cyan'; break; case '47': $back = 'white'; break; // @codingStandardsIgnoreEnd default: //user_error('Unsupported attribute: ' . $mod); $this->ansi = ''; break 2; } } } break; default: //user_error("{$this->ansi} is unsupported\r\n"); } } $this->ansi = ''; continue; } $this->tokenization[count($this->tokenization) - 1] .= $source[$i]; switch ($source[$i]) { case "\r": $this->x = 0; break; case "\n": $this->newLine(); break; case "\x08": // backspace if ($this->x) { $this->x--; $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; $this->screen[$this->y] = substr_replace( $this->screen[$this->y], $source[$i], $this->x, 1 ); } break; case "\x0F": // shift break; case "\x1B": // start ANSI escape code $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1); //if (!strlen($this->tokenization[count($this->tokenization) - 1])) { // array_pop($this->tokenization); //} $this->ansi .= "\x1B"; break; default: $this->attrs[$this->y][$this->x] = clone $this->attr_cell; if ($this->x > strlen($this->screen[$this->y])) { $this->screen[$this->y] = str_repeat(' ', $this->x); } $this->screen[$this->y] = substr_replace( $this->screen[$this->y], $source[$i], $this->x, 1 ); if ($this->x > $this->max_x) { $this->x = 0; $this->newLine(); } else { $this->x++; } } } } /** * Add a new line * * Also update the $this->screen and $this->history buffers * */ private function newLine() { //if ($this->y < $this->max_y) { // $this->y++; //} while ($this->y >= $this->max_y) { $this->history = array_merge($this->history, [array_shift($this->screen)]); $this->screen[] = ''; $this->history_attrs = array_merge($this->history_attrs, [array_shift($this->attrs)]); $this->attrs[] = $this->attr_row; if (count($this->history) >= $this->max_history) { array_shift($this->history); array_shift($this->history_attrs); } $this->y--; } $this->y++; } /** * Returns the current coordinate without preformating * * @param \stdClass $last_attr * @param \stdClass $cur_attr * @param string $char * @return string */ private function processCoordinate(\stdClass $last_attr, \stdClass $cur_attr, $char) { $output = ''; if ($last_attr != $cur_attr) { $close = $open = ''; if ($last_attr->foreground != $cur_attr->foreground) { if ($cur_attr->foreground != 'white') { $open .= '<span style="color: ' . $cur_attr->foreground . '">'; } if ($last_attr->foreground != 'white') { $close = '</span>' . $close; } } if ($last_attr->background != $cur_attr->background) { if ($cur_attr->background != 'black') { $open .= '<span style="background: ' . $cur_attr->background . '">'; } if ($last_attr->background != 'black') { $close = '</span>' . $close; } } if ($last_attr->bold != $cur_attr->bold) { if ($cur_attr->bold) { $open .= '<b>'; } else { $close = '</b>' . $close; } } if ($last_attr->underline != $cur_attr->underline) { if ($cur_attr->underline) { $open .= '<u>'; } else { $close = '</u>' . $close; } } if ($last_attr->blink != $cur_attr->blink) { if ($cur_attr->blink) { $open .= '<blink>'; } else { $close = '</blink>' . $close; } } $output .= $close . $open; } $output .= htmlspecialchars($char); return $output; } /** * Returns the current screen without preformating * * @return string */ private function getScreenHelper() { $output = ''; $last_attr = $this->base_attr_cell; for ($i = 0; $i <= $this->max_y; $i++) { for ($j = 0; $j <= $this->max_x; $j++) { $cur_attr = $this->attrs[$i][$j]; $output .= $this->processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); $last_attr = $this->attrs[$i][$j]; } $output .= "\r\n"; } $output = substr($output, 0, -2); // close any remaining open tags $output .= $this->processCoordinate($last_attr, $this->base_attr_cell, ''); return rtrim($output); } /** * Returns the current screen * * @return string */ public function getScreen() { return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->getScreenHelper() . '</pre>'; } /** * Returns the current screen and the x previous lines * * @return string */ public function getHistory() { $scrollback = ''; $last_attr = $this->base_attr_cell; for ($i = 0; $i < count($this->history); $i++) { for ($j = 0; $j <= $this->max_x + 1; $j++) { $cur_attr = $this->history_attrs[$i][$j]; $scrollback .= $this->processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); $last_attr = $this->history_attrs[$i][$j]; } $scrollback .= "\r\n"; } $base_attr_cell = $this->base_attr_cell; $this->base_attr_cell = $last_attr; $scrollback .= $this->getScreen(); $this->base_attr_cell = $base_attr_cell; return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>'; } } PK2A#]IW�jF�F�1vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.phpnu�[���<?php /** * Pure-PHP implementation of SFTP. * * PHP version 5 * * Supports SFTPv2/3/4/5/6. Defaults to v3. * * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}. * * Here's a short example of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $sftp = new \phpseclib3\Net\SFTP('www.domain.tld'); * if (!$sftp->login('username', 'password')) { * exit('Login Failed'); * } * * echo $sftp->pwd() . "\r\n"; * $sftp->put('filename.ext', 'hello, world!'); * print_r($sftp->nlist()); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Net; use phpseclib3\Common\Functions\Strings; use phpseclib3\Exception\FileNotFoundException; /** * Pure-PHP implementations of SFTP. * * @author Jim Wigginton <terrafrost@php.net> */ class SFTP extends SSH2 { /** * SFTP channel constant * * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1. * * @see \phpseclib3\Net\SSH2::send_channel_packet() * @see \phpseclib3\Net\SSH2::get_channel_packet() */ const CHANNEL = 0x100; /** * Reads data from a local file. * * @see \phpseclib3\Net\SFTP::put() */ const SOURCE_LOCAL_FILE = 1; /** * Reads data from a string. * * @see \phpseclib3\Net\SFTP::put() */ // this value isn't really used anymore but i'm keeping it reserved for historical reasons const SOURCE_STRING = 2; /** * Reads data from callback: * function callback($length) returns string to proceed, null for EOF * * @see \phpseclib3\Net\SFTP::put() */ const SOURCE_CALLBACK = 16; /** * Resumes an upload * * @see \phpseclib3\Net\SFTP::put() */ const RESUME = 4; /** * Append a local file to an already existing remote file * * @see \phpseclib3\Net\SFTP::put() */ const RESUME_START = 8; /** * Packet Types * * @see self::__construct() * @var array * @access private */ private static $packet_types = []; /** * Status Codes * * @see self::__construct() * @var array * @access private */ private static $status_codes = []; /** @var array<int, string> */ private static $attributes; /** @var array<int, string> */ private static $open_flags; /** @var array<int, string> */ private static $open_flags5; /** @var array<int, string> */ private static $file_types; /** * The Request ID * * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support * concurrent actions, so it's somewhat academic, here. * * @var boolean * @see self::_send_sftp_packet() */ private $use_request_id = false; /** * The Packet Type * * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support * concurrent actions, so it's somewhat academic, here. * * @var int * @see self::_get_sftp_packet() */ private $packet_type = -1; /** * Packet Buffer * * @var string * @see self::_get_sftp_packet() */ private $packet_buffer = ''; /** * Extensions supported by the server * * @var array * @see self::_initChannel() */ private $extensions = []; /** * Server SFTP version * * @var int * @see self::_initChannel() */ private $version; /** * Default Server SFTP version * * @var int * @see self::_initChannel() */ private $defaultVersion; /** * Preferred SFTP version * * @var int * @see self::_initChannel() */ private $preferredVersion = 3; /** * Current working directory * * @var string|bool * @see self::realpath() * @see self::chdir() */ private $pwd = false; /** * Packet Type Log * * @see self::getLog() * @var array */ private $packet_type_log = []; /** * Packet Log * * @see self::getLog() * @var array */ private $packet_log = []; /** * Real-time log file pointer * * @see self::_append_log() * @var resource|closed-resource */ private $realtime_log_file; /** * Real-time log file size * * @see self::_append_log() * @var int */ private $realtime_log_size; /** * Real-time log file wrap boolean * * @see self::_append_log() * @var bool */ private $realtime_log_wrap; /** * Current log size * * Should never exceed self::LOG_MAX_SIZE * * @var int */ private $log_size; /** * Error information * * @see self::getSFTPErrors() * @see self::getLastSFTPError() * @var array */ private $sftp_errors = []; /** * Stat Cache * * Rather than always having to open a directory and close it immediately there after to see if a file is a directory * we'll cache the results. * * @see self::_update_stat_cache() * @see self::_remove_from_stat_cache() * @see self::_query_stat_cache() * @var array */ private $stat_cache = []; /** * Max SFTP Packet Size * * @see self::__construct() * @see self::get() * @var int */ private $max_sftp_packet; /** * Stat Cache Flag * * @see self::disableStatCache() * @see self::enableStatCache() * @var bool */ private $use_stat_cache = true; /** * Sort Options * * @see self::_comparator() * @see self::setListOrder() * @var array */ protected $sortOptions = []; /** * Canonicalization Flag * * Determines whether or not paths should be canonicalized before being * passed on to the remote server. * * @see self::enablePathCanonicalization() * @see self::disablePathCanonicalization() * @see self::realpath() * @var bool */ private $canonicalize_paths = true; /** * Request Buffers * * @see self::_get_sftp_packet() * @var array */ private $requestBuffer = []; /** * Preserve timestamps on file downloads / uploads * * @see self::get() * @see self::put() * @var bool */ private $preserveTime = false; /** * Arbitrary Length Packets Flag * * Determines whether or not packets of any length should be allowed, * in cases where the server chooses the packet length (such as * directory listings). By default, packets are only allowed to be * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h) * * @see self::enableArbitraryLengthPackets() * @see self::_get_sftp_packet() * @var bool */ private $allow_arbitrary_length_packets = false; /** * Was the last packet due to the channels being closed or not? * * @see self::get() * @see self::get_sftp_packet() * @var bool */ private $channel_close = false; /** * Has the SFTP channel been partially negotiated? * * @var bool */ private $partial_init = false; /** * Default Constructor. * * Connects to an SFTP server * * $host can either be a string, representing the host, or a stream resource. * * @param mixed $host * @param int $port * @param int $timeout */ public function __construct($host, $port = 22, $timeout = 10) { parent::__construct($host, $port, $timeout); $this->max_sftp_packet = 1 << 15; if (empty(self::$packet_types)) { self::$packet_types = [ 1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', 3 => 'NET_SFTP_OPEN', 4 => 'NET_SFTP_CLOSE', 5 => 'NET_SFTP_READ', 6 => 'NET_SFTP_WRITE', 7 => 'NET_SFTP_LSTAT', 9 => 'NET_SFTP_SETSTAT', 10 => 'NET_SFTP_FSETSTAT', 11 => 'NET_SFTP_OPENDIR', 12 => 'NET_SFTP_READDIR', 13 => 'NET_SFTP_REMOVE', 14 => 'NET_SFTP_MKDIR', 15 => 'NET_SFTP_RMDIR', 16 => 'NET_SFTP_REALPATH', 17 => 'NET_SFTP_STAT', 18 => 'NET_SFTP_RENAME', 19 => 'NET_SFTP_READLINK', 20 => 'NET_SFTP_SYMLINK', 21 => 'NET_SFTP_LINK', 101 => 'NET_SFTP_STATUS', 102 => 'NET_SFTP_HANDLE', 103 => 'NET_SFTP_DATA', 104 => 'NET_SFTP_NAME', 105 => 'NET_SFTP_ATTRS', 200 => 'NET_SFTP_EXTENDED' ]; self::$status_codes = [ 0 => 'NET_SFTP_STATUS_OK', 1 => 'NET_SFTP_STATUS_EOF', 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 4 => 'NET_SFTP_STATUS_FAILURE', 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 13 => 'NET_SFTP_STATUS_NO_MEDIA', 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 21 => 'NET_SFTP_STATUS_LINK_LOOP', 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' ]; // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why self::$attributes = [ 0x00000001 => 'NET_SFTP_ATTR_SIZE', 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 0x00000040 => 'NET_SFTP_ATTR_ACL', 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 0x00008000 => 'NET_SFTP_ATTR_CTIME', // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. (PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED' ]; // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name // the array for that $this->open5_flags and similarly alter the constant names. self::$open_flags = [ 0x00000001 => 'NET_SFTP_OPEN_READ', 0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000004 => 'NET_SFTP_OPEN_APPEND', 0x00000008 => 'NET_SFTP_OPEN_CREATE', 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 0x00000020 => 'NET_SFTP_OPEN_EXCL', 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 ]; // SFTPv5+ changed the flags up: // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 self::$open_flags5 = [ // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', // the rest of the flags are not supported 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', ]; // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation self::$file_types = [ 1 => 'NET_SFTP_TYPE_REGULAR', 2 => 'NET_SFTP_TYPE_DIRECTORY', 3 => 'NET_SFTP_TYPE_SYMLINK', 4 => 'NET_SFTP_TYPE_SPECIAL', 5 => 'NET_SFTP_TYPE_UNKNOWN', // the following types were first defined for use in SFTPv5+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 6 => 'NET_SFTP_TYPE_SOCKET', 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 9 => 'NET_SFTP_TYPE_FIFO' ]; self::define_array( self::$packet_types, self::$status_codes, self::$attributes, self::$open_flags, self::$open_flags5, self::$file_types ); } if (!defined('NET_SFTP_QUEUE_SIZE')) { define('NET_SFTP_QUEUE_SIZE', 32); } if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) { define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024); } } /** * Check a few things before SFTP functions are called * * @return bool */ private function precheck() { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } if ($this->pwd === false) { return $this->init_sftp_connection(); } return true; } /** * Partially initialize an SFTP connection * * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ private function partial_init_sftp_connection() { $response = $this->open_channel(self::CHANNEL, true); if ($response === true && $this->isTimeout()) { return false; } $packet = Strings::packSSH2( 'CNsbs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], 'subsystem', true, 'sftp' ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->get_channel_packet(self::CHANNEL, true); if ($response === false) { // from PuTTY's psftp.exe $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . "exec sftp-server"; // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does // is redundant $packet = Strings::packSSH2( 'CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], 'exec', 1, $command ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->get_channel_packet(self::CHANNEL, true); if ($response === false) { return false; } } elseif ($response === true && $this->isTimeout()) { return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; $this->send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3"); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_VERSION) { throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. ' . 'Got packet type: ' . $this->packet_type); } $this->use_request_id = true; list($this->defaultVersion) = Strings::unpackSSH2('N', $response); while (!empty($response)) { list($key, $value) = Strings::unpackSSH2('ss', $response); $this->extensions[$key] = $value; } $this->partial_init = true; return true; } /** * (Re)initializes the SFTP channel * * @return bool */ private function init_sftp_connection() { if (!$this->partial_init && !$this->partial_init_sftp_connection()) { return false; } /* A Note on SFTPv4/5/6 support: <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following: "If the client wishes to interoperate with servers that support noncontiguous version numbers it SHOULD send '3'" Given that the server only sends its version number after the client has already done so, the above seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the most popular. <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following; "If the server did not send the "versions" extension, or the version-from-list was not included, the server MAY send a status response describing the failure, but MUST then close the channel without processing any further requests." So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib3\Net\SFTP would do is close the channel and reopen it with a new and updated SSH_FXP_INIT packet. */ $this->version = $this->defaultVersion; if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) { $versions = explode(',', $this->extensions['versions']); $supported = [6, 5, 4]; if ($this->preferredVersion) { $supported = array_diff($supported, [$this->preferredVersion]); array_unshift($supported, $this->preferredVersion); } foreach ($supported as $ver) { if (in_array($ver, $versions)) { if ($ver === $this->version) { break; } $this->version = (int) $ver; $packet = Strings::packSSH2('ss', 'version-select', "$ver"); $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); throw new \UnexpectedValueException('Expected NET_SFTP_STATUS_OK. ' . ' Got ' . $status); } break; } } } /* SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that 'newline@vandyke.com' would. */ /* if (isset($this->extensions['newline@vandyke.com'])) { $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; unset($this->extensions['newline@vandyke.com']); } */ if ($this->version < 2 || $this->version > 6) { return false; } $this->pwd = true; try { $this->pwd = $this->realpath('.'); } catch (\UnexpectedValueException $e) { if (!$this->canonicalize_paths) { throw $e; } $this->canonicalize_paths = false; $this->reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); } $this->update_stat_cache($this->pwd, []); return true; } /** * Disable the stat cache * */ public function disableStatCache() { $this->use_stat_cache = false; } /** * Enable the stat cache * */ public function enableStatCache() { $this->use_stat_cache = true; } /** * Clear the stat cache * */ public function clearStatCache() { $this->stat_cache = []; } /** * Enable path canonicalization * */ public function enablePathCanonicalization() { $this->canonicalize_paths = true; } /** * Disable path canonicalization * * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path * */ public function disablePathCanonicalization() { $this->canonicalize_paths = false; } /** * Enable arbitrary length packets * */ public function enableArbitraryLengthPackets() { $this->allow_arbitrary_length_packets = true; } /** * Disable arbitrary length packets * */ public function disableArbitraryLengthPackets() { $this->allow_arbitrary_length_packets = false; } /** * Returns the current directory name * * @return string|bool */ public function pwd() { if (!$this->precheck()) { return false; } return $this->pwd; } /** * Logs errors * * @param string $response * @param int $status */ private function logError($response, $status = -1) { if ($status == -1) { list($status) = Strings::unpackSSH2('N', $response); } $error = self::$status_codes[$status]; if ($this->version > 2) { list($message) = Strings::unpackSSH2('s', $response); $this->sftp_errors[] = "$error: $message"; } else { $this->sftp_errors[] = $error; } } /** * Canonicalize the Server-Side Path Name * * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns * the absolute (canonicalized) path. * * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is. * * @see self::chdir() * @see self::disablePathCanonicalization() * @param string $path * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed */ public function realpath($path) { if ($this->precheck() === false) { return false; } if (!$this->canonicalize_paths) { if ($this->pwd === true) { return '.'; } if (!strlen($path) || $path[0] != '/') { $path = $this->pwd . '/' . $path; } $parts = explode('/', $path); $afterPWD = $beforePWD = []; foreach ($parts as $part) { switch ($part) { //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137 case '.': break; case '..': if (!empty($afterPWD)) { array_pop($afterPWD); } else { $beforePWD[] = '..'; } break; default: $afterPWD[] = $part; } } $beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.'; return $beforePWD . '/' . implode('/', $afterPWD); } if ($this->pwd === true) { // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 $this->send_sftp_packet(NET_SFTP_REALPATH, Strings::packSSH2('s', $path)); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks // at is the first part and that part is defined the same in SFTP versions 3 through 6. list(, $filename) = Strings::unpackSSH2('Ns', $response); return $filename; case NET_SFTP_STATUS: $this->logError($response); return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } } if (!strlen($path) || $path[0] != '/') { $path = $this->pwd . '/' . $path; } $path = explode('/', $path); $new = []; foreach ($path as $dir) { if (!strlen($dir)) { continue; } switch ($dir) { case '..': array_pop($new); // fall-through case '.': break; default: $new[] = $dir; } } return '/' . implode('/', $new); } /** * Changes the current directory * * @param string $dir * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ public function chdir($dir) { if (!$this->precheck()) { return false; } // assume current dir if $dir is empty if ($dir === '') { $dir = './'; // suffix a slash if needed } elseif ($dir[strlen($dir) - 1] != '/') { $dir .= '/'; } $dir = $this->realpath($dir); // confirm that $dir is, in fact, a valid directory if ($this->use_stat_cache && is_array($this->query_stat_cache($dir))) { $this->pwd = $dir; return true; } // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us // the currently logged in user has the appropriate permissions or not. maybe you could see if // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy // way to get those with SFTP $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir)); // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->logError($response); return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' . 'Got packet type: ' . $this->packet_type); } if (!$this->close_handle($handle)) { return false; } $this->update_stat_cache($dir, []); $this->pwd = $dir; return true; } /** * Returns a list of files in the given directory * * @param string $dir * @param bool $recursive * @return array|false */ public function nlist($dir = '.', $recursive = false) { return $this->nlist_helper($dir, $recursive, ''); } /** * Helper method for nlist * * @param string $dir * @param bool $recursive * @param string $relativeDir * @return array|false */ private function nlist_helper($dir, $recursive, $relativeDir) { $files = $this->readlist($dir, false); // If we get an int back, then that is an "unexpected" status. // We do not have a file list, so return false. if (is_int($files)) { return false; } if (!$recursive || $files === false) { return $files; } $result = []; foreach ($files as $value) { if ($value == '.' || $value == '..') { $result[] = $relativeDir . $value; continue; } if (is_array($this->query_stat_cache($this->realpath($dir . '/' . $value)))) { $temp = $this->nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); $temp = is_array($temp) ? $temp : []; $result = array_merge($result, $temp); } else { $result[] = $relativeDir . $value; } } return $result; } /** * Returns a detailed list of files in the given directory * * @param string $dir * @param bool $recursive * @return array|false */ public function rawlist($dir = '.', $recursive = false) { $files = $this->readlist($dir, true); // If we get an int back, then that is an "unexpected" status. // We do not have a file list, so return false. if (is_int($files)) { return false; } if (!$recursive || $files === false) { return $files; } static $depth = 0; foreach ($files as $key => $value) { if ($depth != 0 && $key == '..') { unset($files[$key]); continue; } $is_directory = false; if ($key != '.' && $key != '..') { if ($this->use_stat_cache) { $is_directory = is_array($this->query_stat_cache($this->realpath($dir . '/' . $key))); } else { $stat = $this->lstat($dir . '/' . $key); $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; } } if ($is_directory) { $depth++; $files[$key] = $this->rawlist($dir . '/' . $key, true); $depth--; } else { $files[$key] = (object) $value; } } return $files; } /** * Reads a list, be it detailed or not, of files in the given directory * * @param string $dir * @param bool $raw * @return array|false * @throws \UnexpectedValueException on receipt of unexpected packets */ private function readlist($dir, $raw = true) { if (!$this->precheck()) { return false; } $dir = $this->realpath($dir . '/'); if ($dir === false) { return false; } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir)); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that // represent the length of the string and leave it at that $handle = substr($response, 4); break; case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED list($status) = Strings::unpackSSH2('N', $response); $this->logError($response, $status); return $status; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } $this->update_stat_cache($dir, []); $contents = []; while (true) { // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many // SSH_MSG_CHANNEL_DATA messages is not known to me. $this->send_sftp_packet(NET_SFTP_READDIR, Strings::packSSH2('s', $handle)); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: list($count) = Strings::unpackSSH2('N', $response); for ($i = 0; $i < $count; $i++) { list($shortname) = Strings::unpackSSH2('s', $response); // SFTPv4 "removed the long filename from the names structure-- it can now be // built from information available in the attrs structure." if ($this->version < 4) { list($longname) = Strings::unpackSSH2('s', $response); } $attributes = $this->parseAttributes($response); if (!isset($attributes['type']) && $this->version < 4) { $fileType = $this->parseLongname($longname); if ($fileType) { $attributes['type'] = $fileType; } } $contents[$shortname] = $attributes + ['filename' => $shortname]; if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { $this->update_stat_cache($dir . '/' . $shortname, []); } else { if ($shortname == '..') { $temp = $this->realpath($dir . '/..') . '/.'; } else { $temp = $dir . '/' . $shortname; } $this->update_stat_cache($temp, (object) ['lstat' => $attributes]); } // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the // final SSH_FXP_STATUS packet should tell us that, already. } break; case NET_SFTP_STATUS: list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_EOF) { $this->logError($response, $status); return $status; } break 2; default: throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } } if (!$this->close_handle($handle)) { return false; } if (count($this->sortOptions)) { uasort($contents, [&$this, 'comparator']); } return $raw ? $contents : array_map('strval', array_keys($contents)); } /** * Compares two rawlist entries using parameters set by setListOrder() * * Intended for use with uasort() * * @param array $a * @param array $b * @return int */ private function comparator(array $a, array $b) { switch (true) { case $a['filename'] === '.' || $b['filename'] === '.': if ($a['filename'] === $b['filename']) { return 0; } return $a['filename'] === '.' ? -1 : 1; case $a['filename'] === '..' || $b['filename'] === '..': if ($a['filename'] === $b['filename']) { return 0; } return $a['filename'] === '..' ? -1 : 1; case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: if (!isset($b['type'])) { return 1; } if ($b['type'] !== $a['type']) { return -1; } break; case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: return 1; } foreach ($this->sortOptions as $sort => $order) { if (!isset($a[$sort]) || !isset($b[$sort])) { if (isset($a[$sort])) { return -1; } if (isset($b[$sort])) { return 1; } return 0; } switch ($sort) { case 'filename': $result = strcasecmp($a['filename'], $b['filename']); if ($result) { return $order === SORT_DESC ? -$result : $result; } break; case 'mode': $a[$sort] &= 07777; $b[$sort] &= 07777; // fall-through default: if ($a[$sort] === $b[$sort]) { break; } return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; } } } /** * Defines how nlist() and rawlist() will be sorted - if at all. * * If sorting is enabled directories and files will be sorted independently with * directories appearing before files in the resultant array that is returned. * * Any parameter returned by stat is a valid sort parameter for this function. * Filename comparisons are case insensitive. * * Examples: * * $sftp->setListOrder('filename', SORT_ASC); * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC); * $sftp->setListOrder(true); * Separates directories from files but doesn't do any sorting beyond that * $sftp->setListOrder(); * Don't do any sort of sorting * * @param string ...$args */ public function setListOrder(...$args) { $this->sortOptions = []; if (empty($args)) { return; } $len = count($args) & 0x7FFFFFFE; for ($i = 0; $i < $len; $i += 2) { $this->sortOptions[$args[$i]] = $args[$i + 1]; } if (!count($this->sortOptions)) { $this->sortOptions = ['bogus' => true]; } } /** * Save files / directories to cache * * @param string $path * @param mixed $value */ private function update_stat_cache($path, $value) { if ($this->use_stat_cache === false) { return; } // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i => $dir) { // if $temp is an object that means one of two things. // 1. a file was deleted and changed to a directory behind phpseclib's back // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to if (is_object($temp)) { $temp = []; } if (!isset($temp[$dir])) { $temp[$dir] = []; } if ($i === $max) { if (is_object($temp[$dir]) && is_object($value)) { if (!isset($value->stat) && isset($temp[$dir]->stat)) { $value->stat = $temp[$dir]->stat; } if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { $value->lstat = $temp[$dir]->lstat; } } $temp[$dir] = $value; break; } $temp = &$temp[$dir]; } } /** * Remove files / directories from cache * * @param string $path * @return bool */ private function remove_from_stat_cache($path) { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i => $dir) { if (!is_array($temp)) { return false; } if ($i === $max) { unset($temp[$dir]); return true; } if (!isset($temp[$dir])) { return false; } $temp = &$temp[$dir]; } } /** * Checks cache for path * * Mainly used by file_exists * * @param string $path * @return mixed */ private function query_stat_cache($path) { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; foreach ($dirs as $dir) { if (!is_array($temp)) { return null; } if (!isset($temp[$dir])) { return null; } $temp = &$temp[$dir]; } return $temp; } /** * Returns general information about a file. * * Returns an array on success and false otherwise. * * @param string $filename * @return array|false */ public function stat($filename) { if (!$this->precheck()) { return false; } $filename = $this->realpath($filename); if ($filename === false) { return false; } if ($this->use_stat_cache) { $result = $this->query_stat_cache($filename); if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { return $result['.']->stat; } if (is_object($result) && isset($result->stat)) { return $result->stat; } } $stat = $this->stat_helper($filename, NET_SFTP_STAT); if ($stat === false) { $this->remove_from_stat_cache($filename); return false; } if (isset($stat['type'])) { if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename .= '/.'; } $this->update_stat_cache($filename, (object) ['stat' => $stat]); return $stat; } $pwd = $this->pwd; $stat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename .= '/.'; } $this->update_stat_cache($filename, (object) ['stat' => $stat]); return $stat; } /** * Returns general information about a file or symbolic link. * * Returns an array on success and false otherwise. * * @param string $filename * @return array|false */ public function lstat($filename) { if (!$this->precheck()) { return false; } $filename = $this->realpath($filename); if ($filename === false) { return false; } if ($this->use_stat_cache) { $result = $this->query_stat_cache($filename); if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { return $result['.']->lstat; } if (is_object($result) && isset($result->lstat)) { return $result->lstat; } } $lstat = $this->stat_helper($filename, NET_SFTP_LSTAT); if ($lstat === false) { $this->remove_from_stat_cache($filename); return false; } if (isset($lstat['type'])) { if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename .= '/.'; } $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); return $lstat; } $stat = $this->stat_helper($filename, NET_SFTP_STAT); if ($lstat != $stat) { $lstat = array_merge($lstat, ['type' => NET_SFTP_TYPE_SYMLINK]); $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); return $stat; } $pwd = $this->pwd; $lstat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename .= '/.'; } $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); return $lstat; } /** * Returns general information about a file or symbolic link * * Determines information without calling \phpseclib3\Net\SFTP::realpath(). * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. * * @param string $filename * @param int $type * @throws \UnexpectedValueException on receipt of unexpected packets * @return array|false */ private function stat_helper($filename, $type) { // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: $packet = Strings::packSSH2('s', $filename); $this->send_sftp_packet($type, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_ATTRS: return $this->parseAttributes($response); case NET_SFTP_STATUS: $this->logError($response); return false; } throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } /** * Truncates a file to a given length * * @param string $filename * @param int $new_size * @return bool */ public function truncate($filename, $new_size) { $attr = Strings::packSSH2('NQ', NET_SFTP_ATTR_SIZE, $new_size); return $this->setstat($filename, $attr, false); } /** * Sets access and modification time of file. * * If the file does not exist, it will be created. * * @param string $filename * @param int $time * @param int $atime * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ public function touch($filename, $time = null, $atime = null) { if (!$this->precheck()) { return false; } $filename = $this->realpath($filename); if ($filename === false) { return false; } if (!isset($time)) { $time = time(); } if (!isset($atime)) { $atime = $time; } $attr = $this->version < 4 ? pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time) : Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $atime, $time); $packet = Strings::packSSH2('s', $filename); $packet .= $this->version >= 5 ? pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) : pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL); $packet .= $attr; $this->send_sftp_packet(NET_SFTP_OPEN, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return $this->close_handle(substr($response, 4)); case NET_SFTP_STATUS: $this->logError($response); break; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } return $this->setstat($filename, $attr, false); } /** * Changes file or directory owner * * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string * would be of the form "user@dns_domain" but it does not need to be. * `$sftp->getSupportedVersions()['version']` will return the specific version * that's being used. * * Returns true on success or false on error. * * @param string $filename * @param int|string $uid * @param bool $recursive * @return bool */ public function chown($filename, $uid, $recursive = false) { /* quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>, "To avoid a representation that is tied to a particular underlying implementation at the client or server, the use of UTF-8 strings has been chosen. The string should be of the form "user@dns_domain". This will allow for a client and server that do not use the same local representation the ability to translate to a common syntax that can be interpreted by both. In the case where there is no translation available to the client or server, the attribute value must be constructed without the "@"." phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't have one? phpseclib would have no way of knowing so rather than guess phpseclib will just use whatever value the user provided */ $attr = $this->version < 4 ? // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>, // "if the owner or group is specified as -1, then that ID is not changed" pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>, // "If either the owner or group field is zero length, the field should be // considered absent, and no change should be made to that specific field // during a modification operation" Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, $uid, ''); return $this->setstat($filename, $attr, $recursive); } /** * Changes file or directory group * * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string * would be of the form "user@dns_domain" but it does not need to be. * `$sftp->getSupportedVersions()['version']` will return the specific version * that's being used. * * Returns true on success or false on error. * * @param string $filename * @param int|string $gid * @param bool $recursive * @return bool */ public function chgrp($filename, $gid, $recursive = false) { $attr = $this->version < 4 ? pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) : Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, '', $gid); return $this->setstat($filename, $attr, $recursive); } /** * Set permissions on a file. * * Returns the new file permissions on success or false on error. * If $recursive is true than this just returns true or false. * * @param int $mode * @param string $filename * @param bool $recursive * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed */ public function chmod($mode, $filename, $recursive = false) { if (is_string($mode) && is_int($filename)) { $temp = $mode; $mode = $filename; $filename = $temp; } $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); if (!$this->setstat($filename, $attr, $recursive)) { return false; } if ($recursive) { return true; } $filename = $this->realpath($filename); // rather than return what the permissions *should* be, we'll return what they actually are. this will also // tell us if the file actually exists. // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: $packet = pack('Na*', strlen($filename), $filename); $this->send_sftp_packet(NET_SFTP_STAT, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_ATTRS: $attrs = $this->parseAttributes($response); return $attrs['mode']; case NET_SFTP_STATUS: $this->logError($response); return false; } throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } /** * Sets information about a file * * @param string $filename * @param string $attr * @param bool $recursive * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ private function setstat($filename, $attr, $recursive) { if (!$this->precheck()) { return false; } $filename = $this->realpath($filename); if ($filename === false) { return false; } $this->remove_from_stat_cache($filename); if ($recursive) { $i = 0; $result = $this->setstat_recursive($filename, $attr, $i); $this->read_put_responses($i); return $result; } $packet = Strings::packSSH2('s', $filename); $packet .= $this->version >= 4 ? pack('a*Ca*', substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) : $attr; $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); /* "Because some systems must use separate system calls to set various attributes, it is possible that a failure response will be returned, but yet some of the attributes may be have been successfully modified. If possible, servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 */ $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); return false; } return true; } /** * Recursively sets information on directories on the SFTP server * * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. * * @param string $path * @param string $attr * @param int $i * @return bool */ private function setstat_recursive($path, $attr, &$i) { if (!$this->read_put_responses($i)) { return false; } $i = 0; $entries = $this->readlist($path, true); if ($entries === false || is_int($entries)) { return $this->setstat($path, $attr, false); } // normally $entries would have at least . and .. but it might not if the directories // permissions didn't allow reading if (empty($entries)) { return false; } unset($entries['.'], $entries['..']); foreach ($entries as $filename => $props) { if (!isset($props['type'])) { return false; } $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { if (!$this->setstat_recursive($temp, $attr, $i)) { return false; } } else { $packet = Strings::packSSH2('s', $temp); $packet .= $this->version >= 4 ? pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) : $attr; $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->read_put_responses($i)) { return false; } $i = 0; } } } $packet = Strings::packSSH2('s', $path); $packet .= $this->version >= 4 ? pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) : $attr; $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->read_put_responses($i)) { return false; } $i = 0; } return true; } /** * Return the target of a symbolic link * * @param string $link * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed */ public function readlink($link) { if (!$this->precheck()) { return false; } $link = $this->realpath($link); $this->send_sftp_packet(NET_SFTP_READLINK, Strings::packSSH2('s', $link)); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: break; case NET_SFTP_STATUS: $this->logError($response); return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($count) = Strings::unpackSSH2('N', $response); // the file isn't a symlink if (!$count) { return false; } list($filename) = Strings::unpackSSH2('s', $response); return $filename; } /** * Create a symlink * * symlink() creates a symbolic link to the existing target with the specified name link. * * @param string $target * @param string $link * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ public function symlink($target, $link) { if (!$this->precheck()) { return false; } //$target = $this->realpath($target); $link = $this->realpath($link); /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 : Changed the SYMLINK packet to be LINK and give it the ability to create hard links. Also change it's packet number because many implementation implemented SYMLINK with the arguments reversed. Hopefully the new argument names make it clear which way is which. */ if ($this->version == 6) { $type = NET_SFTP_LINK; $packet = Strings::packSSH2('ssC', $link, $target, 1); } else { $type = NET_SFTP_SYMLINK; /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 : 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK When OpenSSH's sftp-server was implemented, the order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, the reversal was not noticed until the server was widely deployed. Since fixing this to follow the specification would cause incompatibility, the current order was retained. For correct operation, clients should send SSH_FXP_SYMLINK as follows: uint32 id string targetpath string linkpath */ $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ? Strings::packSSH2('ss', $target, $link) : Strings::packSSH2('ss', $link, $target); } $this->send_sftp_packet($type, $packet); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); return false; } return true; } /** * Creates a directory. * * @param string $dir * @param int $mode * @param bool $recursive * @return bool */ public function mkdir($dir, $mode = -1, $recursive = false) { if (!$this->precheck()) { return false; } $dir = $this->realpath($dir); if ($recursive) { $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); if (empty($dirs[0])) { array_shift($dirs); $dirs[0] = '/' . $dirs[0]; } for ($i = 0; $i < count($dirs); $i++) { $temp = array_slice($dirs, 0, $i + 1); $temp = implode('/', $temp); $result = $this->mkdir_helper($temp, $mode); } return $result; } return $this->mkdir_helper($dir, $mode); } /** * Helper function for directory creation * * @param string $dir * @param int $mode * @return bool */ private function mkdir_helper($dir, $mode) { // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing) $this->send_sftp_packet(NET_SFTP_MKDIR, Strings::packSSH2('s', $dir) . "\0\0\0\0"); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); return false; } if ($mode !== -1) { $this->chmod($mode, $dir); } return true; } /** * Removes a directory. * * @param string $dir * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool */ public function rmdir($dir) { if (!$this->precheck()) { return false; } $dir = $this->realpath($dir); if ($dir === false) { return false; } $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $dir)); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? $this->logError($response, $status); return false; } $this->remove_from_stat_cache($dir); // the following will do a soft delete, which would be useful if you deleted a file // and then tried to do a stat on the deleted file. the above, in contrast, does // a hard delete //$this->update_stat_cache($dir, false); return true; } /** * Uploads a file to the SFTP server. * * By default, \phpseclib3\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. * So, for example, if you set $data to 'filename.ext' and then do \phpseclib3\Net\SFTP::get(), you will get a file, twelve bytes * long, containing 'filename.ext' as its contents. * * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how * large $remote_file will be, as well. * * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number * of bytes to return, and returns a string if there is some data or null if there is no more data * * If $data is a resource then it'll be used as a resource instead. * * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take * care of that, yourself. * * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: * * self::SOURCE_LOCAL_FILE | self::RESUME * * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace * self::RESUME with self::RESUME_START. * * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed. * * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the * middle of one. * * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE. * * {@internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib3\Net\SFTP::setMode().} * * @param string $remote_file * @param string|resource $data * @param int $mode * @param int $start * @param int $local_start * @param callable|null $progressCallback * @throws \UnexpectedValueException on receipt of unexpected packets * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid * @throws \phpseclib3\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist * @return bool */ public function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) { if (!$this->precheck()) { return false; } $remote_file = $this->realpath($remote_file); if ($remote_file === false) { return false; } $this->remove_from_stat_cache($remote_file); if ($this->version >= 5) { $flags = NET_SFTP_OPEN_OPEN_OR_CREATE; } else { $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." // in practice, it doesn't seem to do that. //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; } if ($start >= 0) { $offset = $start; } elseif ($mode & (self::RESUME | self::RESUME_START)) { // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called $stat = $this->stat($remote_file); $offset = $stat !== false && $stat['size'] ? $stat['size'] : 0; } else { $offset = 0; if ($this->version >= 5) { $flags = NET_SFTP_OPEN_CREATE_TRUNCATE; } else { $flags |= NET_SFTP_OPEN_TRUNCATE; } } $this->remove_from_stat_cache($remote_file); $packet = Strings::packSSH2('s', $remote_file); $packet .= $this->version >= 5 ? pack('N3', 0, $flags, 0) : pack('N2', $flags, 0); $this->send_sftp_packet(NET_SFTP_OPEN, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->logError($response); return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 $dataCallback = false; switch (true) { case $mode & self::SOURCE_CALLBACK: if (!is_callable($data)) { throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); } $dataCallback = $data; // do nothing break; case is_resource($data): $mode = $mode & ~self::SOURCE_LOCAL_FILE; $info = stream_get_meta_data($data); if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { $fp = fopen('php://memory', 'w+'); stream_copy_to_stream($data, $fp); rewind($fp); } else { $fp = $data; } break; case $mode & self::SOURCE_LOCAL_FILE: if (!is_file($data)) { throw new FileNotFoundException("$data is not a valid file"); } $fp = @fopen($data, 'rb'); if (!$fp) { return false; } } if (isset($fp)) { $stat = fstat($fp); $size = !empty($stat) ? $stat['size'] : 0; if ($local_start >= 0) { fseek($fp, $local_start); $size -= $local_start; } elseif ($mode & self::RESUME) { fseek($fp, $offset); $size -= $offset; } } elseif ($dataCallback) { $size = 0; } else { $size = strlen($data); } $sent = 0; $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $sftp_packet_size = $this->max_sftp_packet; // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header" $sftp_packet_size -= strlen($handle) + 25; $i = $j = 0; while ($dataCallback || ($size === 0 || $sent < $size)) { if ($dataCallback) { $temp = $dataCallback($sftp_packet_size); if (is_null($temp)) { break; } } else { $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); if ($temp === false || $temp === '') { break; } } $subtemp = $offset + $sent; $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); try { $this->send_sftp_packet(NET_SFTP_WRITE, $packet, $j); } catch (\Exception $e) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); } throw $e; } $sent += strlen($temp); if (is_callable($progressCallback)) { $progressCallback($sent); } $i++; $j++; if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) { if (!$this->read_put_responses($i)) { $i = 0; break; } $i = 0; } } $result = $this->close_handle($handle); if (!$this->read_put_responses($i)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); } $this->close_handle($handle); return false; } if ($mode & SFTP::SOURCE_LOCAL_FILE) { if (isset($fp) && is_resource($fp)) { fclose($fp); } if ($this->preserveTime) { $stat = stat($data); $attr = $this->version < 4 ? pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']) : Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $stat['atime'], $stat['mtime']); if (!$this->setstat($remote_file, $attr, false)) { throw new \RuntimeException('Error setting file time'); } } } return $result; } /** * Reads multiple successive SSH_FXP_WRITE responses * * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i * SSH_FXP_WRITEs, in succession, and then reading $i responses. * * @param int $i * @return bool * @throws \UnexpectedValueException on receipt of unexpected packets */ private function read_put_responses($i) { while ($i--) { $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); break; } } return $i < 0; } /** * Close handle * * @param string $handle * @return bool * @throws \UnexpectedValueException on receipt of unexpected packets */ private function close_handle($handle) { $this->send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle)); // "The client MUST release all resources associated with the handle regardless of the status." // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); return false; } return true; } /** * Downloads a file from the SFTP server. * * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the * operation. * * $offset and $length can be used to download files in chunks. * * @param string $remote_file * @param string|bool|resource|callable $local_file * @param int $offset * @param int $length * @param callable|null $progressCallback * @throws \UnexpectedValueException on receipt of unexpected packets * @return string|bool */ public function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) { if (!$this->precheck()) { return false; } $remote_file = $this->realpath($remote_file); if ($remote_file === false) { return false; } $packet = Strings::packSSH2('s', $remote_file); $packet .= $this->version >= 5 ? pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) : pack('N2', NET_SFTP_OPEN_READ, 0); $this->send_sftp_packet(NET_SFTP_OPEN, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED $this->logError($response); return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } if (is_resource($local_file)) { $fp = $local_file; $stat = fstat($fp); $res_offset = $stat['size']; } else { $res_offset = 0; if ($local_file !== false && !is_callable($local_file)) { $fp = fopen($local_file, 'wb'); if (!$fp) { return false; } } else { $content = ''; } } $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file); $start = $offset; $read = 0; while (true) { $i = 0; while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { $tempoffset = $start + $read; $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; $packet = Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); try { $this->send_sftp_packet(NET_SFTP_READ, $packet, $i); } catch (\Exception $e) { if ($fclose_check) { fclose($fp); } throw $e; } $packet = null; $read += $packet_size; $i++; } if (!$i) { break; } $packets_sent = $i - 1; $clear_responses = false; while ($i > 0) { $i--; if ($clear_responses) { $this->get_sftp_packet($packets_sent - $i); continue; } else { $response = $this->get_sftp_packet($packets_sent - $i); } switch ($this->packet_type) { case NET_SFTP_DATA: $temp = substr($response, 4); $offset += strlen($temp); if ($local_file === false) { $content .= $temp; } elseif (is_callable($local_file)) { $local_file($temp); } else { fputs($fp, $temp); } if (is_callable($progressCallback)) { call_user_func($progressCallback, $offset); } $temp = null; break; case NET_SFTP_STATUS: // could, in theory, return false if !strlen($content) but we'll hold off for the time being $this->logError($response); $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses break; default: if ($fclose_check) { fclose($fp); } if ($this->channel_close) { $this->partial_init = false; $this->init_sftp_connection(); return false; } else { throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } } $response = null; } if ($clear_responses) { break; } } if ($fclose_check) { fclose($fp); if ($this->preserveTime) { $stat = $this->stat($remote_file); touch($local_file, $stat['mtime'], $stat['atime']); } } if (!$this->close_handle($handle)) { return false; } // if $content isn't set that means a file was written to return isset($content) ? $content : true; } /** * Deletes a file on the SFTP server. * * @param string $path * @param bool $recursive * @return bool * @throws \UnexpectedValueException on receipt of unexpected packets */ public function delete($path, $recursive = true) { if (!$this->precheck()) { return false; } if (is_object($path)) { // It's an object. Cast it as string before we check anything else. $path = (string) $path; } if (!is_string($path) || $path == '') { return false; } $path = $this->realpath($path); if ($path === false) { return false; } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 $this->send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path)); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); if (!$recursive) { return false; } $i = 0; $result = $this->delete_recursive($path, $i); $this->read_put_responses($i); return $result; } $this->remove_from_stat_cache($path); return true; } /** * Recursively deletes directories on the SFTP server * * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. * * @param string $path * @param int $i * @return bool */ private function delete_recursive($path, &$i) { if (!$this->read_put_responses($i)) { return false; } $i = 0; $entries = $this->readlist($path, true); // The folder does not exist at all, so we cannot delete it. if ($entries === NET_SFTP_STATUS_NO_SUCH_FILE) { return false; } // Normally $entries would have at least . and .. but it might not if the directories // permissions didn't allow reading. If this happens then default to an empty list of files. if ($entries === false || is_int($entries)) { $entries = []; } unset($entries['.'], $entries['..']); foreach ($entries as $filename => $props) { if (!isset($props['type'])) { return false; } $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { if (!$this->delete_recursive($temp, $i)) { return false; } } else { $this->send_sftp_packet(NET_SFTP_REMOVE, Strings::packSSH2('s', $temp)); $this->remove_from_stat_cache($temp); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->read_put_responses($i)) { return false; } $i = 0; } } } $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $path)); $this->remove_from_stat_cache($path); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->read_put_responses($i)) { return false; } $i = 0; } return true; } /** * Checks whether a file or directory exists * * @param string $path * @return bool */ public function file_exists($path) { if ($this->use_stat_cache) { if (!$this->precheck()) { return false; } $path = $this->realpath($path); $result = $this->query_stat_cache($path); if (isset($result)) { // return true if $result is an array or if it's an stdClass object return $result !== false; } } return $this->stat($path) !== false; } /** * Tells whether the filename is a directory * * @param string $path * @return bool */ public function is_dir($path) { $result = $this->get_stat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_DIRECTORY; } /** * Tells whether the filename is a regular file * * @param string $path * @return bool */ public function is_file($path) { $result = $this->get_stat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_REGULAR; } /** * Tells whether the filename is a symbolic link * * @param string $path * @return bool */ public function is_link($path) { $result = $this->get_lstat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_SYMLINK; } /** * Tells whether a file exists and is readable * * @param string $path * @return bool */ public function is_readable($path) { if (!$this->precheck()) { return false; } $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_READ, 0); $this->send_sftp_packet(NET_SFTP_OPEN, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return true; case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED return false; default: throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } } /** * Tells whether the filename is writable * * @param string $path * @return bool */ public function is_writable($path) { if (!$this->precheck()) { return false; } $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_WRITE, 0); $this->send_sftp_packet(NET_SFTP_OPEN, $packet); $response = $this->get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return true; case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED return false; default: throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } } /** * Tells whether the filename is writeable * * Alias of is_writable * * @param string $path * @return bool */ public function is_writeable($path) { return $this->is_writable($path); } /** * Gets last access time of file * * @param string $path * @return mixed */ public function fileatime($path) { return $this->get_stat_cache_prop($path, 'atime'); } /** * Gets file modification time * * @param string $path * @return mixed */ public function filemtime($path) { return $this->get_stat_cache_prop($path, 'mtime'); } /** * Gets file permissions * * @param string $path * @return mixed */ public function fileperms($path) { return $this->get_stat_cache_prop($path, 'mode'); } /** * Gets file owner * * @param string $path * @return mixed */ public function fileowner($path) { return $this->get_stat_cache_prop($path, 'uid'); } /** * Gets file group * * @param string $path * @return mixed */ public function filegroup($path) { return $this->get_stat_cache_prop($path, 'gid'); } /** * Recursively go through rawlist() output to get the total filesize * * @return int */ private static function recursiveFilesize(array $files) { $size = 0; foreach ($files as $name => $file) { if ($name == '.' || $name == '..') { continue; } $size += is_array($file) ? self::recursiveFilesize($file) : $file->size; } return $size; } /** * Gets file size * * @param string $path * @param bool $recursive * @return mixed */ public function filesize($path, $recursive = false) { return !$recursive || $this->filetype($path) != 'dir' ? $this->get_stat_cache_prop($path, 'size') : self::recursiveFilesize($this->rawlist($path, true)); } /** * Gets file type * * @param string $path * @return string|false */ public function filetype($path) { $type = $this->get_stat_cache_prop($path, 'type'); if ($type === false) { return false; } switch ($type) { case NET_SFTP_TYPE_BLOCK_DEVICE: return 'block'; case NET_SFTP_TYPE_CHAR_DEVICE: return 'char'; case NET_SFTP_TYPE_DIRECTORY: return 'dir'; case NET_SFTP_TYPE_FIFO: return 'fifo'; case NET_SFTP_TYPE_REGULAR: return 'file'; case NET_SFTP_TYPE_SYMLINK: return 'link'; default: return false; } } /** * Return a stat properity * * Uses cache if appropriate. * * @param string $path * @param string $prop * @return mixed */ private function get_stat_cache_prop($path, $prop) { return $this->get_xstat_cache_prop($path, $prop, 'stat'); } /** * Return an lstat properity * * Uses cache if appropriate. * * @param string $path * @param string $prop * @return mixed */ private function get_lstat_cache_prop($path, $prop) { return $this->get_xstat_cache_prop($path, $prop, 'lstat'); } /** * Return a stat or lstat properity * * Uses cache if appropriate. * * @param string $path * @param string $prop * @param string $type * @return mixed */ private function get_xstat_cache_prop($path, $prop, $type) { if (!$this->precheck()) { return false; } if ($this->use_stat_cache) { $path = $this->realpath($path); $result = $this->query_stat_cache($path); if (is_object($result) && isset($result->$type)) { return $result->{$type}[$prop]; } } $result = $this->$type($path); if ($result === false || !isset($result[$prop])) { return false; } return $result[$prop]; } /** * Renames a file or a directory on the SFTP server. * * If the file already exists this will return false * * @param string $oldname * @param string $newname * @return bool * @throws \UnexpectedValueException on receipt of unexpected packets */ public function rename($oldname, $newname) { if (!$this->precheck()) { return false; } $oldname = $this->realpath($oldname); $newname = $this->realpath($newname); if ($oldname === false || $newname === false) { return false; } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 $packet = Strings::packSSH2('ss', $oldname, $newname); if ($this->version >= 5) { /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 , 'flags' is 0 or a combination of: SSH_FXP_RENAME_OVERWRITE 0x00000001 SSH_FXP_RENAME_ATOMIC 0x00000002 SSH_FXP_RENAME_NATIVE 0x00000004 (none of these are currently supported) */ $packet .= "\0\0\0\0"; } $this->send_sftp_packet(NET_SFTP_RENAME, $packet); $response = $this->get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED list($status) = Strings::unpackSSH2('N', $response); if ($status != NET_SFTP_STATUS_OK) { $this->logError($response, $status); return false; } // don't move the stat cache entry over since this operation could very well change the // atime and mtime attributes //$this->update_stat_cache($newname, $this->query_stat_cache($oldname)); $this->remove_from_stat_cache($oldname); $this->remove_from_stat_cache($newname); return true; } /** * Parse Time * * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info. * * @param string $key * @param int $flags * @param string $response * @return array */ private function parseTime($key, $flags, &$response) { $attr = []; list($attr[$key]) = Strings::unpackSSH2('Q', $response); if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) { list($attr[$key . '-nseconds']) = Strings::unpackSSH2('N', $response); } return $attr; } /** * Parse Attributes * * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. * * @param string $response * @return array */ protected function parseAttributes(&$response) { if ($this->version >= 4) { list($flags, $attr['type']) = Strings::unpackSSH2('NC', $response); } else { list($flags) = Strings::unpackSSH2('N', $response); } foreach (self::$attributes as $key => $value) { switch ($flags & $key) { case NET_SFTP_ATTR_UIDGID: if ($this->version > 3) { continue 2; } break; case NET_SFTP_ATTR_CREATETIME: case NET_SFTP_ATTR_MODIFYTIME: case NET_SFTP_ATTR_ACL: case NET_SFTP_ATTR_OWNERGROUP: case NET_SFTP_ATTR_SUBSECOND_TIMES: if ($this->version < 4) { continue 2; } break; case NET_SFTP_ATTR_BITS: if ($this->version < 5) { continue 2; } break; case NET_SFTP_ATTR_ALLOCATION_SIZE: case NET_SFTP_ATTR_TEXT_HINT: case NET_SFTP_ATTR_MIME_TYPE: case NET_SFTP_ATTR_LINK_COUNT: case NET_SFTP_ATTR_UNTRANSLATED_NAME: case NET_SFTP_ATTR_CTIME: if ($this->version < 6) { continue 2; } } switch ($flags & $key) { case NET_SFTP_ATTR_SIZE: // 0x00000001 // The size attribute is defined as an unsigned 64-bit integer. // The following will use floats on 32-bit platforms, if necessary. // As can be seen in the BigInteger class, floats are generally // IEEE 754 binary64 "double precision" on such platforms and // as such can represent integers of at least 2^50 without loss // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. list($attr['size']) = Strings::unpackSSH2('Q', $response); break; case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) list($attr['uid'], $attr['gid']) = Strings::unpackSSH2('NN', $response); break; case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 list($attr['mode']) = Strings::unpackSSH2('N', $response); $fileType = $this->parseMode($attr['mode']); if ($this->version < 4 && $fileType !== false) { $attr += ['type' => $fileType]; } break; case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 if ($this->version >= 4) { $attr += $this->parseTime('atime', $flags, $response); break; } list($attr['atime'], $attr['mtime']) = Strings::unpackSSH2('NN', $response); break; case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+) $attr += $this->parseTime('createtime', $flags, $response); break; case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020 $attr += $this->parseTime('mtime', $flags, $response); break; case NET_SFTP_ATTR_ACL: // 0x00000040 // access control list // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7 // currently unsupported list($count) = Strings::unpackSSH2('N', $response); for ($i = 0; $i < $count; $i++) { list($type, $flag, $mask, $who) = Strings::unpackSSH2('N3s', $result); } break; case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080 list($attr['owner'], $attr['$group']) = Strings::unpackSSH2('ss', $response); break; case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100 break; case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+) // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8 // currently unsupported // tells if you file is: // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse // append only, immutable, sync list($attrib_bits, $attrib_bits_valid) = Strings::unpackSSH2('N2', $response); // if we were actually gonna implement the above it ought to be // $attr['attrib-bits'] and $attr['attrib-bits-valid'] // eg. - instead of _ break; case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+) // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4 // represents the number of bytes that the file consumes on the disk. will // usually be larger than the 'size' field list($attr['allocation-size']) = Strings::unpackSSH2('Q', $response); break; case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10 // currently unsupported // tells if file is "known text", "guessed text", "known binary", "guessed binary" list($text_hint) = Strings::unpackSSH2('C', $response); // the above should be $attr['text-hint'] break; case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11 list($attr['mime-type']) = Strings::unpackSSH2('s', $response); break; case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12 list($attr['link-count']) = Strings::unpackSSH2('N', $response); break; case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13 list($attr['untranslated-name']) = Strings::unpackSSH2('s', $response); break; case NET_SFTP_ATTR_CTIME: // 0x00008000 // 'ctime' contains the last time the file attributes were changed. The // exact meaning of this field depends on the server. $attr += $this->parseTime('ctime', $flags, $response); break; case NET_SFTP_ATTR_EXTENDED: // 0x80000000 list($count) = Strings::unpackSSH2('N', $response); for ($i = 0; $i < $count; $i++) { list($key, $value) = Strings::unpackSSH2('ss', $response); $attr[$key] = $value; } } } return $attr; } /** * Attempt to identify the file type * * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway * * @param int $mode * @return int */ private function parseMode($mode) { // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 // see, also, http://linux.die.net/man/2/stat switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 case 0000000: // no file type specified - figure out the file type using alternative means return false; case 0040000: return NET_SFTP_TYPE_DIRECTORY; case 0100000: return NET_SFTP_TYPE_REGULAR; case 0120000: return NET_SFTP_TYPE_SYMLINK; // new types introduced in SFTPv5+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 case 0010000: // named pipe (fifo) return NET_SFTP_TYPE_FIFO; case 0020000: // character special return NET_SFTP_TYPE_CHAR_DEVICE; case 0060000: // block special return NET_SFTP_TYPE_BLOCK_DEVICE; case 0140000: // socket return NET_SFTP_TYPE_SOCKET; case 0160000: // whiteout // "SPECIAL should be used for files that are of // a known type which cannot be expressed in the protocol" return NET_SFTP_TYPE_SPECIAL; default: return NET_SFTP_TYPE_UNKNOWN; } } /** * Parse Longname * * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open * a file as a directory and see if an error is returned or you could try to parse the * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. * The result is returned using the * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. * * If the longname is in an unrecognized format bool(false) is returned. * * @param string $longname * @return mixed */ private function parseLongname($longname) { // http://en.wikipedia.org/wiki/Unix_file_types // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { switch ($longname[0]) { case '-': return NET_SFTP_TYPE_REGULAR; case 'd': return NET_SFTP_TYPE_DIRECTORY; case 'l': return NET_SFTP_TYPE_SYMLINK; default: return NET_SFTP_TYPE_SPECIAL; } } return false; } /** * Sends SFTP Packets * * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. * * @param int $type * @param string $data * @param int $request_id * @see self::_get_sftp_packet() * @see self::send_channel_packet() * @return void */ private function send_sftp_packet($type, $data, $request_id = 1) { // in SSH2.php the timeout is cumulative per function call. eg. exec() will // timeout after 10s. but for SFTP.php it's cumulative per packet $this->curTimeout = $this->timeout; $packet = $this->use_request_id ? pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); $start = microtime(true); $this->send_channel_packet(self::CHANNEL, $packet); $stop = microtime(true); if (defined('NET_SFTP_LOGGING')) { $packet_type = '-> ' . self::$packet_types[$type] . ' (' . round($stop - $start, 4) . 's)'; $this->append_log($packet_type, $data); } } /** * Resets a connection for re-use * * @param int $reason */ protected function reset_connection($reason) { parent::reset_connection($reason); $this->use_request_id = false; $this->pwd = false; $this->requestBuffer = []; $this->partial_init = false; } /** * Receives SFTP Packets * * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. * * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA * messages containing one SFTP packet. * * @see self::_send_sftp_packet() * @return string */ private function get_sftp_packet($request_id = null) { $this->channel_close = false; if (isset($request_id) && isset($this->requestBuffer[$request_id])) { $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; $temp = $this->requestBuffer[$request_id]['packet']; unset($this->requestBuffer[$request_id]); return $temp; } // in SSH2.php the timeout is cumulative per function call. eg. exec() will // timeout after 10s. but for SFTP.php it's cumulative per packet $this->curTimeout = $this->timeout; $start = microtime(true); // SFTP packet length while (strlen($this->packet_buffer) < 4) { $temp = $this->get_channel_packet(self::CHANNEL, true); if ($temp === true) { if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { $this->channel_close = true; } $this->packet_type = false; $this->packet_buffer = ''; return false; } $this->packet_buffer .= $temp; } if (strlen($this->packet_buffer) < 4) { throw new \RuntimeException('Packet is too small'); } extract(unpack('Nlength', Strings::shift($this->packet_buffer, 4))); /** @var integer $length */ $tempLength = $length; $tempLength -= strlen($this->packet_buffer); // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { throw new \RuntimeException('Invalid Size'); } // SFTP packet type and data payload while ($tempLength > 0) { $temp = $this->get_channel_packet(self::CHANNEL, true); if ($temp === true) { if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { $this->channel_close = true; } $this->packet_type = false; $this->packet_buffer = ''; return false; } $this->packet_buffer .= $temp; $tempLength -= strlen($temp); } $stop = microtime(true); $this->packet_type = ord(Strings::shift($this->packet_buffer)); if ($this->use_request_id) { extract(unpack('Npacket_id', Strings::shift($this->packet_buffer, 4))); // remove the request id $length -= 5; // account for the request id and the packet type } else { $length -= 1; // account for the packet type } $packet = Strings::shift($this->packet_buffer, $length); if (defined('NET_SFTP_LOGGING')) { $packet_type = '<- ' . self::$packet_types[$this->packet_type] . ' (' . round($stop - $start, 4) . 's)'; $this->append_log($packet_type, $packet); } if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { $this->requestBuffer[$packet_id] = [ 'packet_type' => $this->packet_type, 'packet' => $packet ]; return $this->get_sftp_packet($request_id); } return $packet; } /** * Logs data packets * * Makes sure that only the last 1MB worth of packets will be logged * * @param string $message_number * @param string $message */ private function append_log($message_number, $message) { $this->append_log_helper( NET_SFTP_LOGGING, $message_number, $message, $this->packet_type_log, $this->packet_log, $this->log_size, $this->realtime_log_file, $this->realtime_log_wrap, $this->realtime_log_size ); } /** * Returns a log of the packets that have been sent and received. * * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') * * @return array|string|false */ public function getSFTPLog() { if (!defined('NET_SFTP_LOGGING')) { return false; } switch (NET_SFTP_LOGGING) { case self::LOG_COMPLEX: return $this->format_log($this->packet_log, $this->packet_type_log); break; //case self::LOG_SIMPLE: default: return $this->packet_type_log; } } /** * Returns all errors on the SFTP layer * * @return array */ public function getSFTPErrors() { return $this->sftp_errors; } /** * Returns the last error on the SFTP layer * * @return string */ public function getLastSFTPError() { return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; } /** * Get supported SFTP versions * * @return array */ public function getSupportedVersions() { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } if (!$this->partial_init) { $this->partial_init_sftp_connection(); } $temp = ['version' => $this->defaultVersion]; if (isset($this->extensions['versions'])) { $temp['extensions'] = $this->extensions['versions']; } return $temp; } /** * Get supported SFTP versions * * @return int|false */ public function getNegotiatedVersion() { if (!$this->precheck()) { return false; } return $this->version; } /** * Set preferred version * * If you're preferred version isn't supported then the highest supported * version of SFTP will be utilized. Set to null or false or int(0) to * unset the preferred version * * @param int $version */ public function setPreferredVersion($version) { $this->preferredVersion = $version; } /** * Disconnect * * @param int $reason * @return false */ protected function disconnect_helper($reason) { $this->pwd = false; return parent::disconnect_helper($reason); } /** * Enable Date Preservation * */ public function enableDatePreservation() { $this->preserveTime = true; } /** * Disable Date Preservation * */ public function disableDatePreservation() { $this->preserveTime = false; } } PK2A#]͍�+%S%S8vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.phpnu�[���<?php /** * SFTP Stream Wrapper * * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc. * * PHP version 5 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2013 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Net\SFTP; use phpseclib3\Crypt\Common\PrivateKey; use phpseclib3\Net\SFTP; use phpseclib3\Net\SSH2; /** * SFTP Stream Wrapper * * @author Jim Wigginton <terrafrost@php.net> */ class Stream { /** * SFTP instances * * Rather than re-create the connection we re-use instances if possible * * @var array */ public static $instances; /** * SFTP instance * * @var object */ private $sftp; /** * Path * * @var string */ private $path; /** * Mode * * @var string */ private $mode; /** * Position * * @var int */ private $pos; /** * Size * * @var int */ private $size; /** * Directory entries * * @var array */ private $entries; /** * EOF flag * * @var bool */ private $eof; /** * Context resource * * Technically this needs to be publicly accessible so PHP can set it directly * * @var resource */ public $context; /** * Notification callback function * * @var callable */ private $notification; /** * Registers this class as a URL wrapper. * * @param string $protocol The wrapper name to be registered. * @return bool True on success, false otherwise. */ public static function register($protocol = 'sftp') { if (in_array($protocol, stream_get_wrappers(), true)) { return false; } return stream_wrapper_register($protocol, get_called_class()); } /** * The Constructor * */ public function __construct() { if (defined('NET_SFTP_STREAM_LOGGING')) { echo "__construct()\r\n"; } } /** * Path Parser * * Extract a path from a URI and actually connect to an SSH server if appropriate * * If "notification" is set as a context parameter the message code for successful login is * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. * * @param string $path * @return string */ protected function parse_path($path) { $orig = $path; extract(parse_url($path) + ['port' => 22]); if (isset($query)) { $path .= '?' . $query; } elseif (preg_match('/(\?|\?#)$/', $orig)) { $path .= '?'; } if (isset($fragment)) { $path .= '#' . $fragment; } elseif ($orig[strlen($orig) - 1] == '#') { $path .= '#'; } if (!isset($host)) { return false; } if (isset($this->context)) { $context = stream_context_get_params($this->context); if (isset($context['notification'])) { $this->notification = $context['notification']; } } if (preg_match('/^{[a-z0-9]+}$/i', $host)) { $host = SSH2::getConnectionByResourceId($host); if ($host === false) { return false; } $this->sftp = $host; } else { if (isset($this->context)) { $context = stream_context_get_options($this->context); } if (isset($context[$scheme]['session'])) { $sftp = $context[$scheme]['session']; } if (isset($context[$scheme]['sftp'])) { $sftp = $context[$scheme]['sftp']; } if (isset($sftp) && $sftp instanceof SFTP) { $this->sftp = $sftp; return $path; } if (isset($context[$scheme]['username'])) { $user = $context[$scheme]['username']; } if (isset($context[$scheme]['password'])) { $pass = $context[$scheme]['password']; } if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof PrivateKey) { $pass = $context[$scheme]['privkey']; } if (!isset($user) || !isset($pass)) { return false; } // casting $pass to a string is necessary in the event that it's a \phpseclib3\Crypt\RSA object if (isset(self::$instances[$host][$port][$user][(string) $pass])) { $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; } else { $this->sftp = new SFTP($host, $port); $this->sftp->disableStatCache(); if (isset($this->notification) && is_callable($this->notification)) { /* if !is_callable($this->notification) we could do this: user_error('fopen(): failed to call user notifier', E_USER_WARNING); the ftp wrapper gives errors like that when the notifier isn't callable. i've opted not to do that, however, since the ftp wrapper gives the line on which the fopen occurred as the line number - not the line that the user_error is on. */ call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); if (!$this->sftp->login($user, $pass)) { call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); return false; } call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); } else { if (!$this->sftp->login($user, $pass)) { return false; } } self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; } } return $path; } /** * Opens file or URL * * @param string $path * @param string $mode * @param int $options * @param string $opened_path * @return bool */ private function _stream_open($path, $mode, $options, &$opened_path) { $path = $this->parse_path($path); if ($path === false) { return false; } $this->path = $path; $this->size = $this->sftp->filesize($path); $this->mode = preg_replace('#[bt]$#', '', $mode); $this->eof = false; if ($this->size === false) { if ($this->mode[0] == 'r') { return false; } else { $this->sftp->touch($path); $this->size = 0; } } else { switch ($this->mode[0]) { case 'x': return false; case 'w': $this->sftp->truncate($path, 0); $this->size = 0; } } $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; return true; } /** * Read from stream * * @param int $count * @return mixed */ private function _stream_read($count) { switch ($this->mode) { case 'w': case 'a': case 'x': case 'c': return false; } // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite //if ($this->pos >= $this->size) { // $this->eof = true; // return false; //} $result = $this->sftp->get($this->path, false, $this->pos, $count); if (isset($this->notification) && is_callable($this->notification)) { if ($result === false) { call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); return 0; } // seems that PHP calls stream_read in 8k chunks call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); } if (empty($result)) { // ie. false or empty string $this->eof = true; return false; } $this->pos += strlen($result); return $result; } /** * Write to stream * * @param string $data * @return int|false */ private function _stream_write($data) { switch ($this->mode) { case 'r': return false; } $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos); if (isset($this->notification) && is_callable($this->notification)) { if (!$result) { call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); return 0; } // seems that PHP splits up strings into 8k blocks before calling stream_write call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); } if ($result === false) { return false; } $this->pos += strlen($data); if ($this->pos > $this->size) { $this->size = $this->pos; } $this->eof = false; return strlen($data); } /** * Retrieve the current position of a stream * * @return int */ private function _stream_tell() { return $this->pos; } /** * Tests for end-of-file on a file pointer * * In my testing there are four classes functions that normally effect the pointer: * fseek, fputs / fwrite, fgets / fread and ftruncate. * * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() * will return false. do fread($fp, 1) and feof() will then return true. * * @return bool */ private function _stream_eof() { return $this->eof; } /** * Seeks to specific location in a stream * * @param int $offset * @param int $whence * @return bool */ private function _stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: if ($offset < 0) { return false; } break; case SEEK_CUR: $offset += $this->pos; break; case SEEK_END: $offset += $this->size; } $this->pos = $offset; $this->eof = false; return true; } /** * Change stream options * * @param string $path * @param int $option * @param mixed $var * @return bool */ private function _stream_metadata($path, $option, $var) { $path = $this->parse_path($path); if ($path === false) { return false; } // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 switch ($option) { case 1: // PHP_STREAM_META_TOUCH $time = isset($var[0]) ? $var[0] : null; $atime = isset($var[1]) ? $var[1] : null; return $this->sftp->touch($path, $time, $atime); case 2: // PHP_STREAM_OWNER_NAME case 3: // PHP_STREAM_GROUP_NAME return false; case 4: // PHP_STREAM_META_OWNER return $this->sftp->chown($path, $var); case 5: // PHP_STREAM_META_GROUP return $this->sftp->chgrp($path, $var); case 6: // PHP_STREAM_META_ACCESS return $this->sftp->chmod($path, $var) !== false; } } /** * Retrieve the underlaying resource * * @param int $cast_as * @return resource */ private function _stream_cast($cast_as) { return $this->sftp->fsock; } /** * Advisory file locking * * @param int $operation * @return bool */ private function _stream_lock($operation) { return false; } /** * Renames a file or directory * * Attempts to rename oldname to newname, moving it between directories if necessary. * If newname exists, it will be overwritten. This is a departure from what \phpseclib3\Net\SFTP * does. * * @param string $path_from * @param string $path_to * @return bool */ private function _rename($path_from, $path_to) { $path1 = parse_url($path_from); $path2 = parse_url($path_to); unset($path1['path'], $path2['path']); if ($path1 != $path2) { return false; } $path_from = $this->parse_path($path_from); $path_to = parse_url($path_to); if ($path_from === false) { return false; } $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 // "It is an error if there already exists a file with the name specified by newpath." // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 if (!$this->sftp->rename($path_from, $path_to)) { if ($this->sftp->stat($path_to)) { return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); } return false; } return true; } /** * Open directory handle * * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and * removed in 5.4 I'm just going to ignore it. * * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting * the SFTP specs: * * The SSH_FXP_NAME response has the following format: * * uint32 id * uint32 count * repeats count times: * string filename * string longname * ATTRS attrs * * @param string $path * @param int $options * @return bool */ private function _dir_opendir($path, $options) { $path = $this->parse_path($path); if ($path === false) { return false; } $this->pos = 0; $this->entries = $this->sftp->nlist($path); return $this->entries !== false; } /** * Read entry from directory handle * * @return mixed */ private function _dir_readdir() { if (isset($this->entries[$this->pos])) { return $this->entries[$this->pos++]; } return false; } /** * Rewind directory handle * * @return bool */ private function _dir_rewinddir() { $this->pos = 0; return true; } /** * Close directory handle * * @return bool */ private function _dir_closedir() { return true; } /** * Create a directory * * Only valid $options is STREAM_MKDIR_RECURSIVE * * @param string $path * @param int $mode * @param int $options * @return bool */ private function _mkdir($path, $mode, $options) { $path = $this->parse_path($path); if ($path === false) { return false; } return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); } /** * Removes a directory * * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however, * <http://php.net/rmdir> does not have a $recursive parameter as mkdir() does so I don't know how * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as * $options. What does 8 correspond to? * * @param string $path * @param int $options * @return bool */ private function _rmdir($path, $options) { $path = $this->parse_path($path); if ($path === false) { return false; } return $this->sftp->rmdir($path); } /** * Flushes the output * * See <http://php.net/fflush>. Always returns true because \phpseclib3\Net\SFTP doesn't cache stuff before writing * * @return bool */ private function _stream_flush() { return true; } /** * Retrieve information about a file resource * * @return mixed */ private function _stream_stat() { $results = $this->sftp->stat($this->path); if ($results === false) { return false; } return $results; } /** * Delete a file * * @param string $path * @return bool */ private function _unlink($path) { $path = $this->parse_path($path); if ($path === false) { return false; } return $this->sftp->delete($path, false); } /** * Retrieve information about a file * * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib3\Net\SFTP\Stream is quiet by default * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll * cross that bridge when and if it's reached * * @param string $path * @param int $flags * @return mixed */ private function _url_stat($path, $flags) { $path = $this->parse_path($path); if ($path === false) { return false; } $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); if ($results === false) { return false; } return $results; } /** * Truncate stream * * @param int $new_size * @return bool */ private function _stream_truncate($new_size) { if (!$this->sftp->truncate($this->path, $new_size)) { return false; } $this->eof = false; $this->size = $new_size; return true; } /** * Change stream options * * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. * The other two aren't supported because of limitations in \phpseclib3\Net\SFTP. * * @param int $option * @param int $arg1 * @param int $arg2 * @return bool */ private function _stream_set_option($option, $arg1, $arg2) { return false; } /** * Close an resource * */ private function _stream_close() { } /** * __call Magic Method * * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function * lets you figure that out. * * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. * * @param string $name * @param array $arguments * @return mixed */ public function __call($name, array $arguments) { if (defined('NET_SFTP_STREAM_LOGGING')) { echo $name . '('; $last = count($arguments) - 1; foreach ($arguments as $i => $argument) { var_export($argument); if ($i != $last) { echo ','; } } echo ")\r\n"; } $name = '_' . $name; if (!method_exists($this, $name)) { return false; } return $this->$name(...$arguments); } } PK2A#]��EH����1vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.phpnu�[���<?php /** * Pure-PHP implementation of SSHv2. * * PHP version 5 * * Here are some examples of how to use this library: * <code> * <?php * include 'vendor/autoload.php'; * * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld'); * if (!$ssh->login('username', 'password')) { * exit('Login Failed'); * } * * echo $ssh->exec('pwd'); * echo $ssh->exec('ls -la'); * ?> * </code> * * <code> * <?php * include 'vendor/autoload.php'; * * $key = \phpseclib3\Crypt\PublicKeyLoader::load('...', '(optional) password'); * * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld'); * if (!$ssh->login('username', $key)) { * exit('Login Failed'); * } * * echo $ssh->read('username@username:~$'); * $ssh->write("ls -la\n"); * echo $ssh->read('username@username:~$'); * ?> * </code> * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib3\Net; use phpseclib3\Common\Functions\Strings; use phpseclib3\Crypt\Blowfish; use phpseclib3\Crypt\ChaCha20; use phpseclib3\Crypt\Common\AsymmetricKey; use phpseclib3\Crypt\Common\PrivateKey; use phpseclib3\Crypt\Common\PublicKey; use phpseclib3\Crypt\Common\SymmetricKey; use phpseclib3\Crypt\DH; use phpseclib3\Crypt\DSA; use phpseclib3\Crypt\EC; use phpseclib3\Crypt\Hash; use phpseclib3\Crypt\Random; use phpseclib3\Crypt\RC4; use phpseclib3\Crypt\Rijndael; use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\TripleDES; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. use phpseclib3\Crypt\Twofish; use phpseclib3\Exception\ConnectionClosedException; use phpseclib3\Exception\InsufficientSetupException; use phpseclib3\Exception\NoSupportedAlgorithmsException; use phpseclib3\Exception\UnableToConnectException; use phpseclib3\Exception\UnsupportedAlgorithmException; use phpseclib3\Exception\UnsupportedCurveException; use phpseclib3\Math\BigInteger; use phpseclib3\System\SSH\Agent; /** * Pure-PHP implementation of SSHv2. * * @author Jim Wigginton <terrafrost@php.net> */ class SSH2 { /**#@+ * Compression Types * */ /** * No compression */ const NET_SSH2_COMPRESSION_NONE = 1; /** * zlib compression */ const NET_SSH2_COMPRESSION_ZLIB = 2; /** * zlib@openssh.com */ const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3; /**#@-*/ // Execution Bitmap Masks const MASK_CONSTRUCTOR = 0x00000001; const MASK_CONNECTED = 0x00000002; const MASK_LOGIN_REQ = 0x00000004; const MASK_LOGIN = 0x00000008; const MASK_SHELL = 0x00000010; const MASK_WINDOW_ADJUST = 0x00000020; /* * Channel constants * * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a * recipient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet: * The 'recipient channel' is the channel number given in the original * open request, and 'sender channel' is the channel number allocated by * the other side. * * @see \phpseclib3\Net\SSH2::send_channel_packet() * @see \phpseclib3\Net\SSH2::get_channel_packet() */ const CHANNEL_EXEC = 1; // PuTTy uses 0x100 const CHANNEL_SHELL = 2; const CHANNEL_SUBSYSTEM = 3; const CHANNEL_AGENT_FORWARD = 4; const CHANNEL_KEEP_ALIVE = 5; /** * Returns the message numbers * * @see \phpseclib3\Net\SSH2::getLog() */ const LOG_SIMPLE = 1; /** * Returns the message content * * @see \phpseclib3\Net\SSH2::getLog() */ const LOG_COMPLEX = 2; /** * Outputs the content real-time */ const LOG_REALTIME = 3; /** * Dumps the content real-time to a file */ const LOG_REALTIME_FILE = 4; /** * Outputs the message numbers real-time */ const LOG_SIMPLE_REALTIME = 5; /** * Make sure that the log never gets larger than this * * @see \phpseclib3\Net\SSH2::getLog() */ const LOG_MAX_SIZE = 1048576; // 1024 * 1024 /** * Returns when a string matching $expect exactly is found * * @see \phpseclib3\Net\SSH2::read() */ const READ_SIMPLE = 1; /** * Returns when a string matching the regular expression $expect is found * * @see \phpseclib3\Net\SSH2::read() */ const READ_REGEX = 2; /** * Returns whenever a data packet is received. * * Some data packets may only contain a single character so it may be necessary * to call read() multiple times when using this option * * @see \phpseclib3\Net\SSH2::read() */ const READ_NEXT = 3; /** * The SSH identifier * * @var string */ private $identifier; /** * The Socket Object * * @var resource|closed-resource|null */ public $fsock; /** * Execution Bitmap * * The bits that are set represent functions that have been called already. This is used to determine * if a requisite function has been successfully executed. If not, an error should be thrown. * * @var int */ protected $bitmap = 0; /** * Error information * * @see self::getErrors() * @see self::getLastError() * @var array */ private $errors = []; /** * Server Identifier * * @see self::getServerIdentification() * @var string|false */ protected $server_identifier = false; /** * Key Exchange Algorithms * * @see self::getKexAlgorithims() * @var array|false */ private $kex_algorithms = false; /** * Key Exchange Algorithm * * @see self::getMethodsNegotiated() * @var string|false */ private $kex_algorithm = false; /** * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods * * @see self::_key_exchange() * @var int */ private $kex_dh_group_size_min = 1536; /** * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods * * @see self::_key_exchange() * @var int */ private $kex_dh_group_size_preferred = 2048; /** * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods * * @see self::_key_exchange() * @var int */ private $kex_dh_group_size_max = 4096; /** * Server Host Key Algorithms * * @see self::getServerHostKeyAlgorithms() * @var array|false */ private $server_host_key_algorithms = false; /** * Supported Private Key Algorithms * * In theory this should be the same as the Server Host Key Algorithms but, in practice, * some servers (eg. Azure) will support rsa-sha2-512 as a server host key algorithm but * not a private key algorithm * * @see self::privatekey_login() * @var array|false */ private $supported_private_key_algorithms = false; /** * Encryption Algorithms: Client to Server * * @see self::getEncryptionAlgorithmsClient2Server() * @var array|false */ private $encryption_algorithms_client_to_server = false; /** * Encryption Algorithms: Server to Client * * @see self::getEncryptionAlgorithmsServer2Client() * @var array|false */ private $encryption_algorithms_server_to_client = false; /** * MAC Algorithms: Client to Server * * @see self::getMACAlgorithmsClient2Server() * @var array|false */ private $mac_algorithms_client_to_server = false; /** * MAC Algorithms: Server to Client * * @see self::getMACAlgorithmsServer2Client() * @var array|false */ private $mac_algorithms_server_to_client = false; /** * Compression Algorithms: Client to Server * * @see self::getCompressionAlgorithmsClient2Server() * @var array|false */ private $compression_algorithms_client_to_server = false; /** * Compression Algorithms: Server to Client * * @see self::getCompressionAlgorithmsServer2Client() * @var array|false */ private $compression_algorithms_server_to_client = false; /** * Languages: Server to Client * * @see self::getLanguagesServer2Client() * @var array|false */ private $languages_server_to_client = false; /** * Languages: Client to Server * * @see self::getLanguagesClient2Server() * @var array|false */ private $languages_client_to_server = false; /** * Preferred Algorithms * * @see self::setPreferredAlgorithms() * @var array */ private $preferred = []; /** * Block Size for Server to Client Encryption * * "Note that the length of the concatenation of 'packet_length', * 'padding_length', 'payload', and 'random padding' MUST be a multiple * of the cipher block size or 8, whichever is larger. This constraint * MUST be enforced, even when using stream ciphers." * * -- http://tools.ietf.org/html/rfc4253#section-6 * * @see self::__construct() * @see self::_send_binary_packet() * @var int */ private $encrypt_block_size = 8; /** * Block Size for Client to Server Encryption * * @see self::__construct() * @see self::_get_binary_packet() * @var int */ private $decrypt_block_size = 8; /** * Server to Client Encryption Object * * @see self::_get_binary_packet() * @var SymmetricKey|false */ private $decrypt = false; /** * Decryption Algorithm Name * * @var string|null */ private $decryptName; /** * Decryption Invocation Counter * * Used by GCM * * @var string|null */ private $decryptInvocationCounter; /** * Fixed Part of Nonce * * Used by GCM * * @var string|null */ private $decryptFixedPart; /** * Server to Client Length Encryption Object * * @see self::_get_binary_packet() * @var object */ private $lengthDecrypt = false; /** * Client to Server Encryption Object * * @see self::_send_binary_packet() * @var SymmetricKey|false */ private $encrypt = false; /** * Encryption Algorithm Name * * @var string|null */ private $encryptName; /** * Encryption Invocation Counter * * Used by GCM * * @var string|null */ private $encryptInvocationCounter; /** * Fixed Part of Nonce * * Used by GCM * * @var string|null */ private $encryptFixedPart; /** * Client to Server Length Encryption Object * * @see self::_send_binary_packet() * @var object */ private $lengthEncrypt = false; /** * Client to Server HMAC Object * * @see self::_send_binary_packet() * @var object */ private $hmac_create = false; /** * Client to Server HMAC Name * * @var string|false */ private $hmac_create_name; /** * Client to Server ETM * * @var int|false */ private $hmac_create_etm; /** * Server to Client HMAC Object * * @see self::_get_binary_packet() * @var object */ private $hmac_check = false; /** * Server to Client HMAC Name * * @var string|false */ private $hmac_check_name; /** * Server to Client ETM * * @var int|false */ private $hmac_check_etm; /** * Size of server to client HMAC * * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is * append it. * * @see self::_get_binary_packet() * @var int */ private $hmac_size = false; /** * Server Public Host Key * * @see self::getServerPublicHostKey() * @var string */ private $server_public_host_key; /** * Session identifier * * "The exchange hash H from the first key exchange is additionally * used as the session identifier, which is a unique identifier for * this connection." * * -- http://tools.ietf.org/html/rfc4253#section-7.2 * * @see self::_key_exchange() * @var string */ private $session_id = false; /** * Exchange hash * * The current exchange hash * * @see self::_key_exchange() * @var string */ private $exchange_hash = false; /** * Message Numbers * * @see self::__construct() * @var array * @access private */ private static $message_numbers = []; /** * Disconnection Message 'reason codes' defined in RFC4253 * * @see self::__construct() * @var array * @access private */ private static $disconnect_reasons = []; /** * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 * * @see self::__construct() * @var array * @access private */ private static $channel_open_failure_reasons = []; /** * Terminal Modes * * @link http://tools.ietf.org/html/rfc4254#section-8 * @see self::__construct() * @var array * @access private */ private static $terminal_modes = []; /** * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes * * @link http://tools.ietf.org/html/rfc4254#section-5.2 * @see self::__construct() * @var array * @access private */ private static $channel_extended_data_type_codes = []; /** * Send Sequence Number * * See 'Section 6.4. Data Integrity' of rfc4253 for more info. * * @see self::_send_binary_packet() * @var int */ private $send_seq_no = 0; /** * Get Sequence Number * * See 'Section 6.4. Data Integrity' of rfc4253 for more info. * * @see self::_get_binary_packet() * @var int */ private $get_seq_no = 0; /** * Server Channels * * Maps client channels to server channels * * @see self::get_channel_packet() * @see self::exec() * @var array */ protected $server_channels = []; /** * Channel Buffers * * If a client requests a packet from one channel but receives two packets from another those packets should * be placed in a buffer * * @see self::get_channel_packet() * @see self::exec() * @var array */ private $channel_buffers = []; /** * Channel Status * * Contains the type of the last sent message * * @see self::get_channel_packet() * @var array */ protected $channel_status = []; /** * The identifier of the interactive channel which was opened most recently * * @see self::getInteractiveChannelId() * @var int */ private $channel_id_last_interactive = 0; /** * Packet Size * * Maximum packet size indexed by channel * * @see self::send_channel_packet() * @var array */ private $packet_size_client_to_server = []; /** * Message Number Log * * @see self::getLog() * @var array */ private $message_number_log = []; /** * Message Log * * @see self::getLog() * @var array */ private $message_log = []; /** * The Window Size * * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) * * @var int * @see self::send_channel_packet() * @see self::exec() */ protected $window_size = 0x7FFFFFFF; /** * What we resize the window to * * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes. * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so * we'll just do what PuTTY does * * @var int * @see self::_send_channel_packet() * @see self::exec() */ private $window_resize = 0x40000000; /** * Window size, server to client * * Window size indexed by channel * * @see self::send_channel_packet() * @var array */ protected $window_size_server_to_client = []; /** * Window size, client to server * * Window size indexed by channel * * @see self::get_channel_packet() * @var array */ private $window_size_client_to_server = []; /** * Server signature * * Verified against $this->session_id * * @see self::getServerPublicHostKey() * @var string */ private $signature = ''; /** * Server signature format * * ssh-rsa or ssh-dss. * * @see self::getServerPublicHostKey() * @var string */ private $signature_format = ''; /** * Interactive Buffer * * @see self::read() * @var string */ private $interactiveBuffer = ''; /** * Current log size * * Should never exceed self::LOG_MAX_SIZE * * @see self::_send_binary_packet() * @see self::_get_binary_packet() * @var int */ private $log_size; /** * Timeout * * @see self::setTimeout() */ protected $timeout; /** * Current Timeout * * @see self::get_channel_packet() */ protected $curTimeout; /** * Keep Alive Interval * * @see self::setKeepAlive() */ private $keepAlive; /** * Real-time log file pointer * * @see self::_append_log() * @var resource|closed-resource */ private $realtime_log_file; /** * Real-time log file size * * @see self::_append_log() * @var int */ private $realtime_log_size; /** * Has the signature been validated? * * @see self::getServerPublicHostKey() * @var bool */ private $signature_validated = false; /** * Real-time log file wrap boolean * * @see self::_append_log() * @var bool */ private $realtime_log_wrap; /** * Flag to suppress stderr from output * * @see self::enableQuietMode() */ private $quiet_mode = false; /** * Time of first network activity * * @var float */ private $last_packet; /** * Exit status returned from ssh if any * * @var int */ private $exit_status; /** * Flag to request a PTY when using exec() * * @var bool * @see self::enablePTY() */ private $request_pty = false; /** * Contents of stdError * * @var string */ private $stdErrorLog; /** * The Last Interactive Response * * @see self::_keyboard_interactive_process() * @var string */ private $last_interactive_response = ''; /** * Keyboard Interactive Request / Responses * * @see self::_keyboard_interactive_process() * @var array */ private $keyboard_requests_responses = []; /** * Banner Message * * Quoting from the RFC, "in some jurisdictions, sending a warning message before * authentication may be relevant for getting legal protection." * * @see self::_filter() * @see self::getBannerMessage() * @var string */ private $banner_message = ''; /** * Did read() timeout or return normally? * * @see self::isTimeout() * @var bool */ private $is_timeout = false; /** * Log Boundary * * @see self::_format_log() * @var string */ private $log_boundary = ':'; /** * Log Long Width * * @see self::_format_log() * @var int */ private $log_long_width = 65; /** * Log Short Width * * @see self::_format_log() * @var int */ private $log_short_width = 16; /** * Hostname * * @see self::__construct() * @see self::_connect() * @var string */ private $host; /** * Port Number * * @see self::__construct() * @see self::_connect() * @var int */ private $port; /** * Number of columns for terminal window size * * @see self::getWindowColumns() * @see self::setWindowColumns() * @see self::setWindowSize() * @var int */ private $windowColumns = 80; /** * Number of columns for terminal window size * * @see self::getWindowRows() * @see self::setWindowRows() * @see self::setWindowSize() * @var int */ private $windowRows = 24; /** * Crypto Engine * * @see self::setCryptoEngine() * @see self::_key_exchange() * @var int */ private static $crypto_engine = false; /** * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario * * @var Agent */ private $agent; /** * Connection storage to replicates ssh2 extension functionality: * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} * * @var array<string, SSH2|\WeakReference<SSH2>> */ private static $connections; /** * Send the identification string first? * * @var bool */ private $send_id_string_first = true; /** * Send the key exchange initiation packet first? * * @var bool */ private $send_kex_first = true; /** * Some versions of OpenSSH incorrectly calculate the key size * * @var bool */ private $bad_key_size_fix = false; /** * Should we try to re-connect to re-establish keys? * * @var bool */ private $retry_connect = false; /** * Binary Packet Buffer * * @var string|false */ private $binary_packet_buffer = false; /** * Preferred Signature Format * * @var string|false */ protected $preferred_signature_format = false; /** * Authentication Credentials * * @var array */ protected $auth = []; /** * Terminal * * @var string */ private $term = 'vt100'; /** * The authentication methods that may productively continue authentication. * * @see https://tools.ietf.org/html/rfc4252#section-5.1 * @var array|null */ private $auth_methods_to_continue = null; /** * Compression method * * @var int */ private $compress = self::NET_SSH2_COMPRESSION_NONE; /** * Decompression method * * @var int */ private $decompress = self::NET_SSH2_COMPRESSION_NONE; /** * Compression context * * @var resource|false|null */ private $compress_context; /** * Decompression context * * @var resource|object */ private $decompress_context; /** * Regenerate Compression Context * * @var bool */ private $regenerate_compression_context = false; /** * Regenerate Decompression Context * * @var bool */ private $regenerate_decompression_context = false; /** * Smart multi-factor authentication flag * * @var bool */ private $smartMFA = true; /** * How many channels are currently opened * * @var int */ private $channelCount = 0; /** * Does the server support multiple channels? If not then error out * when multiple channels are attempted to be opened * * @var bool */ private $errorOnMultipleChannels; /** * Terrapin Countermeasure * * "During initial KEX, terminate the connection if any unexpected or out-of-sequence packet is received" * -- https://github.com/openssh/openssh-portable/commit/1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5 * * @var int */ private $extra_packets; /** * Default Constructor. * * $host can either be a string, representing the host, or a stream resource. * If $host is a stream resource then $port doesn't do anything, altho $timeout * still will be used * * @param mixed $host * @param int $port * @param int $timeout * @see self::login() */ public function __construct($host, $port = 22, $timeout = 10) { if (empty(self::$message_numbers)) { self::$message_numbers = [ 1 => 'NET_SSH2_MSG_DISCONNECT', 2 => 'NET_SSH2_MSG_IGNORE', 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 4 => 'NET_SSH2_MSG_DEBUG', 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308 20 => 'NET_SSH2_MSG_KEXINIT', 21 => 'NET_SSH2_MSG_NEWKEYS', 30 => 'NET_SSH2_MSG_KEXDH_INIT', 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' ]; self::$disconnect_reasons = [ 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 4 => 'NET_SSH2_DISCONNECT_RESERVED', 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' ]; self::$channel_open_failure_reasons = [ 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' ]; self::$terminal_modes = [ 0 => 'NET_SSH2_TTY_OP_END' ]; self::$channel_extended_data_type_codes = [ 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' ]; self::define_array( self::$message_numbers, self::$disconnect_reasons, self::$channel_open_failure_reasons, self::$terminal_modes, self::$channel_extended_data_type_codes, [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] ); } /** * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 * @var \WeakReference<SSH2>|SSH2 */ self::$connections[$this->getResourceId()] = class_exists('WeakReference') ? \WeakReference::create($this) : $this; $this->timeout = $timeout; if (is_resource($host)) { $this->fsock = $host; return; } if (Strings::is_stringable($host)) { $this->host = $host; $this->port = $port; } } /** * Set Crypto Engine Mode * * Possible $engine values: * OpenSSL, mcrypt, Eval, PHP * * @param int $engine */ public static function setCryptoEngine($engine) { self::$crypto_engine = $engine; } /** * Send Identification String First * * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, * both sides MUST send an identification string". It does not say which side sends it first. In * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy * */ public function sendIdentificationStringFirst() { $this->send_id_string_first = true; } /** * Send Identification String Last * * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, * both sides MUST send an identification string". It does not say which side sends it first. In * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy * */ public function sendIdentificationStringLast() { $this->send_id_string_first = false; } /** * Send SSH_MSG_KEXINIT First * * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy * */ public function sendKEXINITFirst() { $this->send_kex_first = true; } /** * Send SSH_MSG_KEXINIT Last * * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy * */ public function sendKEXINITLast() { $this->send_kex_first = false; } /** * stream_select wrapper * * Quoting https://stackoverflow.com/a/14262151/569976, * "The general approach to `EINTR` is to simply handle the error and retry the operation again" * * This wrapper does that loop */ private static function stream_select(&$read, &$write, &$except, $seconds, $microseconds = null) { $remaining = $seconds + $microseconds / 1000000; $start = microtime(true); while (true) { $result = @stream_select($read, $write, $except, $seconds, $microseconds); if ($result !== false) { return $result; } $elapsed = microtime(true) - $start; $seconds = (int) ($remaining - floor($elapsed)); $microseconds = (int) (1000000 * ($remaining - $seconds)); if ($elapsed >= $remaining) { return false; } } } /** * Connect to an SSHv2 server * * @throws \UnexpectedValueException on receipt of unexpected packets * @throws \RuntimeException on other errors */ private function connect() { if ($this->bitmap & self::MASK_CONSTRUCTOR) { return; } $this->bitmap |= self::MASK_CONSTRUCTOR; $this->curTimeout = $this->timeout; $this->last_packet = microtime(true); if (!is_resource($this->fsock)) { $start = microtime(true); // with stream_select a timeout of 0 means that no timeout takes place; // with fsockopen a timeout of 0 means that you instantly timeout // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0 $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); if (!$this->fsock) { $host = $this->host . ':' . $this->port; throw new UnableToConnectException(rtrim("Cannot connect to $host. Error $errno. $errstr")); } $elapsed = microtime(true) - $start; if ($this->curTimeout) { $this->curTimeout -= $elapsed; if ($this->curTimeout < 0) { throw new \RuntimeException('Connection timed out whilst attempting to open socket connection'); } } } $this->identifier = $this->generate_identifier(); if ($this->send_id_string_first) { fputs($this->fsock, $this->identifier . "\r\n"); } /* According to the SSH2 specs, "The server MAY send other lines of data before sending the version string. Each line SHOULD be terminated by a Carriage Return and Line Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients MUST be able to process such lines." */ $data = ''; while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) { $line = ''; while (true) { if ($this->curTimeout) { if ($this->curTimeout < 0) { throw new \RuntimeException('Connection timed out whilst receiving server identification string'); } $read = [$this->fsock]; $write = $except = null; $start = microtime(true); $sec = (int) floor($this->curTimeout); $usec = (int) (1000000 * ($this->curTimeout - $sec)); if (static::stream_select($read, $write, $except, $sec, $usec) === false) { throw new \RuntimeException('Connection timed out whilst receiving server identification string'); } $elapsed = microtime(true) - $start; $this->curTimeout -= $elapsed; } $temp = stream_get_line($this->fsock, 255, "\n"); if ($temp === false) { throw new \RuntimeException('Error reading from socket'); } if (strlen($temp) == 255) { continue; } $line .= "$temp\n"; // quoting RFC4253, "Implementers who wish to maintain // compatibility with older, undocumented versions of this protocol may // want to process the identification string without expecting the // presence of the carriage return character for reasons described in // Section 5 of this document." //if (substr($line, -2) == "\r\n") { // break; //} break; } $data .= $line; } if (feof($this->fsock)) { $this->bitmap = 0; throw new ConnectionClosedException('Connection closed by server'); } $extra = $matches[1]; if (defined('NET_SSH2_LOGGING')) { $this->append_log('<-', $matches[0]); $this->append_log('->', $this->identifier . "\r\n"); } $this->server_identifier = trim($temp, "\r\n"); if (strlen($extra)) { $this->errors[] = $data; } if (version_compare($matches[3], '1.99', '<')) { $this->bitmap = 0; throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers"); } // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info. // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses // when consolekit was incorporated. // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the // issues with how Ubuntu incorporated consolekit $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#'; $match = preg_match($pattern, $this->server_identifier, $matches); $match = $match && version_compare('5.8', $matches[1], '<='); $match = $match && version_compare('6.9', $matches[1], '>='); $this->errorOnMultipleChannels = $match; if (!$this->send_id_string_first) { fputs($this->fsock, $this->identifier . "\r\n"); } if (!$this->send_kex_first) { $response = $this->get_binary_packet(); if (is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { $this->bitmap = 0; throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } $this->key_exchange($response); } if ($this->send_kex_first) { $this->key_exchange(); } $this->bitmap |= self::MASK_CONNECTED; return true; } /** * Generates the SSH identifier * * You should overwrite this method in your own class if you want to use another identifier * * @return string */ private function generate_identifier() { $identifier = 'SSH-2.0-phpseclib_3.0'; $ext = []; if (extension_loaded('sodium')) { $ext[] = 'libsodium'; } if (extension_loaded('openssl')) { $ext[] = 'openssl'; } elseif (extension_loaded('mcrypt')) { $ext[] = 'mcrypt'; } if (extension_loaded('gmp')) { $ext[] = 'gmp'; } elseif (extension_loaded('bcmath')) { $ext[] = 'bcmath'; } if (!empty($ext)) { $identifier .= ' (' . implode(', ', $ext) . ')'; } return $identifier; } /** * Key Exchange * * @return bool * @param string|bool $kexinit_payload_server optional * @throws \UnexpectedValueException on receipt of unexpected packets * @throws \RuntimeException on other errors * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible */ private function key_exchange($kexinit_payload_server = false) { $preferred = $this->preferred; $send_kex = true; $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : SSH2::getSupportedKEXAlgorithms(); $server_host_key_algorithms = isset($preferred['hostkey']) ? $preferred['hostkey'] : SSH2::getSupportedHostKeyAlgorithms(); $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ? $preferred['server_to_client']['crypt'] : SSH2::getSupportedEncryptionAlgorithms(); $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ? $preferred['client_to_server']['crypt'] : SSH2::getSupportedEncryptionAlgorithms(); $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ? $preferred['server_to_client']['mac'] : SSH2::getSupportedMACAlgorithms(); $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ? $preferred['client_to_server']['mac'] : SSH2::getSupportedMACAlgorithms(); $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ? $preferred['server_to_client']['comp'] : SSH2::getSupportedCompressionAlgorithms(); $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ? $preferred['client_to_server']['comp'] : SSH2::getSupportedCompressionAlgorithms(); $kex_algorithms = array_merge($kex_algorithms, ['ext-info-c', 'kex-strict-c-v00@openssh.com']); // some SSH servers have buggy implementations of some of the above algorithms switch (true) { case $this->server_identifier == 'SSH-2.0-SSHD': case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': if (!isset($preferred['server_to_client']['mac'])) { $s2c_mac_algorithms = array_values(array_diff( $s2c_mac_algorithms, ['hmac-sha1-96', 'hmac-md5-96'] )); } if (!isset($preferred['client_to_server']['mac'])) { $c2s_mac_algorithms = array_values(array_diff( $c2s_mac_algorithms, ['hmac-sha1-96', 'hmac-md5-96'] )); } break; case substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_': if (!isset($preferred['server_to_client']['crypt'])) { $s2c_encryption_algorithms = array_values(array_diff( $s2c_encryption_algorithms, ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] )); } if (!isset($preferred['client_to_server']['crypt'])) { $c2s_encryption_algorithms = array_values(array_diff( $c2s_encryption_algorithms, ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] )); } } $client_cookie = Random::string(16); $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie); $kexinit_payload_client .= Strings::packSSH2( 'L10bN', $kex_algorithms, $server_host_key_algorithms, $c2s_encryption_algorithms, $s2c_encryption_algorithms, $c2s_mac_algorithms, $s2c_mac_algorithms, $c2s_compression_algorithms, $s2c_compression_algorithms, [], // language, client to server [], // language, server to client false, // first_kex_packet_follows 0 // reserved for future extension ); if ($kexinit_payload_server === false) { $this->send_binary_packet($kexinit_payload_client); $this->extra_packets = 0; $kexinit_payload_server = $this->get_binary_packet(); if ( is_bool($kexinit_payload_server) || !strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT ) { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } $send_kex = false; } $response = $kexinit_payload_server; Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) $server_cookie = Strings::shift($response, 16); list( $this->kex_algorithms, $this->server_host_key_algorithms, $this->encryption_algorithms_client_to_server, $this->encryption_algorithms_server_to_client, $this->mac_algorithms_client_to_server, $this->mac_algorithms_server_to_client, $this->compression_algorithms_client_to_server, $this->compression_algorithms_server_to_client, $this->languages_client_to_server, $this->languages_server_to_client, $first_kex_packet_follows ) = Strings::unpackSSH2('L10C', $response); if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { if ($this->session_id === false && $this->extra_packets) { throw new \UnexpectedValueException('Possible Terrapin Attack detected'); } } $this->supported_private_key_algorithms = $this->server_host_key_algorithms; if ($send_kex) { $this->send_binary_packet($kexinit_payload_client); } // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the // diffie-hellman key exchange as fast as possible $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt); if ($decryptKeyLength === null) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); } $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt); if ($encryptKeyLength === null) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); } // through diffie-hellman key exchange a symmetric key is obtained $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms); if ($this->kex_algorithm === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); } $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); if ($server_host_key_algorithm === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); } $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); if ($mac_algorithm_out === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); } $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); if ($mac_algorithm_in === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); } $compression_map = [ 'none' => self::NET_SSH2_COMPRESSION_NONE, 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB, 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH ]; $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); if ($compression_algorithm_in === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); } $this->decompress = $compression_map[$compression_algorithm_in]; $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); if ($compression_algorithm_out === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); } $this->compress = $compression_map[$compression_algorithm_out]; switch ($this->kex_algorithm) { case 'diffie-hellman-group15-sha512': case 'diffie-hellman-group16-sha512': case 'diffie-hellman-group17-sha512': case 'diffie-hellman-group18-sha512': case 'ecdh-sha2-nistp521': $kexHash = new Hash('sha512'); break; case 'ecdh-sha2-nistp384': $kexHash = new Hash('sha384'); break; case 'diffie-hellman-group-exchange-sha256': case 'diffie-hellman-group14-sha256': case 'ecdh-sha2-nistp256': case 'curve25519-sha256@libssh.org': case 'curve25519-sha256': $kexHash = new Hash('sha256'); break; default: $kexHash = new Hash('sha1'); } // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. $exchange_hash_rfc4419 = ''; if (strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) { $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ? 'Curve25519' : substr($this->kex_algorithm, 10); $ourPrivate = EC::createKey($curve); $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates(); $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; } else { if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) { $dh_group_sizes_packed = pack( 'NNN', $this->kex_dh_group_size_min, $this->kex_dh_group_size_preferred, $this->kex_dh_group_size_max ); $packet = pack( 'Ca*', NET_SSH2_MSG_KEXDH_GEX_REQUEST, $dh_group_sizes_packed ); $this->send_binary_packet($packet); $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); $response = $this->get_binary_packet(); list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response); if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP'); } $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); $prime = new BigInteger($primeBytes, -256); $g = new BigInteger($gBytes, -256); $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2( 'ss', $primeBytes, $gBytes ); $params = DH::createParameters($prime, $g); $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; } else { $params = DH::createParameters($this->kex_algorithm); $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; } $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength)); $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength $ourPublic = $ourPrivate->getPublicKey()->toBigInteger(); $ourPublicBytes = $ourPublic->toBytes(true); } $data = pack('CNa*', constant($clientKexInitMessage), strlen($ourPublicBytes), $ourPublicBytes); $this->send_binary_packet($data); switch ($clientKexInitMessage) { case 'NET_SSH2_MSG_KEX_ECDH_INIT': $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); break; case 'NET_SSH2_MSG_KEXDH_GEX_INIT': $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); } $response = $this->get_binary_packet(); list( $type, $server_public_host_key, $theirPublicBytes, $this->signature ) = Strings::unpackSSH2('Csss', $response); if ($type != constant($serverKexReplyMessage)) { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException("Expected $serverKexReplyMessage"); } switch ($serverKexReplyMessage) { case 'NET_SSH2_MSG_KEX_ECDH_REPLY': $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); break; case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); } $this->server_public_host_key = $server_public_host_key; list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key); if (strlen($this->signature) < 4) { throw new \LengthException('The signature needs at least four bytes'); } $temp = unpack('Nlength', substr($this->signature, 0, 4)); $this->signature_format = substr($this->signature, 4, $temp['length']); $keyBytes = DH::computeSecret($ourPrivate, $theirPublicBytes); if (($keyBytes & "\xFF\x80") === "\x00\x00") { $keyBytes = substr($keyBytes, 1); } elseif (($keyBytes[0] & "\x80") === "\x80") { $keyBytes = "\0$keyBytes"; } $this->exchange_hash = Strings::packSSH2( 's5', $this->identifier, $this->server_identifier, $kexinit_payload_client, $kexinit_payload_server, $this->server_public_host_key ); $this->exchange_hash .= $exchange_hash_rfc4419; $this->exchange_hash .= Strings::packSSH2( 's3', $ourPublicBytes, $theirPublicBytes, $keyBytes ); $this->exchange_hash = $kexHash->hash($this->exchange_hash); if ($this->session_id === false) { $this->session_id = $this->exchange_hash; } switch ($server_host_key_algorithm) { case 'rsa-sha2-256': case 'rsa-sha2-512': //case 'ssh-rsa': $expected_key_format = 'ssh-rsa'; break; default: $expected_key_format = $server_host_key_algorithm; } if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { switch (true) { case $this->signature_format == $server_host_key_algorithm: case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': case $this->signature_format != 'ssh-rsa': $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')'); } } $packet = pack('C', NET_SSH2_MSG_NEWKEYS); $this->send_binary_packet($packet); $response = $this->get_binary_packet(); if ($response === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); throw new ConnectionClosedException('Connection closed by server'); } list($type) = Strings::unpackSSH2('C', $response); if ($type != NET_SSH2_MSG_NEWKEYS) { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); } if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { $this->get_seq_no = $this->send_seq_no = 0; } $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $this->encrypt = self::encryption_algorithm_to_crypt_instance($encrypt); if ($this->encrypt) { if (self::$crypto_engine) { $this->encrypt->setPreferredEngine(self::$crypto_engine); } if ($this->encrypt->getBlockLengthInBytes()) { $this->encrypt_block_size = $this->encrypt->getBlockLengthInBytes(); } $this->encrypt->disablePadding(); if ($this->encrypt->usesIV()) { $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); while ($this->encrypt_block_size > strlen($iv)) { $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); } switch ($encrypt) { case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); $this->encryptFixedPart = substr($nonce, 0, 4); $this->encryptInvocationCounter = substr($nonce, 4, 8); // fall-through case 'chacha20-poly1305@openssh.com': break; default: $this->encrypt->enableContinuousBuffer(); } $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } switch ($encrypt) { case 'chacha20-poly1305@openssh.com': $encryptKeyLength = 32; $this->lengthEncrypt = self::encryption_algorithm_to_crypt_instance($encrypt); $this->lengthEncrypt->setKey(substr($key, 32, 32)); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); $this->encryptName = $encrypt; } $this->decrypt = self::encryption_algorithm_to_crypt_instance($decrypt); if ($this->decrypt) { if (self::$crypto_engine) { $this->decrypt->setPreferredEngine(self::$crypto_engine); } if ($this->decrypt->getBlockLengthInBytes()) { $this->decrypt_block_size = $this->decrypt->getBlockLengthInBytes(); } $this->decrypt->disablePadding(); if ($this->decrypt->usesIV()) { $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); while ($this->decrypt_block_size > strlen($iv)) { $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); } switch ($decrypt) { case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': // see https://tools.ietf.org/html/rfc5647#section-7.1 $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); $this->decryptFixedPart = substr($nonce, 0, 4); $this->decryptInvocationCounter = substr($nonce, 4, 8); // fall-through case 'chacha20-poly1305@openssh.com': break; default: $this->decrypt->enableContinuousBuffer(); } $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } switch ($decrypt) { case 'chacha20-poly1305@openssh.com': $decryptKeyLength = 32; $this->lengthDecrypt = self::encryption_algorithm_to_crypt_instance($decrypt); $this->lengthDecrypt->setKey(substr($key, 32, 32)); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); $this->decryptName = $decrypt; } /* The "arcfour128" algorithm is the RC4 cipher, as described in [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream generated by the cipher MUST be discarded, and the first byte of the first encrypted packet MUST be encrypted using the 1537th byte of keystream. -- http://tools.ietf.org/html/rfc4345#section-4 */ if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { $this->encrypt->encrypt(str_repeat("\0", 1536)); } if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { $this->decrypt->decrypt(str_repeat("\0", 1536)); } if (!$this->encrypt->usesNonce()) { list($this->hmac_create, $createKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_out); } else { $this->hmac_create = new \stdClass(); $this->hmac_create_name = $mac_algorithm_out; //$mac_algorithm_out = 'none'; $createKeyLength = 0; } if ($this->hmac_create instanceof Hash) { $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); $this->hmac_create_name = $mac_algorithm_out; $this->hmac_create_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_out); } if (!$this->decrypt->usesNonce()) { list($this->hmac_check, $checkKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_in); $this->hmac_size = $this->hmac_check->getLengthInBytes(); } else { $this->hmac_check = new \stdClass(); $this->hmac_check_name = $mac_algorithm_in; //$mac_algorithm_in = 'none'; $checkKeyLength = 0; $this->hmac_size = 0; } if ($this->hmac_check instanceof Hash) { $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); while ($checkKeyLength > strlen($key)) { $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); $this->hmac_check_name = $mac_algorithm_in; $this->hmac_check_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_in); } $this->regenerate_compression_context = $this->regenerate_decompression_context = true; return true; } /** * Maps an encryption algorithm name to the number of key bytes. * * @param string $algorithm Name of the encryption algorithm * @return int|null Number of bytes as an integer or null for unknown */ private function encryption_algorithm_to_key_size($algorithm) { if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) { return 16; } switch ($algorithm) { case 'none': return 0; case 'aes128-gcm@openssh.com': case 'aes128-cbc': case 'aes128-ctr': case 'arcfour': case 'arcfour128': case 'blowfish-cbc': case 'blowfish-ctr': case 'twofish128-cbc': case 'twofish128-ctr': return 16; case '3des-cbc': case '3des-ctr': case 'aes192-cbc': case 'aes192-ctr': case 'twofish192-cbc': case 'twofish192-ctr': return 24; case 'aes256-gcm@openssh.com': case 'aes256-cbc': case 'aes256-ctr': case 'arcfour256': case 'twofish-cbc': case 'twofish256-cbc': case 'twofish256-ctr': return 32; case 'chacha20-poly1305@openssh.com': return 64; } return null; } /** * Maps an encryption algorithm name to an instance of a subclass of * \phpseclib3\Crypt\Common\SymmetricKey. * * @param string $algorithm Name of the encryption algorithm * @return SymmetricKey|null */ private static function encryption_algorithm_to_crypt_instance($algorithm) { switch ($algorithm) { case '3des-cbc': return new TripleDES('cbc'); case '3des-ctr': return new TripleDES('ctr'); case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': return new Rijndael('cbc'); case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': return new Rijndael('ctr'); case 'blowfish-cbc': return new Blowfish('cbc'); case 'blowfish-ctr': return new Blowfish('ctr'); case 'twofish128-cbc': case 'twofish192-cbc': case 'twofish256-cbc': case 'twofish-cbc': return new Twofish('cbc'); case 'twofish128-ctr': case 'twofish192-ctr': case 'twofish256-ctr': return new Twofish('ctr'); case 'arcfour': case 'arcfour128': case 'arcfour256': return new RC4(); case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': return new Rijndael('gcm'); case 'chacha20-poly1305@openssh.com': return new ChaCha20(); } return null; } /** * Maps an encryption algorithm name to an instance of a subclass of * \phpseclib3\Crypt\Hash. * * @param string $algorithm Name of the encryption algorithm * @return array{Hash, int}|null */ private static function mac_algorithm_to_hash_instance($algorithm) { switch ($algorithm) { case 'umac-64@openssh.com': case 'umac-64-etm@openssh.com': return [new Hash('umac-64'), 16]; case 'umac-128@openssh.com': case 'umac-128-etm@openssh.com': return [new Hash('umac-128'), 16]; case 'hmac-sha2-512': case 'hmac-sha2-512-etm@openssh.com': return [new Hash('sha512'), 64]; case 'hmac-sha2-256': case 'hmac-sha2-256-etm@openssh.com': return [new Hash('sha256'), 32]; case 'hmac-sha1': case 'hmac-sha1-etm@openssh.com': return [new Hash('sha1'), 20]; case 'hmac-sha1-96': return [new Hash('sha1-96'), 20]; case 'hmac-md5': return [new Hash('md5'), 16]; case 'hmac-md5-96': return [new Hash('md5-96'), 16]; } } /* * Tests whether or not proposed algorithm has a potential for issues * * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 * @param string $algorithm Name of the encryption algorithm * @return bool */ private static function bad_algorithm_candidate($algorithm) { switch ($algorithm) { case 'arcfour256': case 'aes192-ctr': case 'aes256-ctr': return true; } return false; } /** * Login * * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA|EC|DSA object, a \phpseclib3\System\SSH\Agent object or an array * * @param string $username * @param string|PrivateKey|array[]|Agent|null ...$args * @return bool * @see self::_login() */ public function login($username, ...$args) { if (!$this->retry_connect) { $this->auth[] = func_get_args(); } // try logging with 'none' as an authentication method first since that's what // PuTTY does if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { if ($this->sublogin($username)) { return true; } if (!count($args)) { return false; } } return $this->sublogin($username, ...$args); } /** * Login Helper * * @param string $username * @param string|PrivateKey|array[]|Agent|null ...$args * @return bool * @see self::_login_helper() */ protected function sublogin($username, ...$args) { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { $this->connect(); } if (empty($args)) { return $this->login_helper($username); } foreach ($args as $arg) { switch (true) { case $arg instanceof PublicKey: throw new \UnexpectedValueException('A PublicKey object was passed to the login method instead of a PrivateKey object'); case $arg instanceof PrivateKey: case $arg instanceof Agent: case is_array($arg): case Strings::is_stringable($arg): break; default: throw new \UnexpectedValueException('$password needs to either be an instance of \phpseclib3\Crypt\Common\PrivateKey, \System\SSH\Agent, an array or a string'); } } while (count($args)) { if (!$this->auth_methods_to_continue || !$this->smartMFA) { $newargs = $args; $args = []; } else { $newargs = []; foreach ($this->auth_methods_to_continue as $method) { switch ($method) { case 'publickey': foreach ($args as $key => $arg) { if ($arg instanceof PrivateKey || $arg instanceof Agent) { $newargs[] = $arg; unset($args[$key]); break; } } break; case 'keyboard-interactive': $hasArray = $hasString = false; foreach ($args as $arg) { if ($hasArray || is_array($arg)) { $hasArray = true; break; } if ($hasString || Strings::is_stringable($arg)) { $hasString = true; break; } } if ($hasArray && $hasString) { foreach ($args as $key => $arg) { if (is_array($arg)) { $newargs[] = $arg; break 2; } } } // fall-through case 'password': foreach ($args as $key => $arg) { $newargs[] = $arg; unset($args[$key]); break; } } } } if (!count($newargs)) { return false; } foreach ($newargs as $arg) { if ($this->login_helper($username, $arg)) { return true; } } } return false; } /** * Login Helper * * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages.} * * @param string $username * @param string|AsymmetricKey|array[]|Agent|null ...$args * @return bool * @throws \UnexpectedValueException on receipt of unexpected packets * @throws \RuntimeException on other errors */ private function login_helper($username, $password = null) { if (!($this->bitmap & self::MASK_CONNECTED)) { return false; } if (!($this->bitmap & self::MASK_LOGIN_REQ)) { $packet = Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth'); $this->send_binary_packet($packet); try { $response = $this->get_binary_packet(); } catch (\Exception $e) { if ($this->retry_connect) { $this->retry_connect = false; $this->connect(); return $this->login_helper($username, $password); } $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); throw $e; } list($type) = Strings::unpackSSH2('C', $response); if ($type == NET_SSH2_MSG_EXT_INFO) { list($nr_extensions) = Strings::unpackSSH2('N', $response); for ($i = 0; $i < $nr_extensions; $i++) { list($extension_name, $extension_value) = Strings::unpackSSH2('ss', $response); if ($extension_name == 'server-sig-algs') { $this->supported_private_key_algorithms = explode(',', $extension_value); } } $response = $this->get_binary_packet(); list($type) = Strings::unpackSSH2('C', $response); } list($service) = Strings::unpackSSH2('s', $response); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); } $this->bitmap |= self::MASK_LOGIN_REQ; } if (strlen($this->last_interactive_response)) { return !Strings::is_stringable($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password); } if ($password instanceof PrivateKey) { return $this->privatekey_login($username, $password); } if ($password instanceof Agent) { return $this->ssh_agent_login($username, $password); } if (is_array($password)) { if ($this->keyboard_interactive_login($username, $password)) { $this->bitmap |= self::MASK_LOGIN; return true; } return false; } if (!isset($password)) { $packet = Strings::packSSH2( 'Cs3', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'none' ); $this->send_binary_packet($packet); $response = $this->get_binary_packet(); list($type) = Strings::unpackSSH2('C', $response); switch ($type) { case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; case NET_SSH2_MSG_USERAUTH_FAILURE: list($auth_methods) = Strings::unpackSSH2('L', $response); $this->auth_methods_to_continue = $auth_methods; // fall-through default: return false; } } $packet = Strings::packSSH2( 'Cs3bs', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'password', false, $password ); // remove the username and password from the logged packet if (!defined('NET_SSH2_LOGGING')) { $logged = null; } else { $logged = Strings::packSSH2( 'Cs3bs', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'password', false, 'password' ); } $this->send_binary_packet($packet, $logged); $response = $this->get_binary_packet(); if ($response === false) { return false; } list($type) = Strings::unpackSSH2('C', $response); switch ($type) { case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); list($message) = Strings::unpackSSH2('s', $response); $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message; return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees // multi-factor authentication list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response); $this->auth_methods_to_continue = $auth_methods; if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { if ($this->keyboard_interactive_login($username, $password)) { $this->bitmap |= self::MASK_LOGIN; return true; } return false; } return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; } return false; } /** * Login via keyboard-interactive authentication * * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. * * @param string $username * @param string|array $password * @return bool */ private function keyboard_interactive_login($username, $password) { $packet = Strings::packSSH2( 'Cs5', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'keyboard-interactive', '', // language tag '' // submethods ); $this->send_binary_packet($packet); return $this->keyboard_interactive_process($password); } /** * Handle the keyboard-interactive requests / responses. * * @param string|array ...$responses * @return bool * @throws \RuntimeException on connection error */ private function keyboard_interactive_process(...$responses) { if (strlen($this->last_interactive_response)) { $response = $this->last_interactive_response; } else { $orig = $response = $this->get_binary_packet(); } list($type) = Strings::unpackSSH2('C', $response); switch ($type) { case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: list( , // name; may be empty , // instruction; may be empty , // language tag; may be empty $num_prompts ) = Strings::unpackSSH2('s3N', $response); for ($i = 0; $i < count($responses); $i++) { if (is_array($responses[$i])) { foreach ($responses[$i] as $key => $value) { $this->keyboard_requests_responses[$key] = $value; } unset($responses[$i]); } } $responses = array_values($responses); if (isset($this->keyboard_requests_responses)) { for ($i = 0; $i < $num_prompts; $i++) { list( $prompt, // prompt - ie. "Password: "; must not be empty // echo ) = Strings::unpackSSH2('sC', $response); foreach ($this->keyboard_requests_responses as $key => $value) { if (substr($prompt, 0, strlen($key)) == $key) { $responses[] = $value; break; } } } } // see http://tools.ietf.org/html/rfc4256#section-3.2 if (strlen($this->last_interactive_response)) { $this->last_interactive_response = ''; } else { $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); } if (!count($responses) && $num_prompts) { $this->last_interactive_response = $orig; return false; } /* After obtaining the requested information from the user, the client MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. */ // see http://tools.ietf.org/html/rfc4256#section-3.4 $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); for ($i = 0; $i < count($responses); $i++) { $packet .= Strings::packSSH2('s', $responses[$i]); $logged .= Strings::packSSH2('s', 'dummy-answer'); } $this->send_binary_packet($packet, $logged); $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); /* After receiving the response, the server MUST send either an SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another SSH_MSG_USERAUTH_INFO_REQUEST message. */ // maybe phpseclib should force close the connection after x request / responses? unless something like that is done // there could be an infinite loop of request / responses. return $this->keyboard_interactive_process(); case NET_SSH2_MSG_USERAUTH_SUCCESS: return true; case NET_SSH2_MSG_USERAUTH_FAILURE: list($auth_methods) = Strings::unpackSSH2('L', $response); $this->auth_methods_to_continue = $auth_methods; return false; } return false; } /** * Login with an ssh-agent provided key * * @param string $username * @param \phpseclib3\System\SSH\Agent $agent * @return bool */ private function ssh_agent_login($username, Agent $agent) { $this->agent = $agent; $keys = $agent->requestIdentities(); foreach ($keys as $key) { if ($this->privatekey_login($username, $key)) { return true; } } return false; } /** * Login with an RSA private key * * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages.} * * @param string $username * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey * @return bool * @throws \RuntimeException on connection error */ private function privatekey_login($username, PrivateKey $privatekey) { $publickey = $privatekey->getPublicKey(); if ($publickey instanceof RSA) { $privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1); $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; if (isset($this->preferred['hostkey'])) { $algos = array_intersect($algos, $this->preferred['hostkey']); } $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms); switch ($algo) { case 'rsa-sha2-512': $hash = 'sha512'; $signatureType = 'rsa-sha2-512'; break; case 'rsa-sha2-256': $hash = 'sha256'; $signatureType = 'rsa-sha2-256'; break; //case 'ssh-rsa': default: $hash = 'sha1'; $signatureType = 'ssh-rsa'; } } elseif ($publickey instanceof EC) { $privatekey = $privatekey->withSignatureFormat('SSH2'); $curveName = $privatekey->getCurve(); switch ($curveName) { case 'Ed25519': $hash = 'sha512'; $signatureType = 'ssh-ed25519'; break; case 'secp256r1': // nistp256 $hash = 'sha256'; $signatureType = 'ecdsa-sha2-nistp256'; break; case 'secp384r1': // nistp384 $hash = 'sha384'; $signatureType = 'ecdsa-sha2-nistp384'; break; case 'secp521r1': // nistp521 $hash = 'sha512'; $signatureType = 'ecdsa-sha2-nistp521'; break; default: if (is_array($curveName)) { throw new UnsupportedCurveException('Specified Curves are not supported by SSH2'); } throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib3\'s SSH2 implementation'); } } elseif ($publickey instanceof DSA) { $privatekey = $privatekey->withSignatureFormat('SSH2'); $hash = 'sha1'; $signatureType = 'ssh-dss'; } else { throw new UnsupportedAlgorithmException('Please use either an RSA key, an EC one or a DSA key'); } $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]); $part1 = Strings::packSSH2( 'Csss', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'publickey' ); $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr); $packet = $part1 . chr(0) . $part2; $this->send_binary_packet($packet); $response = $this->get_binary_packet(); list($type) = Strings::unpackSSH2('C', $response); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: list($auth_methods) = Strings::unpackSSH2('L', $response); if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') { $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, ['rsa-sha2-256', 'rsa-sha2-512']); return $this->privatekey_login($username, $privatekey); } $this->auth_methods_to_continue = $auth_methods; $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; return false; case NET_SSH2_MSG_USERAUTH_PK_OK: // we'll just take it on faith that the public key blob and the public key algorithm name are as // they should be $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); break; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; default: $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1'); } $packet = $part1 . chr(1) . $part2; $privatekey = $privatekey->withHash($hash); $signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet); if ($publickey instanceof RSA) { $signature = Strings::packSSH2('ss', $signatureType, $signature); } $packet .= Strings::packSSH2('s', $signature); $this->send_binary_packet($packet); $response = $this->get_binary_packet(); list($type) = Strings::unpackSSH2('C', $response); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: // either the login is bad or the server employs multi-factor authentication list($auth_methods) = Strings::unpackSSH2('L', $response); $this->auth_methods_to_continue = $auth_methods; return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; } $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2'); } /** * Return the currently configured timeout * * @return int */ public function getTimeout() { return $this->timeout; } /** * Set Timeout * * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. * Setting $timeout to false or 0 will mean there is no timeout. * * @param mixed $timeout */ public function setTimeout($timeout) { $this->timeout = $this->curTimeout = $timeout; } /** * Set Keep Alive * * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. * * @param int $interval */ public function setKeepAlive($interval) { $this->keepAlive = $interval; } /** * Get the output from stdError * */ public function getStdError() { return $this->stdErrorLog; } /** * Execute Command * * If $callback is set to false then \phpseclib3\Net\SSH2::get_channel_packet(self::CHANNEL_EXEC) will need to be called manually. * In all likelihood, this is not a feature you want to be taking advantage of. * * @param string $command * @return string|bool * @psalm-return ($callback is callable ? bool : string|bool) * @throws \RuntimeException on connection error */ public function exec($command, callable $callback = null) { $this->curTimeout = $this->timeout; $this->is_timeout = false; $this->stdErrorLog = ''; if (!$this->isAuthenticated()) { return false; } //if ($this->isPTYOpen()) { // throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); //} $this->open_channel(self::CHANNEL_EXEC); if ($this->request_pty === true) { $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); $packet = Strings::packSSH2( 'CNsCsN4s', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], 'pty-req', 1, $this->term, $this->windowColumns, $this->windowRows, 0, 0, $terminal_modes ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to request pseudo-terminal'); } } // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things // down. the one place where it might be desirable is if you're doing something like \phpseclib3\Net\SSH2::exec('ping localhost &'). // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but // neither will your script. // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. $packet = Strings::packSSH2( 'CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], 'exec', 1, $command ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { return false; } $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; if ($this->request_pty === true) { $this->channel_id_last_interactive = self::CHANNEL_EXEC; return true; } $output = ''; while (true) { $temp = $this->get_channel_packet(self::CHANNEL_EXEC); switch (true) { case $temp === true: return is_callable($callback) ? true : $output; case $temp === false: return false; default: if (is_callable($callback)) { if ($callback($temp) === true) { $this->close_channel(self::CHANNEL_EXEC); return true; } } else { $output .= $temp; } } } } /** * How many channels are currently open? * * @return int */ public function getOpenChannelCount() { return $this->channelCount; } /** * Opens a channel * * @param string $channel * @param bool $skip_extended * @return bool */ protected function open_channel($channel, $skip_extended = false) { if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) { throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again'); } $this->channelCount++; if ($this->channelCount > 1 && $this->errorOnMultipleChannels) { throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels"); } // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info $this->window_size_server_to_client[$channel] = $this->window_size; // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy // uses 0x4000, that's what will be used here, as well. $packet_size = 0x4000; $packet = Strings::packSSH2( 'CsN3', NET_SSH2_MSG_CHANNEL_OPEN, 'session', $channel, $this->window_size_server_to_client[$channel], $packet_size ); $this->send_binary_packet($packet); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN; return $this->get_channel_packet($channel, $skip_extended); } /** * Creates an interactive shell * * Returns bool(true) if the shell was opened. * Returns bool(false) if the shell was already open. * * @see self::isShellOpen() * @see self::read() * @see self::write() * @return bool * @throws InsufficientSetupException if not authenticated * @throws \UnexpectedValueException on receipt of unexpected packets * @throws \RuntimeException on other errors */ public function openShell() { if (!$this->isAuthenticated()) { throw new InsufficientSetupException('Operation disallowed prior to login()'); } $this->open_channel(self::CHANNEL_SHELL); $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); $packet = Strings::packSSH2( 'CNsbsN4s', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SHELL], 'pty-req', true, // want reply $this->term, $this->windowColumns, $this->windowRows, 0, 0, $terminal_modes ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->get_channel_packet(self::CHANNEL_SHELL)) { throw new \RuntimeException('Unable to request pty'); } $packet = Strings::packSSH2( 'CNsb', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SHELL], 'shell', true // want reply ); $this->send_binary_packet($packet); $response = $this->get_channel_packet(self::CHANNEL_SHELL); if ($response === false) { throw new \RuntimeException('Unable to request shell'); } $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; $this->channel_id_last_interactive = self::CHANNEL_SHELL; $this->bitmap |= self::MASK_SHELL; return true; } /** * Return the channel to be used with read(), write(), and reset(), if none were specified * @deprecated for lack of transparency in intended channel target, to be potentially replaced * with method which guarantees open-ness of all yielded channels and throws * error for multiple open channels * @see self::read() * @see self::write() * @return int */ private function get_interactive_channel() { switch (true) { case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM): return self::CHANNEL_SUBSYSTEM; case $this->is_channel_status_data(self::CHANNEL_EXEC): return self::CHANNEL_EXEC; default: return self::CHANNEL_SHELL; } } /** * Indicates the DATA status on the given channel * * @param int $channel The channel number to evaluate * @return bool */ private function is_channel_status_data($channel) { return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA; } /** * Return an available open channel * * @return int */ private function get_open_channel() { $channel = self::CHANNEL_EXEC; do { if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { return $channel; } } while ($channel++ < self::CHANNEL_SUBSYSTEM); return false; } /** * Request agent forwarding of remote server * * @return bool */ public function requestAgentForwarding() { $request_channel = $this->get_open_channel(); if ($request_channel === false) { return false; } $packet = Strings::packSSH2( 'CNsC', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[$request_channel], 'auth-agent-req@openssh.com', 1 ); $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; $this->send_binary_packet($packet); if (!$this->get_channel_packet($request_channel)) { return false; } $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; return true; } /** * Returns the output of an interactive shell * * Returns when there's a match for $expect, which can take the form of a string literal or, * if $mode == self::READ_REGEX, a regular expression. * * If not specifying a channel, an open interactive channel will be selected, or, if there are * no open channels, an interactive shell will be created. If there are multiple open * interactive channels, a legacy behavior will apply in which channel selection prioritizes * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive * channels, callers are discouraged from relying on this legacy behavior and should specify * the intended channel. * * @see self::write() * @param string $expect * @param int $mode One of the self::READ_* constants * @param int|null $channel Channel id returned by self::getInteractiveChannelId() * @return string|bool|null * @throws \RuntimeException on connection error * @throws InsufficientSetupException on unexpected channel status, possibly due to closure */ public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null) { if (!$this->isAuthenticated()) { throw new InsufficientSetupException('Operation disallowed prior to login()'); } $this->curTimeout = $this->timeout; $this->is_timeout = false; if ($channel === null) { $channel = $this->get_interactive_channel(); } if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) { if ($channel != self::CHANNEL_SHELL) { throw new InsufficientSetupException('Data is not available on channel'); } elseif (!$this->openShell()) { throw new \RuntimeException('Unable to initiate an interactive shell session'); } } if ($mode == self::READ_NEXT) { return $this->get_channel_packet($channel); } $match = $expect; while (true) { if ($mode == self::READ_REGEX) { preg_match($expect, substr($this->interactiveBuffer, -1024), $matches); $match = isset($matches[0]) ? $matches[0] : ''; } $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; if ($pos !== false) { return Strings::shift($this->interactiveBuffer, $pos + strlen($match)); } $response = $this->get_channel_packet($channel); if ($response === true) { return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); } $this->interactiveBuffer .= $response; } } /** * Inputs a command into an interactive shell. * * If not specifying a channel, an open interactive channel will be selected, or, if there are * no open channels, an interactive shell will be created. If there are multiple open * interactive channels, a legacy behavior will apply in which channel selection prioritizes * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive * channels, callers are discouraged from relying on this legacy behavior and should specify * the intended channel. * * @see SSH2::read() * @param string $cmd * @param int|null $channel Channel id returned by self::getInteractiveChannelId() * @return void * @throws \RuntimeException on connection error * @throws InsufficientSetupException on unexpected channel status, possibly due to closure */ public function write($cmd, $channel = null) { if (!$this->isAuthenticated()) { throw new InsufficientSetupException('Operation disallowed prior to login()'); } if ($channel === null) { $channel = $this->get_interactive_channel(); } if (!$this->is_channel_status_data($channel)) { if ($channel != self::CHANNEL_SHELL) { throw new InsufficientSetupException('Data is not available on channel'); } elseif (!$this->openShell()) { throw new \RuntimeException('Unable to initiate an interactive shell session'); } } $this->send_channel_packet($channel, $cmd); } /** * Start a subsystem. * * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented * if there's sufficient demand for such a feature. * * @see self::stopSubsystem() * @param string $subsystem * @return bool */ public function startSubsystem($subsystem) { $this->open_channel(self::CHANNEL_SUBSYSTEM); $packet = Strings::packSSH2( 'CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SUBSYSTEM], 'subsystem', 1, $subsystem ); $this->send_binary_packet($packet); $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) { return false; } $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM; return true; } /** * Stops a subsystem. * * @see self::startSubsystem() * @return bool */ public function stopSubsystem() { if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) { $this->close_channel(self::CHANNEL_SUBSYSTEM); } return true; } /** * Closes a channel * * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call * * If not specifying a channel, an open interactive channel will be selected. If there are * multiple open interactive channels, a legacy behavior will apply in which channel selection * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple * interactive channels, callers are discouraged from relying on this legacy behavior and * should specify the intended channel. * * @param int|null $channel Channel id returned by self::getInteractiveChannelId() * @return void */ public function reset($channel = null) { if ($channel === null) { $channel = $this->get_interactive_channel(); } if ($this->isInteractiveChannelOpen($channel)) { $this->close_channel($channel); } } /** * Is timeout? * * Did exec() or read() return because they timed out or because they encountered the end? * */ public function isTimeout() { return $this->is_timeout; } /** * Disconnect * */ public function disconnect() { $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { fclose($this->realtime_log_file); } unset(self::$connections[$this->getResourceId()]); } /** * Destructor. * * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call * disconnect(). * */ public function __destruct() { $this->disconnect(); } /** * Is the connection still active? * * $level has 3x possible values: * 0 (default): phpseclib takes a passive approach to see if the connection is still active by calling feof() * on the socket * 1: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_IGNORE * packet that doesn't require a response * 2: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_CHANNEL_OPEN * packet and imediately trying to close that channel. some routers, in particular, however, will only let you * open one channel, so this approach could yield false positives * * @param int $level * @return bool */ public function isConnected($level = 0) { if (!is_int($level) || $level < 0 || $level > 2) { throw new \InvalidArgumentException('$level must be 0, 1 or 2'); } if ($level == 0) { return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock); } try { if ($level == 1) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); } else { $this->open_channel(self::CHANNEL_KEEP_ALIVE); $this->close_channel(self::CHANNEL_KEEP_ALIVE); } return true; } catch (\Exception $e) { return false; } } /** * Have you successfully been logged in? * * @return bool */ public function isAuthenticated() { return (bool) ($this->bitmap & self::MASK_LOGIN); } /** * Is the interactive shell active? * * @return bool */ public function isShellOpen() { return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL); } /** * Is the exec pty active? * * @return bool */ public function isPTYOpen() { return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC); } /** * Is the given interactive channel active? * * @param int $channel Channel id returned by self::getInteractiveChannelId() * @return bool */ public function isInteractiveChannelOpen($channel) { return $this->isAuthenticated() && $this->is_channel_status_data($channel); } /** * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status. * Returns 0 if no interactive channel has been opened. * * @see self::isInteractiveChannelOpen() * @return int */ public function getInteractiveChannelId() { return $this->channel_id_last_interactive; } /** * Pings a server connection, or tries to reconnect if the connection has gone down * * Inspired by http://php.net/manual/en/mysqli.ping.php * * @return bool */ public function ping() { if (!$this->isAuthenticated()) { if (!empty($this->auth)) { return $this->reconnect(); } return false; } try { $this->open_channel(self::CHANNEL_KEEP_ALIVE); } catch (\RuntimeException $e) { return $this->reconnect(); } $this->close_channel(self::CHANNEL_KEEP_ALIVE); return true; } /** * In situ reconnect method * * @return boolean */ private function reconnect() { $this->reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); $this->retry_connect = true; $this->connect(); foreach ($this->auth as $auth) { $result = $this->login(...$auth); } return $result; } /** * Resets a connection for re-use * * @param int $reason */ protected function reset_connection($reason) { $this->disconnect_helper($reason); $this->decrypt = $this->encrypt = false; $this->decrypt_block_size = $this->encrypt_block_size = 8; $this->hmac_check = $this->hmac_create = false; $this->hmac_size = false; $this->session_id = false; $this->retry_connect = true; $this->get_seq_no = $this->send_seq_no = 0; $this->channel_status = []; $this->channel_id_last_interactive = 0; } /** * Gets Binary Packets * * See '6. Binary Packet Protocol' of rfc4253 for more info. * * @see self::_send_binary_packet() * @param bool $skip_channel_filter * @return bool|string */ private function get_binary_packet($skip_channel_filter = false) { if ($skip_channel_filter) { if (!is_resource($this->fsock)) { throw new \InvalidArgumentException('fsock is not a resource.'); } $read = [$this->fsock]; $write = $except = null; if (!$this->curTimeout) { if ($this->keepAlive <= 0) { static::stream_select($read, $write, $except, null); } else { if (!static::stream_select($read, $write, $except, $this->keepAlive)) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); return $this->get_binary_packet(true); } } } else { if ($this->curTimeout < 0) { $this->is_timeout = true; return true; } $start = microtime(true); if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { if (!static::stream_select($read, $write, $except, $this->keepAlive)) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); $elapsed = microtime(true) - $start; $this->curTimeout -= $elapsed; return $this->get_binary_packet(true); } $elapsed = microtime(true) - $start; $this->curTimeout -= $elapsed; } $sec = (int) floor($this->curTimeout); $usec = (int) (1000000 * ($this->curTimeout - $sec)); // this can return a "stream_select(): unable to select [4]: Interrupted system call" error if (!static::stream_select($read, $write, $except, $sec, $usec)) { $this->is_timeout = true; return true; } $elapsed = microtime(true) - $start; $this->curTimeout -= $elapsed; } } if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; $str = 'Connection closed (by server) prematurely'; if (isset($elapsed)) { $str .= ' ' . $elapsed . 's'; } throw new ConnectionClosedException($str); } $start = microtime(true); if ($this->curTimeout) { $sec = (int) floor($this->curTimeout); $usec = (int) (1000000 * ($this->curTimeout - $sec)); stream_set_timeout($this->fsock, $sec, $usec); } $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); if (!strlen($raw)) { $this->bitmap = 0; throw new ConnectionClosedException('No data received from server'); } if ($this->decrypt) { switch ($this->decryptName) { case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': $this->decrypt->setNonce( $this->decryptFixedPart . $this->decryptInvocationCounter ); Strings::increment_str($this->decryptInvocationCounter); $this->decrypt->setAAD($temp = Strings::shift($raw, 4)); extract(unpack('Npacket_length', $temp)); /** * @var integer $packet_length */ $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4); $stop = microtime(true); $tag = stream_get_contents($this->fsock, $this->decrypt_block_size); $this->decrypt->setTag($tag); $raw = $this->decrypt->decrypt($raw); $raw = $temp . $raw; $remaining_length = 0; break; case 'chacha20-poly1305@openssh.com': // This should be impossible, but we are checking anyway to narrow the type for Psalm. if (!($this->decrypt instanceof ChaCha20)) { throw new \LogicException('$this->decrypt is not a ' . ChaCha20::class); } $nonce = pack('N2', 0, $this->get_seq_no); $this->lengthDecrypt->setNonce($nonce); $temp = $this->lengthDecrypt->decrypt($aad = Strings::shift($raw, 4)); extract(unpack('Npacket_length', $temp)); /** * @var integer $packet_length */ $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4); $stop = microtime(true); $tag = stream_get_contents($this->fsock, 16); $this->decrypt->setNonce($nonce); $this->decrypt->setCounter(0); // this is the same approach that's implemented in Salsa20::createPoly1305Key() // but we don't want to use the same AEAD construction that RFC8439 describes // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305()) $this->decrypt->setPoly1305Key( $this->decrypt->encrypt(str_repeat("\0", 32)) ); $this->decrypt->setAAD($aad); $this->decrypt->setCounter(1); $this->decrypt->setTag($tag); $raw = $this->decrypt->decrypt($raw); $raw = $temp . $raw; $remaining_length = 0; break; default: if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) { $raw = $this->decrypt->decrypt($raw); break; } extract(unpack('Npacket_length', $temp = Strings::shift($raw, 4))); /** * @var integer $packet_length */ $raw .= $this->read_remaining_bytes($packet_length - $this->decrypt_block_size + 4); $stop = microtime(true); $encrypted = $temp . $raw; $raw = $temp . $this->decrypt->decrypt($raw); $remaining_length = 0; } } if (strlen($raw) < 5) { $this->bitmap = 0; throw new \RuntimeException('Plaintext is too short'); } extract(unpack('Npacket_length/Cpadding_length', Strings::shift($raw, 5))); /** * @var integer $packet_length * @var integer $padding_length */ if (!isset($remaining_length)) { $remaining_length = $packet_length + 4 - $this->decrypt_block_size; } $buffer = $this->read_remaining_bytes($remaining_length); if (!isset($stop)) { $stop = microtime(true); } if (strlen($buffer)) { $raw .= $this->decrypt ? $this->decrypt->decrypt($buffer) : $buffer; } $payload = Strings::shift($raw, $packet_length - $padding_length - 1); $padding = Strings::shift($raw, $padding_length); // should leave $raw empty if ($this->hmac_check instanceof Hash) { $hmac = stream_get_contents($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); throw new \RuntimeException('Error reading socket'); } $reconstructed = !$this->hmac_check_etm ? pack('NCa*', $packet_length, $padding_length, $payload . $padding) : $encrypted; if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') { $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no)); if ($hmac != $this->hmac_check->hash($reconstructed)) { $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); throw new \RuntimeException('Invalid UMAC'); } } else { if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) { $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); throw new \RuntimeException('Invalid HMAC'); } } } switch ($this->decompress) { case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: if (!$this->isAuthenticated()) { break; } // fall-through case self::NET_SSH2_COMPRESSION_ZLIB: if ($this->regenerate_decompression_context) { $this->regenerate_decompression_context = false; $cmf = ord($payload[0]); $cm = $cmf & 0x0F; if ($cm != 8) { // deflate user_error("Only CM = 8 ('deflate') is supported ($cm)"); } $cinfo = ($cmf & 0xF0) >> 4; if ($cinfo > 7) { user_error("CINFO above 7 is not allowed ($cinfo)"); } $windowSize = 1 << ($cinfo + 8); $flg = ord($payload[1]); //$fcheck = $flg && 0x0F; if ((($cmf << 8) | $flg) % 31) { user_error('fcheck failed'); } $fdict = boolval($flg & 0x20); $flevel = ($flg & 0xC0) >> 6; $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, ['window' => $cinfo + 8]); $payload = substr($payload, 2); } if ($this->decompress_context) { $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH); } } $this->get_seq_no++; if (defined('NET_SSH2_LOGGING')) { $current = microtime(true); $message_number = isset(self::$message_numbers[ord($payload[0])]) ? self::$message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; $message_number = '<- ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; $this->append_log($message_number, $payload); $this->last_packet = $current; } return $this->filter($payload, $skip_channel_filter); } /** * Read Remaining Bytes * * @see self::get_binary_packet() * @param int $remaining_length * @return string */ private function read_remaining_bytes($remaining_length) { if (!$remaining_length) { return ''; } $adjustLength = false; if ($this->decrypt) { switch (true) { case $this->decryptName == 'aes128-gcm@openssh.com': case $this->decryptName == 'aes256-gcm@openssh.com': case $this->decryptName == 'chacha20-poly1305@openssh.com': case $this->hmac_check instanceof Hash && $this->hmac_check_etm: $remaining_length += $this->decrypt_block_size - 4; $adjustLength = true; } } // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>, // "implementations SHOULD check that the packet length is reasonable" // PuTTY uses 0x9000 as the actual max packet size and so to shall we // don't do this when GCM mode is used since GCM mode doesn't encrypt the length if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) { $this->bad_key_size_fix = true; $this->reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); return false; } throw new \RuntimeException('Invalid size'); } if ($adjustLength) { $remaining_length -= $this->decrypt_block_size - 4; } $buffer = ''; while ($remaining_length > 0) { $temp = stream_get_contents($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); throw new \RuntimeException('Error reading from socket'); } $buffer .= $temp; $remaining_length -= strlen($temp); } return $buffer; } /** * Filter Binary Packets * * Because some binary packets need to be ignored... * * @see self::_get_binary_packet() * @param string $payload * @param bool $skip_channel_filter * @return string|bool */ private function filter($payload, $skip_channel_filter) { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: Strings::shift($payload, 1); list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload); $this->errors[] = 'SSH_MSG_DISCONNECT: ' . self::$disconnect_reasons[$reason_code] . "\r\n$message"; $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: $this->extra_packets++; $payload = $this->get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_DEBUG: $this->extra_packets++; Strings::shift($payload, 2); // second byte is "always_display" list($message) = Strings::unpackSSH2('s', $payload); $this->errors[] = "SSH_MSG_DEBUG: $message"; $payload = $this->get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: // this is here for key re-exchanges after the initial key exchange if ($this->session_id !== false) { if (!$this->key_exchange($payload)) { $this->bitmap = 0; return false; } $payload = $this->get_binary_packet($skip_channel_filter); } } // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { Strings::shift($payload, 1); list($this->banner_message) = Strings::unpackSSH2('s', $payload); $payload = $this->get_binary_packet(); } // only called when we've already logged in if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { if (is_bool($payload)) { return $payload; } switch (ord($payload[0])) { case NET_SSH2_MSG_CHANNEL_REQUEST: if (strlen($payload) == 31) { extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { if (ord(substr($payload, 9 + $length))) { // want reply $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); } $payload = $this->get_binary_packet($skip_channel_filter); } } break; case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_EOF: if (!$skip_channel_filter && !empty($this->server_channels)) { $this->binary_packet_buffer = $payload; $this->get_channel_packet(true); $payload = $this->get_binary_packet(); } break; case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 Strings::shift($payload, 1); list($request_name) = Strings::unpackSSH2('s', $payload); $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name"; try { $this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE)); } catch (\RuntimeException $e) { return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); } $payload = $this->get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 Strings::shift($payload, 1); list($data, $server_channel) = Strings::unpackSSH2('sN', $payload); switch ($data) { case 'auth-agent': case 'auth-agent@openssh.com': if (isset($this->agent)) { $new_channel = self::CHANNEL_AGENT_FORWARD; list( $remote_window_size, $remote_maximum_packet_size ) = Strings::unpackSSH2('NN', $payload); $this->packet_size_client_to_server[$new_channel] = $remote_window_size; $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; $this->window_size_client_to_server[$new_channel] = $this->window_size; $packet_size = 0x4000; $packet = pack( 'CN4', NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, $server_channel, $new_channel, $packet_size, $packet_size ); $this->server_channels[$new_channel] = $server_channel; $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; $this->send_binary_packet($packet); } break; default: $packet = Strings::packSSH2( 'CN2ss', NET_SSH2_MSG_CHANNEL_OPEN_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, '', // description '' // language tag ); try { $this->send_binary_packet($packet); } catch (\RuntimeException $e) { return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); } } $payload = $this->get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: Strings::shift($payload, 1); list($channel, $window_size) = Strings::unpackSSH2('NN', $payload); $this->window_size_client_to_server[$channel] += $window_size; $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->get_binary_packet($skip_channel_filter); } } return $payload; } /** * Enable Quiet Mode * * Suppress stderr from output * */ public function enableQuietMode() { $this->quiet_mode = true; } /** * Disable Quiet Mode * * Show stderr in output * */ public function disableQuietMode() { $this->quiet_mode = false; } /** * Returns whether Quiet Mode is enabled or not * * @see self::enableQuietMode() * @see self::disableQuietMode() * @return bool */ public function isQuietModeEnabled() { return $this->quiet_mode; } /** * Enable request-pty when using exec() * */ public function enablePTY() { $this->request_pty = true; } /** * Disable request-pty when using exec() * */ public function disablePTY() { if ($this->isPTYOpen()) { $this->close_channel(self::CHANNEL_EXEC); } $this->request_pty = false; } /** * Returns whether request-pty is enabled or not * * @see self::enablePTY() * @see self::disablePTY() * @return bool */ public function isPTYEnabled() { return $this->request_pty; } /** * Gets channel data * * Returns the data as a string. bool(true) is returned if: * * - the server closes the channel * - if the connection times out * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE * * bool(false) is returned if: * * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_FAILURE * * @param int $client_channel * @param bool $skip_extended * @return mixed * @throws \RuntimeException on connection error */ protected function get_channel_packet($client_channel, $skip_extended = false) { if (!empty($this->channel_buffers[$client_channel])) { switch ($this->channel_status[$client_channel]) { case NET_SSH2_MSG_CHANNEL_REQUEST: foreach ($this->channel_buffers[$client_channel] as $i => $packet) { switch (ord($packet[0])) { case NET_SSH2_MSG_CHANNEL_SUCCESS: case NET_SSH2_MSG_CHANNEL_FAILURE: unset($this->channel_buffers[$client_channel][$i]); return substr($packet, 1); } } break; default: return substr(array_shift($this->channel_buffers[$client_channel]), 1); } } while (true) { if ($this->binary_packet_buffer !== false) { $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { $response = $this->get_binary_packet(true); if ($response === true && $this->is_timeout) { if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { $this->close_channel($client_channel); } return true; } if ($response === false) { $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); throw new ConnectionClosedException('Connection closed by server'); } } if ($client_channel == -1 && $response === true) { return true; } list($type, $channel) = Strings::unpackSSH2('CN', $response); // will not be setup yet on incoming channel open request if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { $this->window_size_server_to_client[$channel] -= strlen($response); // resize the window, if appropriate if ($this->window_size_server_to_client[$channel] < 0) { // PuTTY does something more analogous to the following: //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); $this->send_binary_packet($packet); $this->window_size_server_to_client[$channel] += $this->window_resize; } switch ($type) { case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: /* if ($client_channel == self::CHANNEL_EXEC) { $this->send_channel_packet($client_channel, chr(0)); } */ // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response); $this->stdErrorLog .= $data; if ($skip_extended || $this->quiet_mode) { continue 2; } if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { return $data; } $this->channel_buffers[$channel][] = chr($type) . $data; continue 2; case NET_SSH2_MSG_CHANNEL_REQUEST: if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { continue 2; } list($value) = Strings::unpackSSH2('s', $response); switch ($value) { case 'exit-signal': list( , // FALSE $signal_name, , // core dumped $error_message ) = Strings::unpackSSH2('bsbs', $response); $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name"; if (strlen($error_message)) { $this->errors[count($this->errors) - 1] .= "\r\n$error_message"; } $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; continue 3; case 'exit-status': list(, $this->exit_status) = Strings::unpackSSH2('CN', $response); // "The client MAY ignore these messages." // -- http://tools.ietf.org/html/rfc4254#section-6.10 continue 3; default: // "Some systems may not implement signals, in which case they SHOULD ignore this message." // -- http://tools.ietf.org/html/rfc4254#section-6.9 continue 3; } } switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: list( $this->server_channels[$channel], $window_size, $this->packet_size_client_to_server[$channel] ) = Strings::unpackSSH2('NNN', $response); if ($window_size < 0) { $window_size &= 0x7FFFFFFF; $window_size += 0x80000000; } $this->window_size_client_to_server[$channel] = $window_size; $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended); $this->on_channel_open(); return $result; case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to open channel'); default: if ($client_channel == $channel) { $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unexpected response to open request'); } return $this->get_channel_packet($client_channel, $skip_extended); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: return true; case NET_SSH2_MSG_CHANNEL_FAILURE: return false; case NET_SSH2_MSG_CHANNEL_DATA: list($data) = Strings::unpackSSH2('s', $response); $this->channel_buffers[$channel][] = chr($type) . $data; return $this->get_channel_packet($client_channel, $skip_extended); default: $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to fulfill channel request'); } case NET_SSH2_MSG_CHANNEL_CLOSE: if ($client_channel == $channel && $type == NET_SSH2_MSG_CHANNEL_CLOSE) { return true; } return $this->get_channel_packet($client_channel, $skip_extended); } } // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: /* if ($channel == self::CHANNEL_EXEC) { // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server // this actually seems to make things twice as fast. more to the point, the message right after // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. // in OpenSSH it slows things down but only by a couple thousandths of a second. $this->send_channel_packet($channel, chr(0)); } */ list($data) = Strings::unpackSSH2('s', $response); if ($channel == self::CHANNEL_AGENT_FORWARD) { $agent_response = $this->agent->forwardData($data); if (!is_bool($agent_response)) { $this->send_channel_packet($channel, $agent_response); } break; } if ($client_channel == $channel) { return $data; } $this->channel_buffers[$channel][] = chr($type) . $data; break; case NET_SSH2_MSG_CHANNEL_CLOSE: $this->curTimeout = 5; $this->close_channel_bitmap($channel); if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); } $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; $this->channelCount--; if ($client_channel == $channel) { return true; } // fall-through case NET_SSH2_MSG_CHANNEL_EOF: break; default: $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException("Error reading channel data ($type)"); } } } /** * Sends Binary Packets * * See '6. Binary Packet Protocol' of rfc4253 for more info. * * @param string $data * @param string $logged * @see self::_get_binary_packet() * @return void */ protected function send_binary_packet($data, $logged = null) { if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; throw new ConnectionClosedException('Connection closed prematurely'); } if (!isset($logged)) { $logged = $data; } switch ($this->compress) { case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: if (!$this->isAuthenticated()) { break; } // fall-through case self::NET_SSH2_COMPRESSION_ZLIB: if (!$this->regenerate_compression_context) { $header = ''; } else { $this->regenerate_compression_context = false; $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]); $header = "\x78\x9C"; } if ($this->compress_context) { $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH); } } // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 $packet_length = strlen($data) + 9; if ($this->encrypt && $this->encrypt->usesNonce()) { $packet_length -= 4; } // round up to the nearest $this->encrypt_block_size $packet_length += (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length $padding_length = $packet_length - strlen($data) - 5; switch (true) { case $this->encrypt && $this->encrypt->usesNonce(): case $this->hmac_create instanceof Hash && $this->hmac_create_etm: $padding_length += 4; $packet_length += 4; } $padding = Random::string($padding_length); // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); $hmac = ''; if ($this->hmac_create instanceof Hash && !$this->hmac_create_etm) { if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') { $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no)); $hmac = $this->hmac_create->hash($packet); } else { $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)); } } if ($this->encrypt) { switch ($this->encryptName) { case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': $this->encrypt->setNonce( $this->encryptFixedPart . $this->encryptInvocationCounter ); Strings::increment_str($this->encryptInvocationCounter); $this->encrypt->setAAD($temp = ($packet & "\xFF\xFF\xFF\xFF")); $packet = $temp . $this->encrypt->encrypt(substr($packet, 4)); break; case 'chacha20-poly1305@openssh.com': // This should be impossible, but we are checking anyway to narrow the type for Psalm. if (!($this->encrypt instanceof ChaCha20)) { throw new \LogicException('$this->encrypt is not a ' . ChaCha20::class); } $nonce = pack('N2', 0, $this->send_seq_no); $this->encrypt->setNonce($nonce); $this->lengthEncrypt->setNonce($nonce); $length = $this->lengthEncrypt->encrypt($packet & "\xFF\xFF\xFF\xFF"); $this->encrypt->setCounter(0); // this is the same approach that's implemented in Salsa20::createPoly1305Key() // but we don't want to use the same AEAD construction that RFC8439 describes // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305()) $this->encrypt->setPoly1305Key( $this->encrypt->encrypt(str_repeat("\0", 32)) ); $this->encrypt->setAAD($length); $this->encrypt->setCounter(1); $packet = $length . $this->encrypt->encrypt(substr($packet, 4)); break; default: $packet = $this->hmac_create instanceof Hash && $this->hmac_create_etm ? ($packet & "\xFF\xFF\xFF\xFF") . $this->encrypt->encrypt(substr($packet, 4)) : $this->encrypt->encrypt($packet); } } if ($this->hmac_create instanceof Hash && $this->hmac_create_etm) { if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') { $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no)); $hmac = $this->hmac_create->hash($packet); } else { $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)); } } $this->send_seq_no++; $packet .= $this->encrypt && $this->encrypt->usesNonce() ? $this->encrypt->getTag() : $hmac; $start = microtime(true); $sent = @fputs($this->fsock, $packet); $stop = microtime(true); if (defined('NET_SSH2_LOGGING')) { $current = microtime(true); $message_number = isset(self::$message_numbers[ord($logged[0])]) ? self::$message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; $message_number = '-> ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; $this->append_log($message_number, $logged); $this->last_packet = $current; } if (strlen($packet) != $sent) { $this->bitmap = 0; $message = $sent === false ? 'Unable to write ' . strlen($packet) . ' bytes' : "Only $sent of " . strlen($packet) . " bytes were sent"; throw new \RuntimeException($message); } } /** * Logs data packets * * Makes sure that only the last 1MB worth of packets will be logged * * @param string $message_number * @param string $message */ private function append_log($message_number, $message) { $this->append_log_helper( NET_SSH2_LOGGING, $message_number, $message, $this->message_number_log, $this->message_log, $this->log_size, $this->realtime_log_file, $this->realtime_log_wrap, $this->realtime_log_size ); } /** * Logs data packet helper * * @param int $constant * @param string $message_number * @param string $message * @param array &$message_number_log * @param array &$message_log * @param int &$log_size * @param resource &$realtime_log_file * @param bool &$realtime_log_wrap * @param int &$realtime_log_size */ protected function append_log_helper($constant, $message_number, $message, array &$message_number_log, array &$message_log, &$log_size, &$realtime_log_file, &$realtime_log_wrap, &$realtime_log_size) { // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) if (strlen($message_number) > 2) { Strings::shift($message); } switch ($constant) { // useful for benchmarks case self::LOG_SIMPLE: $message_number_log[] = $message_number; break; case self::LOG_SIMPLE_REALTIME: echo $message_number; echo PHP_SAPI == 'cli' ? "\r\n" : '<br>'; @flush(); @ob_flush(); break; // the most useful log for SSH2 case self::LOG_COMPLEX: $message_number_log[] = $message_number; $log_size += strlen($message); $message_log[] = $message; while ($log_size > self::LOG_MAX_SIZE) { $log_size -= strlen(array_shift($message_log)); array_shift($message_number_log); } break; // dump the output out realtime; packets may be interspersed with non packets, // passwords won't be filtered out and select other packets may not be correctly // identified case self::LOG_REALTIME: switch (PHP_SAPI) { case 'cli': $start = $stop = "\r\n"; break; default: $start = '<pre>'; $stop = '</pre>'; } echo $start . $this->format_log([$message], [$message_number]) . $stop; @flush(); @ob_flush(); break; // basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily // at the beginning of the file case self::LOG_REALTIME_FILE: if (!isset($realtime_log_file)) { // PHP doesn't seem to like using constants in fopen() $filename = NET_SSH2_LOG_REALTIME_FILENAME; $fp = fopen($filename, 'w'); $realtime_log_file = $fp; } if (!is_resource($realtime_log_file)) { break; } $entry = $this->format_log([$message], [$message_number]); if ($realtime_log_wrap) { $temp = "<<< START >>>\r\n"; $entry .= $temp; fseek($realtime_log_file, ftell($realtime_log_file) - strlen($temp)); } $realtime_log_size += strlen($entry); if ($realtime_log_size > self::LOG_MAX_SIZE) { fseek($realtime_log_file, 0); $realtime_log_size = strlen($entry); $realtime_log_wrap = true; } fputs($realtime_log_file, $entry); } } /** * Sends channel data * * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate * * @param int $client_channel * @param string $data * @return void */ protected function send_channel_packet($client_channel, $data) { while (strlen($data)) { if (!$this->window_size_client_to_server[$client_channel]) { $this->bitmap ^= self::MASK_WINDOW_ADJUST; // using an invalid channel will let the buffers be built up for the valid channels $this->get_channel_packet(-1); $this->bitmap ^= self::MASK_WINDOW_ADJUST; } /* The maximum amount of data allowed is determined by the maximum packet size for the channel, and the current window size, whichever is smaller. -- http://tools.ietf.org/html/rfc4254#section-5.2 */ $max_size = min( $this->packet_size_client_to_server[$client_channel], $this->window_size_client_to_server[$client_channel] ); $temp = Strings::shift($data, $max_size); $packet = Strings::packSSH2( 'CNs', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], $temp ); $this->window_size_client_to_server[$client_channel] -= strlen($temp); $this->send_binary_packet($packet); } } /** * Closes and flushes a channel * * \phpseclib3\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server * and for SFTP channels are presumably closed when the client disconnects. This functions is intended * for SCP more than anything. * * @param int $client_channel * @param bool $want_reply * @return void */ private function close_channel($client_channel, $want_reply = false) { // see http://tools.ietf.org/html/rfc4254#section-5.3 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); if (!$want_reply) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); } $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; $this->channelCount--; $this->curTimeout = 5; while (!is_bool($this->get_channel_packet($client_channel))) { } if ($want_reply) { $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); } $this->close_channel_bitmap($client_channel); } /** * Maintains execution state bitmap in response to channel closure * * @param int $client_channel The channel number to maintain closure status of * @return void */ private function close_channel_bitmap($client_channel) { switch ($client_channel) { case self::CHANNEL_SHELL: // Shell status has been maintained in the bitmap for backwards // compatibility sake, but can be removed going forward if ($this->bitmap & self::MASK_SHELL) { $this->bitmap &= ~self::MASK_SHELL; } break; } } /** * Disconnect * * @param int $reason * @return false */ protected function disconnect_helper($reason) { if ($this->bitmap & self::MASK_CONNECTED) { $data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', ''); try { $this->send_binary_packet($data); } catch (\Exception $e) { } } $this->bitmap = 0; if (is_resource($this->fsock) && get_resource_type($this->fsock) === 'stream') { fclose($this->fsock); } return false; } /** * Define Array * * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * * @param mixed[] ...$args * @access protected */ protected static function define_array(...$args) { foreach ($args as $arg) { foreach ($arg as $key => $value) { if (!defined($value)) { define($value, $key); } else { break 2; } } } } /** * Returns a log of the packets that have been sent and received. * * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') * * @return array|false|string */ public function getLog() { if (!defined('NET_SSH2_LOGGING')) { return false; } switch (NET_SSH2_LOGGING) { case self::LOG_SIMPLE: return $this->message_number_log; case self::LOG_COMPLEX: $log = $this->format_log($this->message_log, $this->message_number_log); return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>'; default: return false; } } /** * Formats a log for printing * * @param array $message_log * @param array $message_number_log * @return string */ protected function format_log(array $message_log, array $message_number_log) { $output = ''; for ($i = 0; $i < count($message_log); $i++) { $output .= $message_number_log[$i] . "\r\n"; $current_log = $message_log[$i]; $j = 0; do { if (strlen($current_log)) { $output .= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; } $fragment = Strings::shift($current_log, $this->log_short_width); $hex = substr(preg_replace_callback('#.#s', function ($matches) { return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); }, $fragment), strlen($this->log_boundary)); // replace non ASCII printable characters with dots // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters // also replace < with a . since < messes up the output on web browsers $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); $output .= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; $j++; } while (strlen($current_log)); $output .= "\r\n"; } return $output; } /** * Helper function for agent->on_channel_open() * * Used when channels are created to inform agent * of said channel opening. Must be called after * channel open confirmation received * */ private function on_channel_open() { if (isset($this->agent)) { $this->agent->registerChannelOpen($this); } } /** * Returns the first value of the intersection of two arrays or false if * the intersection is empty. The order is defined by the first parameter. * * @param array $array1 * @param array $array2 * @return mixed False if intersection is empty, else intersected value. */ private static function array_intersect_first(array $array1, array $array2) { foreach ($array1 as $value) { if (in_array($value, $array2)) { return $value; } } return false; } /** * Returns all errors / debug messages on the SSH layer * * If you are looking for messages from the SFTP layer, please see SFTP::getSFTPErrors() * * @return string[] */ public function getErrors() { return $this->errors; } /** * Returns the last error received on the SSH layer * * If you are looking for messages from the SFTP layer, please see SFTP::getLastSFTPError() * * @return string */ public function getLastError() { $count = count($this->errors); if ($count > 0) { return $this->errors[$count - 1]; } } /** * Return the server identification. * * @return string|false */ public function getServerIdentification() { $this->connect(); return $this->server_identifier; } /** * Returns a list of algorithms the server supports * * @return array */ public function getServerAlgorithms() { $this->connect(); return [ 'kex' => $this->kex_algorithms, 'hostkey' => $this->server_host_key_algorithms, 'client_to_server' => [ 'crypt' => $this->encryption_algorithms_client_to_server, 'mac' => $this->mac_algorithms_client_to_server, 'comp' => $this->compression_algorithms_client_to_server, 'lang' => $this->languages_client_to_server ], 'server_to_client' => [ 'crypt' => $this->encryption_algorithms_server_to_client, 'mac' => $this->mac_algorithms_server_to_client, 'comp' => $this->compression_algorithms_server_to_client, 'lang' => $this->languages_server_to_client ] ]; } /** * Returns a list of KEX algorithms that phpseclib supports * * @return array */ public static function getSupportedKEXAlgorithms() { $kex_algorithms = [ // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the // libssh repository for more information. 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp256', // RFC 5656 'ecdh-sha2-nistp384', // RFC 5656 'ecdh-sha2-nistp521', // RFC 5656 'diffie-hellman-group-exchange-sha256',// RFC 4419 'diffie-hellman-group-exchange-sha1', // RFC 4419 // Diffie-Hellman Key Agreement (DH) using integer modulo prime // groups. 'diffie-hellman-group14-sha256', 'diffie-hellman-group14-sha1', // REQUIRED 'diffie-hellman-group15-sha512', 'diffie-hellman-group16-sha512', 'diffie-hellman-group17-sha512', 'diffie-hellman-group18-sha512', 'diffie-hellman-group1-sha1', // REQUIRED ]; return $kex_algorithms; } /** * Returns a list of host key algorithms that phpseclib supports * * @return array */ public static function getSupportedHostKeyAlgorithms() { return [ 'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02 'ecdsa-sha2-nistp256', // RFC 5656 'ecdsa-sha2-nistp384', // RFC 5656 'ecdsa-sha2-nistp521', // RFC 5656 'rsa-sha2-256', // RFC 8332 'rsa-sha2-512', // RFC 8332 'ssh-rsa', // RECOMMENDED sign Raw RSA Key 'ssh-dss' // REQUIRED sign Raw DSS Key ]; } /** * Returns a list of symmetric key algorithms that phpseclib supports * * @return array */ public static function getSupportedEncryptionAlgorithms() { $algos = [ // from <https://tools.ietf.org/html/rfc5647>: 'aes128-gcm@openssh.com', 'aes256-gcm@openssh.com', // from <http://tools.ietf.org/html/rfc4345#section-4>: 'arcfour256', 'arcfour128', //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>: 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key 'aes192-ctr', // RECOMMENDED AES with 192-bit key 'aes256-ctr', // RECOMMENDED AES with 256-bit key // from <https://github.com/openssh/openssh-portable/blob/001aa55/PROTOCOL.chacha20poly1305>: // one of the big benefits of chacha20-poly1305 is speed. the problem is... // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20 // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down. // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC // (which is always gonna be super fast to compute thanks to the hash extension, which // "is bundled and compiled into PHP by default") 'chacha20-poly1305@openssh.com', 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key 'aes128-cbc', // RECOMMENDED AES with a 128-bit key 'aes192-cbc', // OPTIONAL AES with a 192-bit key 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key 'twofish256-cbc', 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" // (this is being retained for historical reasons) 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode '3des-cbc', // REQUIRED three-key 3DES in CBC mode //'none' // OPTIONAL no encryption; NOT RECOMMENDED ]; if (self::$crypto_engine) { $engines = [self::$crypto_engine]; } else { $engines = [ 'libsodium', 'OpenSSL (GCM)', 'OpenSSL', 'mcrypt', 'Eval', 'PHP' ]; } $ciphers = []; foreach ($engines as $engine) { foreach ($algos as $algo) { $obj = self::encryption_algorithm_to_crypt_instance($algo); if ($obj instanceof Rijndael) { $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo)); } switch ($algo) { case 'chacha20-poly1305@openssh.com': case 'arcfour128': case 'arcfour256': if ($engine != 'Eval') { continue 2; } break; case 'aes128-gcm@openssh.com': case 'aes256-gcm@openssh.com': if ($engine == 'OpenSSL') { continue 2; } $obj->setNonce('dummydummydu'); } if ($obj->isValidEngine($engine)) { $algos = array_diff($algos, [$algo]); $ciphers[] = $algo; } } } return $ciphers; } /** * Returns a list of MAC algorithms that phpseclib supports * * @return array */ public static function getSupportedMACAlgorithms() { return [ 'hmac-sha2-256-etm@openssh.com', 'hmac-sha2-512-etm@openssh.com', 'umac-64-etm@openssh.com', 'umac-128-etm@openssh.com', 'hmac-sha1-etm@openssh.com', // from <http://www.ietf.org/rfc/rfc6668.txt>: 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) 'hmac-sha2-512',// OPTIONAL HMAC-SHA512 (digest length = key length = 64) // from <https://tools.ietf.org/html/draft-miller-secsh-umac-01>: 'umac-64@openssh.com', 'umac-128@openssh.com', 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) //'none' // OPTIONAL no MAC; NOT RECOMMENDED ]; } /** * Returns a list of compression algorithms that phpseclib supports * * @return array */ public static function getSupportedCompressionAlgorithms() { $algos = ['none']; // REQUIRED no compression if (function_exists('deflate_init')) { $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed $algos[] = 'zlib'; } return $algos; } /** * Return list of negotiated algorithms * * Uses the same format as https://www.php.net/ssh2-methods-negotiated * * @return array */ public function getAlgorithmsNegotiated() { $this->connect(); $compression_map = [ self::NET_SSH2_COMPRESSION_NONE => 'none', self::NET_SSH2_COMPRESSION_ZLIB => 'zlib', self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com' ]; return [ 'kex' => $this->kex_algorithm, 'hostkey' => $this->signature_format, 'client_to_server' => [ 'crypt' => $this->encryptName, 'mac' => $this->hmac_create_name, 'comp' => $compression_map[$this->compress], ], 'server_to_client' => [ 'crypt' => $this->decryptName, 'mac' => $this->hmac_check_name, 'comp' => $compression_map[$this->decompress], ] ]; } /** * Force multiple channels (even if phpseclib has decided to disable them) */ public function forceMultipleChannels() { $this->errorOnMultipleChannels = false; } /** * Allows you to set the terminal * * @param string $term */ public function setTerminal($term) { $this->term = $term; } /** * Accepts an associative array with up to four parameters as described at * <https://www.php.net/manual/en/function.ssh2-connect.php> * * @param array $methods */ public function setPreferredAlgorithms(array $methods) { $preferred = $methods; if (isset($preferred['kex'])) { $preferred['kex'] = array_intersect( $preferred['kex'], static::getSupportedKEXAlgorithms() ); } if (isset($preferred['hostkey'])) { $preferred['hostkey'] = array_intersect( $preferred['hostkey'], static::getSupportedHostKeyAlgorithms() ); } $keys = ['client_to_server', 'server_to_client']; foreach ($keys as $key) { if (isset($preferred[$key])) { $a = &$preferred[$key]; if (isset($a['crypt'])) { $a['crypt'] = array_intersect( $a['crypt'], static::getSupportedEncryptionAlgorithms() ); } if (isset($a['comp'])) { $a['comp'] = array_intersect( $a['comp'], static::getSupportedCompressionAlgorithms() ); } if (isset($a['mac'])) { $a['mac'] = array_intersect( $a['mac'], static::getSupportedMACAlgorithms() ); } } } $keys = [ 'kex', 'hostkey', 'client_to_server/crypt', 'client_to_server/comp', 'client_to_server/mac', 'server_to_client/crypt', 'server_to_client/comp', 'server_to_client/mac', ]; foreach ($keys as $key) { $p = $preferred; $m = $methods; $subkeys = explode('/', $key); foreach ($subkeys as $subkey) { if (!isset($p[$subkey])) { continue 2; } $p = $p[$subkey]; $m = $m[$subkey]; } if (count($p) != count($m)) { $diff = array_diff($m, $p); $msg = count($diff) == 1 ? ' is not a supported algorithm' : ' are not supported algorithms'; throw new UnsupportedAlgorithmException(implode(', ', $diff) . $msg); } } $this->preferred = $preferred; } /** * Returns the banner message. * * Quoting from the RFC, "in some jurisdictions, sending a warning message before * authentication may be relevant for getting legal protection." * * @return string */ public function getBannerMessage() { return $this->banner_message; } /** * Returns the server public host key. * * Caching this the first time you connect to a server and checking the result on subsequent connections * is recommended. Returns false if the server signature is not signed correctly with the public host key. * * @return string|false * @throws \RuntimeException on badly formatted keys * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format */ public function getServerPublicHostKey() { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { $this->connect(); } $signature = $this->signature; $server_public_host_key = base64_encode($this->server_public_host_key); if ($this->signature_validated) { return $this->bitmap ? $this->signature_format . ' ' . $server_public_host_key : false; } $this->signature_validated = true; switch ($this->signature_format) { case 'ssh-ed25519': case 'ecdsa-sha2-nistp256': case 'ecdsa-sha2-nistp384': case 'ecdsa-sha2-nistp521': $key = EC::loadFormat('OpenSSH', $server_public_host_key) ->withSignatureFormat('SSH2'); switch ($this->signature_format) { case 'ssh-ed25519': $hash = 'sha512'; break; case 'ecdsa-sha2-nistp256': $hash = 'sha256'; break; case 'ecdsa-sha2-nistp384': $hash = 'sha384'; break; case 'ecdsa-sha2-nistp521': $hash = 'sha512'; } $key = $key->withHash($hash); break; case 'ssh-dss': $key = DSA::loadFormat('OpenSSH', $server_public_host_key) ->withSignatureFormat('SSH2') ->withHash('sha1'); break; case 'ssh-rsa': case 'rsa-sha2-256': case 'rsa-sha2-512': // could be ssh-rsa, rsa-sha2-256, rsa-sha2-512 // we don't check here because we already checked in key_exchange // some signatures have the type embedded within the message and some don't list(, $signature) = Strings::unpackSSH2('ss', $signature); $key = RSA::loadFormat('OpenSSH', $server_public_host_key) ->withPadding(RSA::SIGNATURE_PKCS1); switch ($this->signature_format) { case 'rsa-sha2-512': $hash = 'sha512'; break; case 'rsa-sha2-256': $hash = 'sha256'; break; //case 'ssh-rsa': default: $hash = 'sha1'; } $key = $key->withHash($hash); break; default: $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); throw new NoSupportedAlgorithmsException('Unsupported signature format'); } if (!$key->verify($this->exchange_hash, $signature)) { return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); }; return $this->signature_format . ' ' . $server_public_host_key; } /** * Returns the exit status of an SSH command or false. * * @return false|int */ public function getExitStatus() { if (is_null($this->exit_status)) { return false; } return $this->exit_status; } /** * Returns the number of columns for the terminal window size. * * @return int */ public function getWindowColumns() { return $this->windowColumns; } /** * Returns the number of rows for the terminal window size. * * @return int */ public function getWindowRows() { return $this->windowRows; } /** * Sets the number of columns for the terminal window size. * * @param int $value */ public function setWindowColumns($value) { $this->windowColumns = $value; } /** * Sets the number of rows for the terminal window size. * * @param int $value */ public function setWindowRows($value) { $this->windowRows = $value; } /** * Sets the number of columns and rows for the terminal window size. * * @param int $columns * @param int $rows */ public function setWindowSize($columns = 80, $rows = 24) { $this->windowColumns = $columns; $this->windowRows = $rows; } /** * To String Magic Method * * @return string */ #[\ReturnTypeWillChange] public function __toString() { return $this->getResourceId(); } /** * Get Resource ID * * We use {} because that symbols should not be in URL according to * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. * It will safe us from any conflicts, because otherwise regexp will * match all alphanumeric domains. * * @return string */ public function getResourceId() { return '{' . spl_object_hash($this) . '}'; } /** * Return existing connection * * @param string $id * * @return bool|SSH2 will return false if no such connection */ public static function getConnectionByResourceId($id) { if (isset(self::$connections[$id])) { return self::$connections[$id] instanceof \WeakReference ? self::$connections[$id]->get() : self::$connections[$id]; } return false; } /** * Return all excising connections * * @return array<string, SSH2> */ public static function getConnections() { if (!class_exists('WeakReference')) { /** @var array<string, SSH2> */ return self::$connections; } $temp = []; foreach (self::$connections as $key => $ref) { $temp[$key] = $ref->get(); } return $temp; } /* * Update packet types in log history * * @param string $old * @param string $new */ private function updateLogHistory($old, $new) { if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { $this->message_number_log[count($this->message_number_log) - 1] = str_replace( $old, $new, $this->message_number_log[count($this->message_number_log) - 1] ); } } /** * Return the list of authentication methods that may productively continue authentication. * * @see https://tools.ietf.org/html/rfc4252#section-5.1 * @return array|null */ public function getAuthMethodsToContinue() { return $this->auth_methods_to_continue; } /** * Enables "smart" multi-factor authentication (MFA) */ public function enableSmartMFA() { $this->smartMFA = true; } /** * Disables "smart" multi-factor authentication (MFA) */ public function disableSmartMFA() { $this->smartMFA = false; } } PK2A#]5��>��%vendor/phpseclib/phpseclib/BACKERS.mdnu�[���# Backers phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you. ## Backers - Allan Simon - [ChargeOver](https://chargeover.com/) - Raghu Veer Dendukuri - Zane Hooper - [Setasign](https://www.setasign.com/) - [Charles Severance](https://github.com/csev) - [Rachel Fish](https://github.com/itsrachelfish) - Tharyrok - [cjhaas](https://github.com/cjhaas) - [istiak-tridip](https://github.com/istiak-tridip) - [Anna Filina](https://github.com/afilina)PK2A#]nj��<<"vendor/phpseclib/phpseclib/AUTHORSnu�[���phpseclib Lead Developer: TerraFrost (Jim Wigginton) phpseclib Developers: monnerat (Patrick Monnerat) bantu (Andreas Fischer) petrich (Hans-Jürgen Petrich) GrahamCampbell (Graham Campbell) hc-jwormanPK2A#]��U -vendor/phpseclib/bcmath_compat/lib/bcmath.phpnu�[���<?php /** * bcmath polyfill * * PHP versions 5 and 7 * * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ use bcmath_compat\BCMath; if (!function_exists('bcadd')) { /** * Add two arbitrary precision numbers * * @var string $left_operand * @var string $right_operand * @var int $scale optional */ function bcadd($left_operand, $right_operand, $scale = 0) { return BCMath::add($left_operand, $right_operand, $scale); } /** * Compare two arbitrary precision numbers * * @var string $left_operand * @var string $right_operand * @var int $scale optional */ function bccomp($left_operand, $right_operand, $scale = 0) { return BCMath::comp($left_operand, $right_operand, $scale); } /** * Divide two arbitrary precision numbers * * @var string $dividend * @var string $divisor * @var int $scale optional */ function bcdiv($dividend, $divisor, $scale = 0) { return BCMath::div($dividend, $divisor, $scale); } /** * Get modulus of an arbitrary precision number * * @var string $dividend * @var string $divisor * @var int $scale optional */ function bcmod($dividend, $divisor, $scale = 0) { return BCMath::mod($dividend, $divisor, $scale); } /** * Multiply two arbitrary precision numbers * * @var string $left_operand * @var string $right_operand * @var int $scale optional */ function bcmul($dividend, $divisor, $scale = 0) { return BCMath::mul($dividend, $divisor, $scale); } /** * Raise an arbitrary precision number to another * * @var string $base * @var string $exponent * @var int $scale optional */ function bcpow($base, $exponent, $scale = 0) { return BCMath::pow($base, $exponent, $scale); } /** * Raise an arbitrary precision number to another, reduced by a specified modulus * * @var string $base * @var string $exponent * @var string $modulus * @var int $scale optional */ function bcpowmod($base, $exponent, $modulus, $scale = 0) { return BCMath::powmod($base, $exponent, $modulus, $scale); } /** * Set or get default scale parameter for all bc math functions * * @var int $scale */ function bcscale($scale = null) { return BCMath::scale($scale); } /** * Get the square root of an arbitrary precision number * * @var string $operand * @var int $scale optional */ function bcsqrt($operand, $scale = 0) { return BCMath::sqrt($operand, $scale); } /** * Subtract one arbitrary precision number from another * * @var string $left_operand * @var string $right_operand * @var int $scale optional */ function bcsub($left_operand, $right_operand, $scale = 0) { return BCMath::sub($left_operand, $right_operand, $scale); } } // the following were introduced in PHP 7.0.0 if (!class_exists('Error')) { class Error extends Exception { } class ArithmeticError extends Error { } class DivisionByZeroError extends ArithmeticError { } class TypeError extends Error { } } // the following was introduced in PHP 7.1.0 if (!class_exists('ArgumentCountError')) { class ArgumentCountError extends TypeError { } } // the following was introduced in PHP 8.0.0 if (!class_exists('ValueError')) { class ValueError extends Error { } }PK2A#]q�5�R7R7-vendor/phpseclib/bcmath_compat/src/BCMath.phpnu�[���<?php /** * BCMath Emulation Class * * PHP version 5 and 7 * * @author Jim Wigginton <terrafrost@php.net> * @copyright 2019 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License */ namespace bcmath_compat; use phpseclib3\Math\BigInteger; /** * BCMath Emulation Class * * @author Jim Wigginton <terrafrost@php.net> * @access public */ abstract class BCMath { /** * Default scale parameter for all bc math functions */ private static $scale; /** * Set or get default scale parameter for all bc math functions * * Uses the PHP 7.3+ behavior * * @var int $scale optional */ private static function scale($scale = null) { if (isset($scale)) { self::$scale = (int) $scale; } return self::$scale; } /** * Formats numbers * * Places the decimal place at the appropriate place, adds trailing 0's as appropriate, etc * * @var string $x * @var int $scale * @var int $pad * @var boolean $trim */ private static function format($x, $scale, $pad) { $sign = self::isNegative($x) ? '-' : ''; $x = str_replace('-', '', $x); if (strlen($x) != $pad) { $x = str_pad($x, $pad, '0', STR_PAD_LEFT); } $temp = $pad ? substr_replace($x, '.', -$pad, 0) : $x; $temp = explode('.', $temp); if ($temp[0] == '') { $temp[0] = '0'; } if (isset($temp[1])) { $temp[1] = substr($temp[1], 0, $scale); $temp[1] = str_pad($temp[1], $scale, '0'); } elseif ($scale) { $temp[1] = str_repeat('0', $scale); } $result = rtrim(implode('.', $temp), '.'); if ($sign == '-' && preg_match('#^0\.?0*$#', $result)) { $sign = ''; } return $sign . $result; } /** * Negativity Test * * @var BigInteger $x */ private static function isNegative($x) { return $x->compare(new BigInteger()) < 0; } /** * Add two arbitrary precision numbers * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function add($x, $y, $scale, $pad) { $z = $x->add($y); return self::format($z, $scale, $pad); } /** * Subtract one arbitrary precision number from another * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function sub($x, $y, $scale, $pad) { $z = $x->subtract($y); return self::format($z, $scale, $pad); } /** * Multiply two arbitrary precision numbers * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function mul($x, $y, $scale, $pad) { if ($x == '0' || $y == '0') { $r = '0'; if ($scale) { $r.= '.' . str_repeat('0', $scale); } return $r; } $z = $x->abs()->multiply($y->abs()); $result = self::format($z, $scale, 2 * $pad); $sign = (self::isNegative($x) ^ self::isNegative($y)) && !preg_match('#^0\.?0*$#', $result) ? '-' : ''; return $sign . $result; } /** * Divide two arbitrary precision numbers * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function div($x, $y, $scale, $pad) { if ($y == '0') { // < PHP 8.0 triggered a warning // >= PHP 8.0 throws an exception throw new \DivisionByZeroError('Division by zero'); } $temp = '1' . str_repeat('0', $scale); $temp = new BigInteger($temp); list($q) = $x->multiply($temp)->divide($y); return self::format($q, $scale, $scale); } /** * Get modulus of an arbitrary precision number * * Uses the PHP 7.2+ behavior * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function mod($x, $y, $scale, $pad) { if ($y == '0') { // < PHP 8.0 triggered a warning // >= PHP 8.0 throws an exception throw new \DivisionByZeroError('Division by zero'); } list($q) = $x->divide($y); $z = $y->multiply($q); $z = $x->subtract($z); return self::format($z, $scale, $pad); } /** * Compare two arbitrary precision numbers * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function comp($x, $y, $scale, $pad) { $x = new BigInteger($x[0] . substr($x[1], 0, $scale)); $y = new BigInteger($y[0] . substr($y[1], 0, $scale)); return $x->compare($y); } /** * Raise an arbitrary precision number to another * * Uses the PHP 7.2+ behavior * * @var string $x * @var string $y * @var int $scale * @var int $pad */ private static function pow($x, $y, $scale, $pad) { if ($y == '0') { $r = '1'; if ($scale) { $r.= '.' . str_repeat('0', $scale); } return $r; } $min = defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX; if (bccomp($y, PHP_INT_MAX) > 0 || bccomp($y, $min) <= 0) { throw new \ValueError('bcpow(): Argument #2 ($exponent) is too large'); } $sign = self::isNegative($x) ? '-' : ''; $x = $x->abs(); $r = new BigInteger(1); for ($i = 0; $i < abs($y); $i++) { $r = $r->multiply($x); } if ($y < 0) { $temp = '1' . str_repeat('0', $scale + $pad * abs($y)); $temp = new BigInteger($temp); list($r) = $temp->divide($r); $pad = $scale; } else { $pad*= abs($y); } return $sign . self::format($r, $scale, $pad); } /** * Raise an arbitrary precision number to another, reduced by a specified modulus * * @var string $x * @var string $e * @var string $n * @var int $scale * @var int $pad */ private static function powmod($x, $e, $n, $scale, $pad) { if ($e[0] == '-' || $n == '0') { // < PHP 8.0 returned false // >= PHP 8.0 throws an exception throw new \ValueError('bcpowmod(): Argument #2 ($exponent) must be greater than or equal to 0'); } if ($n[0] == '-') { $n = substr($n, 1); } if ($e == '0') { return $scale ? '1.' . str_repeat('0', $scale) : '1'; } $x = new BigInteger($x); $e = new BigInteger($e); $n = new BigInteger($n); $z = $x->powMod($e, $n); return $scale ? "$z." . str_repeat('0', $scale) : "$z"; } /** * Get the square root of an arbitrary precision number * * @var string $n * @var int $scale * @var int $pad */ private static function sqrt($n, $scale, $pad) { // the following is based off of the following URL: // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10) if (!is_numeric($n)) { return '0'; } $temp = explode('.', $n); $decStart = ceil(strlen($temp[0]) / 2); $n = implode('', $temp); if (strlen($n) % 2) { $n = "0$n"; } $parts = str_split($n, 2); $parts = array_map('intval', $parts); $i = 0; $p = 0; // for the first step, p = 0 $c = $parts[$i]; $result = ''; while (true) { // determine the greatest digit x such that x(20p+x) <= c for ($x = 1; $x <= 10; $x++) { if ($x * (20 * $p + $x) > $c) { $x--; break; } } $result.= $x; $y = $x * (20 * $p + $x); $p = 10 * $p + $x; $c = 100 * ($c - $y); if (isset($parts[++$i])) { $c+= $parts[$i]; } if ((!$c && $i >= $decStart) || $i - $decStart == $scale) { break; } if ($decStart == $i) { $result.= '.'; } } $result = explode('.', $result); if (isset($result[1])) { $result[1] = str_pad($result[1], $scale, '0'); } elseif ($scale) { $result[1] = str_repeat('0', $scale); } return implode('.', $result); } /** * __callStatic Magic Method * * @var string $name * @var array $arguments */ public static function __callStatic($name, $arguments) { static $params = [ 'add' => 3, 'comp' => 3, 'div' => 3, 'mod' => 3, 'mul' => 3, 'pow' => 3, 'powmod' => 4, 'scale' => 1, 'sqrt' => 2, 'sub' => 3 ]; if (count($arguments) < $params[$name] - 1) { $min = $params[$name] - 1; throw new \ArgumentCountError("bc$name() expects at least $min parameters, " . func_num_args() . " given"); } if (count($arguments) > $params[$name]) { $str = "bc$name() expects at most {$params[$name]} parameters, " . func_num_args() . " given"; throw new \ArgumentCountError($str); } $numbers = array_slice($arguments, 0, $params[$name] - 1); $ints = []; switch ($name) { case 'pow': $ints = array_slice($numbers, count($numbers) - 1); $numbers = array_slice($numbers, 0, count($numbers) - 1); $names = ['exponent']; break; case 'powmod': $ints = $numbers; $numbers = []; $names = ['base', 'exponent', 'modulus']; break; case 'sqrt': $names = ['num']; break; default: $names = ['num1', 'num2']; } foreach ($ints as $i => &$int) { if (!is_numeric($int)) { $int = '0'; } $pos = strpos($int, '.'); if ($pos !== false) { $int = substr($int, 0, $pos); throw new \ValueError("bc$name(): Argument #2 (\$$names[$i]) cannot have a fractional part"); } } foreach ($numbers as $i => $arg) { $num = $i + 1; switch (true) { case is_bool($arg): case is_numeric($arg): case is_string($arg): case is_object($arg) && method_exists($arg, '__toString'): if (!is_bool($arg) && !is_numeric("$arg")) { throw new \ValueError("bc$name: bcmath function argument is not well-formed"); } break; // PHP >= 8.1 has deprecated the passing of nulls to string parameters case is_null($arg): $error = "bc$name(): Passing null to parameter #$num (\$$names[$i]) of type string is deprecated"; trigger_error($error, E_USER_DEPRECATED); break; default: $type = is_object($arg) ? get_class($arg) : gettype($arg); $error = "bc$name(): Argument #$num (\$$names[$i]) must be of type string, $type given"; throw new \TypeError($error); } } if (!isset(self::$scale)) { $scale = ini_get('bcmath.scale'); self::$scale = $scale !== false ? max(intval($scale), 0) : 0; } $scale = isset($arguments[$params[$name] - 1]) ? $arguments[$params[$name] - 1] : self::$scale; switch (true) { case is_bool($scale): case is_numeric($scale): case is_string($scale) && preg_match('#0-9\.#', $scale[0]): break; default: $type = is_object($arg) ? get_class($arg) : gettype($arg); $str = "bc$name(): Argument #$params[$name] (\$scale) must be of type ?int, string given"; throw new \TypeError($str); } $scale = (int) $scale; if ($scale < 0) { throw new \ValueError("bc$name(): Argument #$params[$name] (\$scale) must be between 0 and 2147483647"); } $pad = 0; foreach ($numbers as &$num) { if (is_bool($num)) { $num = $num ? '1' : '0'; } elseif (!is_numeric($num)) { $num = '0'; } $num = explode('.', $num); if (isset($num[1])) { $pad = max($pad, strlen($num[1])); } } switch ($name) { case 'add': case 'sub': case 'mul': case 'div': case 'mod': case 'pow': foreach ($numbers as &$num) { if (!isset($num[1])) { $num[1] = ''; } $num[1] = str_pad($num[1], $pad, '0'); $num = new BigInteger($num[0] . $num[1]); } break; case 'comp': foreach ($numbers as &$num) { if (!isset($num[1])) { $num[1] = ''; } $num[1] = str_pad($num[1], $pad, '0'); } break; case 'sqrt': $numbers = [$arguments[0]]; } $arguments = array_merge($numbers, $ints, [$scale, $pad]); return call_user_func_array('self::' . $name, $arguments); } } PK2A#]��Skk)vendor/phpseclib/bcmath_compat/LICENSE.mdnu�[���# The MIT License (MIT) Copyright (c) 2019 terrafrost <terrafrost@php.net> > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal > in the Software without restriction, including without limitation the rights > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > copies of the Software, and to permit persons to whom the Software is > furnished to do so, subject to the following conditions: > > The above copyright notice and this permission notice shall be included in > all copies or substantial portions of the Software. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > THE SOFTWARE.PK2A#]��֔��?vendor/symfony/error-handler/Exception/SilencedErrorContext.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Exception; /** * Data Object that represents a Silenced Error. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class SilencedErrorContext implements \JsonSerializable { public $count = 1; private $severity; private $file; private $line; private $trace; public function __construct(int $severity, string $file, int $line, array $trace = [], int $count = 1) { $this->severity = $severity; $this->file = $file; $this->line = $line; $this->trace = $trace; $this->count = $count; } public function getSeverity(): int { return $this->severity; } public function getFile(): string { return $this->file; } public function getLine(): int { return $this->line; } public function getTrace(): array { return $this->trace; } public function jsonSerialize(): array { return [ 'severity' => $this->severity, 'file' => $this->file, 'line' => $this->line, 'trace' => $this->trace, 'count' => $this->count, ]; } } PK2A#]����)�);vendor/symfony/error-handler/Exception/FlattenException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Exception; use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; /** * FlattenException wraps a PHP Error or Exception to be able to serialize it. * * Basically, this class removes all objects from the trace. * * @author Fabien Potencier <fabien@symfony.com> */ class FlattenException { /** @var string */ private $message; /** @var int|string */ private $code; /** @var self|null */ private $previous; /** @var array */ private $trace; /** @var string */ private $traceAsString; /** @var string */ private $class; /** @var int */ private $statusCode; /** @var string */ private $statusText; /** @var array */ private $headers; /** @var string */ private $file; /** @var int */ private $line; /** @var string|null */ private $asString; /** * @return static */ public static function create(\Exception $exception, int $statusCode = null, array $headers = []): self { return static::createFromThrowable($exception, $statusCode, $headers); } /** * @return static */ public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = []): self { $e = new static(); $e->setMessage($exception->getMessage()); $e->setCode($exception->getCode()); if ($exception instanceof HttpExceptionInterface) { $statusCode = $exception->getStatusCode(); $headers = array_merge($headers, $exception->getHeaders()); } elseif ($exception instanceof RequestExceptionInterface) { $statusCode = 400; } if (null === $statusCode) { $statusCode = 500; } if (class_exists(Response::class) && isset(Response::$statusTexts[$statusCode])) { $statusText = Response::$statusTexts[$statusCode]; } else { $statusText = 'Whoops, looks like something went wrong.'; } $e->setStatusText($statusText); $e->setStatusCode($statusCode); $e->setHeaders($headers); $e->setTraceFromThrowable($exception); $e->setClass(\get_class($exception)); $e->setFile($exception->getFile()); $e->setLine($exception->getLine()); $previous = $exception->getPrevious(); if ($previous instanceof \Throwable) { $e->setPrevious(static::createFromThrowable($previous)); } return $e; } public function toArray(): array { $exceptions = []; foreach (array_merge([$this], $this->getAllPrevious()) as $exception) { $exceptions[] = [ 'message' => $exception->getMessage(), 'class' => $exception->getClass(), 'trace' => $exception->getTrace(), ]; } return $exceptions; } public function getStatusCode(): int { return $this->statusCode; } /** * @return $this */ public function setStatusCode(int $code): self { $this->statusCode = $code; return $this; } public function getHeaders(): array { return $this->headers; } /** * @return $this */ public function setHeaders(array $headers): self { $this->headers = $headers; return $this; } public function getClass(): string { return $this->class; } /** * @return $this */ public function setClass(string $class): self { $this->class = false !== strpos($class, "@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : $class; return $this; } public function getFile(): string { return $this->file; } /** * @return $this */ public function setFile(string $file): self { $this->file = $file; return $this; } public function getLine(): int { return $this->line; } /** * @return $this */ public function setLine(int $line): self { $this->line = $line; return $this; } public function getStatusText(): string { return $this->statusText; } /** * @return $this */ public function setStatusText(string $statusText): self { $this->statusText = $statusText; return $this; } public function getMessage(): string { return $this->message; } /** * @return $this */ public function setMessage(string $message): self { if (false !== strpos($message, "@anonymous\0")) { $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; }, $message); } $this->message = $message; return $this; } /** * @return int|string int most of the time (might be a string with PDOException) */ public function getCode() { return $this->code; } /** * @param int|string $code * * @return $this */ public function setCode($code): self { $this->code = $code; return $this; } public function getPrevious(): ?self { return $this->previous; } /** * @return $this */ public function setPrevious(?self $previous): self { $this->previous = $previous; return $this; } /** * @return self[] */ public function getAllPrevious(): array { $exceptions = []; $e = $this; while ($e = $e->getPrevious()) { $exceptions[] = $e; } return $exceptions; } public function getTrace(): array { return $this->trace; } /** * @return $this */ public function setTraceFromThrowable(\Throwable $throwable): self { $this->traceAsString = $throwable->getTraceAsString(); return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine()); } /** * @return $this */ public function setTrace(array $trace, ?string $file, ?int $line): self { $this->trace = []; $this->trace[] = [ 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => $file, 'line' => $line, 'args' => [], ]; foreach ($trace as $entry) { $class = ''; $namespace = ''; if (isset($entry['class'])) { $parts = explode('\\', $entry['class']); $class = array_pop($parts); $namespace = implode('\\', $parts); } $this->trace[] = [ 'namespace' => $namespace, 'short_class' => $class, 'class' => $entry['class'] ?? '', 'type' => $entry['type'] ?? '', 'function' => $entry['function'] ?? null, 'file' => $entry['file'] ?? null, 'line' => $entry['line'] ?? null, 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [], ]; } return $this; } private function flattenArgs(array $args, int $level = 0, int &$count = 0): array { $result = []; foreach ($args as $key => $value) { if (++$count > 1e4) { return ['array', '*SKIPPED over 10000 entries*']; } if ($value instanceof \__PHP_Incomplete_Class) { $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)]; } elseif (\is_object($value)) { $result[$key] = ['object', \get_class($value)]; } elseif (\is_array($value)) { if ($level > 10) { $result[$key] = ['array', '*DEEP NESTED ARRAY*']; } else { $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)]; } } elseif (null === $value) { $result[$key] = ['null', null]; } elseif (\is_bool($value)) { $result[$key] = ['boolean', $value]; } elseif (\is_int($value)) { $result[$key] = ['integer', $value]; } elseif (\is_float($value)) { $result[$key] = ['float', $value]; } elseif (\is_resource($value)) { $result[$key] = ['resource', get_resource_type($value)]; } else { $result[$key] = ['string', (string) $value]; } } return $result; } private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string { $array = new \ArrayObject($value); return $array['__PHP_Incomplete_Class_Name']; } public function getTraceAsString(): string { return $this->traceAsString; } /** * @return $this */ public function setAsString(?string $asString): self { $this->asString = $asString; return $this; } public function getAsString(): string { if (null !== $this->asString) { return $this->asString; } $message = ''; $next = false; foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) { if ($next) { $message .= 'Next '; } else { $next = true; } $message .= $exception->getClass(); if ('' != $exception->getMessage()) { $message .= ': '.$exception->getMessage(); } $message .= ' in '.$exception->getFile().':'.$exception->getLine(). "\nStack trace:\n".$exception->getTraceAsString()."\n\n"; } return rtrim($message); } } PK2A#]poP����8vendor/symfony/error-handler/Internal/TentativeTypes.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Internal; /** * This class has been generated by extract-tentative-return-types.php. * * @internal */ class TentativeTypes { public const RETURN_TYPES = [ 'CURLFile' => [ 'getFilename' => 'string', 'getMimeType' => 'string', 'getPostFilename' => 'string', 'setMimeType' => 'void', 'setPostFilename' => 'void', ], 'DateTimeInterface' => [ 'format' => 'string', 'getTimezone' => 'DateTimeZone|false', 'getOffset' => 'int', 'getTimestamp' => 'int', 'diff' => 'DateInterval', '__wakeup' => 'void', ], 'DateTime' => [ '__wakeup' => 'void', '__set_state' => 'DateTime', 'createFromImmutable' => 'static', 'createFromFormat' => 'DateTime|false', 'getLastErrors' => 'array|false', 'format' => 'string', 'modify' => 'DateTime|false', 'add' => 'DateTime', 'sub' => 'DateTime', 'getTimezone' => 'DateTimeZone|false', 'setTimezone' => 'DateTime', 'getOffset' => 'int', 'setTime' => 'DateTime', 'setDate' => 'DateTime', 'setISODate' => 'DateTime', 'setTimestamp' => 'DateTime', 'getTimestamp' => 'int', 'diff' => 'DateInterval', ], 'DateTimeImmutable' => [ '__wakeup' => 'void', '__set_state' => 'DateTimeImmutable', 'createFromFormat' => 'DateTimeImmutable|false', 'getLastErrors' => 'array|false', 'format' => 'string', 'getTimezone' => 'DateTimeZone|false', 'getOffset' => 'int', 'getTimestamp' => 'int', 'diff' => 'DateInterval', 'modify' => 'DateTimeImmutable|false', 'add' => 'DateTimeImmutable', 'sub' => 'DateTimeImmutable', 'setTimezone' => 'DateTimeImmutable', 'setTime' => 'DateTimeImmutable', 'setDate' => 'DateTimeImmutable', 'setISODate' => 'DateTimeImmutable', 'setTimestamp' => 'DateTimeImmutable', 'createFromMutable' => 'static', ], 'DateTimeZone' => [ 'getName' => 'string', 'getOffset' => 'int', 'getTransitions' => 'array|false', 'getLocation' => 'array|false', 'listAbbreviations' => 'array', 'listIdentifiers' => 'array', '__wakeup' => 'void', '__set_state' => 'DateTimeZone', ], 'DateInterval' => [ 'createFromDateString' => 'DateInterval|false', 'format' => 'string', '__wakeup' => 'void', '__set_state' => 'DateInterval', ], 'DatePeriod' => [ 'getStartDate' => 'DateTimeInterface', 'getEndDate' => '?DateTimeInterface', 'getDateInterval' => 'DateInterval', 'getRecurrences' => '?int', '__wakeup' => 'void', '__set_state' => 'DatePeriod', ], 'DOMNode' => [ 'C14N' => 'string|false', 'C14NFile' => 'int|false', 'getLineNo' => 'int', 'getNodePath' => '?string', 'hasAttributes' => 'bool', 'hasChildNodes' => 'bool', 'isDefaultNamespace' => 'bool', 'isSameNode' => 'bool', 'isSupported' => 'bool', 'lookupNamespaceURI' => '?string', 'lookupPrefix' => '?string', 'normalize' => 'void', ], 'DOMImplementation' => [ 'getFeature' => 'never', 'hasFeature' => 'bool', ], 'DOMDocumentFragment' => [ 'appendXML' => 'bool', ], 'DOMNodeList' => [ 'count' => 'int', ], 'DOMCharacterData' => [ 'appendData' => 'bool', 'insertData' => 'bool', 'deleteData' => 'bool', 'replaceData' => 'bool', ], 'DOMAttr' => [ 'isId' => 'bool', ], 'DOMElement' => [ 'getAttribute' => 'string', 'getAttributeNS' => 'string', 'getElementsByTagName' => 'DOMNodeList', 'getElementsByTagNameNS' => 'DOMNodeList', 'hasAttribute' => 'bool', 'hasAttributeNS' => 'bool', 'removeAttribute' => 'bool', 'removeAttributeNS' => 'void', 'setAttributeNS' => 'void', 'setIdAttribute' => 'void', 'setIdAttributeNS' => 'void', 'setIdAttributeNode' => 'void', ], 'DOMDocument' => [ 'createComment' => 'DOMComment', 'createDocumentFragment' => 'DOMDocumentFragment', 'createTextNode' => 'DOMText', 'getElementById' => '?DOMElement', 'getElementsByTagName' => 'DOMNodeList', 'getElementsByTagNameNS' => 'DOMNodeList', 'normalizeDocument' => 'void', 'registerNodeClass' => 'bool', 'save' => 'int|false', 'saveHTML' => 'string|false', 'saveHTMLFile' => 'int|false', 'saveXML' => 'string|false', 'schemaValidate' => 'bool', 'schemaValidateSource' => 'bool', 'relaxNGValidate' => 'bool', 'relaxNGValidateSource' => 'bool', 'validate' => 'bool', 'xinclude' => 'int|false', ], 'DOMText' => [ 'isWhitespaceInElementContent' => 'bool', 'isElementContentWhitespace' => 'bool', ], 'DOMNamedNodeMap' => [ 'getNamedItem' => '?DOMNode', 'getNamedItemNS' => '?DOMNode', 'item' => '?DOMNode', 'count' => 'int', ], 'DOMXPath' => [ 'evaluate' => 'mixed', 'query' => 'mixed', 'registerNamespace' => 'bool', 'registerPhpFunctions' => 'void', ], 'finfo' => [ 'file' => 'string|false', 'buffer' => 'string|false', ], 'IntlPartsIterator' => [ 'getBreakIterator' => 'IntlBreakIterator', 'getRuleStatus' => 'int', ], 'IntlBreakIterator' => [ 'createCharacterInstance' => '?IntlBreakIterator', 'createCodePointInstance' => 'IntlCodePointBreakIterator', 'createLineInstance' => '?IntlBreakIterator', 'createSentenceInstance' => '?IntlBreakIterator', 'createTitleInstance' => '?IntlBreakIterator', 'createWordInstance' => '?IntlBreakIterator', 'current' => 'int', 'first' => 'int', 'following' => 'int', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', 'getLocale' => 'string|false', 'getPartsIterator' => 'IntlPartsIterator', 'getText' => '?string', 'isBoundary' => 'bool', 'last' => 'int', 'next' => 'int', 'preceding' => 'int', 'previous' => 'int', 'setText' => '?bool', ], 'IntlRuleBasedBreakIterator' => [ 'getBinaryRules' => 'string|false', 'getRules' => 'string|false', 'getRuleStatus' => 'int', 'getRuleStatusVec' => 'array|false', ], 'IntlCodePointBreakIterator' => [ 'getLastCodePoint' => 'int', ], 'IntlCalendar' => [ 'createInstance' => '?IntlCalendar', 'equals' => 'bool', 'fieldDifference' => 'int|false', 'add' => 'bool', 'after' => 'bool', 'before' => 'bool', 'fromDateTime' => '?IntlCalendar', 'get' => 'int|false', 'getActualMaximum' => 'int|false', 'getActualMinimum' => 'int|false', 'getAvailableLocales' => 'array', 'getDayOfWeekType' => 'int|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getFirstDayOfWeek' => 'int|false', 'getGreatestMinimum' => 'int|false', 'getKeywordValuesForLocale' => 'IntlIterator|false', 'getLeastMaximum' => 'int|false', 'getLocale' => 'string|false', 'getMaximum' => 'int|false', 'getMinimalDaysInFirstWeek' => 'int|false', 'getMinimum' => 'int|false', 'getNow' => 'float', 'getRepeatedWallTimeOption' => 'int', 'getSkippedWallTimeOption' => 'int', 'getTime' => 'float|false', 'getTimeZone' => 'IntlTimeZone|false', 'getType' => 'string', 'getWeekendTransition' => 'int|false', 'inDaylightTime' => 'bool', 'isEquivalentTo' => 'bool', 'isLenient' => 'bool', 'isWeekend' => 'bool', 'roll' => 'bool', 'isSet' => 'bool', 'setTime' => 'bool', 'setTimeZone' => 'bool', 'toDateTime' => 'DateTime|false', ], 'IntlGregorianCalendar' => [ 'setGregorianChange' => 'bool', 'getGregorianChange' => 'float', 'isLeapYear' => 'bool', ], 'Collator' => [ 'create' => '?Collator', 'compare' => 'int|false', 'sort' => 'bool', 'sortWithSortKeys' => 'bool', 'asort' => 'bool', 'getAttribute' => 'int|false', 'setAttribute' => 'bool', 'getStrength' => 'int', 'getLocale' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getSortKey' => 'string|false', ], 'IntlIterator' => [ 'current' => 'mixed', 'key' => 'mixed', 'next' => 'void', 'rewind' => 'void', 'valid' => 'bool', ], 'UConverter' => [ 'convert' => 'string|false', 'fromUCallback' => 'string|int|array|null', 'getAliases' => 'array|false|null', 'getAvailable' => 'array', 'getDestinationEncoding' => 'string|false|null', 'getDestinationType' => 'int|false|null', 'getErrorCode' => 'int', 'getErrorMessage' => '?string', 'getSourceEncoding' => 'string|false|null', 'getSourceType' => 'int|false|null', 'getStandards' => '?array', 'getSubstChars' => 'string|false|null', 'reasonText' => 'string', 'setDestinationEncoding' => 'bool', 'setSourceEncoding' => 'bool', 'setSubstChars' => 'bool', 'toUCallback' => 'string|int|array|null', 'transcode' => 'string|false', ], 'IntlDateFormatter' => [ 'create' => '?IntlDateFormatter', 'getDateType' => 'int|false', 'getTimeType' => 'int|false', 'getCalendar' => 'int|false', 'setCalendar' => 'bool', 'getTimeZoneId' => 'string|false', 'getCalendarObject' => 'IntlCalendar|false|null', 'getTimeZone' => 'IntlTimeZone|false', 'setTimeZone' => '?bool', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string|false', 'setLenient' => 'void', 'isLenient' => 'bool', 'format' => 'string|false', 'formatObject' => 'string|false', 'parse' => 'int|float|false', 'localtime' => 'array|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'NumberFormatter' => [ 'create' => '?NumberFormatter', 'format' => 'string|false', 'parse' => 'int|float|false', 'formatCurrency' => 'string|false', 'parseCurrency' => 'float|false', 'setAttribute' => 'bool', 'getAttribute' => 'int|float|false', 'setTextAttribute' => 'bool', 'getTextAttribute' => 'string|false', 'setSymbol' => 'bool', 'getSymbol' => 'string|false', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Locale' => [ 'getDefault' => 'string', 'getPrimaryLanguage' => '?string', 'getScript' => '?string', 'getRegion' => '?string', 'getKeywords' => 'array|false|null', 'getDisplayScript' => 'string|false', 'getDisplayRegion' => 'string|false', 'getDisplayName' => 'string|false', 'getDisplayLanguage' => 'string|false', 'getDisplayVariant' => 'string|false', 'composeLocale' => 'string|false', 'parseLocale' => '?array', 'getAllVariants' => '?array', 'filterMatches' => '?bool', 'lookup' => '?string', 'canonicalize' => '?string', 'acceptFromHttp' => 'string|false', ], 'MessageFormatter' => [ 'create' => '?MessageFormatter', 'format' => 'string|false', 'formatMessage' => 'string|false', 'parse' => 'array|false', 'parseMessage' => 'array|false', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Normalizer' => [ 'normalize' => 'string|false', 'isNormalized' => 'bool', 'getRawDecomposition' => '?string', ], 'ResourceBundle' => [ 'create' => '?ResourceBundle', 'get' => 'mixed', 'count' => 'int', 'getLocales' => 'array|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Spoofchecker' => [ 'isSuspicious' => 'bool', 'areConfusable' => 'bool', 'setAllowedLocales' => 'void', 'setChecks' => 'void', 'setRestrictionLevel' => 'void', ], 'IntlTimeZone' => [ 'countEquivalentIDs' => 'int|false', 'createDefault' => 'IntlTimeZone', 'createEnumeration' => 'IntlIterator|false', 'createTimeZone' => '?IntlTimeZone', 'createTimeZoneIDEnumeration' => 'IntlIterator|false', 'fromDateTimeZone' => '?IntlTimeZone', 'getCanonicalID' => 'string|false', 'getDisplayName' => 'string|false', 'getDSTSavings' => 'int', 'getEquivalentID' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getGMT' => 'IntlTimeZone', 'getID' => 'string|false', 'getOffset' => 'bool', 'getRawOffset' => 'int', 'getRegion' => 'string|false', 'getTZDataVersion' => 'string|false', 'getUnknown' => 'IntlTimeZone', 'getWindowsID' => 'string|false', 'getIDForWindowsID' => 'string|false', 'hasSameRules' => 'bool', 'toDateTimeZone' => 'DateTimeZone|false', 'useDaylightTime' => 'bool', ], 'Transliterator' => [ 'create' => '?Transliterator', 'createFromRules' => '?Transliterator', 'createInverse' => '?Transliterator', 'listIDs' => 'array|false', 'transliterate' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', ], 'IntlChar' => [ 'hasBinaryProperty' => '?bool', 'charAge' => '?array', 'charDigitValue' => '?int', 'charDirection' => '?int', 'charFromName' => '?int', 'charMirror' => 'int|string|null', 'charName' => '?string', 'charType' => '?int', 'chr' => '?string', 'digit' => 'int|false|null', 'enumCharNames' => '?bool', 'enumCharTypes' => 'void', 'foldCase' => 'int|string|null', 'forDigit' => 'int', 'getBidiPairedBracket' => 'int|string|null', 'getBlockCode' => '?int', 'getCombiningClass' => '?int', 'getFC_NFKC_Closure' => 'string|false|null', 'getIntPropertyMaxValue' => 'int', 'getIntPropertyMinValue' => 'int', 'getIntPropertyValue' => '?int', 'getNumericValue' => '?float', 'getPropertyEnum' => 'int', 'getPropertyName' => 'string|false', 'getPropertyValueEnum' => 'int', 'getPropertyValueName' => 'string|false', 'getUnicodeVersion' => 'array', 'isalnum' => '?bool', 'isalpha' => '?bool', 'isbase' => '?bool', 'isblank' => '?bool', 'iscntrl' => '?bool', 'isdefined' => '?bool', 'isdigit' => '?bool', 'isgraph' => '?bool', 'isIDIgnorable' => '?bool', 'isIDPart' => '?bool', 'isIDStart' => '?bool', 'isISOControl' => '?bool', 'isJavaIDPart' => '?bool', 'isJavaIDStart' => '?bool', 'isJavaSpaceChar' => '?bool', 'islower' => '?bool', 'isMirrored' => '?bool', 'isprint' => '?bool', 'ispunct' => '?bool', 'isspace' => '?bool', 'istitle' => '?bool', 'isUAlphabetic' => '?bool', 'isULowercase' => '?bool', 'isupper' => '?bool', 'isUUppercase' => '?bool', 'isUWhiteSpace' => '?bool', 'isWhitespace' => '?bool', 'isxdigit' => '?bool', 'ord' => '?int', 'tolower' => 'int|string|null', 'totitle' => 'int|string|null', 'toupper' => 'int|string|null', ], 'JsonSerializable' => [ 'jsonSerialize' => 'mixed', ], 'mysqli' => [ 'autocommit' => 'bool', 'begin_transaction' => 'bool', 'change_user' => 'bool', 'character_set_name' => 'string', 'commit' => 'bool', 'connect' => 'bool', 'dump_debug_info' => 'bool', 'get_charset' => '?object', 'get_client_info' => 'string', 'get_connection_stats' => 'array', 'get_server_info' => 'string', 'get_warnings' => 'mysqli_warning|false', 'kill' => 'bool', 'multi_query' => 'bool', 'more_results' => 'bool', 'next_result' => 'bool', 'ping' => 'bool', 'poll' => 'int|false', 'prepare' => 'mysqli_stmt|false', 'query' => 'mysqli_result|bool', 'real_connect' => 'bool', 'real_escape_string' => 'string', 'reap_async_query' => 'mysqli_result|bool', 'escape_string' => 'string', 'real_query' => 'bool', 'release_savepoint' => 'bool', 'rollback' => 'bool', 'savepoint' => 'bool', 'select_db' => 'bool', 'set_charset' => 'bool', 'options' => 'bool', 'set_opt' => 'bool', 'stat' => 'string|false', 'stmt_init' => 'mysqli_stmt|false', 'store_result' => 'mysqli_result|false', 'thread_safe' => 'bool', 'use_result' => 'mysqli_result|false', 'refresh' => 'bool', ], 'mysqli_result' => [ 'close' => 'void', 'free' => 'void', 'data_seek' => 'bool', 'fetch_field' => 'object|false', 'fetch_fields' => 'array', 'fetch_field_direct' => 'object|false', 'fetch_all' => 'array', 'fetch_array' => 'array|null|false', 'fetch_assoc' => 'array|null|false', 'fetch_object' => 'object|null|false', 'fetch_row' => 'array|null|false', 'field_seek' => 'bool', 'free_result' => 'void', ], 'mysqli_stmt' => [ 'attr_get' => 'int', 'attr_set' => 'bool', 'bind_param' => 'bool', 'bind_result' => 'bool', 'data_seek' => 'void', 'execute' => 'bool', 'fetch' => '?bool', 'get_warnings' => 'mysqli_warning|false', 'result_metadata' => 'mysqli_result|false', 'more_results' => 'bool', 'next_result' => 'bool', 'num_rows' => 'int|string', 'send_long_data' => 'bool', 'free_result' => 'void', 'reset' => 'bool', 'prepare' => 'bool', 'store_result' => 'bool', 'get_result' => 'mysqli_result|false', ], 'OCILob' => [ 'save' => 'bool', 'import' => 'bool', 'saveFile' => 'bool', 'load' => 'string|false', 'read' => 'string|false', 'eof' => 'bool', 'tell' => 'int|false', 'rewind' => 'bool', 'seek' => 'bool', 'size' => 'int|false', 'write' => 'int|false', 'append' => 'bool', 'truncate' => 'bool', 'erase' => 'int|false', 'flush' => 'bool', 'setBuffering' => 'bool', 'getBuffering' => 'bool', 'writeToFile' => 'bool', 'export' => 'bool', 'writeTemporary' => 'bool', 'close' => 'bool', 'free' => 'bool', ], 'OCICollection' => [ 'free' => 'bool', 'append' => 'bool', 'getElem' => 'string|float|null|false', 'assign' => 'bool', 'assignElem' => 'bool', 'size' => 'int|false', 'max' => 'int|false', 'trim' => 'bool', ], 'PDO' => [ 'beginTransaction' => 'bool', 'commit' => 'bool', 'errorCode' => '?string', 'errorInfo' => 'array', 'exec' => 'int|false', 'getAttribute' => 'mixed', 'getAvailableDrivers' => 'array', 'inTransaction' => 'bool', 'lastInsertId' => 'string|false', 'prepare' => 'PDOStatement|false', 'query' => 'PDOStatement|false', 'quote' => 'string|false', 'rollBack' => 'bool', 'setAttribute' => 'bool', ], 'PDOStatement' => [ 'bindColumn' => 'bool', 'bindParam' => 'bool', 'bindValue' => 'bool', 'closeCursor' => 'bool', 'columnCount' => 'int', 'debugDumpParams' => '?bool', 'errorCode' => '?string', 'errorInfo' => 'array', 'execute' => 'bool', 'fetch' => 'mixed', 'fetchAll' => 'array', 'fetchColumn' => 'mixed', 'fetchObject' => 'object|false', 'getAttribute' => 'mixed', 'getColumnMeta' => 'array|false', 'nextRowset' => 'bool', 'rowCount' => 'int', 'setAttribute' => 'bool', ], 'PDO_PGSql_Ext' => [ 'pgsqlCopyFromArray' => 'bool', 'pgsqlCopyFromFile' => 'bool', 'pgsqlCopyToArray' => 'array|false', 'pgsqlCopyToFile' => 'bool', 'pgsqlLOBCreate' => 'string|false', 'pgsqlLOBUnlink' => 'bool', 'pgsqlGetNotify' => 'array|false', 'pgsqlGetPid' => 'int', ], 'PDO_SQLite_Ext' => [ 'sqliteCreateFunction' => 'bool', 'sqliteCreateAggregate' => 'bool', 'sqliteCreateCollation' => 'bool', ], 'Phar' => [ 'addEmptyDir' => 'void', 'addFile' => 'void', 'addFromString' => 'void', 'buildFromDirectory' => 'array', 'buildFromIterator' => 'array', 'compressFiles' => 'void', 'compress' => '?Phar', 'decompress' => '?Phar', 'convertToExecutable' => '?Phar', 'convertToData' => '?PharData', 'count' => 'int', 'extractTo' => 'bool', 'getAlias' => '?string', 'getPath' => 'string', 'getMetadata' => 'mixed', 'getModified' => 'bool', 'getSignature' => 'array|false', 'getStub' => 'string', 'getVersion' => 'string', 'hasMetadata' => 'bool', 'isBuffering' => 'bool', 'isCompressed' => 'int|false', 'isFileFormat' => 'bool', 'isWritable' => 'bool', 'offsetExists' => 'bool', 'offsetGet' => 'SplFileInfo', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'setAlias' => 'bool', 'setDefaultStub' => 'bool', 'setMetadata' => 'void', 'setSignatureAlgorithm' => 'void', 'startBuffering' => 'void', 'stopBuffering' => 'void', ], 'PharData' => [ 'addEmptyDir' => 'void', 'addFile' => 'void', 'addFromString' => 'void', 'buildFromDirectory' => 'array', 'buildFromIterator' => 'array', 'compressFiles' => 'void', 'compress' => '?PharData', 'decompress' => '?PharData', 'convertToExecutable' => '?Phar', 'convertToData' => '?PharData', 'count' => 'int', 'extractTo' => 'bool', 'getAlias' => '?string', 'getPath' => 'string', 'getMetadata' => 'mixed', 'getModified' => 'bool', 'getSignature' => 'array|false', 'getStub' => 'string', 'getVersion' => 'string', 'hasMetadata' => 'bool', 'isBuffering' => 'bool', 'isCompressed' => 'int|false', 'isFileFormat' => 'bool', 'isWritable' => 'bool', 'offsetExists' => 'bool', 'offsetGet' => 'SplFileInfo', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'setAlias' => 'bool', 'setDefaultStub' => 'bool', 'setMetadata' => 'void', 'setSignatureAlgorithm' => 'void', 'startBuffering' => 'void', 'stopBuffering' => 'void', ], 'PharFileInfo' => [ 'chmod' => 'void', 'getCompressedSize' => 'int', 'getCRC32' => 'int', 'getContent' => 'string', 'getMetadata' => 'mixed', 'getPharFlags' => 'int', 'hasMetadata' => 'bool', 'isCompressed' => 'bool', 'isCRCChecked' => 'bool', 'setMetadata' => 'void', ], 'Reflection' => [ 'getModifierNames' => 'array', ], 'ReflectionFunctionAbstract' => [ 'inNamespace' => 'bool', 'isClosure' => 'bool', 'isDeprecated' => 'bool', 'isInternal' => 'bool', 'isUserDefined' => 'bool', 'isGenerator' => 'bool', 'isVariadic' => 'bool', 'isStatic' => 'bool', 'getClosureThis' => '?object', 'getClosureCalledClass' => '?ReflectionClass', 'getClosureScopeClass' => '?ReflectionClass', 'getDocComment' => 'string|false', 'getEndLine' => 'int|false', 'getExtension' => '?ReflectionExtension', 'getExtensionName' => 'string|false', 'getFileName' => 'string|false', 'getName' => 'string', 'getNamespaceName' => 'string', 'getNumberOfParameters' => 'int', 'getNumberOfRequiredParameters' => 'int', 'getParameters' => 'array', 'getShortName' => 'string', 'getStartLine' => 'int|false', 'getStaticVariables' => 'array', 'returnsReference' => 'bool', 'hasReturnType' => 'bool', 'getReturnType' => '?ReflectionType', ], 'ReflectionFunction' => [ 'isDisabled' => 'bool', 'invoke' => 'mixed', 'invokeArgs' => 'mixed', 'getClosure' => 'Closure', 'getExecutingLine' => 'int', 'getExecutingFile' => 'string', 'getTrace' => 'array', 'getFunction' => 'ReflectionFunctionAbstract', 'getThis' => '?object', 'getExecutingGenerator' => 'Generator', ], 'ReflectionMethod' => [ 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'isAbstract' => 'bool', 'isFinal' => 'bool', 'isConstructor' => 'bool', 'isDestructor' => 'bool', 'getClosure' => 'Closure', 'getModifiers' => 'int', 'invoke' => 'mixed', 'invokeArgs' => 'mixed', 'getDeclaringClass' => 'ReflectionClass', 'getPrototype' => 'ReflectionMethod', 'setAccessible' => 'void', ], 'ReflectionClass' => [ 'getName' => 'string', 'isInternal' => 'bool', 'isUserDefined' => 'bool', 'isAnonymous' => 'bool', 'isInstantiable' => 'bool', 'isCloneable' => 'bool', 'getFileName' => 'string|false', 'getStartLine' => 'int|false', 'getEndLine' => 'int|false', 'getDocComment' => 'string|false', 'getConstructor' => '?ReflectionMethod', 'hasMethod' => 'bool', 'getMethod' => 'ReflectionMethod', 'getMethods' => 'array', 'hasProperty' => 'bool', 'getProperty' => 'ReflectionProperty', 'getProperties' => 'array', 'hasConstant' => 'bool', 'getConstants' => 'array', 'getReflectionConstants' => 'array', 'getConstant' => 'mixed', 'getReflectionConstant' => 'ReflectionClassConstant|false', 'getInterfaces' => 'array', 'getInterfaceNames' => 'array', 'isInterface' => 'bool', 'getTraits' => 'array', 'getTraitNames' => 'array', 'getTraitAliases' => 'array', 'isTrait' => 'bool', 'isAbstract' => 'bool', 'isFinal' => 'bool', 'getModifiers' => 'int', 'isInstance' => 'bool', 'newInstance' => 'object', 'newInstanceWithoutConstructor' => 'object', 'newInstanceArgs' => '?object', 'getParentClass' => 'ReflectionClass|false', 'isSubclassOf' => 'bool', 'getStaticProperties' => '?array', 'getStaticPropertyValue' => 'mixed', 'setStaticPropertyValue' => 'void', 'getDefaultProperties' => 'array', 'isIterable' => 'bool', 'isIterateable' => 'bool', 'implementsInterface' => 'bool', 'getExtension' => '?ReflectionExtension', 'getExtensionName' => 'string|false', 'inNamespace' => 'bool', 'getNamespaceName' => 'string', 'getShortName' => 'string', ], 'ReflectionProperty' => [ 'getName' => 'string', 'getValue' => 'mixed', 'setValue' => 'void', 'isInitialized' => 'bool', 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'isStatic' => 'bool', 'isDefault' => 'bool', 'getModifiers' => 'int', 'getDeclaringClass' => 'ReflectionClass', 'getDocComment' => 'string|false', 'setAccessible' => 'void', 'getType' => '?ReflectionType', 'hasType' => 'bool', 'getDefaultValue' => 'mixed', ], 'ReflectionClassConstant' => [ 'getName' => 'string', 'getValue' => 'mixed', 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'getModifiers' => 'int', 'getDeclaringClass' => 'ReflectionClass', 'getDocComment' => 'string|false', ], 'ReflectionParameter' => [ 'getName' => 'string', 'isPassedByReference' => 'bool', 'canBePassedByValue' => 'bool', 'getDeclaringFunction' => 'ReflectionFunctionAbstract', 'getDeclaringClass' => '?ReflectionClass', 'getClass' => '?ReflectionClass', 'hasType' => 'bool', 'getType' => '?ReflectionType', 'isArray' => 'bool', 'isCallable' => 'bool', 'allowsNull' => 'bool', 'getPosition' => 'int', 'isOptional' => 'bool', 'isDefaultValueAvailable' => 'bool', 'getDefaultValue' => 'mixed', 'isDefaultValueConstant' => 'bool', 'getDefaultValueConstantName' => '?string', 'isVariadic' => 'bool', ], 'ReflectionType' => [ 'allowsNull' => 'bool', ], 'ReflectionNamedType' => [ 'getName' => 'string', 'isBuiltin' => 'bool', ], 'ReflectionExtension' => [ 'getName' => 'string', 'getVersion' => '?string', 'getFunctions' => 'array', 'getConstants' => 'array', 'getINIEntries' => 'array', 'getClasses' => 'array', 'getClassNames' => 'array', 'getDependencies' => 'array', 'info' => 'void', 'isPersistent' => 'bool', 'isTemporary' => 'bool', ], 'ReflectionZendExtension' => [ 'getName' => 'string', 'getVersion' => 'string', 'getAuthor' => 'string', 'getURL' => 'string', 'getCopyright' => 'string', ], 'SessionHandlerInterface' => [ 'open' => 'bool', 'close' => 'bool', 'read' => 'string|false', 'write' => 'bool', 'destroy' => 'bool', 'gc' => 'int|false', ], 'SessionIdInterface' => [ 'create_sid' => 'string', ], 'SessionUpdateTimestampHandlerInterface' => [ 'validateId' => 'bool', 'updateTimestamp' => 'bool', ], 'SessionHandler' => [ 'open' => 'bool', 'close' => 'bool', 'read' => 'string|false', 'write' => 'bool', 'destroy' => 'bool', 'gc' => 'int|false', 'create_sid' => 'string', ], 'SimpleXMLElement' => [ 'xpath' => 'array|null|false', 'registerXPathNamespace' => 'bool', 'asXML' => 'string|bool', 'saveXML' => 'string|bool', 'getNamespaces' => 'array', 'getDocNamespaces' => 'array|false', 'children' => '?SimpleXMLElement', 'attributes' => '?SimpleXMLElement', 'addChild' => '?SimpleXMLElement', 'addAttribute' => 'void', 'getName' => 'string', 'count' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'current' => 'SimpleXMLElement', 'key' => 'string', 'next' => 'void', 'hasChildren' => 'bool', 'getChildren' => '?SimpleXMLElement', ], 'SNMP' => [ 'close' => 'bool', 'setSecurity' => 'bool', 'get' => 'mixed', 'getnext' => 'mixed', 'walk' => 'array|false', 'set' => 'bool', 'getErrno' => 'int', 'getError' => 'string', ], 'SoapServer' => [ 'fault' => 'void', 'addSoapHeader' => 'void', 'setPersistence' => 'void', 'setClass' => 'void', 'setObject' => 'void', 'getFunctions' => 'array', 'addFunction' => 'void', 'handle' => 'void', ], 'SoapClient' => [ '__call' => 'mixed', '__soapCall' => 'mixed', '__getFunctions' => '?array', '__getTypes' => '?array', '__getLastRequest' => '?string', '__getLastResponse' => '?string', '__getLastRequestHeaders' => '?string', '__getLastResponseHeaders' => '?string', '__doRequest' => '?string', '__setCookie' => 'void', '__getCookies' => 'array', '__setSoapHeaders' => 'bool', '__setLocation' => '?string', ], 'ArrayObject' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'append' => 'void', 'getArrayCopy' => 'array', 'count' => 'int', 'getFlags' => 'int', 'setFlags' => 'void', 'asort' => 'bool', 'ksort' => 'bool', 'uasort' => 'bool', 'uksort' => 'bool', 'natsort' => 'bool', 'natcasesort' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', 'getIterator' => 'Iterator', 'exchangeArray' => 'array', 'setIteratorClass' => 'void', 'getIteratorClass' => 'string', '__debugInfo' => 'array', ], 'ArrayIterator' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'append' => 'void', 'getArrayCopy' => 'array', 'count' => 'int', 'getFlags' => 'int', 'setFlags' => 'void', 'asort' => 'bool', 'ksort' => 'bool', 'uasort' => 'bool', 'uksort' => 'bool', 'natsort' => 'bool', 'natcasesort' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'string|int|null', 'next' => 'void', 'valid' => 'bool', 'seek' => 'void', '__debugInfo' => 'array', ], 'RecursiveArrayIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveArrayIterator', ], 'SplFileInfo' => [ 'getPath' => 'string', 'getFilename' => 'string', 'getExtension' => 'string', 'getBasename' => 'string', 'getPathname' => 'string', 'getPerms' => 'int|false', 'getInode' => 'int|false', 'getSize' => 'int|false', 'getOwner' => 'int|false', 'getGroup' => 'int|false', 'getATime' => 'int|false', 'getMTime' => 'int|false', 'getCTime' => 'int|false', 'getType' => 'string|false', 'isWritable' => 'bool', 'isReadable' => 'bool', 'isExecutable' => 'bool', 'isFile' => 'bool', 'isDir' => 'bool', 'isLink' => 'bool', 'getLinkTarget' => 'string|false', 'getRealPath' => 'string|false', 'getFileInfo' => 'SplFileInfo', 'getPathInfo' => '?SplFileInfo', 'openFile' => 'SplFileObject', 'setFileClass' => 'void', 'setInfoClass' => 'void', '__debugInfo' => 'array', '_bad_state_ex' => 'void', ], 'DirectoryIterator' => [ 'getFilename' => 'string', 'getExtension' => 'string', 'getBasename' => 'string', 'isDot' => 'bool', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', 'seek' => 'void', ], 'FilesystemIterator' => [ 'rewind' => 'void', 'key' => 'string', 'current' => 'string|SplFileInfo|FilesystemIterator', 'getFlags' => 'int', 'setFlags' => 'void', ], 'RecursiveDirectoryIterator' => [ 'hasChildren' => 'bool', 'getChildren' => 'RecursiveDirectoryIterator', 'getSubPath' => 'string', 'getSubPathname' => 'string', ], 'GlobIterator' => [ 'count' => 'int', ], 'SplFileObject' => [ 'rewind' => 'void', 'eof' => 'bool', 'valid' => 'bool', 'fgets' => 'string', 'fread' => 'string|false', 'fgetcsv' => 'array|false', 'fputcsv' => 'int|false', 'setCsvControl' => 'void', 'getCsvControl' => 'array', 'flock' => 'bool', 'fflush' => 'bool', 'ftell' => 'int|false', 'fseek' => 'int', 'fgetc' => 'string|false', 'fpassthru' => 'int', 'fscanf' => 'array|int|null', 'fwrite' => 'int|false', 'fstat' => 'array', 'ftruncate' => 'bool', 'current' => 'string|array|false', 'key' => 'int', 'next' => 'void', 'setFlags' => 'void', 'getFlags' => 'int', 'setMaxLineLen' => 'void', 'getMaxLineLen' => 'int', 'hasChildren' => 'false', 'getChildren' => 'null', 'seek' => 'void', 'getCurrentLine' => 'string', ], 'SplDoublyLinkedList' => [ 'add' => 'void', 'pop' => 'mixed', 'shift' => 'mixed', 'push' => 'void', 'unshift' => 'void', 'top' => 'mixed', 'bottom' => 'mixed', '__debugInfo' => 'array', 'count' => 'int', 'isEmpty' => 'bool', 'setIteratorMode' => 'int', 'getIteratorMode' => 'int', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'prev' => 'void', 'next' => 'void', 'valid' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', ], 'SplQueue' => [ 'enqueue' => 'void', 'dequeue' => 'mixed', ], 'SplFixedArray' => [ '__wakeup' => 'void', 'count' => 'int', 'toArray' => 'array', 'fromArray' => 'SplFixedArray', 'getSize' => 'int', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', ], 'SplPriorityQueue' => [ 'compare' => 'int', 'setExtractFlags' => 'int', 'top' => 'mixed', 'extract' => 'mixed', 'count' => 'int', 'isEmpty' => 'bool', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'next' => 'void', 'valid' => 'bool', 'isCorrupted' => 'bool', 'getExtractFlags' => 'int', '__debugInfo' => 'array', ], 'SplHeap' => [ 'extract' => 'mixed', 'insert' => 'bool', 'top' => 'mixed', 'count' => 'int', 'isEmpty' => 'bool', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'next' => 'void', 'valid' => 'bool', 'recoverFromCorruption' => 'bool', 'compare' => 'int', 'isCorrupted' => 'bool', '__debugInfo' => 'array', ], 'SplMinHeap' => [ 'compare' => 'int', ], 'SplMaxHeap' => [ 'compare' => 'int', ], 'EmptyIterator' => [ 'current' => 'never', 'next' => 'void', 'key' => 'never', 'valid' => 'false', 'rewind' => 'void', ], 'CallbackFilterIterator' => [ 'accept' => 'bool', ], 'RecursiveCallbackFilterIterator' => [ 'hasChildren' => 'bool', 'getChildren' => 'RecursiveCallbackFilterIterator', ], 'RecursiveIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveIterator', ], 'RecursiveIteratorIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', 'getDepth' => 'int', 'getSubIterator' => '?RecursiveIterator', 'getInnerIterator' => 'RecursiveIterator', 'beginIteration' => 'void', 'endIteration' => 'void', 'callHasChildren' => 'bool', 'callGetChildren' => '?RecursiveIterator', 'beginChildren' => 'void', 'endChildren' => 'void', 'nextElement' => 'void', 'setMaxDepth' => 'void', 'getMaxDepth' => 'int|false', ], 'OuterIterator' => [ 'getInnerIterator' => '?Iterator', ], 'IteratorIterator' => [ 'getInnerIterator' => '?Iterator', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', ], 'FilterIterator' => [ 'accept' => 'bool', 'rewind' => 'void', 'next' => 'void', ], 'RecursiveFilterIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveFilterIterator', ], 'ParentIterator' => [ 'accept' => 'bool', ], 'SeekableIterator' => [ 'seek' => 'void', ], 'LimitIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'next' => 'void', 'seek' => 'int', 'getPosition' => 'int', ], 'CachingIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'next' => 'void', 'hasNext' => 'bool', 'getFlags' => 'int', 'setFlags' => 'void', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'offsetExists' => 'bool', 'getCache' => 'array', 'count' => 'int', ], 'RecursiveCachingIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveCachingIterator', ], 'NoRewindIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', ], 'AppendIterator' => [ 'append' => 'void', 'rewind' => 'void', 'valid' => 'bool', 'current' => 'mixed', 'next' => 'void', 'getIteratorIndex' => '?int', 'getArrayIterator' => 'ArrayIterator', ], 'InfiniteIterator' => [ 'next' => 'void', ], 'RegexIterator' => [ 'accept' => 'bool', 'getMode' => 'int', 'setMode' => 'void', 'getFlags' => 'int', 'setFlags' => 'void', 'getRegex' => 'string', 'getPregFlags' => 'int', 'setPregFlags' => 'void', ], 'RecursiveRegexIterator' => [ 'accept' => 'bool', 'hasChildren' => 'bool', 'getChildren' => 'RecursiveRegexIterator', ], 'RecursiveTreeIterator' => [ 'key' => 'mixed', 'current' => 'mixed', 'getPrefix' => 'string', 'setPostfix' => 'void', 'setPrefixPart' => 'void', 'getEntry' => 'string', 'getPostfix' => 'string', ], 'SplObserver' => [ 'update' => 'void', ], 'SplSubject' => [ 'attach' => 'void', 'detach' => 'void', 'notify' => 'void', ], 'SplObjectStorage' => [ 'attach' => 'void', 'detach' => 'void', 'contains' => 'bool', 'addAll' => 'int', 'removeAll' => 'int', 'removeAllExcept' => 'int', 'getInfo' => 'mixed', 'setInfo' => 'void', 'count' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'int', 'current' => 'object', 'next' => 'void', 'unserialize' => 'void', 'serialize' => 'string', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'getHash' => 'string', '__serialize' => 'array', '__unserialize' => 'void', '__debugInfo' => 'array', ], 'MultipleIterator' => [ 'getFlags' => 'int', 'setFlags' => 'void', 'attachIterator' => 'void', 'detachIterator' => 'void', 'containsIterator' => 'bool', 'countIterators' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'array', 'current' => 'array', 'next' => 'void', '__debugInfo' => 'array', ], 'SQLite3' => [ 'open' => 'void', 'version' => 'array', 'lastInsertRowID' => 'int', 'lastErrorCode' => 'int', 'lastExtendedErrorCode' => 'int', 'lastErrorMsg' => 'string', 'changes' => 'int', 'busyTimeout' => 'bool', 'loadExtension' => 'bool', 'backup' => 'bool', 'escapeString' => 'string', 'prepare' => 'SQLite3Stmt|false', 'exec' => 'bool', 'query' => 'SQLite3Result|false', 'querySingle' => 'mixed', 'createFunction' => 'bool', 'createAggregate' => 'bool', 'createCollation' => 'bool', 'enableExceptions' => 'bool', 'enableExtendedResultCodes' => 'bool', 'setAuthorizer' => 'bool', ], 'SQLite3Stmt' => [ 'bindParam' => 'bool', 'bindValue' => 'bool', 'clear' => 'bool', 'close' => 'bool', 'execute' => 'SQLite3Result|false', 'getSQL' => 'string|false', 'paramCount' => 'int', 'readOnly' => 'bool', 'reset' => 'bool', ], 'SQLite3Result' => [ 'numColumns' => 'int', 'columnName' => 'string|false', 'columnType' => 'int|false', 'fetchArray' => 'array|false', 'reset' => 'bool', ], 'Directory' => [ 'close' => 'void', 'rewind' => 'void', 'read' => 'string|false', ], 'php_user_filter' => [ 'filter' => 'int', 'onCreate' => 'bool', 'onClose' => 'void', ], 'tidy' => [ 'getOpt' => 'string|int|bool', 'cleanRepair' => 'bool', 'parseFile' => 'bool', 'parseString' => 'bool', 'repairString' => 'string|false', 'repairFile' => 'string|false', 'diagnose' => 'bool', 'getRelease' => 'string', 'getConfig' => 'array', 'getStatus' => 'int', 'getHtmlVer' => 'int', 'getOptDoc' => 'string|false', 'isXhtml' => 'bool', 'isXml' => 'bool', 'root' => '?tidyNode', 'head' => '?tidyNode', 'html' => '?tidyNode', 'body' => '?tidyNode', ], 'XMLReader' => [ 'getAttribute' => '?string', 'getAttributeNo' => '?string', 'getAttributeNs' => '?string', 'getParserProperty' => 'bool', 'isValid' => 'bool', 'lookupNamespace' => '?string', 'moveToAttribute' => 'bool', 'moveToAttributeNo' => 'bool', 'moveToAttributeNs' => 'bool', 'moveToElement' => 'bool', 'moveToFirstAttribute' => 'bool', 'moveToNextAttribute' => 'bool', 'read' => 'bool', 'next' => 'bool', 'readInnerXml' => 'string', 'readOuterXml' => 'string', 'readString' => 'string', 'setSchema' => 'bool', 'setParserProperty' => 'bool', 'setRelaxNGSchema' => 'bool', 'setRelaxNGSchemaSource' => 'bool', 'expand' => 'DOMNode|false', ], 'XMLWriter' => [ 'openUri' => 'bool', 'openMemory' => 'bool', 'setIndent' => 'bool', 'setIndentString' => 'bool', 'startComment' => 'bool', 'endComment' => 'bool', 'startAttribute' => 'bool', 'endAttribute' => 'bool', 'writeAttribute' => 'bool', 'startAttributeNs' => 'bool', 'writeAttributeNs' => 'bool', 'startElement' => 'bool', 'endElement' => 'bool', 'fullEndElement' => 'bool', 'startElementNs' => 'bool', 'writeElement' => 'bool', 'writeElementNs' => 'bool', 'startPi' => 'bool', 'endPi' => 'bool', 'writePi' => 'bool', 'startCdata' => 'bool', 'endCdata' => 'bool', 'writeCdata' => 'bool', 'text' => 'bool', 'writeRaw' => 'bool', 'startDocument' => 'bool', 'endDocument' => 'bool', 'writeComment' => 'bool', 'startDtd' => 'bool', 'endDtd' => 'bool', 'writeDtd' => 'bool', 'startDtdElement' => 'bool', 'endDtdElement' => 'bool', 'writeDtdElement' => 'bool', 'startDtdAttlist' => 'bool', 'endDtdAttlist' => 'bool', 'writeDtdAttlist' => 'bool', 'startDtdEntity' => 'bool', 'endDtdEntity' => 'bool', 'writeDtdEntity' => 'bool', 'outputMemory' => 'string', 'flush' => 'string|int', ], 'XSLTProcessor' => [ 'importStylesheet' => 'bool', 'transformToDoc' => 'DOMDocument|false', 'transformToUri' => 'int', 'transformToXml' => 'string|null|false', 'setParameter' => 'bool', 'getParameter' => 'string|false', 'removeParameter' => 'bool', 'hasExsltSupport' => 'bool', 'registerPHPFunctions' => 'void', 'setSecurityPrefs' => 'int', 'getSecurityPrefs' => 'int', ], 'ZipArchive' => [ 'open' => 'bool|int', 'setPassword' => 'bool', 'close' => 'bool', 'count' => 'int', 'getStatusString' => 'string', 'addEmptyDir' => 'bool', 'addFromString' => 'bool', 'addFile' => 'bool', 'replaceFile' => 'bool', 'addGlob' => 'array|false', 'addPattern' => 'array|false', 'renameIndex' => 'bool', 'renameName' => 'bool', 'setArchiveComment' => 'bool', 'getArchiveComment' => 'string|false', 'setCommentIndex' => 'bool', 'setCommentName' => 'bool', 'setMtimeIndex' => 'bool', 'setMtimeName' => 'bool', 'getCommentIndex' => 'string|false', 'getCommentName' => 'string|false', 'deleteIndex' => 'bool', 'deleteName' => 'bool', 'statName' => 'array|false', 'statIndex' => 'array|false', 'locateName' => 'int|false', 'getNameIndex' => 'string|false', 'unchangeArchive' => 'bool', 'unchangeAll' => 'bool', 'unchangeIndex' => 'bool', 'unchangeName' => 'bool', 'extractTo' => 'bool', 'getFromName' => 'string|false', 'getFromIndex' => 'string|false', 'setExternalAttributesName' => 'bool', 'setExternalAttributesIndex' => 'bool', 'getExternalAttributesName' => 'bool', 'getExternalAttributesIndex' => 'bool', 'setCompressionName' => 'bool', 'setCompressionIndex' => 'bool', 'setEncryptionName' => 'bool', 'setEncryptionIndex' => 'bool', 'registerProgressCallback' => 'bool', 'registerCancelCallback' => 'bool', ], 'Exception' => [ '__wakeup' => 'void', ], 'Error' => [ '__wakeup' => 'void', ], 'IteratorAggregate' => [ 'getIterator' => 'Traversable', ], 'Iterator' => [ 'current' => 'mixed', 'next' => 'void', 'key' => 'mixed', 'valid' => 'bool', 'rewind' => 'void', ], 'ArrayAccess' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', ], 'Countable' => [ 'count' => 'int', ], ]; } PK2A#]�U��Ivendor/symfony/error-handler/Resources/assets/images/icon-plus-square.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1408 960V832q0-26-19-45t-45-19h-320V448q0-26-19-45t-45-19H832q-26 0-45 19t-19 45v320H448q-26 0-45 19t-19 45v128q0 26 19 45t45 19h320v320q0 26 19 45t45 19h128q26 0 45-19t19-45v-320h320q26 0 45-19t19-45zm256-544v960q0 119-84.5 203.5T1376 1664H416q-119 0-203.5-84.5T128 1376V416q0-119 84.5-203.5T416 128h960q119 0 203.5 84.5T1664 416z"/></svg> PK2A#]�s0��Lvendor/symfony/error-handler/Resources/assets/images/icon-minus-square-o.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 800v64q0 14-9 23t-23 9H480q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448V416q0-66-47-113t-113-47H480q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5T1312 1536H480q-119 0-203.5-84.5T192 1248V416q0-119 84.5-203.5T480 128h832q119 0 203.5 84.5T1600 416z"/></svg> PK2A#]�p�QQJvendor/symfony/error-handler/Resources/assets/images/icon-minus-square.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1408 960V832q0-26-19-45t-45-19H448q-26 0-45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45-19t19-45zm256-544v960q0 119-84.5 203.5T1376 1664H416q-119 0-203.5-84.5T128 1376V416q0-119 84.5-203.5T416 128h960q119 0 203.5 84.5T1664 416z"/></svg> PK2A#]�"i^��Gvendor/symfony/error-handler/Resources/assets/images/favicon.png.base64nu�[���data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAYAAAABtRhCAAADVUlEQVRIx82XX0jTURTHLYPyqZdefQx66CEo80+aYpoIkqzUikz6Z5klQoWUWYRIJYEUGpQ+lIr9U5dOTLdCtkmWZis3rbnC5fw/neYW002307mX/cZvP3/7o1PwwOdh95x7vnf39zvnd29AgBer2xO6DclAXiMqZAqxIiNIN/IYSUS2BPhjmGATchUxI+ADWiRhpWK7HKuHFVBFdmU5YvnI4grFGCaReF/EBH4KsZlGgj2JBTuCYBWRIYF8YoEOJ6wBt/gEs7mBbyOjQXruPLSdOgPCiEiPSUUHDoL8Ug5IUo9B/d5wrt+G7OAKNrODPuVdB6vRCIzN6SdBlpW9RIgk/1FeAXabzRlrUPVCS/JhbmwudztnGeeH9AyXBIwtmM3wLinZJZHifjHw2V+NBoRh+9ixQrbgbnaSIcl7cGea6hoXQbNe7za241oeO5Z0p42M4BV2EqP2D50wo+6HzvwC6C4sApNOR8cmOrtcnhtj2kYRyC9eBvXzKrBZrXSs72kFd1t3MoKVbMekQkEnSNKOO8fac3LpmK6l1TlGtsxmsdKFsecPYgwxst0cwROMYDXboSotg0WLBRqjY51jLYcENElXwW2XJKPydvoI2GN9T8rBtrAArYIUruBJXkFheCQYlCpQP6uk5dAQFQNaUROMSGVQFxLmkoQsxDJrhLbTZ+nvVsERME9MgPJRKV/58AsyomTSzE813WLFvWK++qI0xSfQl8k8Pg46sYRuv5t6dS+4RqxDwaa4BGjYH+NTQvKScIp9+YL/hoZh3jDtLRHtt2C3g6bmhX+CpsFBWg7ilDSPgj0lD2ncr5ev/BP8VvyAJhqVyZeUhPOrEhEFxgEtjft846Z/guQTNT89Q5P9flMLoth4F7808wKtWWKzAwNQHxrh/1vaid2F+XpYTSbQf1XA2McOmOpROnvpvMEA4tSjq1cW0sws2gCYxswY6TKkvzYnJq1NHZLnRU4BX+4U0uburvusu8Kv8iHY7qefkM4IFngJHEOUXmLEPgiGsI8YnlZILit3vSSLRTQe/MPIZva5pshNIEmyFQlCvruJKXPkCEfmePzkphXHdzZNQdoRI9KPlBAxlj/I8U97ERPS5bjGbWDFbEdqHVe5caTBeZZx2H/IMvzeN15yoQAAAABJRU5ErkJggg== PK2A#]�����Bvendor/symfony/error-handler/Resources/assets/images/icon-book.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M1703 478q40 57 18 129l-275 906q-19 64-76.5 107.5T1247 1664H324q-77 0-148.5-53.5T76 1479q-24-67-2-127 0-4 3-27t4-37q1-8-3-21.5t-3-19.5q2-11 8-21t16.5-23.5T116 1179q23-38 45-91.5t30-91.5q3-10 .5-30t-.5-28q3-11 17-28t17-23q21-36 42-92t25-90q1-9-2.5-32t.5-28q4-13 22-30.5t22-22.5q19-26 42.5-84.5T404 411q1-8-3-25.5t-2-26.5q2-8 9-18t18-23 17-21q8-12 16.5-30.5t15-35 16-36 19.5-32 26.5-23.5 36-11.5T620 134l-1 3q38-9 51-9h761q74 0 114 56t18 130l-274 906q-36 119-71.5 153.5T1089 1408H220q-27 0-38 15-11 16-1 43 24 70 144 70h923q29 0 56-15.5t35-41.5l300-987q7-22 5-57 38 15 59 43zm-1064 2q-4 13 2 22.5t20 9.5h608q13 0 25.5-9.5T1311 480l21-64q4-13-2-22.5t-20-9.5H702q-13 0-25.5 9.5T660 416zm-83 256q-4 13 2 22.5t20 9.5h608q13 0 25.5-9.5T1228 736l21-64q4-13-2-22.5t-20-9.5H619q-13 0-25.5 9.5T577 672z"/></svg> PK2A#]"�Ȗ Bvendor/symfony/error-handler/Resources/assets/images/icon-copy.svgnu�[���<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>PK2A#]-��Kvendor/symfony/error-handler/Resources/assets/images/icon-plus-square-o.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 800v64q0 14-9 23t-23 9H960v352q0 14-9 23t-23 9h-64q-14 0-23-9t-9-23V896H480q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h352V416q0-14 9-23t23-9h64q14 0 23 9t9 23v352h352q14 0 23 9t9 23zm128 448V416q0-66-47-113t-113-47H480q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5T1312 1536H480q-119 0-203.5-84.5T192 1248V416q0-119 84.5-203.5T480 128h832q119 0 203.5 84.5T1600 416z"/></svg> PK2A#]%0�ɮ�Evendor/symfony/error-handler/Resources/assets/images/symfony-logo.svgnu�[���<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#FFF" d="M12 .9C5.8.9.9 5.8.9 12a11 11 0 1 0 22.2 0A11 11 0 0 0 12 .9zm6.5 6c-.6 0-.9-.3-.9-.8 0-.2 0-.4.2-.6l.2-.4c0-.3-.5-.4-.6-.4-1.8.1-2.3 2.5-2.7 4.4l-.2 1c1 .2 1.8 0 2.2-.3.6-.4-.2-.7-.1-1.2.1-.3.5-.5.7-.6.5 0 .7.5.7.9 0 .7-1 1.8-3 1.8l-.6-.1-.6 2.4c-.4 1.6-.8 3.8-2.4 5.7-1.4 1.7-2.9 1.9-3.5 1.9-1.2 0-1.9-.6-2-1.5 0-.8.7-1.3 1.2-1.3.6 0 1.1.5 1.1 1s-.2.6-.4.6c-.1.1-.3.2-.3.4 0 .1.1.3.4.3.5 0 .8-.3 1.1-.5 1.2-.9 1.6-2.7 2.2-5.7l.1-.7.7-3.2c-.8-.6-1.3-1.4-2.4-1.7-.6-.1-1.1.1-1.5.5-.4.5-.2 1.1.2 1.5l.7.6c.7.8 1.2 1.6 1 2.5-.3 1.5-2 2.6-4 1.9-1.8-.6-2-1.8-1.8-2.5.2-.6.6-.7 1.1-.6.5.2.6.7.6 1.2l-.1.3c-.2.1-.3.3-.3.4-.1.4.4.6.7.7.7.3 1.6-.2 1.8-.8a1 1 0 0 0-.4-1.1l-.7-.8c-.4-.4-1.1-1.4-.7-2.6.1-.5.4-.9.7-1.3a4 4 0 0 1 2.8-.6c1.2.4 1.8 1.1 2.6 1.8.5-1.2 1-2.4 1.8-3.5.9-.9 1.9-1.6 3.1-1.7 1.3.2 2.2.7 2.2 1.6 0 .4-.2 1.1-.9 1.1z"/></svg> PK2A#]�Fvendor/symfony/error-handler/Resources/assets/images/chevron-right.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45L531 45q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"/></svg> PK2A#]����Jvendor/symfony/error-handler/Resources/assets/images/symfony-ghost.svg.phpnu�[���<svg viewBox="0 0 136 81" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.4"><path d="M92.4 20.4a23.2 23.2 0 0 1 9 1.9 23.7 23.7 0 0 1 5.2 3 24.3 24.3 0 0 1 3.4 3.4 24.8 24.8 0 0 1 5 9.4c.5 1.7.8 3.4 1 5.2v14.5h.4l.5.2a7.4 7.4 0 0 0 2.5.2l.2-.2.6-.8.8-1.3-.2-.1a5.5 5.5 0 0 1-.8-.3 5.6 5.6 0 0 1-2.3-1.8 5.7 5.7 0 0 1-.9-1.6 6.5 6.5 0 0 1-.2-2.8 7.3 7.3 0 0 1 .5-2l.3-.3.8-.9.3-.3c.2-.2.5-.3.8-.3H120.7c.2 0 .3-.1.4 0h.4l.2.1.3.2.2-.4.3-.4.1-.1 1.2-1 .3-.2.4-.1.4-.1h.3l1.5.1.4.1.8.5.1.2 1 1.1v.2H129.4l.4-.2 1.4-.5h1.1c.3 0 .7.2 1 .4.2 0 .3.2.5.3l.2.2.5.3.4.6.1.3.4 1.4.1.4v.6a7.8 7.8 0 0 1-.1.6 9.9 9.9 0 0 1-.8 2.4 7.8 7.8 0 0 1-3 3.3 6.4 6.4 0 0 1-1 .5 6.1 6.1 0 0 1-.6.2l-.7.1h-.1a23.4 23.4 0 0 1-.2 1.7 14.3 14.3 0 0 1-.6 2.1l-.8 2a9.2 9.2 0 0 1-.4.6l-.7 1a9.1 9.1 0 0 1-2.3 2.2c-.9.5-2 .6-3 .7l-1.4.1h-.5l-.4.1a15.8 15.8 0 0 1-2.8-.1v4.2a9.7 9.7 0 0 1-.7 3.5 9.6 9.6 0 0 1-1.7 2.8 9.3 9.3 0 0 1-3 2.3 9 9 0 0 1-5.4.7 9 9 0 0 1-3-1 9.4 9.4 0 0 1-2.7-2.5 10 10 0 0 1-1 1.2 9.3 9.3 0 0 1-2 1.3 9 9 0 0 1-2.4 1 9 9 0 0 1-6.5-1.1A9.4 9.4 0 0 1 85 77V77a10.9 10.9 0 0 1-.6.6 9.3 9.3 0 0 1-2.7 2 9 9 0 0 1-6 .8 9 9 0 0 1-2.4-1 9.3 9.3 0 0 1-2.3-1.7 9.6 9.6 0 0 1-1.8-2.8 9.7 9.7 0 0 1-.8-3.7v-4a18.5 18.5 0 0 1-2.9.2l-1.2-.1c-1.9-.3-3.7-1-5.1-2.2a8.2 8.2 0 0 1-1.1-1 10.2 10.2 0 0 1-.9-1.2 15.3 15.3 0 0 1-.7-1.3 20.8 20.8 0 0 1-1.9-6.2v-.2a6.5 6.5 0 0 1-1-.3 6.1 6.1 0 0 1-.6-.3 6.6 6.6 0 0 1-.9-.6 8.2 8.2 0 0 1-2.7-3.7 10 10 0 0 1-.3-1 10.3 10.3 0 0 1-.3-1.9V47v-.4l.1-.4.6-1.4.1-.2a2 2 0 0 1 .8-.8l.3-.2.3-.2a3.2 3.2 0 0 1 1.8-.5h.4l.3.2 1.4.6.2.2.4.3.3.4.7-.7.2-.2.4-.2.6-.2h2.1l.4.2.4.2.3.2.8 1 .2-.1h.1v-.1H63l1.1.1h.3l.8.5.3.4.7 1 .2.3.1.5a11 11 0 0 1 .2 1.5c0 .8 0 1.6-.3 2.3a6 6 0 0 1-.5 1.2 5.5 5.5 0 0 1-3.3 2.5 12.3 12.3 0 0 0 1.4 3h.1l.2.1 1 .2h1.5l.5-.2H67.8l.5-.2h.1V44v-.4a26.7 26.7 0 0 1 .3-2.3 24.7 24.7 0 0 1 5.7-12.5 24.2 24.2 0 0 1 3.5-3.3 23.7 23.7 0 0 1 4.9-3 23.2 23.2 0 0 1 5.6-1.7 23.7 23.7 0 0 1 4-.3zm-.3 2a21.2 21.2 0 0 0-8 1.7 21.6 21.6 0 0 0-4.8 2.7 22.2 22.2 0 0 0-3.2 3 22.7 22.7 0 0 0-5 9.2 23.4 23.4 0 0 0-.7 4.9v15.7l-.5.1a34.3 34.3 0 0 1-1.5.3h-.2l-.4.1h-.4l-.9.2a10 10 0 0 1-1.9 0c-.5 0-1-.2-1.5-.4a1.8 1.8 0 0 1-.3-.2 2 2 0 0 1-.3-.3 5.2 5.2 0 0 1-.1-.2 9 9 0 0 1-.6-.9 13.8 13.8 0 0 1-1-2 14.3 14.3 0 0 1-.6-2 14 14 0 0 1-.1-.8v-.2h.3a12.8 12.8 0 0 0 1.4-.2 4.4 4.4 0 0 0 .3 0 3.6 3.6 0 0 0 1.1-.7 3.4 3.4 0 0 0 1.2-1.7l.2-1.2a5.1 5.1 0 0 0 0-.8 7.2 7.2 0 0 0-.1-.8l-.7-1-1.2-.2-1 .7-.1 1.3a5 5 0 0 1 .1.4v.6a1 1 0 0 1 0 .3c-.1.3-.4.4-.7.5l-1.2.4v-.7A9.9 9.9 0 0 1 60 49l.3-.6v-.2l.1-.1v-1.6l-1-1.2h-1.5l-1 1.1v.4a5.3 5.3 0 0 0-.2.6 5.5 5.5 0 0 0 0 .5c0 .7 0 1.4.3 2 0 .4.2.8.4 1.2L57 51a9.5 9.5 0 0 1-1.1-.5h-.2a2 2 0 0 1-.4-.3c-.4-.4-.5-1-.6-1.6a5.6 5.6 0 0 1 0-.5v-.5-.5l-.6-1.5-1.4-.6-.9.3s-.2 0-.3.2a2 2 0 0 1-.1 0l-.6 1.4v.7a8.5 8.5 0 0 0 .5 2c.4 1.1 1 2.1 2 2.8a4.7 4.7 0 0 0 2.1.9h1a22.8 22.8 0 0 0 .1 1 18.1 18.1 0 0 0 .8 3.8 18.2 18.2 0 0 0 1.6 3.7l1 1.3c1 1 2.3 1.6 3.7 2a11.7 11.7 0 0 0 4.8 0h.4l.5-.2.5-.1.6-.2v6.6a8 8 0 0 0 .1 1.3 7.5 7.5 0 0 0 2.4 4.3 7.2 7.2 0 0 0 2.3 1.3 7 7 0 0 0 7-1.1 7.5 7.5 0 0 0 2-2.6A7.7 7.7 0 0 0 85 72V71a8.2 8.2 0 0 0 .2 1.3c0 .7.3 1.4.6 2a7.5 7.5 0 0 0 1.7 2.3 7.3 7.3 0 0 0 2.2 1.4 7.1 7.1 0 0 0 4.6.2 7.2 7.2 0 0 0 2.4-1.2 7.5 7.5 0 0 0 2.1-2.7 7.8 7.8 0 0 0 .7-2.4V71a9.3 9.3 0 0 0 .1.6 7.6 7.6 0 0 0 .6 2.5 7.5 7.5 0 0 0 2.4 3 7.1 7.1 0 0 0 7 .8 7.3 7.3 0 0 0 2.3-1.5 7.5 7.5 0 0 0 1.6-2.3 7.6 7.6 0 0 0 .5-2l.1-1.1v-6.7l.4.1a12.2 12.2 0 0 0 2 .5 11.1 11.1 0 0 0 2.5 0h.8l1.2-.1a9.5 9.5 0 0 0 1.4-.2l.9-.3a3.5 3.5 0 0 0 .6-.4l1.2-1.4a12.2 12.2 0 0 0 .8-1.2c0-.3.2-.5.3-.7a15.9 15.9 0 0 0 .7-2l.3-1.6v-1.3l.2-.9V54.6a15.5 15.5 0 0 0 1.8 0 4.5 4.5 0 0 0 1.4-.5 5.7 5.7 0 0 0 2.5-3.2 7.6 7.6 0 0 0 .4-1.5v-.3l-.4-1.4a5.2 5.2 0 0 1-.2-.1l-.4-.4a3.8 3.8 0 0 0-.2 0 1.4 1.4 0 0 0-.5-.2l-1.4.4-.7 1.3v.7a5.7 5.7 0 0 1-.1.8l-.7 1.4a1.9 1.9 0 0 1-.5.3h-.3a9.6 9.6 0 0 1-.8.3 8.8 8.8 0 0 1-.6 0l.2-.4.2-.5.2-.3v-.4l.1-.2V50l.1-1 .1-.6v-.6a4.8 4.8 0 0 0 0-.8v-.2l-1-1.1-1.5-.2-1.1 1-.2 1.4v.1l.2.4.2.3v.4l.1 1.1v.3l.1.5v.8a9.6 9.6 0 0 1-.8-.3l-.2-.1h-.3l-.8-.1h-.2a1.6 1.6 0 0 1-.2-.2.9.9 0 0 1-.2-.2 1 1 0 0 1-.1-.5l.2-.9v-1.2l-.9-.8h-1.2l-.8.9v.3a4.8 4.8 0 0 0-.3 2l.3.9a3.5 3.5 0 0 0 1.2 1.6l1 .5.8.2 1.4.1h.4l.2.1a12.1 12.1 0 0 1-1 2.6 13.2 13.2 0 0 1-.8 1.5 9.5 9.5 0 0 1-1 1.2l-.2.3a1.7 1.7 0 0 1-.4.3 2.4 2.4 0 0 1-.7.2h-2.5a7.8 7.8 0 0 1-.6-.2l-.7-.2h-.2a14.8 14.8 0 0 1-.6-.2 23.4 23.4 0 0 1-.4-.1l-.4-.1-.3-.1V43.9a34.6 34.6 0 0 0 0-.6 23.6 23.6 0 0 0-.4-3 22.7 22.7 0 0 0-1.5-4.7 22.6 22.6 0 0 0-4.6-6.7 21.9 21.9 0 0 0-6.9-4.7 21.2 21.2 0 0 0-8.1-1.8H92zm9.1 33.7l.3.1a1 1 0 0 1 .6.8v.4a8.4 8.4 0 0 1 0 .5 8.8 8.8 0 0 1-1.6 4.2l-1 1.3A10 10 0 0 1 95 66c-1.3.3-2.7.4-4 .3a10.4 10.4 0 0 1-2.7-.8 10 10 0 0 1-3.6-2.5 9.3 9.3 0 0 1-.8-1 9 9 0 0 1-.7-1.2 8.6 8.6 0 0 1-.8-3.4V57a1 1 0 0 1 .3-.6 1 1 0 0 1 1.3-.2 1 1 0 0 1 .4.8v.4a6.5 6.5 0 0 0 .5 2.2 7 7 0 0 0 2.1 2.8l1 .6c2.6 1.6 6 1.6 8.5 0a8 8 0 0 0 1.1-.6 7.6 7.6 0 0 0 1.2-1.2 7 7 0 0 0 1-1.7 6.5 6.5 0 0 0 .4-2.5 1 1 0 0 1 .7-1h.4zM30.7 43.7c-15.5 1-28.5-6-30.1-16.4C-1.2 15.7 11.6 4 29 1.3 46.6-1.7 62.3 5.5 64 17.1c1.6 10.4-8.7 21-23.7 25a31.2 31.2 0 0 0 0 .9v.3a19 19 0 0 0 .1 1l.1.4.1.9a4.7 4.7 0 0 0 .5 1l.7 1a9.2 9.2 0 0 0 1.2 1l1.5.8.6.8-.7.6-1.1.3a11.2 11.2 0 0 1-2.6.4 8.6 8.6 0 0 1-3-.5 8.5 8.5 0 0 1-1-.4 11.2 11.2 0 0 1-1.8-1.2 13.3 13.3 0 0 1-1-1 18 18 0 0 1-.7-.6l-.4-.4a23.4 23.4 0 0 1-1.3-1.8l-.1-.1-.3-.5V45l-.3-.6v-.7zM83.1 36c3.6 0 6.5 3.2 6.5 7.1 0 4-3 7.2-6.5 7.2S76.7 47 76.7 43 79.6 36 83 36zm18 0c3.6 0 6.5 3.2 6.5 7.1 0 4-2.9 7.2-6.4 7.2S94.7 47 94.7 43s3-7.1 6.5-7.1zm-18 6.1c2 0 3.5 1.6 3.5 3.6S85 49.2 83 49.2s-3.4-1.6-3.4-3.6S81.2 42 83 42zm17.9 0c1.9 0 3.4 1.6 3.4 3.6s-1.5 3.6-3.4 3.6c-2 0-3.5-1.6-3.5-3.6S99.1 42 101 42zM17 28c-.3 1.6-1.8 5-5.2 5.8-2.5.6-4.1-.8-4.5-2.6-.4-1.9.7-3.5 2.1-4.5A3.5 3.5 0 0 1 8 24.6c-.4-2 .8-3.7 3.2-4.2 1.9-.5 3.1.2 3.4 1.5.3 1.1-.5 2.2-1.8 2.5-.9.3-1.6 0-1.7-.6a1.4 1.4 0 0 1 0-.7s.3.2 1 0c.7-.1 1-.7.9-1.2-.2-.6-1-.8-1.8-.6-1 .2-2 1-1.7 2.6.3 1 .9 1.6 1.5 1.8l.7-.2c1-.2 1.5 0 1.6.5 0 .4-.2 1-1.2 1.2a3.3 3.3 0 0 1-1.5 0c-.9.7-1.6 1.9-1.3 3.2.3 1.3 1.3 2.2 3 1.8 2.5-.7 3.8-3.7 4.2-5-.3-.5-.6-1-.7-1.6-.1-.5.1-1 .9-1.2.4 0 .7.2.8.8a2.8 2.8 0 0 1 0 1l.7 1c.6-2 1.4-4 1.7-4 .6-.2 1.5.6 1.5.6-.8.7-1.7 2.4-2.3 4.2.8.6 1.6 1 2.1 1 .5-.1.8-.6 1-1.2-.3-2.2 1-4.3 2.3-4.6.7-.2 1.3.2 1.4.8.1.5 0 1.3-.9 1.7-.2-1-.6-1.3-1-1.3-.4.1-.7 1.4-.4 2.8.2 1 .7 1.5 1.3 1.4.8-.2 1.3-1.2 1.7-2.1-.3-2.1.9-4.2 2.2-4.5.7-.2 1.2.1 1.4 1 .4 1.4-1 2.8-2.2 3.4.3.7.7 1 1.3.9 1-.3 1.6-1.5 2-2.5l-.5-3v-.3s1.6-.3 1.8.6v.1c.2-.6.7-1.2 1.3-1.4.8-.1 1.5.6 1.7 1.6.5 2.2-.5 4.4-1.8 4.7H33a31.9 31.9 0 0 0 1 5.2c-.4.1-1.8.4-2-.4l-.5-5.6c-.5 1-1.3 2.2-2.5 2.4-1 .3-1.6-.3-2-1.1-.5 1-1.3 2.1-2.4 2.4-.8.2-1.5-.1-2-1-.3.8-.9 1.5-1.5 1.7-.7.1-1.5-.3-2.4-1-.3.8-.4 1.6-.4 2.2 0 0-.7 0-.8-.4-.1-.5 0-1.5.3-2.7a10.3 10.3 0 0 1-.7-.8zm38.2-17.8l.2.9c.5 1.9.4 4.4.8 6.4 0 .6-.4 3-1.4 3.3-.2 0-.3 0-.4-.4-.1-.7 0-1.6-.3-2.6-.2-1.1-.8-1.6-1.5-1.5-.8.2-1.3 1-1.6 2l-.1-.5c-.2-1-1.8-.6-1.8-.6a6.2 6.2 0 0 1 .4 1.3l.2 1c-.2.5-.6 1-1.2 1l-.2.1a7 7 0 0 0-.1-.8c-.3-1.1-1-2-1.6-1.8a.7.7 0 0 0-.4.3c-1.3.3-2.4 2-2.1 3.9-.2.9-.6 1.7-1 1.9-.5 0-.8-.5-1.1-1.8l-.1-1.2a4 4 0 0 0 0-1.7c0-.4-.4-.7-.8-.6-.7.2-.9 1.7-.5 3.8-.2 1-.6 2-1.3 2-.4.2-.8-.2-1-1l-.2-3c1.2-.5 2-1 1.8-1.7-.1-.5-.8-.7-.8-.7s0 .7-1 1.2l-.2-1.4c-.1-.6-.4-1-1.7-.6l.4 1 .2 1.5h-1v.8c0 .3.4.3 1 .2 0 1.3 0 2.7.2 3.6.3 1.4 1.2 2 2 1.7 1-.2 1.6-1.3 2-2.3.3 1.2 1 2 1.9 1.7.7-.2 1.2-1.1 1.6-2.2.4.8 1.1 1.1 2 1 1.2-.4 1.7-1.6 1.8-2.8h.2c.6-.2 1-.6 1.3-1 0 .8 0 1.5.2 2.1.1.5.3.7.6.6.5-.1 1-.9 1-.9a4 4 0 0 1-.3-1c-.3-1.3.3-3.6 1-3.7.2 0 .3.2.5.7v.8l.2 1.5v.7c.2.7.7 1.3 1.5 1 1.3-.2 2-2.6 2.1-3.9.3.2.6.2 1 .1-.6-2.2 0-6.1-.3-7.9-.1-.4-1-.5-1.7-.5h-.4zm-21.5 12c.4 0 .7.3 1 1.1.2 1.3-.3 2.6-.9 2.8-.2 0-.7 0-1-1.2v-.4c0-1.3.4-2 1-2.2zm-5.2 1c.3 0 .6.2.6.5.2.6-.3 1.3-1.2 2-.3-1.4.1-2.3.6-2.5zm18-.4c-.5.2-1-.4-1.2-1.2-.2-1 0-2.1.7-2.5v.5c.2.7.6 1.5 1.3 1.9 0 .7-.2 1.2-.7 1.3zm10-1.6c0 .5.4.7 1 .6.8-.2 1-1 .8-1.6 0-.5-.4-1-1-.8-.5.1-1 .9-.8 1.8zm-14.3-5.5c0-.4-.5-.7-1-.5-.8.2-1 1-.9 1.5.2.6.5 1 1 .8.5 0 1.1-1 1-1.8z" fill="#fff" fill-opacity=".6"/><?= $this->addElementToGhost(); ?></svg> PK2A#]S���zzEvendor/symfony/error-handler/Resources/assets/images/icon-support.svgnu�[���<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M896 0q182 0 348 71t286 191 191 286 71 348-71 348-191 286-286 191-348 71-348-71-286-191-191-286T0 896t71-348 191-286T548 71 896 0zm0 128q-190 0-361 90l194 194q82-28 167-28t167 28l194-194q-171-90-361-90zM218 1257l194-194q-28-82-28-167t28-167L218 535q-90 171-90 361t90 361zm678 407q190 0 361-90l-194-194q-82 28-167 28t-167-28l-194 194q171 90 361 90zm0-384q159 0 271.5-112.5T1280 896t-112.5-271.5T896 512 624.5 624.5 512 896t112.5 271.5T896 1280zm484-217l194 194q90-171 90-361t-90-361l-194 194q28 82 28 167t-28 167z"/></svg> PK2A#]�|vN@7@7?vendor/symfony/error-handler/Resources/assets/css/exception.cssnu�[���/* This file is based on WebProfilerBundle/Resources/views/Profiler/profiler.css.twig. If you make any change in this file, verify the same change is needed in the other file. */ :root { --font-sans-serif: Helvetica, Arial, sans-serif; --page-background: #f9f9f9; --color-text: #222; /* when updating any of these colors, do the same in toolbar.css.twig */ --color-success: #4f805d; --color-warning: #a46a1f; --color-error: #b0413e; --color-muted: #999; --tab-background: #fff; --tab-color: #444; --tab-active-background: #666; --tab-active-color: #fafafa; --tab-disabled-background: #f5f5f5; --tab-disabled-color: #999; --metric-value-background: #fff; --metric-value-color: inherit; --metric-unit-color: #999; --metric-label-background: #e0e0e0; --metric-label-color: inherit; --table-border: #e0e0e0; --table-background: #fff; --table-header: #e0e0e0; --trace-selected-background: #F7E5A1; --tree-active-background: #F7E5A1; --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(128, 128, 128, .2); --border: 1px solid #e0e0e0; --background-error: var(--color-error); --highlight-comment: #969896; --highlight-default: #222222; --highlight-keyword: #a71d5d; --highlight-string: #183691; --base-0: #fff; --base-1: #f5f5f5; --base-2: #e0e0e0; --base-3: #ccc; --base-4: #666; --base-5: #444; --base-6: #222; } .theme-dark { --page-background: #36393e; --color-text: #e0e0e0; --color-muted: #777; --color-error: #d43934; --tab-background: #555; --tab-color: #ccc; --tab-active-background: #888; --tab-active-color: #fafafa; --tab-disabled-background: var(--page-background); --tab-disabled-color: #777; --metric-value-background: #555; --metric-value-color: inherit; --metric-unit-color: #999; --metric-label-background: #777; --metric-label-color: #e0e0e0; --trace-selected-background: #71663acc; --table-border: #444; --table-background: #333; --table-header: #555; --info-background: rgba(79, 148, 195, 0.5); --tree-active-background: var(--metric-label-background); --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(32, 32, 32, .2); --border: 1px solid #666; --background-error: #b0413e; --highlight-comment: #dedede; --highlight-default: var(--base-6); --highlight-keyword: #ff413c; --highlight-string: #70a6fd; --base-0: #2e3136; --base-1: #444; --base-2: #666; --base-3: #666; --base-4: #666; --base-5: #e0e0e0; --base-6: #f5f5f5; --card-label-background: var(--tab-active-background); --card-label-color: var(--tab-active-color); } html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0} html { /* always display the vertical scrollbar to avoid jumps when toggling contents */ overflow-y: scroll; } body { background-color: var(--page-background); color: var(--base-6); font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; } a { cursor: pointer; text-decoration: none; } a:hover { text-decoration: underline; } abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } table, tr, th, td { background: var(--base-0); border-collapse: collapse; vertical-align: top; } table { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } table th, table td { border: solid var(--base-2); border-width: 1px 0; padding: 8px 10px; } table th { background-color: var(--base-2); font-weight: bold; text-align: left; } .m-t-5 { margin-top: 5px; } .hidden-xs-down { display: none; } .block { display: block; } .full-width { width: 100%; } .hidden { display: none; } .prewrap { white-space: pre-wrap; } .nowrap { white-space: nowrap; } .newline { display: block; } .break-long-words { word-wrap: break-word; overflow-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; min-width: 0; } .text-small { font-size: 12px !important; } .text-muted { color: #999; } .text-bold { font-weight: bold; } .empty { border: 4px dashed var(--base-2); color: #999; margin: 1em 0; padding: .5em 2em; } .status-success { background: rgba(94, 151, 110, 0.3); } .status-warning { background: rgba(240, 181, 24, 0.3); } .status-error { background: rgba(176, 65, 62, 0.2); } .status-success td, .status-warning td, .status-error td { background: transparent; } tr.status-error td, tr.status-warning td { border-bottom: 1px solid var(--base-2); border-top: 1px solid var(--base-2); } .status-warning .colored { color: #A46A1F; } .status-error .colored { color: var(--color-error); } .sf-toggle { cursor: pointer; position: relative; } .sf-toggle-content { -moz-transition: display .25s ease; -webkit-transition: display .25s ease; transition: display .25s ease; } .sf-toggle-content.sf-toggle-hidden { display: none; } .sf-toggle-content.sf-toggle-visible { display: block; } thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; } .sf-toggle-off .icon-close, .sf-toggle-on .icon-open { display: none; } .sf-toggle-off .icon-open, .sf-toggle-on .icon-close { display: block; } .tab-navigation { margin: 0 0 1em 0; padding: 0; } .tab-navigation li { background: var(--tab-background); border: 1px solid var(--table-border); color: var(--tab-color); cursor: pointer; display: inline-block; font-size: 16px; margin: 0 0 0 -1px; padding: .5em .75em; z-index: 1; } .tab-navigation li .badge { background-color: var(--base-1); color: var(--base-4); display: inline-block; font-size: 14px; font-weight: bold; margin-left: 8px; min-width: 10px; padding: 1px 6px; text-align: center; white-space: nowrap; } .tab-navigation li.disabled { background: var(--tab-disabled-background); color: var(--tab-disabled-color); } .tab-navigation li.active { background: var(--tab-active-background); color: var(--tab-active-color); z-index: 1100; } .tab-navigation li.active .badge { background-color: var(--base-5); color: var(--base-2); } .tab-content > *:first-child { margin-top: 0; } .tab-navigation li .badge.status-warning { background: var(--color-warning); color: #FFF; } .tab-navigation li .badge.status-error { background: var(--background-error); color: #FFF; } .sf-tabs .tab:not(:first-child) { display: none; } [data-filters] { position: relative; } [data-filtered] { cursor: pointer; } [data-filtered]:after { content: '\00a0\25BE'; } [data-filtered]:hover .filter-list li { display: inline-flex; } [class*="filter-hidden-"] { display: none; } .filter-list { position: absolute; border: var(--border); box-shadow: var(--shadow); margin: 0; padding: 0; display: flex; flex-direction: column; } .filter-list :after { content: ''; } .filter-list li { background: var(--tab-disabled-background); border-bottom: var(--border); color: var(--tab-disabled-color); display: none; list-style: none; margin: 0; padding: 5px 10px; text-align: left; font-weight: normal; } .filter-list li.active { background: var(--tab-background); color: var(--tab-color); } .filter-list li.last-active { background: var(--tab-active-background); color: var(--tab-active-color); } .filter-list-level li { cursor: s-resize; } .filter-list-level li.active { cursor: n-resize; } .filter-list-level li.last-active { cursor: default; } .filter-list-level li.last-active:before { content: '\2714\00a0'; } .filter-list-choice li:before { content: '\2714\00a0'; color: transparent; } .filter-list-choice li.active:before { color: unset; } .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } .container::after { content: ""; display: table; clear: both; } header { background-color: #222; color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; } header .container { display: flex; justify-content: space-between; } .logo { flex: 1; font-size: 13px; font-weight: normal; margin: 0; padding: 0; } .logo svg { height: 18px; width: 18px; opacity: .8; vertical-align: -5px; } .help-link { margin-left: 15px; } .help-link a { color: inherit; } .help-link .icon svg { height: 15px; width: 15px; opacity: .7; vertical-align: -2px; } .help-link a:hover { color: #EEE; text-decoration: none; } .help-link a:hover svg { opacity: .9; } .exception-summary { background: var(--background-error); border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 15px; } .exception-metadata { background: rgba(0, 0, 0, 0.1); padding: 7px 0; } .exception-metadata .container { display: flex; flex-direction: row; justify-content: space-between; } .exception-metadata h2, .exception-metadata h2 > a { color: rgba(255, 255, 255, 0.8); font-size: 13px; font-weight: 400; margin: 0; } .exception-http small { font-size: 13px; opacity: .7; } .exception-hierarchy { flex: 1; } .exception-hierarchy .icon { margin: 0 3px; opacity: .7; } .exception-hierarchy .icon svg { height: 13px; width: 13px; vertical-align: -2px; } .exception-without-message .exception-message-wrapper { display: none; } .exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 15px 8px; } .exception-message { flex-grow: 1; } .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } .exception-message.long { font-size: 18px; } .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } .exception-message a:hover { border-bottom-color: #ffffff; } .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } .trace + .trace { margin-top: 30px; } .trace-head { background-color: var(--base-2); padding: 10px; position: relative; } .trace-head .trace-class { color: var(--base-6); font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } .trace-head .trace-namespace { color: #999; display: block; font-size: 13px; } .trace-head .icon { position: absolute; right: 0; top: 0; } .trace-head .icon svg { fill: var(--base-5); height: 24px; width: 24px; } .trace-details { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; table-layout: fixed; } .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } .trace-line { position: relative; padding-top: 8px; padding-bottom: 8px; } .trace-line + .trace-line { border-top: var(--border); } .trace-line:hover { background: var(--base-1); } .trace-line a { color: var(--base-6); } .trace-line .icon { opacity: .4; position: absolute; left: 10px; } .trace-line .icon svg { fill: var(--base-5); height: 16px; width: 16px; } .trace-line .icon.icon-copy { left: auto; top: auto; padding-left: 5px; display: none } .trace-line:hover .icon.icon-copy:not(.hidden) { display: inline-block } .trace-line-header { padding-left: 36px; padding-right: 10px; } .trace-file-path, .trace-file-path a { color: var(--base-6); font-size: 13px; } .trace-class { color: var(--color-error); } .trace-type { padding: 0 2px; } .trace-method { color: var(--color-error); font-weight: bold; } .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } .trace-code { background: var(--base-0); font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; } .trace-code ol { margin: 0; float: left; } .trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; } .trace-code li + li { margin-top: 5px; } .trace-code li.selected { background: var(--trace-selected-background); margin-top: 2px; } .trace-code li code { color: var(--base-6); white-space: nowrap; } .trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; } @media (min-width: 575px) { .hidden-xs-down { display: initial; } .help-link { margin-left: 30px; } } PK2A#]�%7;vendor/symfony/error-handler/Resources/assets/css/error.cssnu�[���body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } .container { margin: 30px; max-width: 600px; } h1 { color: #dc3545; font-size: 24px; } h2 { font-size: 18px; } PK2A#]*�bc� � Dvendor/symfony/error-handler/Resources/assets/css/exception_full.cssnu�[���.sf-reset .traces { padding-bottom: 14px; } .sf-reset .traces li { font-size: 12px; color: #868686; padding: 5px 4px; list-style-type: decimal; margin-left: 20px; } .sf-reset #logs .traces li.error { font-style: normal; color: #AA3333; background: #f9ecec; } .sf-reset #logs .traces li.warning { font-style: normal; background: #ffcc00; } /* fix for Opera not liking empty <li> */ .sf-reset .traces li:after { content: "\00A0"; } .sf-reset .trace { border: 1px solid #D3D3D3; padding: 10px; overflow: auto; margin: 10px 0 20px; } .sf-reset .block-exception { -moz-border-radius: 16px; -webkit-border-radius: 16px; border-radius: 16px; margin-bottom: 20px; background-color: #f6f6f6; border: 1px solid #dfdfdf; padding: 30px 28px; word-wrap: break-word; overflow: hidden; } .sf-reset .block-exception div { color: #313131; font-size: 10px; } .sf-reset .block-exception-detected .illustration-exception, .sf-reset .block-exception-detected .text-exception { float: left; } .sf-reset .block-exception-detected .illustration-exception { width: 152px; } .sf-reset .block-exception-detected .text-exception { width: 670px; padding: 30px 44px 24px 46px; position: relative; } .sf-reset .text-exception .open-quote, .sf-reset .text-exception .close-quote { font-family: Arial, Helvetica, sans-serif; position: absolute; color: #C9C9C9; font-size: 8em; } .sf-reset .open-quote { top: 0; left: 0; } .sf-reset .close-quote { bottom: -0.5em; right: 50px; } .sf-reset .block-exception p { font-family: Arial, Helvetica, sans-serif; } .sf-reset .block-exception p a, .sf-reset .block-exception p a:hover { color: #565656; } .sf-reset .logs h2 { float: left; width: 654px; } .sf-reset .error-count, .sf-reset .support { float: right; width: 170px; text-align: right; } .sf-reset .error-count span { display: inline-block; background-color: #aacd4e; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; padding: 4px; color: white; margin-right: 2px; font-size: 11px; font-weight: bold; } .sf-reset .support a { display: inline-block; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; padding: 4px; color: #000000; margin-right: 2px; font-size: 11px; font-weight: bold; } .sf-reset .toggle { vertical-align: middle; } .sf-reset .linked ul, .sf-reset .linked li { display: inline; } .sf-reset #output-content { color: #000; font-size: 12px; } .sf-reset #traces-text pre { white-space: pre; font-size: 12px; font-family: monospace; } PK2A#]�2d44=vendor/symfony/error-handler/Resources/assets/js/exception.jsnu�[���/* This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig. If you make any change in this file, verify the same change is needed in the other file. */ /*<![CDATA[*/ (function() { "use strict"; if ('classList' in document.documentElement) { var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); }; var removeClass = function(el, cssClass) { el.classList.remove(cssClass); }; var addClass = function(el, cssClass) { el.classList.add(cssClass); }; var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); }; } else { var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); }; var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); }; var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } }; var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); }; } var addEventListener; var el = document.createElement('div'); if (!('addEventListener' in el)) { addEventListener = function (element, eventName, callback) { element.attachEvent('on' + eventName, callback); }; } else { addEventListener = function (element, eventName, callback) { element.addEventListener(eventName, callback, false); }; } if (navigator.clipboard) { document.querySelectorAll('[data-clipboard-text]').forEach(function(element) { removeClass(element, 'hidden'); element.addEventListener('click', function() { navigator.clipboard.writeText(element.getAttribute('data-clipboard-text')); }) }); } (function createTabs() { var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])'); /* create the tab navigation for each group of tabs */ for (var i = 0; i < tabGroups.length; i++) { var tabs = tabGroups[i].querySelectorAll(':scope > .tab'); var tabNavigation = document.createElement('ul'); tabNavigation.className = 'tab-navigation'; var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */ for (var j = 0; j < tabs.length; j++) { var tabId = 'tab-' + i + '-' + j; var tabTitle = tabs[j].querySelector('.tab-title').innerHTML; var tabNavigationItem = document.createElement('li'); tabNavigationItem.setAttribute('data-tab-id', tabId); if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; } if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); } tabNavigationItem.innerHTML = tabTitle; tabNavigation.appendChild(tabNavigationItem); var tabContent = tabs[j].querySelector('.tab-content'); tabContent.parentElement.setAttribute('id', tabId); } tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild); addClass(document.querySelector('[data-tab-id="' + selectedTabId + '"]'), 'active'); } /* display the active tab and add the 'click' event listeners */ for (i = 0; i < tabGroups.length; i++) { tabNavigation = tabGroups[i].querySelectorAll(':scope >.tab-navigation li'); for (j = 0; j < tabNavigation.length; j++) { tabId = tabNavigation[j].getAttribute('data-tab-id'); document.getElementById(tabId).querySelector('.tab-title').className = 'hidden'; if (hasClass(tabNavigation[j], 'active')) { document.getElementById(tabId).className = 'block'; } else { document.getElementById(tabId).className = 'hidden'; } tabNavigation[j].addEventListener('click', function(e) { var activeTab = e.target || e.srcElement; /* needed because when the tab contains HTML contents, user can click */ /* on any of those elements instead of their parent '<li>' element */ while (activeTab.tagName.toLowerCase() !== 'li') { activeTab = activeTab.parentNode; } /* get the full list of tabs through the parent of the active tab element */ var tabNavigation = activeTab.parentNode.children; for (var k = 0; k < tabNavigation.length; k++) { var tabId = tabNavigation[k].getAttribute('data-tab-id'); document.getElementById(tabId).className = 'hidden'; removeClass(tabNavigation[k], 'active'); } addClass(activeTab, 'active'); var activeTabId = activeTab.getAttribute('data-tab-id'); document.getElementById(activeTabId).className = 'block'; }); } tabGroups[i].setAttribute('data-processed', 'true'); } })(); (function createToggles() { var toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])'); for (var i = 0; i < toggles.length; i++) { var elementSelector = toggles[i].getAttribute('data-toggle-selector'); var element = document.querySelector(elementSelector); addClass(element, 'sf-toggle-content'); if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') { addClass(toggles[i], 'sf-toggle-on'); addClass(element, 'sf-toggle-visible'); } else { addClass(toggles[i], 'sf-toggle-off'); addClass(element, 'sf-toggle-hidden'); } addEventListener(toggles[i], 'click', function(e) { e.preventDefault(); if ('' !== window.getSelection().toString()) { /* Don't do anything on text selection */ return; } var toggle = e.target || e.srcElement; /* needed because when the toggle contains HTML contents, user can click */ /* on any of those elements instead of their parent '.sf-toggle' element */ while (!hasClass(toggle, 'sf-toggle')) { toggle = toggle.parentNode; } var element = document.querySelector(toggle.getAttribute('data-toggle-selector')); toggleClass(toggle, 'sf-toggle-on'); toggleClass(toggle, 'sf-toggle-off'); toggleClass(element, 'sf-toggle-hidden'); toggleClass(element, 'sf-toggle-visible'); /* the toggle doesn't change its contents when clicking on it */ if (!toggle.hasAttribute('data-toggle-alt-content')) { return; } if (!toggle.hasAttribute('data-toggle-original-content')) { toggle.setAttribute('data-toggle-original-content', toggle.innerHTML); } var currentContent = toggle.innerHTML; var originalContent = toggle.getAttribute('data-toggle-original-content'); var altContent = toggle.getAttribute('data-toggle-alt-content'); toggle.innerHTML = currentContent !== altContent ? altContent : originalContent; }); /* Prevents from disallowing clicks on links inside toggles */ var toggleLinks = toggles[i].querySelectorAll('a'); for (var j = 0; j < toggleLinks.length; j++) { addEventListener(toggleLinks[j], 'click', function(e) { e.stopPropagation(); }); } /* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */ var copyToClipboardElements = toggles[i].querySelectorAll('span[data-clipboard-text]'); for (var k = 0; k < copyToClipboardElements.length; k++) { addEventListener(copyToClipboardElements[k], 'click', function(e) { e.stopPropagation(); }); } toggles[i].setAttribute('data-processed', 'true'); } })(); (function createFilters() { document.querySelectorAll('[data-filters] [data-filter]').forEach(function (filter) { var filters = filter.closest('[data-filters]'), type = 'choice', name = filter.dataset.filter, ucName = name.charAt(0).toUpperCase()+name.slice(1), list = document.createElement('ul'), values = filters.dataset['filter'+ucName] || filters.querySelectorAll('[data-filter-'+name+']'), labels = {}, defaults = null, indexed = {}, processed = {}; if (typeof values === 'string') { type = 'level'; labels = values.split(','); values = values.toLowerCase().split(','); defaults = values.length - 1; } addClass(list, 'filter-list'); addClass(list, 'filter-list-'+type); values.forEach(function (value, i) { if (value instanceof HTMLElement) { value = value.dataset['filter'+ucName]; } if (value in processed) { return; } var option = document.createElement('li'), label = i in labels ? labels[i] : value, active = false, matches; if ('' === label) { option.innerHTML = '<em>(none)</em>'; } else { option.innerText = label; } option.dataset.filter = value; option.setAttribute('title', 1 === (matches = filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').length) ? 'Matches 1 row' : 'Matches '+matches+' rows'); indexed[value] = i; list.appendChild(option); addEventListener(option, 'click', function () { if ('choice' === type) { filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) { if (option.dataset.filter === row.dataset['filter'+ucName]) { toggleClass(row, 'filter-hidden-'+name); } }); toggleClass(option, 'active'); } else if ('level' === type) { if (i === this.parentNode.querySelectorAll('.active').length - 1) { return; } this.parentNode.querySelectorAll('li').forEach(function (currentOption, j) { if (j <= i) { addClass(currentOption, 'active'); if (i === j) { addClass(currentOption, 'last-active'); } else { removeClass(currentOption, 'last-active'); } } else { removeClass(currentOption, 'active'); removeClass(currentOption, 'last-active'); } }); filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) { if (i < indexed[row.dataset['filter'+ucName]]) { addClass(row, 'filter-hidden-'+name); } else { removeClass(row, 'filter-hidden-'+name); } }); } }); if ('choice' === type) { active = null === defaults || 0 <= defaults.indexOf(value); } else if ('level' === type) { active = i <= defaults; if (active && i === defaults) { addClass(option, 'last-active'); } } if (active) { addClass(option, 'active'); } else { filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').forEach(function (row) { toggleClass(row, 'filter-hidden-'+name); }); } processed[value] = true; }); if (1 < list.childNodes.length) { filter.appendChild(list); filter.dataset.filtered = ''; } }); })(); })(); /*]]>*/ PK2A#]�D�>yyBvendor/symfony/error-handler/Resources/bin/patch-type-declarationsnu�[���#!/usr/bin/env php <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== \PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } if (\in_array('-h', $argv) || \in_array('--help', $argv)) { echo implode(PHP_EOL, [ ' Patches type declarations based on "@return" PHPDoc and triggers deprecations for', ' incompatible method declarations.', '', ' This assists you to make your package compatible with Symfony 6, but it can be used', ' for any class/package.', '', ' Available configuration via environment variables:', ' SYMFONY_PATCH_TYPE_DECLARATIONS', ' An url-encoded string to change the behavior of the script. Available parameters:', ' - "force": any value enables deprecation notices - can be any of:', ' - "phpdoc" to patch only docblock annotations', ' - "2" to add all possible return types', ' - "1" to add return types but only to tests/final/internal/private methods', ' - "php": the target version of PHP - e.g. "7.1" doesn\'t generate "object" types', ' - "deprecations": "1" to trigger a deprecation notice when a child class misses a', ' return type while the parent declares an "@return" annotation', '', ' SYMFONY_PATCH_TYPE_EXCLUDE', ' A regex matched against the full path to the class - any match will be excluded', '', ' Example: "SYMFONY_PATCH_TYPE_DECLARATIONS=php=7.4 ./patch-type-declarations"', ]); exit; } if (false === getenv('SYMFONY_PATCH_TYPE_DECLARATIONS')) { putenv('SYMFONY_PATCH_TYPE_DECLARATIONS=force=2'); echo 'No SYMFONY_PATCH_TYPE_DECLARATIONS env var set, patching type declarations in all methods (run the command with "-h" for more information).'.PHP_EOL; } if (is_file($autoload = __DIR__.'/../../../../autoload.php')) { // noop } elseif (is_file($autoload = __DIR__.'/../../../../../../../autoload.php')) { // noop } else { echo PHP_EOL.' /!\ Cannot find the Composer autoloader, did you forget to run "composer install"?'.PHP_EOL; exit(1); } if (is_file($phpunitAutoload = dirname($autoload).'/bin/.phpunit/phpunit/vendor/autoload.php')) { require $phpunitAutoload; } $loader = require $autoload; Symfony\Component\ErrorHandler\DebugClassLoader::enable(); $deprecations = []; set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$deprecations) { if (\E_USER_DEPRECATED !== $type) { return; } [,,,,, $class,] = explode('"', $msg); $deprecations[$class][] = $msg; }); $exclude = getenv('SYMFONY_PATCH_TYPE_EXCLUDE') ?: null; foreach ($loader->getClassMap() as $class => $file) { if (false !== strpos($file = realpath($file), \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR)) { continue; } if ($exclude && preg_match($exclude, $file)) { continue; } class_exists($class); } Symfony\Component\ErrorHandler\DebugClassLoader::checkClasses(); foreach ($deprecations as $class => $classDeprecations) { echo $class.' ('.\count($classDeprecations).')'.PHP_EOL; echo implode(PHP_EOL, $classDeprecations).PHP_EOL.PHP_EOL; } if ($deprecations && false !== strpos(getenv('SYMFONY_PATCH_TYPE_DECLARATIONS') ?? '', 'force')) { echo 'These deprecations might be fixed by the patch script, run this again to check for type deprecations.'.PHP_EOL; } PK2A#]/��Mvendor/symfony/error-handler/Resources/bin/extract-tentative-return-types.phpnu�[���#!/usr/bin/env php <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== \PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } // Run from the root of the php-src repository, this script generates // a table with all the methods that have a tentative return type. // // Usage: find -name *.stub.php | sort | /path/to/extract-tentative-return-types.php > /path/to/TentativeTypes.php echo <<<EOPHP <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Internal; /** * This class has been generated by extract-tentative-return-types.php. * * @internal */ class TentativeTypes { public const RETURN_TYPES = [ EOPHP; while (false !== $file = fgets(\STDIN)) { $code = file_get_contents(substr($file, 0, -1)); if (!str_contains($code, '@tentative-return-type')) { continue; } $code = preg_split('{^\s*(?:(?:abstract )?class|interface|trait) ([^\s]++)}m', $code, -1, \PREG_SPLIT_DELIM_CAPTURE); if (1 === count($code)) { continue; } for ($i = 1; null !== $class = $code[$i] ?? null; $i += 2) { $methods = $code[1 + $i]; if (!str_contains($methods, '@tentative-return-type')) { continue; } echo " '$class' => [\n"; preg_replace_callback('{@tentative-return-type.*?[\s]function ([^(]++)[^)]++\)\s*+:\s*+([^\n;\{]++)}s', function ($m) { $m[2] = str_replace(' ', '', $m[2]); echo " '$m[1]' => '$m[2]',\n"; return ''; }, $methods); echo " ],\n"; } } echo <<<EOPHP ]; } EOPHP; PK2A#]��l�[[:vendor/symfony/error-handler/Resources/views/logs.html.phpnu�[���<table class="logs" data-filter-level="Emergency,Alert,Critical,Error,Warning,Notice,Info,Debug" data-filters> <?php $channelIsDefined = isset($logs[0]['channel']); ?> <thead> <tr> <th data-filter="level">Level</th> <?php if ($channelIsDefined) { ?><th data-filter="channel">Channel</th><?php } ?> <th class="full-width">Message</th> </tr> </thead> <tbody> <?php foreach ($logs as $log) { if ($log['priority'] >= 400) { $status = 'error'; } elseif ($log['priority'] >= 300) { $status = 'warning'; } else { $severity = 0; if (($exception = $log['context']['exception'] ?? null) instanceof \ErrorException || $exception instanceof \Symfony\Component\ErrorHandler\Exception\SilencedErrorContext) { $severity = $exception->getSeverity(); } $status = \E_DEPRECATED === $severity || \E_USER_DEPRECATED === $severity ? 'warning' : 'normal'; } ?> <tr class="status-<?= $status; ?>" data-filter-level="<?= strtolower($this->escape($log['priorityName'])); ?>"<?php if ($channelIsDefined) { ?> data-filter-channel="<?= $this->escape($log['channel']); ?>"<?php } ?>> <td class="text-small nowrap"> <span class="colored text-bold"><?= $this->escape($log['priorityName']); ?></span> <span class="text-muted newline"><?= date('H:i:s', $log['timestamp']); ?></span> </td> <?php if ($channelIsDefined) { ?> <td class="text-small text-bold nowrap"> <?= $this->escape($log['channel']); ?> </td> <?php } ?> <td> <?= $this->formatLogMessage($log['message'], $log['context']); ?> <?php if ($log['context']) { ?> <pre class="text-muted prewrap m-t-5"><?= $this->escape(json_encode($log['context'], \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES)); ?></pre> <?php } ?> </td> </tr> <?php } ?> </tbody> </table> PK2A#]@�[�Avendor/symfony/error-handler/Resources/views/traces_text.html.phpnu�[���<table class="trace trace-as-text"> <thead class="trace-head"> <tr> <th class="sf-toggle" data-toggle-selector="#trace-text-<?= $index; ?>" data-toggle-initial="<?= 1 === $index ? 'display' : ''; ?>"> <div class="trace-class"> <?php if ($numExceptions > 1) { ?> <span class="text-muted">[<?= $numExceptions - $index + 1; ?>/<?= $numExceptions; ?>]</span> <?php } ?> <?= ($parts = explode('\\', $exception['class'])) ? end($parts) : ''; ?> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square-o.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square-o.svg'); ?></span> </div> </th> </tr> </thead> <tbody id="trace-text-<?= $index; ?>"> <tr> <td> <?php if ($exception['trace']) { ?> <pre class="stacktrace"> <?php echo $this->escape($exception['class']).":\n"; if ($exception['message']) { echo $this->escape($exception['message'])."\n"; } foreach ($exception['trace'] as $trace) { echo "\n "; if ($trace['function']) { echo $this->escape('at '.$trace['class'].$trace['type'].$trace['function']).'('.(isset($trace['args']) ? $this->formatArgsAsText($trace['args']) : '').')'; } if ($trace['file'] && $trace['line']) { echo($trace['function'] ? "\n (" : 'at ').strtr(strip_tags($this->formatFile($trace['file'], $trace['line'])), [' at line '.$trace['line'] => '']).':'.$trace['line'].($trace['function'] ? ')' : ''); } } ?> </pre> <?php } ?> </td> </tr> </tbody> </table> PK2A#]��r11Dvendor/symfony/error-handler/Resources/views/exception_full.html.phpnu�[���<!-- <?= $_message = sprintf('%s (%d %s)', $exceptionMessage, $statusCode, $statusText); ?> --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= $this->charset; ?>" /> <meta name="robots" content="noindex,nofollow" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title><?= $_message; ?></title> <link rel="icon" type="image/png" href="<?= $this->include('assets/images/favicon.png.base64'); ?>"> <style><?= $this->include('assets/css/exception.css'); ?></style> <style><?= $this->include('assets/css/exception_full.css'); ?></style> </head> <body> <script> document.body.classList.add( localStorage.getItem('symfony/profiler/theme') || (matchMedia('(prefers-color-scheme: dark)').matches ? 'theme-dark' : 'theme-light') ); </script> <?php if (class_exists(\Symfony\Component\HttpKernel\Kernel::class)) { ?> <header> <div class="container"> <h1 class="logo"><?= $this->include('assets/images/symfony-logo.svg'); ?> Symfony Exception</h1> <div class="help-link"> <a href="https://symfony.com/doc/<?= Symfony\Component\HttpKernel\Kernel::VERSION; ?>/index.html"> <span class="icon"><?= $this->include('assets/images/icon-book.svg'); ?></span> <span class="hidden-xs-down">Symfony</span> Docs </a> </div> </div> </header> <?php } ?> <?= $this->include('views/exception.html.php', $context); ?> <script> <?= $this->include('assets/js/exception.js'); ?> </script> </body> </html> <!-- <?= $_message; ?> --> PK2A#]�aľ�?vendor/symfony/error-handler/Resources/views/exception.html.phpnu�[���<div class="exception-summary <?= !$exceptionMessage ? 'exception-without-message' : ''; ?>"> <div class="exception-metadata"> <div class="container"> <h2 class="exception-hierarchy"> <?php foreach (array_reverse($exception->getAllPrevious(), true) as $index => $previousException) { ?> <a href="#trace-box-<?= $index + 2; ?>"><?= $this->abbrClass($previousException->getClass()); ?></a> <span class="icon"><?= $this->include('assets/images/chevron-right.svg'); ?></span> <?php } ?> <a href="#trace-box-1"><?= $this->abbrClass($exception->getClass()); ?></a> </h2> <h2 class="exception-http"> HTTP <?= $statusCode; ?> <small><?= $statusText; ?></small> </h2> </div> </div> <div class="exception-message-wrapper"> <div class="container"> <h1 class="break-long-words exception-message<?= mb_strlen($exceptionMessage) > 180 ? ' long' : ''; ?>"><?= $this->formatFileFromText(nl2br($exceptionMessage)); ?></h1> <div class="exception-illustration hidden-xs-down"> <?= $this->include('assets/images/symfony-ghost.svg.php'); ?> </div> </div> </div> </div> <div class="container"> <div class="sf-tabs"> <div class="tab"> <?php $exceptionAsArray = $exception->toArray(); $exceptionWithUserCode = []; $exceptionAsArrayCount = count($exceptionAsArray); $last = $exceptionAsArrayCount - 1; foreach ($exceptionAsArray as $i => $e) { foreach ($e['trace'] as $trace) { if ($trace['file'] && false === mb_strpos($trace['file'], '/vendor/') && false === mb_strpos($trace['file'], '/var/cache/') && $i < $last) { $exceptionWithUserCode[] = $i; } } } ?> <h3 class="tab-title"> <?php if ($exceptionAsArrayCount > 1) { ?> Exceptions <span class="badge"><?= $exceptionAsArrayCount; ?></span> <?php } else { ?> Exception <?php } ?> </h3> <div class="tab-content"> <?php foreach ($exceptionAsArray as $i => $e) { echo $this->include('views/traces.html.php', [ 'exception' => $e, 'index' => $i + 1, 'expand' => in_array($i, $exceptionWithUserCode, true) || ([] === $exceptionWithUserCode && 0 === $i), ]); } ?> </div> </div> <?php if ($logger) { ?> <div class="tab <?= !$logger->getLogs() ? 'disabled' : ''; ?>"> <h3 class="tab-title"> Logs <?php if ($logger->countErrors()) { ?><span class="badge status-error"><?= $logger->countErrors(); ?></span><?php } ?> </h3> <div class="tab-content"> <?php if ($logger->getLogs()) { ?> <?= $this->include('views/logs.html.php', ['logs' => $logger->getLogs()]); ?> <?php } else { ?> <div class="empty"> <p>No log messages</p> </div> <?php } ?> </div> </div> <?php } ?> <div class="tab"> <h3 class="tab-title"> <?php if ($exceptionAsArrayCount > 1) { ?> Stack Traces <span class="badge"><?= $exceptionAsArrayCount; ?></span> <?php } else { ?> Stack Trace <?php } ?> </h3> <div class="tab-content"> <?php foreach ($exceptionAsArray as $i => $e) { echo $this->include('views/traces_text.html.php', [ 'exception' => $e, 'index' => $i + 1, 'numExceptions' => $exceptionAsArrayCount, ]); } ?> </div> </div> <?php if ($currentContent) { ?> <div class="tab"> <h3 class="tab-title">Output content</h3> <div class="tab-content"> <?= $currentContent; ?> </div> </div> <?php } ?> </div> </div> PK2A#]*B��;vendor/symfony/error-handler/Resources/views/error.html.phpnu�[���<!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= $this->charset; ?>" /> <meta name="robots" content="noindex,nofollow,noarchive" /> <title>An Error Occurred: <?= $statusText; ?></title> <style><?= $this->include('assets/css/error.css'); ?></style> </head> <body> <div class="container"> <h1>Oops! An Error Occurred</h1> <h2>The server returned a "<?= $statusCode; ?> <?= $statusText; ?>".</h2> <p> Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused. </p> </div> </body> </html> PK2A#]:(Ɉ � <vendor/symfony/error-handler/Resources/views/traces.html.phpnu�[���<div class="trace trace-as-html" id="trace-box-<?= $index; ?>"> <div class="trace-details"> <div class="trace-head"> <div class="sf-toggle" data-toggle-selector="#trace-html-<?= $index; ?>" data-toggle-initial="<?= $expand ? 'display' : ''; ?>"> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square-o.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square-o.svg'); ?></span> <?php $separator = strrpos($exception['class'], '\\'); $separator = false === $separator ? 0 : $separator + 1; $namespace = substr($exception['class'], 0, $separator); $class = substr($exception['class'], $separator); ?> <?php if ('' === $class) { ?> <br> <?php } else { ?> <h3 class="trace-class"> <?php if ('' !== $namespace) { ?> <span class="trace-namespace"><?= $namespace; ?></span> <?php } ?> <?= $class; ?> </h3> <?php } ?> <?php if ($exception['message'] && $index > 1) { ?> <p class="break-long-words trace-message"><?= $this->escape($exception['message']); ?></p> <?php } ?> </div> </div> <div id="trace-html-<?= $index; ?>" class="sf-toggle-content"> <?php $isFirstUserCode = true; foreach ($exception['trace'] as $i => $trace) { $isVendorTrace = $trace['file'] && (false !== mb_strpos($trace['file'], '/vendor/') || false !== mb_strpos($trace['file'], '/var/cache/')); $displayCodeSnippet = $isFirstUserCode && !$isVendorTrace; if ($displayCodeSnippet) { $isFirstUserCode = false; } ?> <div class="trace-line <?= $isVendorTrace ? 'trace-from-vendor' : ''; ?>"> <?= $this->include('views/trace.html.php', [ 'prefix' => $index, 'i' => $i, 'trace' => $trace, 'style' => $isVendorTrace ? 'compact' : ($displayCodeSnippet ? 'expanded' : ''), ]); ?> </div> <?php } ?> </div> </div> </div> PK2A#]�Fˤ ;vendor/symfony/error-handler/Resources/views/trace.html.phpnu�[���<div class="trace-line-header break-long-words <?= $trace['file'] ? 'sf-toggle' : ''; ?>" data-toggle-selector="#trace-html-<?= $prefix; ?>-<?= $i; ?>" data-toggle-initial="<?= 'expanded' === $style ? 'display' : ''; ?>"> <?php if ($trace['file']) { ?> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square.svg'); ?></span> <?php } ?> <?php if ('compact' !== $style && $trace['function']) { ?> <span class="trace-class"><?= $this->abbrClass($trace['class']); ?></span><?php if ($trace['type']) { ?><span class="trace-type"><?= $trace['type']; ?></span><?php } ?><span class="trace-method"><?= $trace['function']; ?></span><?php if (isset($trace['args'])) { ?><span class="trace-arguments">(<?= $this->formatArgs($trace['args']); ?>)</span><?php } ?> <?php } ?> <?php if ($trace['file']) { ?> <?php $lineNumber = $trace['line'] ?: 1; $fileLink = $this->getFileLink($trace['file'], $lineNumber); $filePath = strtr(strip_tags($this->formatFile($trace['file'], $lineNumber)), [' at line '.$lineNumber => '']); $filePathParts = explode(\DIRECTORY_SEPARATOR, $filePath); ?> <span class="block trace-file-path"> in <a href="<?= $fileLink; ?>"> <?= implode(\DIRECTORY_SEPARATOR, array_slice($filePathParts, 0, -1)).\DIRECTORY_SEPARATOR; ?><strong><?= end($filePathParts); ?></strong> </a> <?php if ('compact' === $style && $trace['function']) { ?> <span class="trace-type"><?= $trace['type']; ?></span> <span class="trace-method"><?= $trace['function']; ?></span> <?php } ?> (line <?= $lineNumber; ?>) <span class="icon icon-copy hidden" data-clipboard-text="<?php echo implode(\DIRECTORY_SEPARATOR, $filePathParts).':'.$lineNumber; ?>"> <?php echo $this->include('assets/images/icon-copy.svg'); ?> </span> </span> <?php } ?> </div> <?php if ($trace['file']) { ?> <div id="trace-html-<?= $prefix.'-'.$i; ?>" class="trace-code sf-toggle-content"> <?= strtr($this->fileExcerpt($trace['file'], $trace['line'], 5), [ '#DD0000' => 'var(--highlight-string)', '#007700' => 'var(--highlight-keyword)', '#0000BB' => 'var(--highlight-default)', '#FF8000' => 'var(--highlight-comment)', ]); ?> </div> <?php } ?> PK2A#]�%�0��Kvendor/symfony/error-handler/ErrorEnhancer/UndefinedMethodErrorEnhancer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\UndefinedMethodError; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class UndefinedMethodErrorEnhancer implements ErrorEnhancerInterface { /** * {@inheritdoc} */ public function enhance(\Throwable $error): ?\Throwable { if ($error instanceof FatalError) { return null; } $message = $error->getMessage(); preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $message, $matches); if (!$matches) { return null; } $className = $matches[1]; $methodName = $matches[2]; $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); if ('' === $methodName || !class_exists($className) || null === $methods = get_class_methods($className)) { // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class) return new UndefinedMethodError($message, $error); } $candidates = []; foreach ($methods as $definedMethodName) { $lev = levenshtein($methodName, $definedMethodName); if ($lev <= \strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { $candidates[] = $definedMethodName; } } if ($candidates) { sort($candidates); $last = array_pop($candidates).'"?'; if ($candidates) { $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; } else { $candidates = '"'.$last; } $message .= "\nDid you mean to call ".$candidates; } return new UndefinedMethodError($message, $error); } } PK2A#]����rrIvendor/symfony/error-handler/ErrorEnhancer/ClassNotFoundErrorEnhancer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Composer\Autoload\ClassLoader; use Symfony\Component\ErrorHandler\DebugClassLoader; use Symfony\Component\ErrorHandler\Error\ClassNotFoundError; use Symfony\Component\ErrorHandler\Error\FatalError; /** * @author Fabien Potencier <fabien@symfony.com> */ class ClassNotFoundErrorEnhancer implements ErrorEnhancerInterface { /** * {@inheritdoc} */ public function enhance(\Throwable $error): ?\Throwable { // Some specific versions of PHP produce a fatal error when extending a not found class. $message = !$error instanceof FatalError ? $error->getMessage() : $error->getError()['message']; if (!preg_match('/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/', $message, $matches)) { return null; } $typeName = strtolower($matches[1]); $fullyQualifiedClassName = $matches[2]; if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); $tail = ' for another namespace?'; } else { $className = $fullyQualifiedClassName; $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); $tail = '?'; } if ($candidates = $this->getClassCandidates($className)) { $tail = array_pop($candidates).'"?'; if ($candidates) { $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; } else { $tail = ' for "'.$tail; } } $message .= "\nDid you forget a \"use\" statement".$tail; return new ClassNotFoundError($message, $error); } /** * Tries to guess the full namespace for a given class name. * * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer * autoloader (that should cover all common cases). * * @param string $class A class name (without its namespace) * * Returns an array of possible fully qualified class names */ private function getClassCandidates(string $class): array { if (!\is_array($functions = spl_autoload_functions())) { return []; } // find Symfony and Composer autoloaders $classes = []; foreach ($functions as $function) { if (!\is_array($function)) { continue; } // get class loaders wrapped by DebugClassLoader if ($function[0] instanceof DebugClassLoader) { $function = $function[0]->getClassLoader(); if (!\is_array($function)) { continue; } } if ($function[0] instanceof ClassLoader) { foreach ($function[0]->getPrefixes() as $prefix => $paths) { foreach ($paths as $path) { $classes[] = $this->findClassInPath($path, $class, $prefix); } } foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { foreach ($paths as $path) { $classes[] = $this->findClassInPath($path, $class, $prefix); } } } } return array_unique(array_merge([], ...$classes)); } private function findClassInPath(string $path, string $class, string $prefix): array { if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { return []; } $classes = []; $filename = $class.'.php'; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { $classes[] = $class; } } return $classes; } private function convertFileToClass(string $path, string $file, string $prefix): ?string { $candidates = [ // namespaced class $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file), // namespaced class (with target dir) $prefix.$namespacedClass, // namespaced class (with target dir and separator) $prefix.'\\'.$namespacedClass, // PEAR class str_replace('\\', '_', $namespacedClass), // PEAR class (with target dir) str_replace('\\', '_', $prefix.$namespacedClass), // PEAR class (with target dir and separator) str_replace('\\', '_', $prefix.'\\'.$namespacedClass), ]; if ($prefix) { $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); }); } // We cannot use the autoloader here as most of them use require; but if the class // is not found, the new autoloader call will require the file again leading to a // "cannot redeclare class" error. foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } try { require_once $file; } catch (\Throwable $e) { return null; } foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } return null; } private function classExists(string $class): bool { return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); } } PK2A#]3��^��Evendor/symfony/error-handler/ErrorEnhancer/ErrorEnhancerInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; interface ErrorEnhancerInterface { /** * Returns an \Throwable instance if the class is able to improve the error, null otherwise. */ public function enhance(\Throwable $error): ?\Throwable; } PK2A#]�&VMvendor/symfony/error-handler/ErrorEnhancer/UndefinedFunctionErrorEnhancer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError; /** * @author Fabien Potencier <fabien@symfony.com> */ class UndefinedFunctionErrorEnhancer implements ErrorEnhancerInterface { /** * {@inheritdoc} */ public function enhance(\Throwable $error): ?\Throwable { if ($error instanceof FatalError) { return null; } $message = $error->getMessage(); $messageLen = \strlen($message); $notFoundSuffix = '()'; $notFoundSuffixLen = \strlen($notFoundSuffix); if ($notFoundSuffixLen > $messageLen) { return null; } if (0 !== substr_compare($message, $notFoundSuffix, -$notFoundSuffixLen)) { return null; } $prefix = 'Call to undefined function '; $prefixLen = \strlen($prefix); if (0 !== strpos($message, $prefix)) { return null; } $fullyQualifiedFunctionName = substr($message, $prefixLen, -$notFoundSuffixLen); if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); } else { $functionName = $fullyQualifiedFunctionName; $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); } $candidates = []; foreach (get_defined_functions() as $type => $definedFunctionNames) { foreach ($definedFunctionNames as $definedFunctionName) { if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); } else { $definedFunctionNameBasename = $definedFunctionName; } if ($definedFunctionNameBasename === $functionName) { $candidates[] = '\\'.$definedFunctionName; } } } if ($candidates) { sort($candidates); $last = array_pop($candidates).'"?'; if ($candidates) { $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; } else { $candidates = '"'.$last; } $message .= "\nDid you mean to call ".$candidates; } return new UndefinedFunctionError($message, $error); } } PK2A#]�*��t�t-vendor/symfony/error-handler/ErrorHandler.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\OutOfMemoryError; use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorEnhancer\ErrorEnhancerInterface; use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorRenderer\CliErrorRenderer; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; /** * A generic ErrorHandler for the PHP engine. * * Provides five bit fields that control how errors are handled: * - thrownErrors: errors thrown as \ErrorException * - loggedErrors: logged errors, when not @-silenced * - scopedErrors: errors thrown or logged with their local context * - tracedErrors: errors logged with their stack trace * - screamedErrors: never @-silenced errors * * Each error level can be logged by a dedicated PSR-3 logger object. * Screaming only applies to logging. * Throwing takes precedence over logging. * Uncaught exceptions are logged as E_ERROR. * E_DEPRECATED and E_USER_DEPRECATED levels never throw. * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. * As errors have a performance cost, repeated errors are all logged, so that the developer * can see them and weight them as more important to fix than others of the same level. * * @author Nicolas Grekas <p@tchwork.com> * @author Grégoire Pineau <lyrixx@lyrixx.info> * * @final */ class ErrorHandler { private $levels = [ \E_DEPRECATED => 'Deprecated', \E_USER_DEPRECATED => 'User Deprecated', \E_NOTICE => 'Notice', \E_USER_NOTICE => 'User Notice', \E_STRICT => 'Runtime Notice', \E_WARNING => 'Warning', \E_USER_WARNING => 'User Warning', \E_COMPILE_WARNING => 'Compile Warning', \E_CORE_WARNING => 'Core Warning', \E_USER_ERROR => 'User Error', \E_RECOVERABLE_ERROR => 'Catchable Fatal Error', \E_COMPILE_ERROR => 'Compile Error', \E_PARSE => 'Parse Error', \E_ERROR => 'Error', \E_CORE_ERROR => 'Core Error', ]; private $loggers = [ \E_DEPRECATED => [null, LogLevel::INFO], \E_USER_DEPRECATED => [null, LogLevel::INFO], \E_NOTICE => [null, LogLevel::WARNING], \E_USER_NOTICE => [null, LogLevel::WARNING], \E_STRICT => [null, LogLevel::WARNING], \E_WARNING => [null, LogLevel::WARNING], \E_USER_WARNING => [null, LogLevel::WARNING], \E_COMPILE_WARNING => [null, LogLevel::WARNING], \E_CORE_WARNING => [null, LogLevel::WARNING], \E_USER_ERROR => [null, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], \E_PARSE => [null, LogLevel::CRITICAL], \E_ERROR => [null, LogLevel::CRITICAL], \E_CORE_ERROR => [null, LogLevel::CRITICAL], ]; private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE private $loggedErrors = 0; private $configureException; private $debug; private $isRecursive = 0; private $isRoot = false; private $exceptionHandler; private $bootstrappingLogger; private static $reservedMemory; private static $toStringException; private static $silencedErrorCache = []; private static $silencedErrorCount = 0; private static $exitCode = 0; /** * Registers the error handler. */ public static function register(self $handler = null, bool $replace = true): self { if (null === self::$reservedMemory) { self::$reservedMemory = str_repeat('x', 32768); register_shutdown_function(__CLASS__.'::handleFatalError'); } if ($handlerIsNew = null === $handler) { $handler = new static(); } if (null === $prev = set_error_handler([$handler, 'handleError'])) { restore_error_handler(); // Specifying the error types earlier would expose us to https://bugs.php.net/63206 set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors); $handler->isRoot = true; } if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) { $handler = $prev[0]; $replace = false; } if (!$replace && $prev) { restore_error_handler(); $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; } else { $handlerIsRegistered = true; } if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) { restore_exception_handler(); if (!$handlerIsRegistered) { $handler = $prev[0]; } elseif ($handler !== $prev[0] && $replace) { set_exception_handler([$handler, 'handleException']); $p = $prev[0]->setExceptionHandler(null); $handler->setExceptionHandler($p); $prev[0]->setExceptionHandler($p); } } else { $handler->setExceptionHandler($prev ?? [$handler, 'renderException']); } $handler->throwAt(\E_ALL & $handler->thrownErrors, true); return $handler; } /** * Calls a function and turns any PHP error into \ErrorException. * * @return mixed What $function(...$arguments) returns * * @throws \ErrorException When $function(...$arguments) triggers a PHP error */ public static function call(callable $function, ...$arguments) { set_error_handler(static function (int $type, string $message, string $file, int $line) { if (__FILE__ === $file) { $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3); $file = $trace[2]['file'] ?? $file; $line = $trace[2]['line'] ?? $line; } throw new \ErrorException($message, 0, $type, $file, $line); }); try { return $function(...$arguments); } finally { restore_error_handler(); } } public function __construct(BufferingLogger $bootstrappingLogger = null, bool $debug = false) { if ($bootstrappingLogger) { $this->bootstrappingLogger = $bootstrappingLogger; $this->setDefaultLogger($bootstrappingLogger); } $traceReflector = new \ReflectionProperty(\Exception::class, 'trace'); $traceReflector->setAccessible(true); $this->configureException = \Closure::bind(static function ($e, $trace, $file = null, $line = null) use ($traceReflector) { $traceReflector->setValue($e, $trace); $e->file = $file ?? $e->file; $e->line = $line ?? $e->line; }, null, new class() extends \Exception { }); $this->debug = $debug; } /** * Sets a logger to non assigned errors levels. * * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels * @param array|int|null $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants * @param bool $replace Whether to replace or not any existing logger */ public function setDefaultLogger(LoggerInterface $logger, $levels = \E_ALL, bool $replace = false): void { $loggers = []; if (\is_array($levels)) { foreach ($levels as $type => $logLevel) { if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { $loggers[$type] = [$logger, $logLevel]; } } } else { if (null === $levels) { $levels = \E_ALL; } foreach ($this->loggers as $type => $log) { if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { $log[0] = $logger; $loggers[$type] = $log; } } } $this->setLoggers($loggers); } /** * Sets a logger for each error level. * * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map * * @return array The previous map * * @throws \InvalidArgumentException */ public function setLoggers(array $loggers): array { $prevLogged = $this->loggedErrors; $prev = $this->loggers; $flush = []; foreach ($loggers as $type => $log) { if (!isset($prev[$type])) { throw new \InvalidArgumentException('Unknown error type: '.$type); } if (!\is_array($log)) { $log = [$log]; } elseif (!\array_key_exists(0, $log)) { throw new \InvalidArgumentException('No logger provided.'); } if (null === $log[0]) { $this->loggedErrors &= ~$type; } elseif ($log[0] instanceof LoggerInterface) { $this->loggedErrors |= $type; } else { throw new \InvalidArgumentException('Invalid logger provided.'); } $this->loggers[$type] = $log + $prev[$type]; if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { $flush[$type] = $type; } } $this->reRegister($prevLogged | $this->thrownErrors); if ($flush) { foreach ($this->bootstrappingLogger->cleanLogs() as $log) { $type = ThrowableUtils::getSeverity($log[2]['exception']); if (!isset($flush[$type])) { $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); } elseif ($this->loggers[$type][0]) { $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); } } } return $prev; } /** * Sets a user exception handler. * * @param callable(\Throwable $e)|null $handler * * @return callable|null The previous exception handler */ public function setExceptionHandler(?callable $handler): ?callable { $prev = $this->exceptionHandler; $this->exceptionHandler = $handler; return $prev; } /** * Sets the PHP error levels that throw an exception when a PHP error occurs. * * @param int $levels A bit field of E_* constants for thrown errors * @param bool $replace Replace or amend the previous value * * @return int The previous value */ public function throwAt(int $levels, bool $replace = false): int { $prev = $this->thrownErrors; $this->thrownErrors = ($levels | \E_RECOVERABLE_ERROR | \E_USER_ERROR) & ~\E_USER_DEPRECATED & ~\E_DEPRECATED; if (!$replace) { $this->thrownErrors |= $prev; } $this->reRegister($prev | $this->loggedErrors); return $prev; } /** * Sets the PHP error levels for which local variables are preserved. * * @param int $levels A bit field of E_* constants for scoped errors * @param bool $replace Replace or amend the previous value * * @return int The previous value */ public function scopeAt(int $levels, bool $replace = false): int { $prev = $this->scopedErrors; $this->scopedErrors = $levels; if (!$replace) { $this->scopedErrors |= $prev; } return $prev; } /** * Sets the PHP error levels for which the stack trace is preserved. * * @param int $levels A bit field of E_* constants for traced errors * @param bool $replace Replace or amend the previous value * * @return int The previous value */ public function traceAt(int $levels, bool $replace = false): int { $prev = $this->tracedErrors; $this->tracedErrors = $levels; if (!$replace) { $this->tracedErrors |= $prev; } return $prev; } /** * Sets the error levels where the @-operator is ignored. * * @param int $levels A bit field of E_* constants for screamed errors * @param bool $replace Replace or amend the previous value * * @return int The previous value */ public function screamAt(int $levels, bool $replace = false): int { $prev = $this->screamedErrors; $this->screamedErrors = $levels; if (!$replace) { $this->screamedErrors |= $prev; } return $prev; } /** * Re-registers as a PHP error handler if levels changed. */ private function reRegister(int $prev): void { if ($prev !== ($this->thrownErrors | $this->loggedErrors)) { $handler = set_error_handler('is_int'); $handler = \is_array($handler) ? $handler[0] : null; restore_error_handler(); if ($handler === $this) { restore_error_handler(); if ($this->isRoot) { set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors); } else { set_error_handler([$this, 'handleError']); } } } } /** * Handles errors by filtering then logging them according to the configured bit fields. * * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself * * @throws \ErrorException When $this->thrownErrors requests so * * @internal */ public function handleError(int $type, string $message, string $file, int $line): bool { if (\PHP_VERSION_ID >= 70300 && \E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) { $type = \E_DEPRECATED; } // Level is the current error reporting level to manage silent error. $level = error_reporting(); $silenced = 0 === ($level & $type); // Strong errors are not authorized to be silenced. $level |= \E_RECOVERABLE_ERROR | \E_USER_ERROR | \E_DEPRECATED | \E_USER_DEPRECATED; $log = $this->loggedErrors & $type; $throw = $this->thrownErrors & $type & $level; $type &= $level | $this->screamedErrors; // Never throw on warnings triggered by assert() if (\E_WARNING === $type && 'a' === $message[0] && 0 === strncmp($message, 'assert(): ', 10)) { $throw = 0; } if (!$type || (!$log && !$throw)) { return false; } $logMessage = $this->levels[$type].': '.$message; if (null !== self::$toStringException) { $errorAsException = self::$toStringException; self::$toStringException = null; } elseif (!$throw && !($type & $level)) { if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : []; $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace); } elseif (isset(self::$silencedErrorCache[$id][$message])) { $lightTrace = null; $errorAsException = self::$silencedErrorCache[$id][$message]; ++$errorAsException->count; } else { $lightTrace = []; $errorAsException = null; } if (100 < ++self::$silencedErrorCount) { self::$silencedErrorCache = $lightTrace = []; self::$silencedErrorCount = 1; } if ($errorAsException) { self::$silencedErrorCache[$id][$message] = $errorAsException; } if (null === $lightTrace) { return true; } } else { if (false !== strpos($message, '@anonymous')) { $backtrace = debug_backtrace(false, 5); for ($i = 1; isset($backtrace[$i]); ++$i) { if (isset($backtrace[$i]['function'], $backtrace[$i]['args'][0]) && ('trigger_error' === $backtrace[$i]['function'] || 'user_error' === $backtrace[$i]['function']) ) { if ($backtrace[$i]['args'][0] !== $message) { $message = $this->parseAnonymousClass($backtrace[$i]['args'][0]); $logMessage = $this->levels[$type].': '.$message; } break; } } } $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); if ($throw || $this->tracedErrors & $type) { $backtrace = $errorAsException->getTrace(); $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); ($this->configureException)($errorAsException, $lightTrace, $file, $line); } else { ($this->configureException)($errorAsException, []); $backtrace = []; } } if ($throw) { if (\PHP_VERSION_ID < 70400 && \E_USER_ERROR & $type) { for ($i = 1; isset($backtrace[$i]); ++$i) { if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) && '__toString' === $backtrace[$i]['function'] && '->' === $backtrace[$i]['type'] && !isset($backtrace[$i - 1]['class']) && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) ) { // Here, we know trigger_error() has been called from __toString(). // PHP triggers a fatal error when throwing from __toString(). // A small convention allows working around the limitation: // given a caught $e exception in __toString(), quitting the method with // `return trigger_error($e, E_USER_ERROR);` allows this error handler // to make $e get through the __toString() barrier. $context = 4 < \func_num_args() ? (func_get_arg(4) ?: []) : []; foreach ($context as $e) { if ($e instanceof \Throwable && $e->__toString() === $message) { self::$toStringException = $e; return true; } } // Display the original error message instead of the default one. $this->handleException($errorAsException); // Stop the process by giving back the error to the native handler. return false; } } } throw $errorAsException; } if ($this->isRecursive) { $log = 0; } else { if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) { $currentErrorHandler = set_error_handler('is_int'); restore_error_handler(); } try { $this->isRecursive = true; $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []); } finally { $this->isRecursive = false; if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) { set_error_handler($currentErrorHandler); } } } return !$silenced && $type && $log; } /** * Handles an exception by logging then forwarding it to another handler. * * @internal */ public function handleException(\Throwable $exception) { $handlerException = null; if (!$exception instanceof FatalError) { self::$exitCode = 255; $type = ThrowableUtils::getSeverity($exception); } else { $type = $exception->getError()['type']; } if ($this->loggedErrors & $type) { if (false !== strpos($message = $exception->getMessage(), "@anonymous\0")) { $message = $this->parseAnonymousClass($message); } if ($exception instanceof FatalError) { $message = 'Fatal '.$message; } elseif ($exception instanceof \Error) { $message = 'Uncaught Error: '.$message; } elseif ($exception instanceof \ErrorException) { $message = 'Uncaught '.$message; } else { $message = 'Uncaught Exception: '.$message; } try { $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]); } catch (\Throwable $handlerException) { } } if (!$exception instanceof OutOfMemoryError) { foreach ($this->getErrorEnhancers() as $errorEnhancer) { if ($e = $errorEnhancer->enhance($exception)) { $exception = $e; break; } } } $exceptionHandler = $this->exceptionHandler; $this->exceptionHandler = [$this, 'renderException']; if (null === $exceptionHandler || $exceptionHandler === $this->exceptionHandler) { $this->exceptionHandler = null; } try { if (null !== $exceptionHandler) { return $exceptionHandler($exception); } $handlerException = $handlerException ?: $exception; } catch (\Throwable $handlerException) { } if ($exception === $handlerException && null === $this->exceptionHandler) { self::$reservedMemory = null; // Disable the fatal error handler throw $exception; // Give back $exception to the native handler } $loggedErrors = $this->loggedErrors; if ($exception === $handlerException) { $this->loggedErrors &= ~$type; } try { $this->handleException($handlerException); } finally { $this->loggedErrors = $loggedErrors; } } /** * Shutdown registered function for handling PHP fatal errors. * * @param array|null $error An array as returned by error_get_last() * * @internal */ public static function handleFatalError(array $error = null): void { if (null === self::$reservedMemory) { return; } $handler = self::$reservedMemory = null; $handlers = []; $previousHandler = null; $sameHandlerLimit = 10; while (!\is_array($handler) || !$handler[0] instanceof self) { $handler = set_exception_handler('is_int'); restore_exception_handler(); if (!$handler) { break; } restore_exception_handler(); if ($handler !== $previousHandler) { array_unshift($handlers, $handler); $previousHandler = $handler; } elseif (0 === --$sameHandlerLimit) { $handler = null; break; } } foreach ($handlers as $h) { set_exception_handler($h); } if (!$handler) { return; } if ($handler !== $h) { $handler[0]->setExceptionHandler($h); } $handler = $handler[0]; $handlers = []; if ($exit = null === $error) { $error = error_get_last(); } if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) { // Let's not throw anymore but keep logging $handler->throwAt(0, true); $trace = $error['backtrace'] ?? null; if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { $fatalError = new OutOfMemoryError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, false, $trace); } else { $fatalError = new FatalError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, true, $trace); } } else { $fatalError = null; } try { if (null !== $fatalError) { self::$exitCode = 255; $handler->handleException($fatalError); } } catch (FatalError $e) { // Ignore this re-throw } if ($exit && self::$exitCode) { $exitCode = self::$exitCode; register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); } } /** * Renders the given exception. * * As this method is mainly called during boot where nothing is yet available, * the output is always either HTML or CLI depending where PHP runs. */ private function renderException(\Throwable $exception): void { $renderer = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliErrorRenderer() : new HtmlErrorRenderer($this->debug); $exception = $renderer->render($exception); if (!headers_sent()) { http_response_code($exception->getStatusCode()); foreach ($exception->getHeaders() as $name => $value) { header($name.': '.$value, false); } } echo $exception->getAsString(); } /** * Override this method if you want to define more error enhancers. * * @return ErrorEnhancerInterface[] */ protected function getErrorEnhancers(): iterable { return [ new UndefinedFunctionErrorEnhancer(), new UndefinedMethodErrorEnhancer(), new ClassNotFoundErrorEnhancer(), ]; } /** * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader. */ private function cleanTrace(array $backtrace, int $type, string &$file, int &$line, bool $throw): array { $lightTrace = $backtrace; for ($i = 0; isset($backtrace[$i]); ++$i) { if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { $lightTrace = \array_slice($lightTrace, 1 + $i); break; } } if (\E_USER_DEPRECATED === $type) { for ($i = 0; isset($lightTrace[$i]); ++$i) { if (!isset($lightTrace[$i]['file'], $lightTrace[$i]['line'], $lightTrace[$i]['function'])) { continue; } if (!isset($lightTrace[$i]['class']) && 'trigger_deprecation' === $lightTrace[$i]['function']) { $file = $lightTrace[$i]['file']; $line = $lightTrace[$i]['line']; $lightTrace = \array_slice($lightTrace, 1 + $i); break; } } } if (class_exists(DebugClassLoader::class, false)) { for ($i = \count($lightTrace) - 2; 0 < $i; --$i) { if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) { array_splice($lightTrace, --$i, 2); } } } if (!($throw || $this->scopedErrors & $type)) { for ($i = 0; isset($lightTrace[$i]); ++$i) { unset($lightTrace[$i]['args'], $lightTrace[$i]['object']); } } return $lightTrace; } /** * Parse the error message by removing the anonymous class notation * and using the parent class instead if possible. */ private function parseAnonymousClass(string $message): string { return preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', static function ($m) { return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; }, $message); } } PK2A#]�� (=�=�1vendor/symfony/error-handler/DebugClassLoader.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Composer\InstalledVersions; use Doctrine\Common\Persistence\Proxy as LegacyProxy; use Doctrine\Persistence\Proxy; use Mockery\MockInterface; use Phake\IMock; use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation; use PHPUnit\Framework\MockObject\MockObject; use Prophecy\Prophecy\ProphecySubjectInterface; use ProxyManager\Proxy\ProxyInterface; use Symfony\Component\ErrorHandler\Internal\TentativeTypes; use Symfony\Component\HttpClient\HttplugClient; /** * Autoloader checking if the class is really defined in the file found. * * The ClassLoader will wrap all registered autoloaders * and will throw an exception if a file is found but does * not declare the class. * * It can also patch classes to turn docblocks into actual return types. * This behavior is controlled by the SYMFONY_PATCH_TYPE_DECLARATIONS env var, * which is a url-encoded array with the follow parameters: * - "force": any value enables deprecation notices - can be any of: * - "phpdoc" to patch only docblock annotations * - "2" to add all possible return types * - "1" to add return types but only to tests/final/internal/private methods * - "php": the target version of PHP - e.g. "7.1" doesn't generate "object" types * - "deprecations": "1" to trigger a deprecation notice when a child class misses a * return type while the parent declares an "@return" annotation * * Note that patching doesn't care about any coding style so you'd better to run * php-cs-fixer after, with rules "phpdoc_trim_consecutive_blank_line_separation" * and "no_superfluous_phpdoc_tags" enabled typically. * * @author Fabien Potencier <fabien@symfony.com> * @author Christophe Coevoet <stof@notk.org> * @author Nicolas Grekas <p@tchwork.com> * @author Guilhem Niot <guilhem.niot@gmail.com> */ class DebugClassLoader { private const SPECIAL_RETURN_TYPES = [ 'void' => 'void', 'null' => 'null', 'resource' => 'resource', 'boolean' => 'bool', 'true' => 'true', 'false' => 'false', 'integer' => 'int', 'array' => 'array', 'bool' => 'bool', 'callable' => 'callable', 'float' => 'float', 'int' => 'int', 'iterable' => 'iterable', 'object' => 'object', 'string' => 'string', 'self' => 'self', 'parent' => 'parent', 'mixed' => 'mixed', 'static' => 'static', '$this' => 'static', 'list' => 'array', 'class-string' => 'string', 'never' => 'never', ]; private const BUILTIN_RETURN_TYPES = [ 'void' => true, 'array' => true, 'false' => true, 'bool' => true, 'callable' => true, 'float' => true, 'int' => true, 'iterable' => true, 'object' => true, 'string' => true, 'self' => true, 'parent' => true, 'mixed' => true, 'static' => true, 'null' => true, 'true' => true, 'never' => true, ]; private const MAGIC_METHODS = [ '__isset' => 'bool', '__sleep' => 'array', '__toString' => 'string', '__debugInfo' => 'array', '__serialize' => 'array', ]; private $classLoader; private $isFinder; private $loaded = []; private $patchTypes; private static $caseCheck; private static $checkedClasses = []; private static $final = []; private static $finalMethods = []; private static $deprecated = []; private static $internal = []; private static $internalMethods = []; private static $annotatedParameters = []; private static $darwinCache = ['/' => ['/', []]]; private static $method = []; private static $returnTypes = []; private static $methodTraits = []; private static $fileOffsets = []; public function __construct(callable $classLoader) { $this->classLoader = $classLoader; $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile'); parse_str(getenv('SYMFONY_PATCH_TYPE_DECLARATIONS') ?: '', $this->patchTypes); $this->patchTypes += [ 'force' => null, 'php' => \PHP_MAJOR_VERSION.'.'.\PHP_MINOR_VERSION, 'deprecations' => \PHP_VERSION_ID >= 70400, ]; if ('phpdoc' === $this->patchTypes['force']) { $this->patchTypes['force'] = 'docblock'; } if (!isset(self::$caseCheck)) { $file = is_file(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR); $i = strrpos($file, \DIRECTORY_SEPARATOR); $dir = substr($file, 0, 1 + $i); $file = substr($file, 1 + $i); $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); $test = realpath($dir.$test); if (false === $test || false === $i) { // filesystem is case sensitive self::$caseCheck = 0; } elseif (substr($test, -\strlen($file)) === $file) { // filesystem is case insensitive and realpath() normalizes the case of characters self::$caseCheck = 1; } elseif ('Darwin' === \PHP_OS_FAMILY) { // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters self::$caseCheck = 2; } else { // filesystem case checks failed, fallback to disabling them self::$caseCheck = 0; } } } public function getClassLoader(): callable { return $this->classLoader; } /** * Wraps all autoloaders. */ public static function enable(): void { // Ensures we don't hit https://bugs.php.net/42098 class_exists(\Symfony\Component\ErrorHandler\ErrorHandler::class); class_exists(\Psr\Log\LogLevel::class); if (!\is_array($functions = spl_autoload_functions())) { return; } foreach ($functions as $function) { spl_autoload_unregister($function); } foreach ($functions as $function) { if (!\is_array($function) || !$function[0] instanceof self) { $function = [new static($function), 'loadClass']; } spl_autoload_register($function); } } /** * Disables the wrapping. */ public static function disable(): void { if (!\is_array($functions = spl_autoload_functions())) { return; } foreach ($functions as $function) { spl_autoload_unregister($function); } foreach ($functions as $function) { if (\is_array($function) && $function[0] instanceof self) { $function = $function[0]->getClassLoader(); } spl_autoload_register($function); } } public static function checkClasses(): bool { if (!\is_array($functions = spl_autoload_functions())) { return false; } $loader = null; foreach ($functions as $function) { if (\is_array($function) && $function[0] instanceof self) { $loader = $function[0]; break; } } if (null === $loader) { return false; } static $offsets = [ 'get_declared_interfaces' => 0, 'get_declared_traits' => 0, 'get_declared_classes' => 0, ]; foreach ($offsets as $getSymbols => $i) { $symbols = $getSymbols(); for (; $i < \count($symbols); ++$i) { if (!is_subclass_of($symbols[$i], MockObject::class) && !is_subclass_of($symbols[$i], ProphecySubjectInterface::class) && !is_subclass_of($symbols[$i], Proxy::class) && !is_subclass_of($symbols[$i], ProxyInterface::class) && !is_subclass_of($symbols[$i], LegacyProxy::class) && !is_subclass_of($symbols[$i], MockInterface::class) && !is_subclass_of($symbols[$i], IMock::class) ) { $loader->checkClass($symbols[$i]); } } $offsets[$getSymbols] = $i; } return true; } public function findFile(string $class): ?string { return $this->isFinder ? ($this->classLoader[0]->findFile($class) ?: null) : null; } /** * Loads the given class or interface. * * @throws \RuntimeException */ public function loadClass(string $class): void { $e = error_reporting(error_reporting() | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR); try { if ($this->isFinder && !isset($this->loaded[$class])) { $this->loaded[$class] = true; if (!$file = $this->classLoader[0]->findFile($class) ?: '') { // no-op } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) { include $file; return; } elseif (false === include $file) { return; } } else { ($this->classLoader)($class); $file = ''; } } finally { error_reporting($e); } $this->checkClass($class, $file); } private function checkClass(string $class, string $file = null): void { $exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); if (null !== $file && $class && '\\' === $class[0]) { $class = substr($class, 1); } if ($exists) { if (isset(self::$checkedClasses[$class])) { return; } self::$checkedClasses[$class] = true; $refl = new \ReflectionClass($class); if (null === $file && $refl->isInternal()) { return; } $name = $refl->getName(); if ($name !== $class && 0 === strcasecmp($name, $class)) { throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); } $deprecations = $this->checkAnnotations($refl, $name); foreach ($deprecations as $message) { @trigger_error($message, \E_USER_DEPRECATED); } } if (!$file) { return; } if (!$exists) { if (false !== strpos($class, '/')) { throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); } throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); } if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) { throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); } } public function checkAnnotations(\ReflectionClass $refl, string $class): array { if ( 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7' === $class || 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV6' === $class ) { return []; } $deprecations = []; $className = false !== strpos($class, "@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : $class; // Don't trigger deprecations for classes in the same vendor if ($class !== $className) { $vendor = preg_match('/^namespace ([^;\\\\\s]++)[;\\\\]/m', @file_get_contents($refl->getFileName()), $vendor) ? $vendor[1].'\\' : ''; $vendorLen = \strlen($vendor); } elseif (2 > $vendorLen = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) { $vendorLen = 0; $vendor = ''; } else { $vendor = str_replace('_', '\\', substr($class, 0, $vendorLen)); } $parent = get_parent_class($class) ?: null; self::$returnTypes[$class] = []; $classIsTemplate = false; // Detect annotations on the class if ($doc = $this->parsePhpDoc($refl)) { $classIsTemplate = isset($doc['template']); foreach (['final', 'deprecated', 'internal'] as $annotation) { if (null !== $description = $doc[$annotation][0] ?? null) { self::${$annotation}[$class] = '' !== $description ? ' '.$description.(preg_match('/[.!]$/', $description) ? '' : '.') : '.'; } } if ($refl->isInterface() && isset($doc['method'])) { foreach ($doc['method'] as $name => [$static, $returnType, $signature, $description]) { self::$method[$class][] = [$class, $static, $returnType, $name.$signature, $description]; if ('' !== $returnType) { $this->setReturnType($returnType, $refl->name, $name, $refl->getFileName(), $parent); } } } } $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); if ($parent) { $parentAndOwnInterfaces[$parent] = $parent; if (!isset(self::$checkedClasses[$parent])) { $this->checkClass($parent); } if (isset(self::$final[$parent])) { $deprecations[] = sprintf('The "%s" class is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $className); } } // Detect if the parent is annotated foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) { if (!isset(self::$checkedClasses[$use])) { $this->checkClass($use); } if (isset(self::$deprecated[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && !isset(self::$deprecated[$class]) && !(HttplugClient::class === $class && \in_array($use, [\Http\Client\HttpClient::class, \Http\Message\RequestFactory::class, \Http\Message\StreamFactory::class, \Http\Message\UriFactory::class], true)) ) { $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s', $className, $type, $verb, $use, self::$deprecated[$use]); } if (isset(self::$internal[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen)) { $deprecations[] = sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className); } if (isset(self::$method[$use])) { if ($refl->isAbstract()) { if (isset(self::$method[$class])) { self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]); } else { self::$method[$class] = self::$method[$use]; } } elseif (!$refl->isInterface()) { if (!strncmp($vendor, str_replace('_', '\\', $use), $vendorLen) && 0 === strpos($className, 'Symfony\\') && (!class_exists(InstalledVersions::class) || 'symfony/symfony' !== InstalledVersions::getRootPackage()['name']) ) { // skip "same vendor" @method deprecations for Symfony\* classes unless symfony/symfony is being tested continue; } $hasCall = $refl->hasMethod('__call'); $hasStaticCall = $refl->hasMethod('__callStatic'); foreach (self::$method[$use] as [$interface, $static, $returnType, $name, $description]) { if ($static ? $hasStaticCall : $hasCall) { continue; } $realName = substr($name, 0, strpos($name, '(')); if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) { $deprecations[] = sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description); } } } } } if (trait_exists($class)) { $file = $refl->getFileName(); foreach ($refl->getMethods() as $method) { if ($method->getFileName() === $file) { self::$methodTraits[$file][$method->getStartLine()] = $class; } } return $deprecations; } // Inherit @final, @internal, @param and @return annotations for methods self::$finalMethods[$class] = []; self::$internalMethods[$class] = []; self::$annotatedParameters[$class] = []; foreach ($parentAndOwnInterfaces as $use) { foreach (['finalMethods', 'internalMethods', 'annotatedParameters', 'returnTypes'] as $property) { if (isset(self::${$property}[$use])) { self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use]; } } if (null !== (TentativeTypes::RETURN_TYPES[$use] ?? null)) { foreach (TentativeTypes::RETURN_TYPES[$use] as $method => $returnType) { $returnType = explode('|', $returnType); foreach ($returnType as $i => $t) { if ('?' !== $t && !isset(self::BUILTIN_RETURN_TYPES[$t])) { $returnType[$i] = '\\'.$t; } } $returnType = implode('|', $returnType); self::$returnTypes[$class] += [$method => [$returnType, 0 === strpos($returnType, '?') ? substr($returnType, 1).'|null' : $returnType, $use, '']]; } } } foreach ($refl->getMethods() as $method) { if ($method->class !== $class) { continue; } if (null === $ns = self::$methodTraits[$method->getFileName()][$method->getStartLine()] ?? null) { $ns = $vendor; $len = $vendorLen; } elseif (2 > $len = 1 + (strpos($ns, '\\') ?: strpos($ns, '_'))) { $len = 0; $ns = ''; } else { $ns = str_replace('_', '\\', substr($ns, 0, $len)); } if ($parent && isset(self::$finalMethods[$parent][$method->name])) { [$declaringClass, $message] = self::$finalMethods[$parent][$method->name]; $deprecations[] = sprintf('The "%s::%s()" method is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } if (isset(self::$internalMethods[$class][$method->name])) { [$declaringClass, $message] = self::$internalMethods[$class][$method->name]; if (strncmp($ns, $declaringClass, $len)) { $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } } // To read method annotations $doc = $this->parsePhpDoc($method); if (($classIsTemplate || isset($doc['template'])) && $method->hasReturnType()) { unset($doc['return']); } if (isset(self::$annotatedParameters[$class][$method->name])) { $definedParameters = []; foreach ($method->getParameters() as $parameter) { $definedParameters[$parameter->name] = true; } foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) { if (!isset($definedParameters[$parameterName]) && !isset($doc['param'][$parameterName])) { $deprecations[] = sprintf($deprecation, $className); } } } $forcePatchTypes = $this->patchTypes['force']; if ($canAddReturnType = null !== $forcePatchTypes && false === strpos($method->getFileName(), \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR)) { if ('void' !== (self::MAGIC_METHODS[$method->name] ?? 'void')) { $this->patchTypes['force'] = $forcePatchTypes ?: 'docblock'; } $canAddReturnType = 2 === (int) $forcePatchTypes || false !== stripos($method->getFileName(), \DIRECTORY_SEPARATOR.'Tests'.\DIRECTORY_SEPARATOR) || $refl->isFinal() || $method->isFinal() || $method->isPrivate() || ('.' === (self::$internal[$class] ?? null) && !$refl->isAbstract()) || '.' === (self::$final[$class] ?? null) || '' === ($doc['final'][0] ?? null) || '' === ($doc['internal'][0] ?? null) ; } if (null !== ($returnType = self::$returnTypes[$class][$method->name] ?? null) && 'docblock' === $this->patchTypes['force'] && !$method->hasReturnType() && isset(TentativeTypes::RETURN_TYPES[$returnType[2]][$method->name])) { $this->patchReturnTypeWillChange($method); } if (null !== ($returnType ?? $returnType = self::MAGIC_METHODS[$method->name] ?? null) && !$method->hasReturnType() && !isset($doc['return'])) { [$normalizedType, $returnType, $declaringClass, $declaringFile] = \is_string($returnType) ? [$returnType, $returnType, '', ''] : $returnType; if ($canAddReturnType && 'docblock' !== $this->patchTypes['force']) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } if (!isset($doc['deprecated']) && strncmp($ns, $declaringClass, $len)) { if ('docblock' === $this->patchTypes['force']) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } elseif ('' !== $declaringClass && $this->patchTypes['deprecations']) { $deprecations[] = sprintf('Method "%s::%s()" might add "%s" as a native return type declaration in the future. Do the same in %s "%s" now to avoid errors or add an explicit @return annotation to suppress this message.', $declaringClass, $method->name, $normalizedType, interface_exists($declaringClass) ? 'implementation' : 'child class', $className); } } } if (!$doc) { $this->patchTypes['force'] = $forcePatchTypes; continue; } if (isset($doc['return']) || 'void' !== (self::MAGIC_METHODS[$method->name] ?? 'void')) { $this->setReturnType($doc['return'] ?? self::MAGIC_METHODS[$method->name], $method->class, $method->name, $method->getFileName(), $parent, $method->getReturnType()); if (isset(self::$returnTypes[$class][$method->name][0]) && $canAddReturnType) { $this->fixReturnStatements($method, self::$returnTypes[$class][$method->name][0]); } if ($method->isPrivate()) { unset(self::$returnTypes[$class][$method->name]); } } $this->patchTypes['force'] = $forcePatchTypes; if ($method->isPrivate()) { continue; } $finalOrInternal = false; foreach (['final', 'internal'] as $annotation) { if (null !== $description = $doc[$annotation][0] ?? null) { self::${$annotation.'Methods'}[$class][$method->name] = [$class, '' !== $description ? ' '.$description.(preg_match('/[[:punct:]]$/', $description) ? '' : '.') : '.']; $finalOrInternal = true; } } if ($finalOrInternal || $method->isConstructor() || !isset($doc['param']) || StatelessInvocation::class === $class) { continue; } if (!isset(self::$annotatedParameters[$class][$method->name])) { $definedParameters = []; foreach ($method->getParameters() as $parameter) { $definedParameters[$parameter->name] = true; } } foreach ($doc['param'] as $parameterName => $parameterType) { if (!isset($definedParameters[$parameterName])) { self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); } } } return $deprecations; } public function checkCase(\ReflectionClass $refl, string $file, string $class): ?array { $real = explode('\\', $class.strrchr($file, '.')); $tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file)); $i = \count($tail) - 1; $j = \count($real) - 1; while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { --$i; --$j; } array_splice($tail, 0, $i + 1); if (!$tail) { return null; } $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail); $tailLen = \strlen($tail); $real = $refl->getFileName(); if (2 === self::$caseCheck) { $real = $this->darwinRealpath($real); } if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) ) { return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)]; } return null; } /** * `realpath` on MacOSX doesn't normalize the case of characters. */ private function darwinRealpath(string $real): string { $i = 1 + strrpos($real, '/'); $file = substr($real, $i); $real = substr($real, 0, $i); if (isset(self::$darwinCache[$real])) { $kDir = $real; } else { $kDir = strtolower($real); if (isset(self::$darwinCache[$kDir])) { $real = self::$darwinCache[$kDir][0]; } else { $dir = getcwd(); if (!@chdir($real)) { return $real.$file; } $real = getcwd().'/'; chdir($dir); $dir = $real; $k = $kDir; $i = \strlen($dir) - 1; while (!isset(self::$darwinCache[$k])) { self::$darwinCache[$k] = [$dir, []]; self::$darwinCache[$dir] = &self::$darwinCache[$k]; while ('/' !== $dir[--$i]) { } $k = substr($k, 0, ++$i); $dir = substr($dir, 0, $i--); } } } $dirFiles = self::$darwinCache[$kDir][1]; if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) { // Get the file name from "file_name.php(123) : eval()'d code" $file = substr($file, 0, strrpos($file, '(', -17)); } if (isset($dirFiles[$file])) { return $real.$dirFiles[$file]; } $kFile = strtolower($file); if (!isset($dirFiles[$kFile])) { foreach (scandir($real, 2) as $f) { if ('.' !== $f[0]) { $dirFiles[$f] = $f; if ($f === $file) { $kFile = $k = $file; } elseif ($f !== $k = strtolower($f)) { $dirFiles[$k] = $f; } } } self::$darwinCache[$kDir][1] = $dirFiles; } return $real.$dirFiles[$kFile]; } /** * `class_implements` includes interfaces from the parents so we have to manually exclude them. * * @return string[] */ private function getOwnInterfaces(string $class, ?string $parent): array { $ownInterfaces = class_implements($class, false); if ($parent) { foreach (class_implements($parent, false) as $interface) { unset($ownInterfaces[$interface]); } } foreach ($ownInterfaces as $interface) { foreach (class_implements($interface) as $interface) { unset($ownInterfaces[$interface]); } } return $ownInterfaces; } private function setReturnType(string $types, string $class, string $method, string $filename, ?string $parent, \ReflectionType $returnType = null): void { if ('__construct' === $method) { return; } if ('null' === $types) { self::$returnTypes[$class][$method] = ['null', 'null', $class, $filename]; return; } if ($nullable = 0 === strpos($types, 'null|')) { $types = substr($types, 5); } elseif ($nullable = '|null' === substr($types, -5)) { $types = substr($types, 0, -5); } $arrayType = ['array' => 'array']; $typesMap = []; $glue = false !== strpos($types, '&') ? '&' : '|'; foreach (explode($glue, $types) as $t) { $t = self::SPECIAL_RETURN_TYPES[strtolower($t)] ?? $t; $typesMap[$this->normalizeType($t, $class, $parent, $returnType)][$t] = $t; } if (isset($typesMap['array'])) { if (isset($typesMap['Traversable']) || isset($typesMap['\Traversable'])) { $typesMap['iterable'] = $arrayType !== $typesMap['array'] ? $typesMap['array'] : ['iterable']; unset($typesMap['array'], $typesMap['Traversable'], $typesMap['\Traversable']); } elseif ($arrayType !== $typesMap['array'] && isset(self::$returnTypes[$class][$method]) && !$returnType) { return; } } if (isset($typesMap['array']) && isset($typesMap['iterable'])) { if ($arrayType !== $typesMap['array']) { $typesMap['iterable'] = $typesMap['array']; } unset($typesMap['array']); } $iterable = $object = true; foreach ($typesMap as $n => $t) { if ('null' !== $n) { $iterable = $iterable && (\in_array($n, ['array', 'iterable']) || false !== strpos($n, 'Iterator')); $object = $object && (\in_array($n, ['callable', 'object', '$this', 'static']) || !isset(self::SPECIAL_RETURN_TYPES[$n])); } } $phpTypes = []; $docTypes = []; foreach ($typesMap as $n => $t) { if ('null' === $n) { $nullable = true; continue; } $docTypes[] = $t; if ('mixed' === $n || 'void' === $n) { $nullable = false; $phpTypes = ['' => $n]; continue; } if ('resource' === $n) { // there is no native type for "resource" return; } if (!isset($phpTypes[''])) { $phpTypes[] = $n; } } $docTypes = array_merge([], ...$docTypes); if (!$phpTypes) { return; } if (1 < \count($phpTypes)) { if ($iterable && '8.0' > $this->patchTypes['php']) { $phpTypes = $docTypes = ['iterable']; } elseif ($object && 'object' === $this->patchTypes['force']) { $phpTypes = $docTypes = ['object']; } elseif ('8.0' > $this->patchTypes['php']) { // ignore multi-types return declarations return; } } $phpType = sprintf($nullable ? (1 < \count($phpTypes) ? '%s|null' : '?%s') : '%s', implode($glue, $phpTypes)); $docType = sprintf($nullable ? '%s|null' : '%s', implode($glue, $docTypes)); self::$returnTypes[$class][$method] = [$phpType, $docType, $class, $filename]; } private function normalizeType(string $type, string $class, ?string $parent, ?\ReflectionType $returnType): string { if (isset(self::SPECIAL_RETURN_TYPES[$lcType = strtolower($type)])) { if ('parent' === $lcType = self::SPECIAL_RETURN_TYPES[$lcType]) { $lcType = null !== $parent ? '\\'.$parent : 'parent'; } elseif ('self' === $lcType) { $lcType = '\\'.$class; } return $lcType; } // We could resolve "use" statements to return the FQDN // but this would be too expensive for a runtime checker if ('[]' !== substr($type, -2)) { return $type; } if ($returnType instanceof \ReflectionNamedType) { $type = $returnType->getName(); if ('mixed' !== $type) { return isset(self::SPECIAL_RETURN_TYPES[$type]) ? $type : '\\'.$type; } } return 'array'; } /** * Utility method to add #[ReturnTypeWillChange] where php triggers deprecations. */ private function patchReturnTypeWillChange(\ReflectionMethod $method) { if (\PHP_VERSION_ID >= 80000 && \count($method->getAttributes(\ReturnTypeWillChange::class))) { return; } if (!is_file($file = $method->getFileName())) { return; } $fileOffset = self::$fileOffsets[$file] ?? 0; $code = file($file); $startLine = $method->getStartLine() + $fileOffset - 2; if (false !== stripos($code[$startLine], 'ReturnTypeWillChange')) { return; } $code[$startLine] .= " #[\\ReturnTypeWillChange]\n"; self::$fileOffsets[$file] = 1 + $fileOffset; file_put_contents($file, $code); } /** * Utility method to add @return annotations to the Symfony code-base where it triggers self-deprecations. */ private function patchMethod(\ReflectionMethod $method, string $returnType, string $declaringFile, string $normalizedType) { static $patchedMethods = []; static $useStatements = []; if (!is_file($file = $method->getFileName()) || isset($patchedMethods[$file][$startLine = $method->getStartLine()])) { return; } $patchedMethods[$file][$startLine] = true; $fileOffset = self::$fileOffsets[$file] ?? 0; $startLine += $fileOffset - 2; if ($nullable = '|null' === substr($returnType, -5)) { $returnType = substr($returnType, 0, -5); } $glue = false !== strpos($returnType, '&') ? '&' : '|'; $returnType = explode($glue, $returnType); $code = file($file); foreach ($returnType as $i => $type) { if (preg_match('/((?:\[\])+)$/', $type, $m)) { $type = substr($type, 0, -\strlen($m[1])); $format = '%s'.$m[1]; } else { $format = null; } if (isset(self::SPECIAL_RETURN_TYPES[$type]) || ('\\' === $type[0] && !$p = strrpos($type, '\\', 1))) { continue; } [$namespace, $useOffset, $useMap] = $useStatements[$file] ?? $useStatements[$file] = self::getUseStatements($file); if ('\\' !== $type[0]) { [$declaringNamespace, , $declaringUseMap] = $useStatements[$declaringFile] ?? $useStatements[$declaringFile] = self::getUseStatements($declaringFile); $p = strpos($type, '\\', 1); $alias = $p ? substr($type, 0, $p) : $type; if (isset($declaringUseMap[$alias])) { $type = '\\'.$declaringUseMap[$alias].($p ? substr($type, $p) : ''); } else { $type = '\\'.$declaringNamespace.$type; } $p = strrpos($type, '\\', 1); } $alias = substr($type, 1 + $p); $type = substr($type, 1); if (!isset($useMap[$alias]) && (class_exists($c = $namespace.$alias) || interface_exists($c) || trait_exists($c))) { $useMap[$alias] = $c; } if (!isset($useMap[$alias])) { $useStatements[$file][2][$alias] = $type; $code[$useOffset] = "use $type;\n".$code[$useOffset]; ++$fileOffset; } elseif ($useMap[$alias] !== $type) { $alias .= 'FIXME'; $useStatements[$file][2][$alias] = $type; $code[$useOffset] = "use $type as $alias;\n".$code[$useOffset]; ++$fileOffset; } $returnType[$i] = null !== $format ? sprintf($format, $alias) : $alias; } if ('docblock' === $this->patchTypes['force'] || ('object' === $normalizedType && '7.1' === $this->patchTypes['php'])) { $returnType = implode($glue, $returnType).($nullable ? '|null' : ''); if (false !== strpos($code[$startLine], '#[')) { --$startLine; } if ($method->getDocComment()) { $code[$startLine] = " * @return $returnType\n".$code[$startLine]; } else { $code[$startLine] .= <<<EOTXT /** * @return $returnType */ EOTXT; } $fileOffset += substr_count($code[$startLine], "\n") - 1; } self::$fileOffsets[$file] = $fileOffset; file_put_contents($file, $code); $this->fixReturnStatements($method, $normalizedType); } private static function getUseStatements(string $file): array { $namespace = ''; $useMap = []; $useOffset = 0; if (!is_file($file)) { return [$namespace, $useOffset, $useMap]; } $file = file($file); for ($i = 0; $i < \count($file); ++$i) { if (preg_match('/^(class|interface|trait|abstract) /', $file[$i])) { break; } if (0 === strpos($file[$i], 'namespace ')) { $namespace = substr($file[$i], \strlen('namespace '), -2).'\\'; $useOffset = $i + 2; } if (0 === strpos($file[$i], 'use ')) { $useOffset = $i; for (; 0 === strpos($file[$i], 'use '); ++$i) { $u = explode(' as ', substr($file[$i], 4, -2), 2); if (1 === \count($u)) { $p = strrpos($u[0], '\\'); $useMap[substr($u[0], false !== $p ? 1 + $p : 0)] = $u[0]; } else { $useMap[$u[1]] = $u[0]; } } break; } } return [$namespace, $useOffset, $useMap]; } private function fixReturnStatements(\ReflectionMethod $method, string $returnType) { if ('docblock' !== $this->patchTypes['force']) { if ('7.1' === $this->patchTypes['php'] && 'object' === ltrim($returnType, '?')) { return; } if ('7.4' > $this->patchTypes['php'] && $method->hasReturnType()) { return; } if ('8.0' > $this->patchTypes['php'] && (false !== strpos($returnType, '|') || \in_array($returnType, ['mixed', 'static'], true))) { return; } if ('8.1' > $this->patchTypes['php'] && false !== strpos($returnType, '&')) { return; } } if (!is_file($file = $method->getFileName())) { return; } $fixedCode = $code = file($file); $i = (self::$fileOffsets[$file] ?? 0) + $method->getStartLine(); if ('?' !== $returnType && 'docblock' !== $this->patchTypes['force']) { $fixedCode[$i - 1] = preg_replace('/\)(?::[^;\n]++)?(;?\n)/', "): $returnType\\1", $code[$i - 1]); } $end = $method->isGenerator() ? $i : $method->getEndLine(); $inClosure = false; $braces = 0; for (; $i < $end; ++$i) { if (!$inClosure) { $inClosure = str_contains($code[$i], 'function ('); } if ($inClosure) { $braces += substr_count($code[$i], '{') - substr_count($code[$i], '}'); $inClosure = $braces > 0; continue; } if ('void' === $returnType) { $fixedCode[$i] = str_replace(' return null;', ' return;', $code[$i]); } elseif ('mixed' === $returnType || '?' === $returnType[0]) { $fixedCode[$i] = str_replace(' return;', ' return null;', $code[$i]); } else { $fixedCode[$i] = str_replace(' return;', " return $returnType!?;", $code[$i]); } } if ($fixedCode !== $code) { file_put_contents($file, $fixedCode); } } /** * @param \ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflector */ private function parsePhpDoc(\Reflector $reflector): array { if (!$doc = $reflector->getDocComment()) { return []; } $tagName = ''; $tagContent = ''; $tags = []; foreach (explode("\n", substr($doc, 3, -2)) as $line) { $line = ltrim($line); $line = ltrim($line, '*'); if ('' === $line = trim($line)) { if ('' !== $tagName) { $tags[$tagName][] = $tagContent; } $tagName = $tagContent = ''; continue; } if ('@' === $line[0]) { if ('' !== $tagName) { $tags[$tagName][] = $tagContent; $tagContent = ''; } if (preg_match('{^@([-a-zA-Z0-9_:]++)(\s|$)}', $line, $m)) { $tagName = $m[1]; $tagContent = str_replace("\t", ' ', ltrim(substr($line, 2 + \strlen($tagName)))); } else { $tagName = ''; } } elseif ('' !== $tagName) { $tagContent .= ' '.str_replace("\t", ' ', $line); } } if ('' !== $tagName) { $tags[$tagName][] = $tagContent; } foreach ($tags['method'] ?? [] as $i => $method) { unset($tags['method'][$i]); $parts = preg_split('{(\s++|\((?:[^()]*+|(?R))*\)(?: *: *[^ ]++)?|<(?:[^<>]*+|(?R))*>|\{(?:[^{}]*+|(?R))*\})}', $method, -1, \PREG_SPLIT_DELIM_CAPTURE); $returnType = ''; $static = 'static' === $parts[0]; for ($i = $static ? 2 : 0; null !== $p = $parts[$i] ?? null; $i += 2) { if (\in_array($p, ['', '|', '&', 'callable'], true) || \in_array(substr($returnType, -1), ['|', '&'], true)) { $returnType .= trim($parts[$i - 1] ?? '').$p; continue; } $signature = '(' === ($parts[$i + 1][0] ?? '(') ? $parts[$i + 1] ?? '()' : null; if (null === $signature && '' === $returnType) { $returnType = $p; continue; } if ($static && 2 === $i) { $static = false; $returnType = 'static'; } if (\in_array($description = trim(implode('', \array_slice($parts, 2 + $i))), ['', '.'], true)) { $description = null; } elseif (!preg_match('/[.!]$/', $description)) { $description .= '.'; } $tags['method'][$p] = [$static, $returnType, $signature ?? '()', $description]; break; } } foreach ($tags['param'] ?? [] as $i => $param) { unset($tags['param'][$i]); if (\strlen($param) !== strcspn($param, '<{(')) { $param = preg_replace('{\(([^()]*+|(?R))*\)(?: *: *[^ ]++)?|<([^<>]*+|(?R))*>|\{([^{}]*+|(?R))*\}}', '', $param); } if (false === $i = strpos($param, '$')) { continue; } $type = 0 === $i ? '' : rtrim(substr($param, 0, $i), ' &'); $param = substr($param, 1 + $i, (strpos($param, ' ', $i) ?: (1 + $i + \strlen($param))) - $i - 1); $tags['param'][$param] = $type; } foreach (['var', 'return'] as $k) { if (null === $v = $tags[$k][0] ?? null) { continue; } if (\strlen($v) !== strcspn($v, '<{(')) { $v = preg_replace('{\(([^()]*+|(?R))*\)(?: *: *[^ ]++)?|<([^<>]*+|(?R))*>|\{([^{}]*+|(?R))*\}}', '', $v); } $tags[$k] = substr($v, 0, strpos($v, ' ') ?: \strlen($v)) ?: null; } return $tags; } } PK2A#]��..&vendor/symfony/error-handler/Debug.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; /** * Registers all the debug tools. * * @author Fabien Potencier <fabien@symfony.com> */ class Debug { public static function enable(): ErrorHandler { error_reporting(-1); if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { ini_set('display_errors', 0); } elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOLEAN) || \ini_get('error_log')) { // CLI - display errors only if they're not already logged to STDERR ini_set('display_errors', 1); } @ini_set('zend.assertions', 1); ini_set('assert.active', 1); ini_set('assert.exception', 1); DebugClassLoader::enable(); return ErrorHandler::register(new ErrorHandler(new BufferingLogger(), true)); } } PK2A#]��R�/vendor/symfony/error-handler/ThrowableUtils.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; /** * @internal */ class ThrowableUtils { /** * @param SilencedErrorContext|\Throwable */ public static function getSeverity($throwable): int { if ($throwable instanceof \ErrorException || $throwable instanceof SilencedErrorContext) { return $throwable->getSeverity(); } if ($throwable instanceof \ParseError) { return \E_PARSE; } if ($throwable instanceof \TypeError) { return \E_RECOVERABLE_ERROR; } return \E_ERROR; } } PK2A#]�o:s��0vendor/symfony/error-handler/BufferingLogger.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Psr\Log\AbstractLogger; /** * A buffering logger that stacks logs for later. * * @author Nicolas Grekas <p@tchwork.com> */ class BufferingLogger extends AbstractLogger { private $logs = []; public function log($level, $message, array $context = []): void { $this->logs[] = [$level, $message, $context]; } public function cleanLogs(): array { $logs = $this->logs; $this->logs = []; return $logs; } /** * @return array */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } public function __destruct() { foreach ($this->logs as [$level, $message, $context]) { if (false !== strpos($message, '{')) { foreach ($context as $key => $val) { if (null === $val || \is_scalar($val) || (\is_object($val) && \is_callable([$val, '__toString']))) { $message = str_replace("{{$key}}", $val, $message); } elseif ($val instanceof \DateTimeInterface) { $message = str_replace("{{$key}}", $val->format(\DateTime::RFC3339), $message); } elseif (\is_object($val)) { $message = str_replace("{{$key}}", '[object '.\get_class($val).']', $message); } else { $message = str_replace("{{$key}}", '['.\gettype($val).']', $message); } } } error_log(sprintf('%s [%s] %s', date(\DateTime::RFC3339), $level, $message)); } } } PK2A#]2���Evendor/symfony/error-handler/ErrorRenderer/ErrorRendererInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; /** * Formats an exception to be used as response content. * * @author Yonel Ceruto <yonelceruto@gmail.com> */ interface ErrorRendererInterface { /** * Renders a Throwable as a FlattenException. */ public function render(\Throwable $exception): FlattenException; } PK2A#]t:��tt?vendor/symfony/error-handler/ErrorRenderer/CliErrorRenderer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; // Help opcache.preload discover always-needed symbols class_exists(CliDumper::class); /** * @author Nicolas Grekas <p@tchwork.com> */ class CliErrorRenderer implements ErrorRendererInterface { /** * {@inheritdoc} */ public function render(\Throwable $exception): FlattenException { $cloner = new VarCloner(); $dumper = new class() extends CliDumper { protected function supportsColors(): bool { $outputStream = $this->outputStream; $this->outputStream = fopen('php://stdout', 'w'); try { return parent::supportsColors(); } finally { $this->outputStream = $outputStream; } } }; return FlattenException::createFromThrowable($exception) ->setAsString($dumper->dump($cloner->cloneVar($exception), true)); } } PK2A#]I#��Fvendor/symfony/error-handler/ErrorRenderer/SerializerErrorRenderer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\SerializerInterface; /** * Formats an exception using Serializer for rendering. * * @author Nicolas Grekas <p@tchwork.com> */ class SerializerErrorRenderer implements ErrorRendererInterface { private $serializer; private $format; private $fallbackErrorRenderer; private $debug; /** * @param string|callable(FlattenException) $format The format as a string or a callable that should return it * formats not supported by Request::getMimeTypes() should be given as mime types * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it */ public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false) { if (!\is_string($format) && !\is_callable($format)) { throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a string or a callable, "%s" given.', __METHOD__, \gettype($format))); } if (!\is_bool($debug) && !\is_callable($debug)) { throw new \TypeError(sprintf('Argument 4 passed to "%s()" must be a boolean or a callable, "%s" given.', __METHOD__, \gettype($debug))); } $this->serializer = $serializer; $this->format = $format; $this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer(); $this->debug = $debug; } /** * {@inheritdoc} */ public function render(\Throwable $exception): FlattenException { $headers = ['Vary' => 'Accept']; $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception); if ($debug) { $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage()); $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } $flattenException = FlattenException::createFromThrowable($exception, null, $headers); try { $format = \is_string($this->format) ? $this->format : ($this->format)($flattenException); $headers['Content-Type'] = Request::getMimeTypes($format)[0] ?? $format; $flattenException->setAsString($this->serializer->serialize($flattenException, $format, [ 'exception' => $exception, 'debug' => $debug, ])); } catch (NotEncodableValueException $e) { $flattenException = $this->fallbackErrorRenderer->render($exception); } return $flattenException->setHeaders($flattenException->getHeaders() + $headers); } public static function getPreferredFormat(RequestStack $requestStack): \Closure { return static function () use ($requestStack) { if (!$request = $requestStack->getCurrentRequest()) { throw new NotEncodableValueException(); } return $request->getPreferredFormat(); }; } } PK2A#]��Y�\�\@vendor/symfony/error-handler/ErrorRenderer/HtmlErrorRenderer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Psr\Log\LoggerInterface; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; /** * @author Yonel Ceruto <yonelceruto@gmail.com> */ class HtmlErrorRenderer implements ErrorRendererInterface { private const GHOST_ADDONS = [ '02-14' => self::GHOST_HEART, '02-29' => self::GHOST_PLUS, '10-18' => self::GHOST_GIFT, ]; private const GHOST_GIFT = 'M124.00534057617188,5.3606138080358505 C124.40059661865234,4.644828304648399 125.1237564086914,3.712414965033531 123.88127899169922,3.487462028861046 C123.53517150878906,3.3097832053899765 123.18894958496094,2.9953975528478622 122.8432846069336,3.345616325736046 C122.07421112060547,3.649444565176964 121.40750122070312,4.074306473135948 122.2164306640625,4.869479164481163 C122.57514953613281,5.3830065578222275 122.90142822265625,6.503447040915489 123.3077621459961,6.626829609274864 C123.55027770996094,6.210384353995323 123.7774658203125,5.785196766257286 124.00534057617188,5.3606138080358505 zM122.30630493164062,7.336987480521202 C121.60028076171875,6.076864704489708 121.03211975097656,4.72498320043087 120.16796875,3.562500938773155 C119.11695098876953,2.44033907353878 117.04605865478516,2.940566048026085 116.57544708251953,4.387995228171349 C115.95028686523438,5.819030746817589 117.2991714477539,7.527640804648399 118.826171875,7.348545059561729 C119.98493194580078,7.367936596274376 121.15027618408203,7.420116886496544 122.30630493164062,7.336987480521202 zM128.1732177734375,7.379541382193565 C129.67486572265625,7.17823551595211 130.53842163085938,5.287807449698448 129.68344116210938,4.032590612769127 C128.92578125,2.693056806921959 126.74605560302734,2.6463639587163925 125.98509216308594,4.007616028189659 C125.32617950439453,5.108129009604454 124.75428009033203,6.258124336600304 124.14962768554688,7.388818249106407 C125.48638916015625,7.465229496359825 126.8357162475586,7.447416767477989 128.1732177734375,7.379541382193565 zM130.6601104736328,8.991325363516808 C131.17202758789062,8.540884003043175 133.1543731689453,8.009847149252892 131.65304565429688,7.582054600119591 C131.2811279296875,7.476506695151329 130.84751892089844,6.99234913289547 130.5132598876953,7.124847874045372 C129.78744506835938,8.02728746831417 128.67140197753906,8.55669592320919 127.50616455078125,8.501235947012901 C127.27806091308594,8.576229080557823 126.11459350585938,8.38720129430294 126.428955078125,8.601900085806847 C127.25099182128906,9.070617660880089 128.0523223876953,9.579657539725304 128.902587890625,9.995706543326378 C129.49813842773438,9.678531631827354 130.0761260986328,9.329126343131065 130.6601104736328,8.991325363516808 zM118.96446990966797,9.246344551444054 C119.4022445678711,8.991325363516808 119.84001922607422,8.736305221915245 120.27779388427734,8.481284126639366 C118.93965911865234,8.414779648184776 117.40827941894531,8.607666000723839 116.39698791503906,7.531384453177452 C116.11186981201172,7.212117180228233 115.83845520019531,6.846597656607628 115.44329071044922,7.248530372977257 C114.96995544433594,7.574637398123741 113.5140609741211,7.908811077475548 114.63501739501953,8.306883797049522 C115.61112976074219,8.883499130606651 116.58037567138672,9.474181160330772 117.58061218261719,10.008124336600304 C118.05723571777344,9.784612640738487 118.50651550292969,9.5052699893713 118.96446990966797,9.246344551444054 zM125.38018035888672,12.091858848929405 C125.9474868774414,11.636047348380089 127.32159423828125,11.201767906546593 127.36749267578125,10.712632164359093 C126.08487701416016,9.974547371268272 124.83960723876953,9.152772888541222 123.49772644042969,8.528907760977745 C123.03594207763672,8.353693947196007 122.66152954101562,8.623294815421104 122.28982543945312,8.857431396842003 C121.19065856933594,9.51122473180294 120.06505584716797,10.12446115911007 119.00167083740234,10.835315689444542 C120.39238739013672,11.69529627263546 121.79983520507812,12.529837593436241 123.22095489501953,13.338589653372765 C123.94580841064453,12.932025894522667 124.66128540039062,12.508862480521202 125.38018035888672,12.091858848929405 zM131.07164001464844,13.514615997672081 C131.66018676757812,13.143282875418663 132.2487335205078,12.771927818655968 132.8372802734375,12.400571808218956 C132.8324737548828,11.156818374991417 132.8523406982422,9.912529930472374 132.81829833984375,8.669195160269737 C131.63046264648438,9.332009300589561 130.45948791503906,10.027913078665733 129.30828857421875,10.752535805106163 C129.182373046875,12.035354599356651 129.24623107910156,13.33940313756466 129.27359008789062,14.628684982657433 C129.88104248046875,14.27079389989376 130.4737548828125,13.888019546866417 131.07164001464844,13.514640793204308 zM117.26847839355469,12.731024727225304 C117.32825469970703,11.67083452641964 117.45709991455078,10.46224020421505 116.17853546142578,10.148179039359093 C115.37110900878906,9.77159021794796 114.25194549560547,8.806716904044151 113.62991333007812,8.81639002263546 C113.61052703857422,10.0110072940588 113.62078857421875,11.20585821568966 113.61869049072266,12.400571808218956 C114.81139373779297,13.144886955618858 115.98292541503906,13.925040230154991 117.20137023925781,14.626662239432335 C117.31951141357422,14.010867103934288 117.24227905273438,13.35805033147335 117.26847839355469,12.731024727225304 zM125.80937957763672,16.836034759879112 C126.51483917236328,16.390663132071495 127.22030639648438,15.945291504263878 127.92576599121094,15.49991987645626 C127.92250061035156,14.215868934988976 127.97560119628906,12.929980263113976 127.91757202148438,11.647302612662315 C127.14225769042969,11.869626984000206 126.25550079345703,12.556857094168663 125.43866729736328,12.983742699027061 C124.82704162597656,13.342005714774132 124.21542358398438,13.700271591544151 123.60379028320312,14.05853746831417 C123.61585235595703,15.429577812552452 123.57081604003906,16.803131088614464 123.64839172363281,18.172149643301964 C124.37957000732422,17.744937881827354 125.09130859375,17.284801468253136 125.80937957763672,16.836034759879112 zM122.8521499633789,16.115344032645226 C122.8521499633789,15.429741844534874 122.8521499633789,14.744139656424522 122.8521499633789,14.05853746831417 C121.43595123291016,13.230924591422081 120.02428436279297,12.395455345511436 118.60256958007812,11.577354416251183 C118.52394104003906,12.888403877615929 118.56887817382812,14.204405769705772 118.55702209472656,15.517732605338097 C119.97289276123047,16.4041957706213 121.37410736083984,17.314891800284386 122.80789947509766,18.172149643301964 C122.86368560791016,17.488990768790245 122.84332275390625,16.800363525748253 122.8521499633789,16.115344032645226 zM131.10684204101562,18.871450409293175 C131.68399047851562,18.48711584508419 132.2611541748047,18.10278509557247 132.8383026123047,17.718475326895714 C132.81423950195312,16.499977096915245 132.89776611328125,15.264989838004112 132.77627563476562,14.05993078649044 C131.5760040283203,14.744719490408897 130.41763305664062,15.524359688162804 129.23875427246094,16.255397781729698 C129.26707458496094,17.516149505972862 129.18060302734375,18.791316971182823 129.3108367919922,20.041303619742393 C129.91973876953125,19.667551025748253 130.51010131835938,19.264152511954308 131.10684204101562,18.871450409293175 zM117.2557373046875,18.188333496451378 C117.25104522705078,17.549470886588097 117.24633026123047,16.91058538854122 117.24163055419922,16.271720871329308 C116.04924774169922,15.525708183646202 114.87187957763672,14.75476549565792 113.66158294677734,14.038097366690636 C113.5858383178711,15.262084946036339 113.62901306152344,16.49083898961544 113.61761474609375,17.717010483145714 C114.82051086425781,18.513254150748253 116.00987243652344,19.330610260367393 117.22888946533203,20.101993545889854 C117.27559661865234,19.466014847159386 117.25241088867188,18.825733169913292 117.2557373046875,18.188333496451378 zM125.8398666381836,22.38675306737423 C126.54049682617188,21.921453461050987 127.24110412597656,21.456151947379112 127.94172668457031,20.99083136022091 C127.94009399414062,19.693386062979698 127.96646118164062,18.395381912589073 127.93160247802734,17.098379120230675 C126.50540924072266,17.97775076329708 125.08877563476562,18.873308166861534 123.68258666992188,19.78428266942501 C123.52366638183594,21.03710363805294 123.626708984375,22.32878302037716 123.62647247314453,23.595300659537315 C124.06291198730469,23.86113165318966 125.1788101196289,22.68297766149044 125.8398666381836,22.38675306737423 zM122.8521499633789,21.83134649693966 C122.76741790771484,20.936696991324425 123.21651458740234,19.67745779454708 122.0794677734375,19.330633148550987 C120.93280029296875,18.604360565543175 119.7907485961914,17.870157226920128 118.62899780273438,17.16818617284298 C118.45966339111328,18.396427139639854 118.63676452636719,19.675991043448448 118.50668334960938,20.919256195425987 C119.89984130859375,21.92635916173458 121.32942199707031,22.88914106786251 122.78502655029297,23.803510650992393 C122.90177917480469,23.1627406924963 122.82917022705078,22.48402212560177 122.8521499633789,21.83134649693966 zM117.9798355102539,21.59483526647091 C116.28416442871094,20.46288488805294 114.58848571777344,19.330957397818565 112.892822265625,18.199007019400597 C112.89473724365234,14.705654129385948 112.84647369384766,11.211485847830772 112.90847778320312,7.718807205557823 C113.7575912475586,7.194885239005089 114.66117858886719,6.765397056937218 115.5350341796875,6.284702762961388 C114.97061157226562,4.668964847922325 115.78496551513672,2.7054970115423203 117.42159271240234,2.1007001250982285 C118.79354095458984,1.537783369421959 120.44731903076172,2.0457767099142075 121.32200622558594,3.23083733022213 C121.95732116699219,2.9050118774175644 122.59264373779297,2.5791852325201035 123.22796630859375,2.253336176276207 C123.86669921875,2.5821153968572617 124.50543975830078,2.9108948558568954 125.1441650390625,3.23967407643795 C126.05941009521484,2.154020771384239 127.62747192382812,1.5344576686620712 128.986328125,2.1429056972265244 C130.61741638183594,2.716217741370201 131.50650024414062,4.675290569663048 130.9215545654297,6.2884936183691025 C131.8018341064453,6.78548763692379 132.7589111328125,7.1738648265600204 133.5660400390625,7.780336365103722 C133.60182189941406,11.252970680594444 133.56637573242188,14.726140961050987 133.5631103515625,18.199007019400597 C130.18914794921875,20.431867584586143 126.86984252929688,22.74994657933712 123.44108581542969,24.897907242178917 C122.44406127929688,24.897628769278526 121.5834732055664,23.815067276358604 120.65831756591797,23.37616156041622 C119.76387023925781,22.784828171133995 118.87168884277344,22.19007681310177 117.9798355102539,21.59483526647091 z'; private const GHOST_HEART = 'M125.91386369681868,8.305165958366445 C128.95033202169043,-0.40540639102854037 140.8469835342744,8.305165958366445 125.91386369681868,19.504526138305664 C110.98208663272044,8.305165958366445 122.87795231771452,-0.40540639102854037 125.91386369681868,8.305165958366445 z'; private const GHOST_PLUS = 'M111.36824226379395,8.969108581542969 L118.69175148010254,8.969108581542969 L118.69175148010254,1.6455793380737305 L126.20429420471191,1.6455793380737305 L126.20429420471191,8.969108581542969 L133.52781105041504,8.969108581542969 L133.52781105041504,16.481630325317383 L126.20429420471191,16.481630325317383 L126.20429420471191,23.805158615112305 L118.69175148010254,23.805158615112305 L118.69175148010254,16.481630325317383 L111.36824226379395,16.481630325317383 z'; private $debug; private $charset; private $fileLinkFormat; private $projectDir; private $outputBuffer; private $logger; private static $template = 'views/error.html.php'; /** * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it * @param string|FileLinkFormatter|null $fileLinkFormat * @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it */ public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null) { if (!\is_bool($debug) && !\is_callable($debug)) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a boolean or a callable, "%s" given.', __METHOD__, \gettype($debug))); } if (!\is_string($outputBuffer) && !\is_callable($outputBuffer)) { throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be a string or a callable, "%s" given.', __METHOD__, \gettype($outputBuffer))); } $this->debug = $debug; $this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8'); $this->fileLinkFormat = $fileLinkFormat ?: (\ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')); $this->projectDir = $projectDir; $this->outputBuffer = $outputBuffer; $this->logger = $logger; } /** * {@inheritdoc} */ public function render(\Throwable $exception): FlattenException { $headers = ['Content-Type' => 'text/html; charset='.$this->charset]; if (\is_bool($this->debug) ? $this->debug : ($this->debug)($exception)) { $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage()); $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } $exception = FlattenException::createFromThrowable($exception, null, $headers); return $exception->setAsString($this->renderException($exception)); } /** * Gets the HTML content associated with the given exception. */ public function getBody(FlattenException $exception): string { return $this->renderException($exception, 'views/exception.html.php'); } /** * Gets the stylesheet associated with the given exception. */ public function getStylesheet(): string { if (!$this->debug) { return $this->include('assets/css/error.css'); } return $this->include('assets/css/exception.css'); } public static function isDebug(RequestStack $requestStack, bool $debug): \Closure { return static function () use ($requestStack, $debug): bool { if (!$request = $requestStack->getCurrentRequest()) { return $debug; } return $debug && $request->attributes->getBoolean('showException', true); }; } public static function getAndCleanOutputBuffer(RequestStack $requestStack): \Closure { return static function () use ($requestStack): string { if (!$request = $requestStack->getCurrentRequest()) { return ''; } $startObLevel = $request->headers->get('X-Php-Ob-Level', -1); if (ob_get_level() <= $startObLevel) { return ''; } Response::closeOutputBuffers($startObLevel + 1, true); return ob_get_clean(); }; } private function renderException(FlattenException $exception, string $debugTemplate = 'views/exception_full.html.php'): string { $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception); $statusText = $this->escape($exception->getStatusText()); $statusCode = $this->escape($exception->getStatusCode()); if (!$debug) { return $this->include(self::$template, [ 'statusText' => $statusText, 'statusCode' => $statusCode, ]); } $exceptionMessage = $this->escape($exception->getMessage()); return $this->include($debugTemplate, [ 'exception' => $exception, 'exceptionMessage' => $exceptionMessage, 'statusText' => $statusText, 'statusCode' => $statusCode, 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null, 'currentContent' => \is_string($this->outputBuffer) ? $this->outputBuffer : ($this->outputBuffer)(), ]); } /** * Formats an array as a string. */ private function formatArgs(array $args): string { $result = []; foreach ($args as $key => $item) { if ('object' === $item[0]) { $formattedValue = sprintf('<em>object</em>(%s)', $this->abbrClass($item[1])); } elseif ('array' === $item[0]) { $formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); } elseif ('null' === $item[0]) { $formattedValue = '<em>null</em>'; } elseif ('boolean' === $item[0]) { $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>'; } elseif ('resource' === $item[0]) { $formattedValue = '<em>resource</em>'; } else { $formattedValue = str_replace("\n", '', $this->escape(var_export($item[1], true))); } $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escape($key), $formattedValue); } return implode(', ', $result); } private function formatArgsAsText(array $args) { return strip_tags($this->formatArgs($args)); } private function escape(string $string): string { return htmlspecialchars($string, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset); } private function abbrClass(string $class): string { $parts = explode('\\', $class); $short = array_pop($parts); return sprintf('<abbr title="%s">%s</abbr>', $class, $short); } private function getFileRelative(string $file): ?string { $file = str_replace('\\', '/', $file); if (null !== $this->projectDir && 0 === strpos($file, $this->projectDir)) { return ltrim(substr($file, \strlen($this->projectDir)), '/'); } return null; } /** * Returns the link for a given file/line pair. * * @return string|false */ private function getFileLink(string $file, int $line) { if ($fmt = $this->fileLinkFormat) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); } return false; } /** * Formats a file path. * * @param string $file An absolute file path * @param int $line The line number * @param string $text Use this text for the link rather than the file path */ private function formatFile(string $file, int $line, string $text = null): string { $file = trim($file); if (null === $text) { $text = $file; if (null !== $rel = $this->getFileRelative($text)) { $rel = explode('/', $rel, 2); $text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? '')); } } if (0 < $line) { $text .= ' at line '.$line; } if (false !== $link = $this->getFileLink($file, $line)) { return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', $this->escape($link), $text); } return $text; } /** * Returns an excerpt of a code file around the given line number. * * @param string $file A file path * @param int $line The selected line number * @param int $srcContext The number of displayed lines around or -1 for the whole file */ private function fileExcerpt(string $file, int $line, int $srcContext = 3): string { if (is_file($file) && is_readable($file)) { // highlight_file could throw warnings // see https://bugs.php.net/25725 $code = @highlight_file($file, true); // remove main code/span tags $code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code); // split multiline spans $code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) { return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>'; }, $code); $content = explode('<br />', $code); $lines = []; if (0 > $srcContext) { $srcContext = \count($content); } for ($i = max($line - $srcContext, 1), $max = min($line + $srcContext, \count($content)); $i <= $max; ++$i) { $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.$this->fixCodeMarkup($content[$i - 1]).'</code></li>'; } return '<ol start="'.max($line - $srcContext, 1).'">'.implode("\n", $lines).'</ol>'; } return ''; } private function fixCodeMarkup(string $line) { // </span> ending tag from previous line $opening = strpos($line, '<span'); $closing = strpos($line, '</span>'); if (false !== $closing && (false === $opening || $closing < $opening)) { $line = substr_replace($line, '', $closing, 7); } // missing </span> tag at the end of line $opening = strrpos($line, '<span'); $closing = strrpos($line, '</span>'); if (false !== $opening && (false === $closing || $closing < $opening)) { $line .= '</span>'; } return trim($line); } private function formatFileFromText(string $text) { return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) { return 'in '.$this->formatFile($match[2], $match[3]); }, $text); } private function formatLogMessage(string $message, array $context) { if ($context && false !== strpos($message, '{')) { $replacements = []; foreach ($context as $key => $val) { if (\is_scalar($val)) { $replacements['{'.$key.'}'] = $val; } } if ($replacements) { $message = strtr($message, $replacements); } } return $this->escape($message); } private function addElementToGhost(): string { if (!isset(self::GHOST_ADDONS[date('m-d')])) { return ''; } return '<path d="'.self::GHOST_ADDONS[date('m-d')].'" fill="#fff" fill-opacity="0.6"></path>'; } private function include(string $name, array $context = []): string { extract($context, \EXTR_SKIP); ob_start(); include is_file(\dirname(__DIR__).'/Resources/'.$name) ? \dirname(__DIR__).'/Resources/'.$name : $name; return trim(ob_get_clean()); } /** * Allows overriding the default non-debug template. * * @param string $template path to the custom template file to render */ public static function setTemplate(string $template): void { self::$template = $template; } } PK2A#]ԩj�;vendor/symfony/error-handler/Error/UndefinedMethodError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class UndefinedMethodError extends \Error { /** * {@inheritdoc} */ public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setAccessible(true); $refl->setValue($this, $value); } } } PK2A#]�4� }}9vendor/symfony/error-handler/Error/ClassNotFoundError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class ClassNotFoundError extends \Error { /** * {@inheritdoc} */ public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setAccessible(true); $refl->setValue($this, $value); } } } PK2A#]��`���=vendor/symfony/error-handler/Error/UndefinedFunctionError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class UndefinedFunctionError extends \Error { /** * {@inheritdoc} */ public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setAccessible(true); $refl->setValue($this, $value); } } } PK2A#]���zMM7vendor/symfony/error-handler/Error/OutOfMemoryError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class OutOfMemoryError extends FatalError { } PK2A#]���� 1vendor/symfony/error-handler/Error/FatalError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class FatalError extends \Error { private $error; /** * {@inheritdoc} * * @param array $error An array as returned by error_get_last() */ public function __construct(string $message, int $code, array $error, int $traceOffset = null, bool $traceArgs = true, array $trace = null) { parent::__construct($message, $code); $this->error = $error; if (null !== $trace) { if (!$traceArgs) { foreach ($trace as &$frame) { unset($frame['args'], $frame['this'], $frame); } } } elseif (null !== $traceOffset) { if (\function_exists('xdebug_get_function_stack') && $trace = @xdebug_get_function_stack()) { if (0 < $traceOffset) { array_splice($trace, -$traceOffset); } foreach ($trace as &$frame) { if (!isset($frame['type'])) { // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 if (isset($frame['class'])) { $frame['type'] = '::'; } } elseif ('dynamic' === $frame['type']) { $frame['type'] = '->'; } elseif ('static' === $frame['type']) { $frame['type'] = '::'; } // XDebug also has a different name for the parameters array if (!$traceArgs) { unset($frame['params'], $frame['args']); } elseif (isset($frame['params']) && !isset($frame['args'])) { $frame['args'] = $frame['params']; unset($frame['params']); } } unset($frame); $trace = array_reverse($trace); } else { $trace = []; } } foreach ([ 'file' => $error['file'], 'line' => $error['line'], 'trace' => $trace, ] as $property => $value) { if (null !== $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setAccessible(true); $refl->setValue($this, $value); } } } /** * {@inheritdoc} */ public function getError(): array { return $this->error; } } PK2A#]զ_�,,$vendor/symfony/error-handler/LICENSEnu�[���Copyright (c) 2019-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]���tww/vendor/symfony/web-link/GenericLinkProvider.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\WebLink; use Psr\Link\EvolvableLinkProviderInterface; use Psr\Link\LinkInterface; class GenericLinkProvider implements EvolvableLinkProviderInterface { /** * @var LinkInterface[] */ private $links = []; /** * @param LinkInterface[] $links */ public function __construct(array $links = []) { $that = $this; foreach ($links as $link) { $that = $that->withLink($link); } $this->links = $that->links; } /** * {@inheritdoc} */ public function getLinks(): array { return array_values($this->links); } /** * {@inheritdoc} */ public function getLinksByRel($rel): array { $links = []; foreach ($this->links as $link) { if (\in_array($rel, $link->getRels())) { $links[] = $link; } } return $links; } /** * {@inheritdoc} * * @return static */ public function withLink(LinkInterface $link) { $that = clone $this; $that->links[spl_object_id($link)] = $link; return $that; } /** * {@inheritdoc} * * @return static */ public function withoutLink(LinkInterface $link) { $that = clone $this; unset($that->links[spl_object_id($link)]); return $that; } } PK2A#]Þ��##?vendor/symfony/web-link/EventListener/AddLinkHeaderListener.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\WebLink\EventListener; use Psr\Link\LinkProviderInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\WebLink\HttpHeaderSerializer; // Help opcache.preload discover always-needed symbols class_exists(HttpHeaderSerializer::class); /** * Adds the Link HTTP header to the response. * * @author Kévin Dunglas <dunglas@gmail.com> * * @final */ class AddLinkHeaderListener implements EventSubscriberInterface { private $serializer; public function __construct() { $this->serializer = new HttpHeaderSerializer(); } public function onKernelResponse(ResponseEvent $event) { if (!$event->isMainRequest()) { return; } $linkProvider = $event->getRequest()->attributes->get('_links'); if (!$linkProvider instanceof LinkProviderInterface || !$links = $linkProvider->getLinks()) { return; } $event->getResponse()->headers->set('Link', $this->serializer->serialize($links), false); } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [KernelEvents::RESPONSE => 'onKernelResponse']; } } PK2A#]���G G vendor/symfony/web-link/Link.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\WebLink; use Psr\Link\EvolvableLinkInterface; class Link implements EvolvableLinkInterface { // Relations defined in https://www.w3.org/TR/html5/links.html#links and applicable on link elements public const REL_ALTERNATE = 'alternate'; public const REL_AUTHOR = 'author'; public const REL_HELP = 'help'; public const REL_ICON = 'icon'; public const REL_LICENSE = 'license'; public const REL_SEARCH = 'search'; public const REL_STYLESHEET = 'stylesheet'; public const REL_NEXT = 'next'; public const REL_PREV = 'prev'; // Relation defined in https://www.w3.org/TR/preload/ public const REL_PRELOAD = 'preload'; // Relations defined in https://www.w3.org/TR/resource-hints/ public const REL_DNS_PREFETCH = 'dns-prefetch'; public const REL_PRECONNECT = 'preconnect'; public const REL_PREFETCH = 'prefetch'; public const REL_PRERENDER = 'prerender'; // Extra relations public const REL_MERCURE = 'mercure'; private $href = ''; /** * @var string[] */ private $rel = []; /** * @var array<string, string|bool|string[]> */ private $attributes = []; public function __construct(string $rel = null, string $href = '') { if (null !== $rel) { $this->rel[$rel] = $rel; } $this->href = $href; } /** * {@inheritdoc} */ public function getHref(): string { return $this->href; } /** * {@inheritdoc} */ public function isTemplated(): bool { return $this->hrefIsTemplated($this->href); } /** * {@inheritdoc} */ public function getRels(): array { return array_values($this->rel); } /** * {@inheritdoc} */ public function getAttributes(): array { return $this->attributes; } /** * {@inheritdoc} * * @return static */ public function withHref($href) { $that = clone $this; $that->href = $href; return $that; } /** * {@inheritdoc} * * @return static */ public function withRel($rel) { $that = clone $this; $that->rel[$rel] = $rel; return $that; } /** * {@inheritdoc} * * @return static */ public function withoutRel($rel) { $that = clone $this; unset($that->rel[$rel]); return $that; } /** * {@inheritdoc} * * @param string|\Stringable|int|float|bool|string[] $value * * @return static */ public function withAttribute($attribute, $value) { $that = clone $this; $that->attributes[$attribute] = $value; return $that; } /** * {@inheritdoc} * * @return static */ public function withoutAttribute($attribute) { $that = clone $this; unset($that->attributes[$attribute]); return $that; } private function hrefIsTemplated(string $href): bool { return str_contains($href, '{') || str_contains($href, '}'); } } PK2A#]��]���0vendor/symfony/web-link/HttpHeaderSerializer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\WebLink; use Psr\Link\LinkInterface; /** * Serializes a list of Link instances to an HTTP Link header. * * @see https://tools.ietf.org/html/rfc5988 * * @author Kévin Dunglas <dunglas@gmail.com> */ final class HttpHeaderSerializer { /** * Builds the value of the "Link" HTTP header. * * @param LinkInterface[]|\Traversable $links */ public function serialize(iterable $links): ?string { $elements = []; foreach ($links as $link) { if ($link->isTemplated()) { continue; } $attributesParts = ['', sprintf('rel="%s"', implode(' ', $link->getRels()))]; foreach ($link->getAttributes() as $key => $value) { if (\is_array($value)) { foreach ($value as $v) { $attributesParts[] = sprintf('%s="%s"', $key, preg_replace('/(?<!\\\\)"/', '\"', $v)); } continue; } if (!\is_bool($value)) { $attributesParts[] = sprintf('%s="%s"', $key, preg_replace('/(?<!\\\\)"/', '\"', $value)); continue; } if (true === $value) { $attributesParts[] = $key; } } $elements[] = sprintf('<%s>%s', $link->getHref(), implode('; ', $attributesParts)); } return $elements ? implode(',', $elements) : null; } } PK2A#]���U,,vendor/symfony/web-link/LICENSEnu�[���Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]�v���3vendor/symfony/service-contracts/ResetInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service; /** * Provides a way to reset an object to its initial state. * * When calling the "reset()" method on an object, it should be put back to its * initial state. This usually means clearing any internal buffers and forwarding * the call to internal dependencies. All properties of the object should be put * back to the same state it had when it was first ready to use. * * This method could be called, for example, to recycle objects that are used as * services, so that they can be used to handle several requests in the same * process loop (note that we advise making your services stateless instead of * implementing this interface when possible.) */ interface ResetInterface { public function reset(); } PK2A#]za ��8vendor/symfony/service-contracts/ServiceLocatorTrait.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; // Help opcache.preload discover always-needed symbols class_exists(ContainerExceptionInterface::class); class_exists(NotFoundExceptionInterface::class); /** * A trait to help implement ServiceProviderInterface. * * @author Robin Chalas <robin.chalas@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ trait ServiceLocatorTrait { private $factories; private $loading = []; private $providedTypes; /** * @param callable[] $factories */ public function __construct(array $factories) { $this->factories = $factories; } /** * {@inheritdoc} * * @return bool */ public function has(string $id) { return isset($this->factories[$id]); } /** * {@inheritdoc} * * @return mixed */ public function get(string $id) { if (!isset($this->factories[$id])) { throw $this->createNotFoundException($id); } if (isset($this->loading[$id])) { $ids = array_values($this->loading); $ids = \array_slice($this->loading, array_search($id, $ids)); $ids[] = $id; throw $this->createCircularReferenceException($id, $ids); } $this->loading[$id] = $id; try { return $this->factories[$id]($this); } finally { unset($this->loading[$id]); } } /** * {@inheritdoc} */ public function getProvidedServices(): array { if (null === $this->providedTypes) { $this->providedTypes = []; foreach ($this->factories as $name => $factory) { if (!\is_callable($factory)) { $this->providedTypes[$name] = '?'; } else { $type = (new \ReflectionFunction($factory))->getReturnType(); $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?'; } } } return $this->providedTypes; } private function createNotFoundException(string $id): NotFoundExceptionInterface { if (!$alternatives = array_keys($this->factories)) { $message = 'is empty...'; } else { $last = array_pop($alternatives); if ($alternatives) { $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); } else { $message = sprintf('only knows about the "%s" service.', $last); } } if ($this->loading) { $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); } else { $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); } return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface { }; } private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface { return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { }; } } PK2A#]5�))(vendor/symfony/service-contracts/LICENSEnu�[���Copyright (c) 2018-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]��3͍�<vendor/symfony/service-contracts/Test/ServiceLocatorTest.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service\Test; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceLocatorTrait; abstract class ServiceLocatorTest extends TestCase { /** * @return ContainerInterface */ protected function getServiceLocator(array $factories) { return new class($factories) implements ContainerInterface { use ServiceLocatorTrait; }; } public function testHas() { $locator = $this->getServiceLocator([ 'foo' => function () { return 'bar'; }, 'bar' => function () { return 'baz'; }, function () { return 'dummy'; }, ]); $this->assertTrue($locator->has('foo')); $this->assertTrue($locator->has('bar')); $this->assertFalse($locator->has('dummy')); } public function testGet() { $locator = $this->getServiceLocator([ 'foo' => function () { return 'bar'; }, 'bar' => function () { return 'baz'; }, ]); $this->assertSame('bar', $locator->get('foo')); $this->assertSame('baz', $locator->get('bar')); } public function testGetDoesNotMemoize() { $i = 0; $locator = $this->getServiceLocator([ 'foo' => function () use (&$i) { ++$i; return 'bar'; }, ]); $this->assertSame('bar', $locator->get('foo')); $this->assertSame('bar', $locator->get('foo')); $this->assertSame(2, $i); } public function testThrowsOnUndefinedInternalService() { if (!$this->getExpectedException()) { $this->expectException(\Psr\Container\NotFoundExceptionInterface::class); $this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.'); } $locator = $this->getServiceLocator([ 'foo' => function () use (&$locator) { return $locator->get('bar'); }, ]); $locator->get('foo'); } public function testThrowsOnCircularReference() { $this->expectException(\Psr\Container\ContainerExceptionInterface::class); $this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".'); $locator = $this->getServiceLocator([ 'foo' => function () use (&$locator) { return $locator->get('bar'); }, 'bar' => function () use (&$locator) { return $locator->get('baz'); }, 'baz' => function () use (&$locator) { return $locator->get('bar'); }, ]); $locator->get('foo'); } } PK2A#]��� @vendor/symfony/service-contracts/Attribute/SubscribedService.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service\Attribute; use Symfony\Contracts\Service\ServiceSubscriberTrait; /** * Use with {@see ServiceSubscriberTrait} to mark a method's return type * as a subscribed service. * * @author Kevin Bond <kevinbond@gmail.com> */ #[\Attribute(\Attribute::TARGET_METHOD)] final class SubscribedService { /** * @param string|null $key The key to use for the service * If null, use "ClassName::methodName" */ public function __construct( public ?string $key = null ) { } } PK2A#]e;Z��7vendor/symfony/service-contracts/Attribute/Required.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service\Attribute; /** * A required dependency. * * This attribute indicates that a property holds a required dependency. The annotated property or method should be * considered during the instantiation process of the containing class. * * @author Alexander M. Turek <me@derrabus.de> */ #[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)] final class Required { } PK2A#]�B�6��?vendor/symfony/service-contracts/ServiceSubscriberInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service; /** * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. * * The getSubscribedServices method returns an array of service types required by such instances, * optionally keyed by the service names used internally. Service types that start with an interrogation * mark "?" are optional, while the other ones are mandatory service dependencies. * * The injected service locators SHOULD NOT allow access to any other services not specified by the method. * * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. * This interface does not dictate any injection method for these service locators, although constructor * injection is recommended. * * @author Nicolas Grekas <p@tchwork.com> */ interface ServiceSubscriberInterface { /** * Returns an array of service types required by such instances, optionally keyed by the service names used internally. * * For mandatory dependencies: * * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name * internally to fetch a service which must implement Psr\Log\LoggerInterface. * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name * internally to fetch an iterable of Psr\Log\LoggerInterface instances. * * ['Psr\Log\LoggerInterface'] is a shortcut for * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface'] * * otherwise: * * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency * * ['?Psr\Log\LoggerInterface'] is a shortcut for * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] * * @return string[] The required service types, optionally keyed by service names */ public static function getSubscribedServices(); } PK2A#]�mXº�=vendor/symfony/service-contracts/ServiceProviderInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service; use Psr\Container\ContainerInterface; /** * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container. * * @author Nicolas Grekas <p@tchwork.com> * @author Mateusz Sip <mateusz.sip@gmail.com> */ interface ServiceProviderInterface extends ContainerInterface { /** * Returns an associative array of service types keyed by the identifiers provided by the current container. * * Examples: * * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface * * ['foo' => '?'] means the container provides service name "foo" of unspecified type * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null * * @return string[] The provided service types, keyed by service names */ public function getProvidedServices(): array; } PK2A#]�Cя�;vendor/symfony/service-contracts/ServiceSubscriberTrait.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Contracts\Service; use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\Attribute\SubscribedService; /** * Implementation of ServiceSubscriberInterface that determines subscribed services from * method return types. Service ids are available as "ClassName::methodName". * * @author Kevin Bond <kevinbond@gmail.com> */ trait ServiceSubscriberTrait { /** @var ContainerInterface */ protected $container; /** * {@inheritdoc} */ public static function getSubscribedServices(): array { $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; $attributeOptIn = false; if (\PHP_VERSION_ID >= 80000) { foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { if (self::class !== $method->getDeclaringClass()->name) { continue; } if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) { continue; } if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); } if (!$returnType = $method->getReturnType()) { throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); } $serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; if ($returnType->allowsNull()) { $serviceId = '?'.$serviceId; } $services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId; $attributeOptIn = true; } } if (!$attributeOptIn) { foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { continue; } if (self::class !== $method->getDeclaringClass()->name) { continue; } if (!($returnType = $method->getReturnType()) instanceof \ReflectionNamedType) { continue; } if ($returnType->isBuiltin()) { continue; } if (\PHP_VERSION_ID >= 80000) { trigger_deprecation('symfony/service-contracts', '2.5', 'Using "%s" in "%s" without using the "%s" attribute on any method is deprecated.', ServiceSubscriberTrait::class, self::class, SubscribedService::class); } $services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType); } } return $services; } /** * @required * * @return ContainerInterface|null */ public function setContainer(ContainerInterface $container) { $this->container = $container; if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { return parent::setContainer($container); } return null; } } PK2A#]��H,,-vendor/symfony/polyfill-intl-grapheme/LICENSEnu�[���Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]��E{g g 5vendor/symfony/polyfill-intl-grapheme/bootstrap80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Intl\Grapheme as p; if (!defined('GRAPHEME_EXTR_COUNT')) { define('GRAPHEME_EXTR_COUNT', 0); } if (!defined('GRAPHEME_EXTR_MAXBYTES')) { define('GRAPHEME_EXTR_MAXBYTES', 1); } if (!defined('GRAPHEME_EXTR_MAXCHARS')) { define('GRAPHEME_EXTR_MAXCHARS', 2); } if (!function_exists('grapheme_extract')) { function grapheme_extract(?string $haystack, ?int $size, ?int $type = GRAPHEME_EXTR_COUNT, ?int $offset = 0, &$next = null): string|false { return p\Grapheme::grapheme_extract((string) $haystack, (int) $size, (int) $type, (int) $offset, $next); } } if (!function_exists('grapheme_stripos')) { function grapheme_stripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_stripos((string) $haystack, (string) $needle, (int) $offset); } } if (!function_exists('grapheme_stristr')) { function grapheme_stristr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_stristr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } } if (!function_exists('grapheme_strlen')) { function grapheme_strlen(?string $string): int|false|null { return p\Grapheme::grapheme_strlen((string) $string); } } if (!function_exists('grapheme_strpos')) { function grapheme_strpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strpos((string) $haystack, (string) $needle, (int) $offset); } } if (!function_exists('grapheme_strripos')) { function grapheme_strripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strripos((string) $haystack, (string) $needle, (int) $offset); } } if (!function_exists('grapheme_strrpos')) { function grapheme_strrpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strrpos((string) $haystack, (string) $needle, (int) $offset); } } if (!function_exists('grapheme_strstr')) { function grapheme_strstr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_strstr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } } if (!function_exists('grapheme_substr')) { function grapheme_substr(?string $string, ?int $offset, ?int $length = null): string|false { return p\Grapheme::grapheme_substr((string) $string, (int) $offset, $length); } } PK2A#]��I8&8&2vendor/symfony/polyfill-intl-grapheme/Grapheme.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Intl\Grapheme; \define('SYMFONY_GRAPHEME_CLUSTER_RX', ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) ? '\X' : Grapheme::GRAPHEME_CLUSTER_RX); /** * Partial intl implementation in pure PHP. * * Implemented: * - grapheme_extract - Extract a sequence of grapheme clusters from a text buffer, which must be encoded in UTF-8 * - grapheme_stripos - Find position (in grapheme units) of first occurrence of a case-insensitive string * - grapheme_stristr - Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack * - grapheme_strlen - Get string length in grapheme units * - grapheme_strpos - Find position (in grapheme units) of first occurrence of a string * - grapheme_strripos - Find position (in grapheme units) of last occurrence of a case-insensitive string * - grapheme_strrpos - Find position (in grapheme units) of last occurrence of a string * - grapheme_strstr - Returns part of haystack string from the first occurrence of needle to the end of haystack * - grapheme_substr - Return part of a string * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Grapheme { // (CRLF|([ZWNJ-ZWJ]|T+|L*(LV?V+|LV|LVT)T*|L+|[^Control])[Extend]*|[Control]) // This regular expression is a work around for http://bugs.exim.org/1279 public const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])'; private const CASE_FOLD = [ ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], ]; public static function grapheme_extract($s, $size, $type = \GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0) { if (0 > $start) { $start = \strlen($s) + $start; } if (!\is_scalar($s)) { $hasError = false; set_error_handler(function () use (&$hasError) { $hasError = true; }); $next = substr($s, $start); restore_error_handler(); if ($hasError) { substr($s, $start); $s = ''; } else { $s = $next; } } else { $s = substr($s, $start); } $size = (int) $size; $type = (int) $type; $start = (int) $start; if (\GRAPHEME_EXTR_COUNT !== $type && \GRAPHEME_EXTR_MAXBYTES !== $type && \GRAPHEME_EXTR_MAXCHARS !== $type) { if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError('grapheme_extract(): Argument #3 ($type) must be one of GRAPHEME_EXTR_COUNT, GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS'); } if (!isset($s[0]) || 0 > $size || 0 > $start) { return false; } if (0 === $size) { return ''; } $next = $start; $s = preg_split('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', "\r\n".$s, $size + 1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); if (!isset($s[1])) { return false; } $i = 1; $ret = ''; do { if (\GRAPHEME_EXTR_COUNT === $type) { --$size; } elseif (\GRAPHEME_EXTR_MAXBYTES === $type) { $size -= \strlen($s[$i]); } else { $size -= iconv_strlen($s[$i], 'UTF-8//IGNORE'); } if ($size >= 0) { $ret .= $s[$i]; } } while (isset($s[++$i]) && $size > 0); $next += \strlen($ret); return $ret; } public static function grapheme_strlen($s) { preg_replace('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', '', $s, -1, $len); return 0 === $len && '' !== $s ? null : $len; } public static function grapheme_substr($s, $start, $len = null) { if (null === $len) { $len = 2147483647; } preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s); $slen = \count($s[0]); $start = (int) $start; if (0 > $start) { $start += $slen; } if (0 > $start) { if (\PHP_VERSION_ID < 80000) { return false; } $start = 0; } if ($start >= $slen) { return \PHP_VERSION_ID >= 80000 ? '' : false; } $rem = $slen - $start; if (0 > $len) { $len += $rem; } if (0 === $len) { return ''; } if (0 > $len) { return \PHP_VERSION_ID >= 80000 ? '' : false; } if ($len > $rem) { $len = $rem; } return implode('', \array_slice($s[0], $start, $len)); } public static function grapheme_strpos($s, $needle, $offset = 0) { return self::grapheme_position($s, $needle, $offset, 0); } public static function grapheme_stripos($s, $needle, $offset = 0) { return self::grapheme_position($s, $needle, $offset, 1); } public static function grapheme_strrpos($s, $needle, $offset = 0) { return self::grapheme_position($s, $needle, $offset, 2); } public static function grapheme_strripos($s, $needle, $offset = 0) { return self::grapheme_position($s, $needle, $offset, 3); } public static function grapheme_stristr($s, $needle, $beforeNeedle = false) { return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8'); } public static function grapheme_strstr($s, $needle, $beforeNeedle = false) { return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8'); } private static function grapheme_position($s, $needle, $offset, $mode) { $needle = (string) $needle; if (80000 > \PHP_VERSION_ID && !preg_match('/./us', $needle)) { return false; } $s = (string) $s; if (!preg_match('/./us', $s)) { return false; } if ($offset > 0) { $s = self::grapheme_substr($s, $offset); } elseif ($offset < 0) { if (2 > $mode) { $offset += self::grapheme_strlen($s); $s = self::grapheme_substr($s, $offset); if (0 > $offset) { $offset = 0; } } elseif (0 > $offset += self::grapheme_strlen($needle)) { $s = self::grapheme_substr($s, 0, $offset); $offset = 0; } else { $offset = 0; } } // As UTF-8 is self-synchronizing, and we have ensured the strings are valid UTF-8, // we can use normal binary string functions here. For case-insensitive searches, // case fold the strings first. $caseInsensitive = $mode & 1; $reverse = $mode & 2; if ($caseInsensitive) { // Use the same case folding mode as mbstring does for mb_stripos(). // Stick to SIMPLE case folding to avoid changing the length of the string, which // might result in offsets being shifted. $mode = \defined('MB_CASE_FOLD_SIMPLE') ? \MB_CASE_FOLD_SIMPLE : \MB_CASE_LOWER; $s = mb_convert_case($s, $mode, 'UTF-8'); $needle = mb_convert_case($needle, $mode, 'UTF-8'); if (!\defined('MB_CASE_FOLD_SIMPLE')) { $s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s); $needle = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $needle); } } if ($reverse) { $needlePos = strrpos($s, $needle); } else { $needlePos = strpos($s, $needle); } return false !== $needlePos ? self::grapheme_strlen(substr($s, 0, $needlePos)) + $offset : false; } } PK2A#]����3vendor/symfony/polyfill-intl-grapheme/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Intl\Grapheme as p; if (extension_loaded('intl')) { return; } if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!defined('GRAPHEME_EXTR_COUNT')) { define('GRAPHEME_EXTR_COUNT', 0); } if (!defined('GRAPHEME_EXTR_MAXBYTES')) { define('GRAPHEME_EXTR_MAXBYTES', 1); } if (!defined('GRAPHEME_EXTR_MAXCHARS')) { define('GRAPHEME_EXTR_MAXCHARS', 2); } if (!function_exists('grapheme_extract')) { function grapheme_extract($haystack, $size, $type = 0, $start = 0, &$next = 0) { return p\Grapheme::grapheme_extract($haystack, $size, $type, $start, $next); } } if (!function_exists('grapheme_stripos')) { function grapheme_stripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_stripos($haystack, $needle, $offset); } } if (!function_exists('grapheme_stristr')) { function grapheme_stristr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_stristr($haystack, $needle, $beforeNeedle); } } if (!function_exists('grapheme_strlen')) { function grapheme_strlen($input) { return p\Grapheme::grapheme_strlen($input); } } if (!function_exists('grapheme_strpos')) { function grapheme_strpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strpos($haystack, $needle, $offset); } } if (!function_exists('grapheme_strripos')) { function grapheme_strripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strripos($haystack, $needle, $offset); } } if (!function_exists('grapheme_strrpos')) { function grapheme_strrpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strrpos($haystack, $needle, $offset); } } if (!function_exists('grapheme_strstr')) { function grapheme_strstr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_strstr($haystack, $needle, $beforeNeedle); } } if (!function_exists('grapheme_substr')) { function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); } } PK2A#]�5�5vendor/symfony/ldap/Adapter/EntryManagerInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; /** * @author Charles Sarrazin <charles@sarraz.in> * @author Bob van de Vijver <bobvandevijver@hotmail.com> * @author Kevin Schuurmans <kevin.schuurmans@freshheads.com> */ interface EntryManagerInterface { /** * Adds a new entry in the Ldap server. * * @throws NotBoundException * @throws LdapException */ public function add(Entry $entry); /** * Updates an entry from the Ldap server. * * @throws NotBoundException * @throws LdapException */ public function update(Entry $entry); /** * Moves an entry on the Ldap server. * * @throws NotBoundException * @throws LdapException */ public function move(Entry $entry, string $newParent); /** * Renames an entry on the Ldap server. * * @throws NotBoundException * @throws LdapException */ public function rename(Entry $entry, string $newRdn, bool $removeOldRdn = true); /** * Removes an entry from the Ldap server. * * @throws NotBoundException * @throws LdapException */ public function remove(Entry $entry); } PK2A#]S�����2vendor/symfony/ldap/Adapter/AbstractConnection.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; /** * @author Charles Sarrazin <charles@sarraz.in> */ abstract class AbstractConnection implements ConnectionInterface { protected $config; public function __construct(array $config = []) { $resolver = new OptionsResolver(); $this->configureOptions($resolver); $this->config = $resolver->resolve($config); } protected function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'host' => 'localhost', 'version' => 3, 'connection_string' => null, 'encryption' => 'none', 'options' => [], ]); $resolver->setDefault('port', function (Options $options) { return 'ssl' === $options['encryption'] ? 636 : 389; }); $resolver->setDefault('connection_string', function (Options $options) { return sprintf('ldap%s://%s:%s', 'ssl' === $options['encryption'] ? 's' : '', $options['host'], $options['port']); }); $resolver->setAllowedTypes('host', 'string'); $resolver->setAllowedTypes('port', 'numeric'); $resolver->setAllowedTypes('connection_string', 'string'); $resolver->setAllowedTypes('version', 'numeric'); $resolver->setAllowedValues('encryption', ['none', 'ssl', 'tls']); $resolver->setAllowedTypes('options', 'array'); } } PK2A#]�'�7AA4vendor/symfony/ldap/Adapter/ExtLdap/EntryManager.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\EntryManagerInterface; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; use Symfony\Component\Ldap\Exception\UpdateOperationException; /** * @author Charles Sarrazin <charles@sarraz.in> * @author Bob van de Vijver <bobvandevijver@hotmail.com> */ class EntryManager implements EntryManagerInterface { private $connection; public function __construct(Connection $connection) { $this->connection = $connection; } /** * {@inheritdoc} */ public function add(Entry $entry) { $con = $this->getConnectionResource(); if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) { throw new LdapException(sprintf('Could not add entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } return $this; } /** * {@inheritdoc} */ public function update(Entry $entry) { $con = $this->getConnectionResource(); if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) { throw new LdapException(sprintf('Could not update entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } } /** * {@inheritdoc} */ public function remove(Entry $entry) { $con = $this->getConnectionResource(); if (!@ldap_delete($con, $entry->getDn())) { throw new LdapException(sprintf('Could not remove entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } } /** * Adds values to an entry's multi-valued attribute from the LDAP server. * * @throws NotBoundException * @throws LdapException */ public function addAttributeValues(Entry $entry, string $attribute, array $values) { $con = $this->getConnectionResource(); if (!@ldap_mod_add($con, $entry->getDn(), [$attribute => $values])) { throw new LdapException(sprintf('Could not add values to entry "%s", attribute "%s": ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con)); } } /** * Removes values from an entry's multi-valued attribute from the LDAP server. * * @throws NotBoundException * @throws LdapException */ public function removeAttributeValues(Entry $entry, string $attribute, array $values) { $con = $this->getConnectionResource(); if (!@ldap_mod_del($con, $entry->getDn(), [$attribute => $values])) { throw new LdapException(sprintf('Could not remove values from entry "%s", attribute "%s": ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con)); } } /** * {@inheritdoc} */ public function rename(Entry $entry, string $newRdn, bool $removeOldRdn = true) { $con = $this->getConnectionResource(); if (!@ldap_rename($con, $entry->getDn(), $newRdn, '', $removeOldRdn)) { throw new LdapException(sprintf('Could not rename entry "%s" to "%s": ', $entry->getDn(), $newRdn).ldap_error($con), ldap_errno($con)); } } /** * Moves an entry on the Ldap server. * * @throws NotBoundException if the connection has not been previously bound * @throws LdapException if an error is thrown during the rename operation */ public function move(Entry $entry, string $newParent) { $con = $this->getConnectionResource(); $rdn = $this->parseRdnFromEntry($entry); // deleteOldRdn does not matter here, since the Rdn will not be changing in the move. if (!@ldap_rename($con, $entry->getDn(), $rdn, $newParent, true)) { throw new LdapException(sprintf('Could not move entry "%s" to "%s": ', $entry->getDn(), $newParent).ldap_error($con), ldap_errno($con)); } } /** * Get the connection resource, but first check if the connection is bound. */ private function getConnectionResource() { // If the connection is not bound, throw an exception. Users should use an explicit bind call first. if (!$this->connection->isBound()) { throw new NotBoundException('Query execution is not possible without binding the connection first.'); } return $this->connection->getResource(); } /** * @param iterable<int, UpdateOperation> $operations An array or iterable of UpdateOperation instances * * @throws UpdateOperationException in case of an error */ public function applyOperations(string $dn, iterable $operations): void { $operationsMapped = []; foreach ($operations as $modification) { $operationsMapped[] = $modification->toArray(); } $con = $this->getConnectionResource(); if (!@ldap_modify_batch($con, $dn, $operationsMapped)) { throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($con), ldap_errno($con)); } } private function parseRdnFromEntry(Entry $entry): string { if (!preg_match('/(^[^,\\\\]*(?:\\\\.[^,\\\\]*)*),/', $entry->getDn(), $matches)) { throw new LdapException(sprintf('Entry "%s" malformed, could not parse RDN.', $entry->getDn())); } return $matches[1]; } } PK2A#]�����7vendor/symfony/ldap/Adapter/ExtLdap/UpdateOperation.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Exception\UpdateOperationException; class UpdateOperation { private $operationType; private $values; private $attribute; private const VALID_OPERATION_TYPES = [ \LDAP_MODIFY_BATCH_ADD, \LDAP_MODIFY_BATCH_REMOVE, \LDAP_MODIFY_BATCH_REMOVE_ALL, \LDAP_MODIFY_BATCH_REPLACE, ]; /** * @param int $operationType An LDAP_MODIFY_BATCH_* constant * @param string $attribute The attribute to batch modify on * * @throws UpdateOperationException on consistency errors during construction */ public function __construct(int $operationType, string $attribute, ?array $values) { if (!\in_array($operationType, self::VALID_OPERATION_TYPES, true)) { throw new UpdateOperationException(sprintf('"%s" is not a valid modification type.', $operationType)); } if (\LDAP_MODIFY_BATCH_REMOVE_ALL === $operationType && null !== $values) { throw new UpdateOperationException(sprintf('$values must be null for LDAP_MODIFY_BATCH_REMOVE_ALL operation, "%s" given.', get_debug_type($values))); } $this->operationType = $operationType; $this->attribute = $attribute; $this->values = $values; } public function toArray(): array { return [ 'attrib' => $this->attribute, 'modtype' => $this->operationType, 'values' => $this->values, ]; } } PK2A#]��U�UU9vendor/symfony/ldap/Adapter/ExtLdap/ConnectionOptions.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Exception\LdapException; /** * A class representing the Ldap extension's options, which can be used with * ldap_set_option or ldap_get_option. * * @author Charles Sarrazin <charles@sarraz.in> * * @internal */ final class ConnectionOptions { public const API_INFO = 0x00; public const DEREF = 0x02; public const SIZELIMIT = 0x03; public const TIMELIMIT = 0x04; public const REFERRALS = 0x08; public const RESTART = 0x09; public const PROTOCOL_VERSION = 0x11; public const SERVER_CONTROLS = 0x12; public const CLIENT_CONTROLS = 0x13; public const API_FEATURE_INFO = 0x15; public const HOST_NAME = 0x30; public const ERROR_NUMBER = 0x31; public const ERROR_STRING = 0x32; public const MATCHED_DN = 0x33; public const DEBUG_LEVEL = 0x5001; public const TIMEOUT = 0x5002; public const NETWORK_TIMEOUT = 0x5005; public const X_TLS_CACERTFILE = 0x6002; public const X_TLS_CACERTDIR = 0x6003; public const X_TLS_CERTFILE = 0x6004; public const X_TLS_CRL_ALL = 0x02; public const X_TLS_CRL_NONE = 0x00; public const X_TLS_CRL_PEER = 0x01; public const X_TLS_KEYFILE = 0x6005; public const X_TLS_REQUIRE_CERT = 0x6006; public const X_TLS_PROTOCOL_MIN = 0x6007; public const X_TLS_CIPHER_SUITE = 0x6008; public const X_TLS_RANDOM_FILE = 0x6009; public const X_TLS_CRLFILE = 0x6010; public const X_TLS_PACKAGE = 0x6011; public const X_TLS_CRLCHECK = 0x600B; public const X_TLS_DHFILE = 0x600E; public const X_SASL_MECH = 0x6100; public const X_SASL_REALM = 0x6101; public const X_SASL_AUTHCID = 0x6102; public const X_SASL_AUTHZID = 0x6103; public const X_KEEPALIVE_IDLE = 0x6300; public const X_KEEPALIVE_PROBES = 0x6301; public const X_KEEPALIVE_INTERVAL = 0x6302; public static function getOptionName(string $name): string { return sprintf('%s::%s', self::class, strtoupper($name)); } /** * Fetches an option's corresponding constant value from an option name. * The option name can either be in snake or camel case. * * @throws LdapException */ public static function getOption(string $name): int { // Convert $constantName = self::getOptionName($name); if (!\defined($constantName)) { throw new LdapException(sprintf('Unknown option "%s".', $name)); } return \constant($constantName); } public static function isOption(string $name): bool { return \defined(self::getOptionName($name)); } } PK2A#]B��2vendor/symfony/ldap/Adapter/ExtLdap/Collection.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\CollectionInterface; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\LdapException; /** * @author Charles Sarrazin <charles@sarraz.in> */ class Collection implements CollectionInterface { private $connection; private $search; /** @var list<Entry>|null */ private $entries; public function __construct(Connection $connection, Query $search) { $this->connection = $connection; $this->search = $search; } /** * {@inheritdoc} */ public function toArray() { if (null === $this->entries) { $this->entries = iterator_to_array($this->getIterator(), false); } return $this->entries; } /** * @return int */ #[\ReturnTypeWillChange] public function count() { $con = $this->connection->getResource(); $searches = $this->search->getResources(); $count = 0; foreach ($searches as $search) { $searchCount = ldap_count_entries($con, $search); if (false === $searchCount) { throw new LdapException('Error while retrieving entry count: '.ldap_error($con)); } $count += $searchCount; } return $count; } /** * @return \Traversable<int, Entry> */ #[\ReturnTypeWillChange] public function getIterator() { if (0 === $this->count()) { return; } $con = $this->connection->getResource(); $searches = $this->search->getResources(); foreach ($searches as $search) { $current = ldap_first_entry($con, $search); if (false === $current) { throw new LdapException('Could not rewind entries array: '.ldap_error($con)); } yield $this->getSingleEntry($con, $current); while (false !== $current = ldap_next_entry($con, $current)) { yield $this->getSingleEntry($con, $current); } } } /** * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($offset) { $this->toArray(); return isset($this->entries[$offset]); } /** * @return Entry|null */ #[\ReturnTypeWillChange] public function offsetGet($offset) { $this->toArray(); return $this->entries[$offset] ?? null; } /** * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->toArray(); $this->entries[$offset] = $value; } /** * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->toArray(); unset($this->entries[$offset]); } private function getSingleEntry($con, $current): Entry { $attributes = ldap_get_attributes($con, $current); if (false === $attributes) { throw new LdapException('Could not fetch attributes: '.ldap_error($con)); } $attributes = $this->cleanupAttributes($attributes); $dn = ldap_get_dn($con, $current); if (false === $dn) { throw new LdapException('Could not fetch DN: '.ldap_error($con)); } return new Entry($dn, $attributes); } private function cleanupAttributes(array $entry): array { $attributes = array_diff_key($entry, array_flip(range(0, $entry['count'] - 1)) + [ 'count' => null, 'dn' => null, ]); array_walk($attributes, function (&$value) { unset($value['count']); }); return $attributes; } } PK2A#]�.�o��2vendor/symfony/ldap/Adapter/ExtLdap/Connection.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use LDAP\Connection as LDAPConnection; use Symfony\Component\Ldap\Adapter\AbstractConnection; use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\Exception\ConnectionTimeoutException; use Symfony\Component\Ldap\Exception\InvalidCredentialsException; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; /** * @author Charles Sarrazin <charles@sarraz.in> */ class Connection extends AbstractConnection { private const LDAP_INVALID_CREDENTIALS = 0x31; private const LDAP_TIMEOUT = 0x55; private const LDAP_ALREADY_EXISTS = 0x44; private const PRECONNECT_OPTIONS = [ ConnectionOptions::DEBUG_LEVEL, ConnectionOptions::X_TLS_CACERTDIR, ConnectionOptions::X_TLS_CACERTFILE, ConnectionOptions::X_TLS_REQUIRE_CERT, ]; /** @var bool */ private $bound = false; /** @var resource|LDAPConnection */ private $connection; /** * @return array */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } public function __destruct() { $this->disconnect(); } /** * {@inheritdoc} */ public function isBound() { return $this->bound; } /** * {@inheritdoc} * * @param string $password WARNING: When the LDAP server allows unauthenticated binds, a blank $password will always be valid */ public function bind(string $dn = null, string $password = null) { if (!$this->connection) { $this->connect(); } if (false === @ldap_bind($this->connection, $dn, $password)) { $error = ldap_error($this->connection); switch (ldap_errno($this->connection)) { case self::LDAP_INVALID_CREDENTIALS: throw new InvalidCredentialsException($error); case self::LDAP_TIMEOUT: throw new ConnectionTimeoutException($error); case self::LDAP_ALREADY_EXISTS: throw new AlreadyExistsException($error); } throw new ConnectionException($error); } $this->bound = true; } /** * @return resource|LDAPConnection * * @internal */ public function getResource() { return $this->connection; } public function setOption(string $name, $value) { if (!@ldap_set_option($this->connection, ConnectionOptions::getOption($name), $value)) { throw new LdapException(sprintf('Could not set value "%s" for option "%s".', $value, $name)); } } public function getOption(string $name) { if (!@ldap_get_option($this->connection, ConnectionOptions::getOption($name), $ret)) { throw new LdapException(sprintf('Could not retrieve value for option "%s".', $name)); } return $ret; } protected function configureOptions(OptionsResolver $resolver) { parent::configureOptions($resolver); $resolver->setDefault('debug', false); $resolver->setAllowedTypes('debug', 'bool'); $resolver->setDefault('referrals', false); $resolver->setAllowedTypes('referrals', 'bool'); $resolver->setDefault('options', function (OptionsResolver $options, Options $parent) { $options->setDefined(array_map('strtolower', array_keys((new \ReflectionClass(ConnectionOptions::class))->getConstants()))); if (true === $parent['debug']) { $options->setDefault('debug_level', 7); } if (!isset($parent['network_timeout'])) { $options->setDefault('network_timeout', \ini_get('default_socket_timeout')); } $options->setDefaults([ 'protocol_version' => $parent['version'], 'referrals' => $parent['referrals'], ]); }); } private function connect() { if ($this->connection) { return; } foreach ($this->config['options'] as $name => $value) { if (\in_array(ConnectionOptions::getOption($name), self::PRECONNECT_OPTIONS, true)) { $this->setOption($name, $value); } } if (false === $connection = ldap_connect($this->config['connection_string'])) { throw new LdapException('Invalid connection string: '.$this->config['connection_string']); } else { $this->connection = $connection; } foreach ($this->config['options'] as $name => $value) { if (!\in_array(ConnectionOptions::getOption($name), self::PRECONNECT_OPTIONS, true)) { $this->setOption($name, $value); } } if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) { throw new LdapException('Could not initiate TLS connection: '.ldap_error($this->connection)); } } private function disconnect() { if ($this->connection) { ldap_unbind($this->connection); } $this->connection = null; $this->bound = false; } } PK2A#]�M0t(#(#-vendor/symfony/ldap/Adapter/ExtLdap/Query.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use LDAP\Connection as LDAPConnection; use LDAP\Result; use Symfony\Component\Ldap\Adapter\AbstractQuery; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; /** * @author Charles Sarrazin <charles@sarraz.in> * @author Bob van de Vijver <bobvandevijver@hotmail.com> */ class Query extends AbstractQuery { // As of PHP 7.2, we can use LDAP_CONTROL_PAGEDRESULTS instead of this public const PAGINATION_OID = '1.2.840.113556.1.4.319'; /** @var Connection */ protected $connection; /** @var resource[]|Result[] */ private $results; /** @var array */ private $serverctrls = []; public function __construct(Connection $connection, string $dn, string $query, array $options = []) { parent::__construct($connection, $dn, $query, $options); } /** * @return array */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } public function __destruct() { $con = $this->connection->getResource(); $this->connection = null; if (null === $this->results) { return; } foreach ($this->results as $result) { if (false === $result || null === $result) { continue; } if (!ldap_free_result($result)) { throw new LdapException('Could not free results: '.ldap_error($con)); } } $this->results = null; } /** * {@inheritdoc} */ public function execute() { if (null === $this->results) { // If the connection is not bound, throw an exception. Users should use an explicit bind call first. if (!$this->connection->isBound()) { throw new NotBoundException('Query execution is not possible without binding the connection first.'); } $this->results = []; $con = $this->connection->getResource(); switch ($this->options['scope']) { case static::SCOPE_BASE: $func = 'ldap_read'; break; case static::SCOPE_ONE: $func = 'ldap_list'; break; case static::SCOPE_SUB: $func = 'ldap_search'; break; default: throw new LdapException(sprintf('Could not search in scope "%s".', $this->options['scope'])); } $itemsLeft = $maxItems = $this->options['maxItems']; $pageSize = $this->options['pageSize']; // Deal with the logic to handle maxItems properly. If we can satisfy it in // one request based on pageSize, we don't need to bother sending page control // to the server so that it can determine what we already know. if (0 !== $maxItems && $pageSize > $maxItems) { $pageSize = 0; } elseif (0 !== $maxItems) { $pageSize = min($maxItems, $pageSize); } $pageControl = $this->options['scope'] != static::SCOPE_BASE && $pageSize > 0; $cookie = ''; do { if ($pageControl) { $this->controlPagedResult($con, $pageSize, true, $cookie); } $sizeLimit = $itemsLeft; if ($pageSize > 0 && $sizeLimit >= $pageSize) { $sizeLimit = 0; } $search = $this->callSearchFunction($con, $func, $sizeLimit); if (false === $search) { $ldapError = ''; if ($errno = ldap_errno($con)) { $ldapError = sprintf(' LDAP error was [%d] %s', $errno, ldap_error($con)); } if ($pageControl) { $this->resetPagination(); } throw new LdapException(sprintf('Could not complete search with dn "%s", query "%s" and filters "%s".%s.', $this->dn, $this->query, implode(',', $this->options['filter']), $ldapError)); } $this->results[] = $search; $itemsLeft -= min($itemsLeft, $pageSize); if (0 !== $maxItems && 0 === $itemsLeft) { break; } if ($pageControl) { $cookie = $this->controlPagedResultResponse($con, $search, $cookie); } } while (null !== $cookie && '' !== $cookie); if ($pageControl) { $this->resetPagination(); } } return new Collection($this->connection, $this); } /** * Returns an LDAP search resource. If this query resulted in multiple searches, only the first * page will be returned. * * @return resource|Result|null * * @internal */ public function getResource(int $idx = 0) { return $this->results[$idx] ?? null; } /** * Returns all LDAP search resources. * * @return resource[]|Result[] * * @internal */ public function getResources(): array { return $this->results; } /** * Resets pagination on the current connection. */ private function resetPagination() { $con = $this->connection->getResource(); $this->controlPagedResult($con, 0, false, ''); $this->serverctrls = []; // This is a workaround for a bit of a bug in the above invocation // of ldap_control_paged_result. Instead of indicating to extldap that // we no longer wish to page queries on this link, this invocation sets // the LDAP_CONTROL_PAGEDRESULTS OID with a page size of 0. This isn't // well defined by RFC 2696 if there is no cookie present, so some servers // will interpret it differently and do the wrong thing. Forcefully remove // the OID for now until a fix can make its way through the versions of PHP // the we support. // // This is not supported in PHP < 7.2, so these versions will remain broken. $ctl = []; ldap_get_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl); if (!empty($ctl)) { foreach ($ctl as $idx => $info) { if (static::PAGINATION_OID == $info['oid']) { unset($ctl[$idx]); } } ldap_set_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl); } } /** * Sets LDAP pagination controls. * * @param resource|LDAPConnection $con */ private function controlPagedResult($con, int $pageSize, bool $critical, string $cookie): bool { if (\PHP_VERSION_ID < 70300) { return ldap_control_paged_result($con, $pageSize, $critical, $cookie); } $this->serverctrls = [ [ 'oid' => \LDAP_CONTROL_PAGEDRESULTS, 'isCritical' => $critical, 'value' => [ 'size' => $pageSize, 'cookie' => $cookie, ], ], ]; return true; } /** * Retrieve LDAP pagination cookie. * * @param resource|LDAPConnection $con * @param resource|Result $result */ private function controlPagedResultResponse($con, $result, string $cookie = ''): string { if (\PHP_VERSION_ID < 70300) { ldap_control_paged_result_response($con, $result, $cookie); return $cookie; } ldap_parse_result($con, $result, $errcode, $matcheddn, $errmsg, $referrals, $controls); return $controls[\LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? ''; } /** * Calls actual LDAP search function with the prepared options and parameters. * * @param resource|LDAPConnection $con * * @return resource|Result|false */ private function callSearchFunction($con, callable $func, int $sizeLimit) { if (\PHP_VERSION_ID < 70300) { return @$func($con, $this->dn, $this->query, $this->options['filter'], $this->options['attrsOnly'], $sizeLimit, $this->options['timeout'], $this->options['deref']); } return @$func($con, $this->dn, $this->query, $this->options['filter'], $this->options['attrsOnly'], $sizeLimit, $this->options['timeout'], $this->options['deref'], $this->serverctrls); } } PK2A#]S�߂�/vendor/symfony/ldap/Adapter/ExtLdap/Adapter.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter\ExtLdap; use Symfony\Component\Ldap\Adapter\AdapterInterface; use Symfony\Component\Ldap\Exception\LdapException; /** * @author Charles Sarrazin <charles@sarraz.in> */ class Adapter implements AdapterInterface { private $config; private $connection; private $entryManager; public function __construct(array $config = []) { if (!\extension_loaded('ldap')) { throw new LdapException('The LDAP PHP extension is not enabled.'); } $this->config = $config; } /** * {@inheritdoc} */ public function getConnection() { if (null === $this->connection) { $this->connection = new Connection($this->config); } return $this->connection; } /** * {@inheritdoc} */ public function getEntryManager() { if (null === $this->entryManager) { $this->entryManager = new EntryManager($this->getConnection()); } return $this->entryManager; } /** * {@inheritdoc} */ public function createQuery(string $dn, string $query, array $options = []) { return new Query($this->getConnection(), $dn, $query, $options); } /** * {@inheritdoc} */ public function escape(string $subject, string $ignore = '', int $flags = 0) { $value = ldap_escape($subject, $ignore, $flags); // Per RFC 4514, leading/trailing spaces should be encoded in DNs, as well as carriage returns. if ($flags & \LDAP_ESCAPE_DN) { if (!empty($value) && ' ' === $value[0]) { $value = '\\20'.substr($value, 1); } if (!empty($value) && ' ' === $value[\strlen($value) - 1]) { $value = substr($value, 0, -1).'\\20'; } $value = str_replace("\r", '\0d', $value); } return $value; } } PK2A#]ւ��hh3vendor/symfony/ldap/Adapter/CollectionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\Ldap\Entry; /** * @author Charles Sarrazin <charles@sarraz.in> * * @extends \ArrayAccess<int, Entry> * @extends \IteratorAggregate<int, Entry> */ interface CollectionInterface extends \Countable, \IteratorAggregate, \ArrayAccess { /** * @return list<Entry> */ public function toArray(); } PK2A#]�w����0vendor/symfony/ldap/Adapter/AdapterInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; /** * @author Charles Sarrazin <charles@sarraz.in> */ interface AdapterInterface { /** * Returns the current connection. * * @return ConnectionInterface */ public function getConnection(); /** * Creates a new Query. * * @return QueryInterface */ public function createQuery(string $dn, string $query, array $options = []); /** * Fetches the entry manager instance. * * @return EntryManagerInterface */ public function getEntryManager(); /** * Escape a string for use in an LDAP filter or DN. * * @return string */ public function escape(string $subject, string $ignore = '', int $flags = 0); } PK2A#]. ~��.vendor/symfony/ldap/Adapter/QueryInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; /** * @author Charles Sarrazin <charles@sarraz.in> * @author Bob van de Vijver <bobvandevijver@hotmail.com> */ interface QueryInterface { public const DEREF_NEVER = 0x00; public const DEREF_SEARCHING = 0x01; public const DEREF_FINDING = 0x02; public const DEREF_ALWAYS = 0x03; public const SCOPE_BASE = 'base'; public const SCOPE_ONE = 'one'; public const SCOPE_SUB = 'sub'; /** * Executes a query and returns the list of Ldap entries. * * @return CollectionInterface * * @throws NotBoundException * @throws LdapException */ public function execute(); } PK2A#]D�PP-vendor/symfony/ldap/Adapter/AbstractQuery.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; /** * @author Charles Sarrazin <charles@sarraz.in> */ abstract class AbstractQuery implements QueryInterface { protected $connection; protected $dn; protected $query; protected $options; public function __construct(ConnectionInterface $connection, string $dn, string $query, array $options = []) { $resolver = new OptionsResolver(); $resolver->setDefaults([ 'filter' => '*', 'maxItems' => 0, 'sizeLimit' => 0, 'timeout' => 0, 'deref' => static::DEREF_NEVER, 'attrsOnly' => 0, 'scope' => static::SCOPE_SUB, 'pageSize' => 0, ]); $resolver->setAllowedValues('deref', [static::DEREF_ALWAYS, static::DEREF_NEVER, static::DEREF_FINDING, static::DEREF_SEARCHING]); $resolver->setAllowedValues('scope', [static::SCOPE_BASE, static::SCOPE_ONE, static::SCOPE_SUB]); $resolver->setNormalizer('filter', function (Options $options, $value) { return \is_array($value) ? $value : [$value]; }); $this->connection = $connection; $this->dn = $dn; $this->query = $query; $this->options = $resolver->resolve($options); } } PK2A#]|7 ҿ�3vendor/symfony/ldap/Adapter/ConnectionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Adapter; use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\ConnectionTimeoutException; use Symfony\Component\Ldap\Exception\InvalidCredentialsException; /** * @author Charles Sarrazin <charles@sarraz.in> */ interface ConnectionInterface { /** * Checks whether the connection was already bound or not. * * @return bool */ public function isBound(); /** * Binds the connection against a user's DN and password. * * @throws AlreadyExistsException When the connection can't be created because of an LDAP_ALREADY_EXISTS error * @throws ConnectionTimeoutException When the connection can't be created because of an LDAP_TIMEOUT error * @throws InvalidCredentialsException When the connection can't be created because of an LDAP_INVALID_CREDENTIALS error */ public function bind(string $dn = null, string $password = null); } PK2A#]NtIv��9vendor/symfony/ldap/Exception/DriverNotFoundException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * LdapException is thrown if php ldap module is not loaded. * * @author Charles Sarrazin <charles@sarraz.in> */ class DriverNotFoundException extends \RuntimeException implements ExceptionInterface { } PK2A#]��=�=vendor/symfony/ldap/Exception/InvalidCredentialsException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * InvalidCredentialsException is thrown if binding to ldap has been done with invalid credentials. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> */ class InvalidCredentialsException extends ConnectionException { } PK2A#]W�X�TT:vendor/symfony/ldap/Exception/UpdateOperationException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; class UpdateOperationException extends LdapException { } PK2A#]�����8vendor/symfony/ldap/Exception/AlreadyExistsException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * AlreadyExistsException is thrown if the element already exists. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> */ class AlreadyExistsException extends ConnectionException { } PK2A#]Ve�33vendor/symfony/ldap/Exception/NotBoundException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * NotBoundException is thrown if the connection with the LDAP server is not yet bound. * * @author Bob van de Vijver <bobvandevijver@hotmail.com> */ class NotBoundException extends \RuntimeException implements ExceptionInterface { } PK2A#]��Z��<vendor/symfony/ldap/Exception/ConnectionTimeoutException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * ConnectionTimeoutException is thrown if binding to ldap time out. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> */ class ConnectionTimeoutException extends ConnectionException { } PK2A#]&yp,��5vendor/symfony/ldap/Exception/ConnectionException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * ConnectionException is thrown if binding to ldap cannot be established. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class ConnectionException extends \RuntimeException implements ExceptionInterface { } PK2A#]����/vendor/symfony/ldap/Exception/LdapException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * LdapException is thrown if php ldap module is not loaded. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class LdapException extends \RuntimeException implements ExceptionInterface { } PK2A#]������4vendor/symfony/ldap/Exception/ExceptionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Exception; /** * Base ExceptionInterface for the Ldap component. * * @author Charles Sarrazin <charles@sarraz.in> */ interface ExceptionInterface extends \Throwable { } PK2A#]���U,,vendor/symfony/ldap/LICENSEnu�[���Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]� a�vendor/symfony/ldap/Ldap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap; use Symfony\Component\Ldap\Adapter\AdapterInterface; use Symfony\Component\Ldap\Adapter\EntryManagerInterface; use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter; use Symfony\Component\Ldap\Adapter\QueryInterface; use Symfony\Component\Ldap\Exception\DriverNotFoundException; /** * @author Charles Sarrazin <charles@sarraz.in> */ final class Ldap implements LdapInterface { private $adapter; public function __construct(AdapterInterface $adapter) { $this->adapter = $adapter; } /** * {@inheritdoc} */ public function bind(string $dn = null, string $password = null) { $this->adapter->getConnection()->bind($dn, $password); } /** * {@inheritdoc} */ public function query(string $dn, string $query, array $options = []): QueryInterface { return $this->adapter->createQuery($dn, $query, $options); } /** * {@inheritdoc} */ public function getEntryManager(): EntryManagerInterface { return $this->adapter->getEntryManager(); } /** * {@inheritdoc} */ public function escape(string $subject, string $ignore = '', int $flags = 0): string { return $this->adapter->escape($subject, $ignore, $flags); } /** * Creates a new Ldap instance. * * @param string $adapter The adapter name * @param array $config The adapter's configuration * * @return static */ public static function create(string $adapter, array $config = []): self { if ('ext_ldap' !== $adapter) { throw new DriverNotFoundException(sprintf('Adapter "%s" not found. Only "ext_ldap" is supported at the moment.', $adapter)); } return new self(new Adapter($config)); } } PK2A#]�#U*%vendor/symfony/ldap/LdapInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap; use Symfony\Component\Ldap\Adapter\EntryManagerInterface; use Symfony\Component\Ldap\Adapter\QueryInterface; use Symfony\Component\Ldap\Exception\ConnectionException; /** * Ldap interface. * * @author Charles Sarrazin <charles@sarraz.in> */ interface LdapInterface { public const ESCAPE_FILTER = 0x01; public const ESCAPE_DN = 0x02; /** * Return a connection bound to the ldap. * * @throws ConnectionException if dn / password could not be bound */ public function bind(string $dn = null, string $password = null); /** * Queries a ldap server for entries matching the given criteria. * * @return QueryInterface */ public function query(string $dn, string $query, array $options = []); /** * @return EntryManagerInterface */ public function getEntryManager(); /** * Escape a string for use in an LDAP filter or DN. * * @return string */ public function escape(string $subject, string $ignore = '', int $flags = 0); } PK2A#]K�6�^^vendor/symfony/ldap/Entry.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap; /** * @author Charles Sarrazin <charles@sarraz.in> * @author Karl Shea <karl@karlshea.com> */ class Entry { private $dn; /** * @var array<string, array> */ private $attributes = []; /** * @var array<string, string> */ private $lowerMap = []; /** * @param array<string, array> $attributes */ public function __construct(string $dn, array $attributes = []) { $this->dn = $dn; foreach ($attributes as $key => $attribute) { $this->setAttribute($key, $attribute); } } /** * Returns the entry's DN. * * @return string */ public function getDn() { return $this->dn; } /** * Returns whether an attribute exists. * * @param string $name The name of the attribute * @param bool $caseSensitive Whether the check should be case-sensitive * * @return bool */ public function hasAttribute(string $name/* , bool $caseSensitive = true */) { $caseSensitive = 2 > \func_num_args() || true === func_get_arg(1); $attributeKey = $this->getAttributeKey($name, $caseSensitive); if (null === $attributeKey) { return false; } return isset($this->attributes[$attributeKey]); } /** * Returns a specific attribute's value. * * As LDAP can return multiple values for a single attribute, * this value is returned as an array. * * @param string $name The name of the attribute * @param bool $caseSensitive Whether the attribute name is case-sensitive * * @return array|null */ public function getAttribute(string $name/* , bool $caseSensitive = true */) { $caseSensitive = 2 > \func_num_args() || true === func_get_arg(1); $attributeKey = $this->getAttributeKey($name, $caseSensitive); if (null === $attributeKey) { return null; } return $this->attributes[$attributeKey] ?? null; } /** * Returns the complete list of attributes. * * @return array */ public function getAttributes() { return $this->attributes; } /** * Sets a value for the given attribute. */ public function setAttribute(string $name, array $value) { $this->attributes[$name] = $value; $this->lowerMap[strtolower($name)] = $name; } /** * Removes a given attribute. */ public function removeAttribute(string $name) { unset($this->attributes[$name]); unset($this->lowerMap[strtolower($name)]); } private function getAttributeKey(string $name, bool $caseSensitive = true): ?string { if ($caseSensitive) { return $name; } return $this->lowerMap[strtolower($name)] ?? null; } } PK2A#]����=vendor/symfony/ldap/Security/CheckLdapCredentialsListener.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Security; use Psr\Container\ContainerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\LdapInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** * Verifies password credentials using an LDAP service whenever the * LdapBadge is attached to the Security passport. * * @author Wouter de Jong <wouter@wouterj.nl> */ class CheckLdapCredentialsListener implements EventSubscriberInterface { private $ldapLocator; public function __construct(ContainerInterface $ldapLocator) { $this->ldapLocator = $ldapLocator; } public function onCheckPassport(CheckPassportEvent $event) { $passport = $event->getPassport(); if (!$passport->hasBadge(LdapBadge::class)) { return; } /** @var LdapBadge $ldapBadge */ $ldapBadge = $passport->getBadge(LdapBadge::class); if ($ldapBadge->isResolved()) { return; } if (!$passport instanceof UserPassportInterface || !$passport->hasBadge(PasswordCredentials::class)) { throw new \LogicException(sprintf('LDAP authentication requires a passport containing a user and password credentials, authenticator "%s" does not fulfill these requirements.', \get_class($event->getAuthenticator()))); } /** @var PasswordCredentials $passwordCredentials */ $passwordCredentials = $passport->getBadge(PasswordCredentials::class); if ($passwordCredentials->isResolved()) { throw new \LogicException('LDAP authentication password verification cannot be completed because something else has already resolved the PasswordCredentials.'); } if (!$this->ldapLocator->has($ldapBadge->getLdapServiceId())) { throw new \LogicException(sprintf('Cannot check credentials using the "%s" ldap service, as such service is not found. Did you maybe forget to add the "ldap" service tag to this service?', $ldapBadge->getLdapServiceId())); } $presentedPassword = $passwordCredentials->getPassword(); if ('' === $presentedPassword) { throw new BadCredentialsException('The presented password cannot be empty.'); } $user = $passport->getUser(); if (!$user instanceof PasswordAuthenticatedUserInterface) { trigger_deprecation('symfony/ldap', '5.3', 'Not implementing the "%s" interface in class "%s" while using password-based authenticators is deprecated.', PasswordAuthenticatedUserInterface::class, get_debug_type($user)); } /** @var LdapInterface $ldap */ $ldap = $this->ldapLocator->get($ldapBadge->getLdapServiceId()); try { if ($ldapBadge->getQueryString()) { if ('' !== $ldapBadge->getSearchDn() && '' !== $ldapBadge->getSearchPassword()) { $ldap->bind($ldapBadge->getSearchDn(), $ldapBadge->getSearchPassword()); } else { throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); } // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 $username = $ldap->escape(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), '', LdapInterface::ESCAPE_FILTER); $query = str_replace(['{username}', '{user_identifier}'], $username, $ldapBadge->getQueryString()); $result = $ldap->query($ldapBadge->getDnString(), $query)->execute(); if (1 !== $result->count()) { throw new BadCredentialsException('The presented username is invalid.'); } $dn = $result[0]->getDn(); } else { // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 $username = $ldap->escape(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), '', LdapInterface::ESCAPE_DN); $dn = str_replace('{username}', $username, $ldapBadge->getDnString()); } $ldap->bind($dn, $presentedPassword); } catch (ConnectionException $e) { throw new BadCredentialsException('The presented password is invalid.'); } $passwordCredentials->markResolved(); $ldapBadge->markResolved(); } public static function getSubscribedEvents(): array { return [CheckPassportEvent::class => ['onCheckPassport', 144]]; } } PK2A#]�L�:*vendor/symfony/ldap/Security/LdapBadge.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Security; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface; /** * A badge indicating that the credentials should be checked using LDAP. * * This badge must be used together with PasswordCredentials. * * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class LdapBadge implements BadgeInterface { private $resolved = false; private $ldapServiceId; private $dnString; private $searchDn; private $searchPassword; private $queryString; public function __construct(string $ldapServiceId, string $dnString = '{username}', string $searchDn = '', string $searchPassword = '', string $queryString = null) { $this->ldapServiceId = $ldapServiceId; $this->dnString = $dnString; $this->searchDn = $searchDn; $this->searchPassword = $searchPassword; $this->queryString = $queryString; } public function getLdapServiceId(): string { return $this->ldapServiceId; } public function getDnString(): string { return $this->dnString; } public function getSearchDn(): string { return $this->searchDn; } public function getSearchPassword(): string { return $this->searchPassword; } public function getQueryString(): ?string { return $this->queryString; } public function markResolved(): void { $this->resolved = true; } public function isResolved(): bool { return $this->resolved; } } PK2A#]oO���1vendor/symfony/ldap/Security/LdapUserProvider.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Security; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\Exception\ExceptionInterface; use Symfony\Component\Ldap\LdapInterface; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; /** * LdapUserProvider is a simple user provider on top of LDAP. * * @author Grégoire Pineau <lyrixx@lyrixx.info> * @author Charles Sarrazin <charles@sarraz.in> * @author Robin Chalas <robin.chalas@gmail.com> */ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterface { private $ldap; private $baseDn; private $searchDn; private $searchPassword; private $defaultRoles; private $uidKey; private $defaultSearch; private $passwordAttribute; private $extraFields; public function __construct(LdapInterface $ldap, string $baseDn, string $searchDn = null, string $searchPassword = null, array $defaultRoles = [], string $uidKey = null, string $filter = null, string $passwordAttribute = null, array $extraFields = []) { if (null === $uidKey) { $uidKey = 'sAMAccountName'; } if (null === $filter) { $filter = '({uid_key}={user_identifier})'; } $this->ldap = $ldap; $this->baseDn = $baseDn; $this->searchDn = $searchDn; $this->searchPassword = $searchPassword; $this->defaultRoles = $defaultRoles; $this->uidKey = $uidKey; $this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter); $this->passwordAttribute = $passwordAttribute; $this->extraFields = $extraFields; } /** * {@inheritdoc} */ public function loadUserByUsername(string $username) { trigger_deprecation('symfony/ldap', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); return $this->loadUserByIdentifier($username); } public function loadUserByIdentifier(string $identifier): UserInterface { try { $this->ldap->bind($this->searchDn, $this->searchPassword); $identifier = $this->ldap->escape($identifier, '', LdapInterface::ESCAPE_FILTER); $query = str_replace(['{username}', '{user_identifier}'], $identifier, $this->defaultSearch); $search = $this->ldap->query($this->baseDn, $query, ['filter' => 0 == \count($this->extraFields) ? '*' : $this->extraFields]); } catch (ConnectionException $e) { $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier), 0, $e); $e->setUserIdentifier($identifier); throw $e; } $entries = $search->execute(); $count = \count($entries); if (!$count) { $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier)); $e->setUserIdentifier($identifier); throw $e; } if ($count > 1) { $e = new UserNotFoundException('More than one user found.'); $e->setUserIdentifier($identifier); throw $e; } $entry = $entries[0]; try { if (null !== $this->uidKey) { $identifier = $this->getAttributeValue($entry, $this->uidKey); } } catch (InvalidArgumentException $e) { } return $this->loadUser($identifier, $entry); } /** * {@inheritdoc} */ public function refreshUser(UserInterface $user) { if (!$user instanceof LdapUser) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } return new LdapUser($user->getEntry(), $user->getUserIdentifier(), $user->getPassword(), $user->getRoles(), $user->getExtraFields()); } /** * {@inheritdoc} * * @final */ public function upgradePassword($user, string $newHashedPassword): void { if (!$user instanceof LdapUser) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } if (null === $this->passwordAttribute) { return; } try { $user->getEntry()->setAttribute($this->passwordAttribute, [$newHashedPassword]); $this->ldap->getEntryManager()->update($user->getEntry()); $user->setPassword($newHashedPassword); } catch (ExceptionInterface $e) { // ignore failed password upgrades } } /** * {@inheritdoc} */ public function supportsClass(string $class) { return LdapUser::class === $class; } /** * Loads a user from an LDAP entry. * * @return UserInterface */ protected function loadUser(string $identifier, Entry $entry) { $password = null; $extraFields = []; if (null !== $this->passwordAttribute) { $password = $this->getAttributeValue($entry, $this->passwordAttribute); } foreach ($this->extraFields as $field) { $extraFields[$field] = $this->getAttributeValue($entry, $field); } return new LdapUser($entry, $identifier, $password, $this->defaultRoles, $extraFields); } private function getAttributeValue(Entry $entry, string $attribute) { if (!$entry->hasAttribute($attribute)) { throw new InvalidArgumentException(sprintf('Missing attribute "%s" for user "%s".', $attribute, $entry->getDn())); } $values = $entry->getAttribute($attribute); if (!\in_array($attribute, [$this->uidKey, $this->passwordAttribute])) { return $values; } if (1 !== \count($values)) { throw new InvalidArgumentException(sprintf('Attribute "%s" has multiple values.', $attribute)); } return $values[0]; } } PK2A#]eҸ̴�2vendor/symfony/ldap/Security/LdapAuthenticator.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Security; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException; /** * This class decorates internal authenticators to add the LDAP integration. * * In your own authenticators, it is recommended to directly use the * LdapBadge in the authenticate() method. This class should only be * used for Symfony or third party authenticators. * * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class LdapAuthenticator implements AuthenticationEntryPointInterface, InteractiveAuthenticatorInterface { private $authenticator; private $ldapServiceId; private $dnString; private $searchDn; private $searchPassword; private $queryString; public function __construct(AuthenticatorInterface $authenticator, string $ldapServiceId, string $dnString = '{username}', string $searchDn = '', string $searchPassword = '', string $queryString = '') { $this->authenticator = $authenticator; $this->ldapServiceId = $ldapServiceId; $this->dnString = $dnString; $this->searchDn = $searchDn; $this->searchPassword = $searchPassword; $this->queryString = $queryString; } public function supports(Request $request): ?bool { return $this->authenticator->supports($request); } public function authenticate(Request $request): Passport { $passport = $this->authenticator->authenticate($request); $passport->addBadge(new LdapBadge($this->ldapServiceId, $this->dnString, $this->searchDn, $this->searchPassword, $this->queryString)); return $passport; } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/ldap', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } public function createToken(Passport $passport, string $firewallName): TokenInterface { // @deprecated since Symfony 5.4, in 6.0 change to: // return $this->authenticator->createToken($passport, $firewallName); return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { return $this->authenticator->onAuthenticationFailure($request, $exception); } public function start(Request $request, AuthenticationException $authException = null): Response { if (!$this->authenticator instanceof AuthenticationEntryPointInterface) { throw new NotAnEntryPointException(sprintf('Decorated authenticator "%s" does not implement interface "%s".', get_debug_type($this->authenticator), AuthenticationEntryPointInterface::class)); } return $this->authenticator->start($request, $authException); } public function isInteractive(): bool { if ($this->authenticator instanceof InteractiveAuthenticatorInterface) { return $this->authenticator->isInteractive(); } return false; } } PK2A#]�Ӝ##)vendor/symfony/ldap/Security/LdapUser.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Ldap\Security; use Symfony\Component\Ldap\Entry; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; /** * @author Robin Chalas <robin.chalas@gmail.com> * * @final */ class LdapUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface { private $entry; private $username; private $password; private $roles; private $extraFields; public function __construct(Entry $entry, string $username, ?string $password, array $roles = [], array $extraFields = []) { if (!$username) { throw new \InvalidArgumentException('The username cannot be empty.'); } $this->entry = $entry; $this->username = $username; $this->password = $password; $this->roles = $roles; $this->extraFields = $extraFields; } public function getEntry(): Entry { return $this->entry; } /** * {@inheritdoc} */ public function getRoles(): array { return $this->roles; } /** * {@inheritdoc} */ public function getPassword(): ?string { return $this->password; } /** * {@inheritdoc} */ public function getSalt(): ?string { return null; } /** * {@inheritdoc} */ public function getUsername(): string { trigger_deprecation('symfony/ldap', '5.3', 'Method "%s()" is deprecated and will be removed in 6.0, use getUserIdentifier() instead.', __METHOD__); return $this->username; } public function getUserIdentifier(): string { return $this->username; } /** * {@inheritdoc} */ public function eraseCredentials() { $this->password = null; } public function getExtraFields(): array { return $this->extraFields; } public function setPassword(string $password) { $this->password = $password; } /** * {@inheritdoc} */ public function isEqualTo(UserInterface $user): bool { if (!$user instanceof self) { return false; } if ($this->getPassword() !== $user->getPassword()) { return false; } if ($this->getSalt() !== $user->getSalt()) { return false; } if ($this->getUserIdentifier() !== $user->getUserIdentifier()) { return false; } return true; } } PK2A#]�,��a#a#0vendor/symfony/polyfill-mbstring/bootstrap80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Mbstring as p; if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); } } if (!function_exists('mb_decode_mimeheader')) { function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); } } if (!function_exists('mb_encode_mimeheader')) { function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); } } if (!function_exists('mb_decode_numericentity')) { function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); } } if (!function_exists('mb_encode_numericentity')) { function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); } } if (!function_exists('mb_convert_case')) { function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); } } if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } } if (!function_exists('mb_language')) { function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); } } if (!function_exists('mb_list_encodings')) { function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } } if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); } } if (!function_exists('mb_check_encoding')) { function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } } if (!function_exists('mb_detect_encoding')) { function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); } } if (!function_exists('mb_detect_order')) { function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } } if (!function_exists('mb_parse_str')) { function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; } } if (!function_exists('mb_strlen')) { function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); } } if (!function_exists('mb_strpos')) { function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strtolower')) { function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); } } if (!function_exists('mb_strtoupper')) { function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); } } if (!function_exists('mb_substitute_character')) { function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } } if (!function_exists('mb_substr')) { function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); } } if (!function_exists('mb_stripos')) { function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_stristr')) { function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strrchr')) { function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strrichr')) { function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strripos')) { function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strrpos')) { function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strstr')) { function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_get_info')) { function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); } } if (!function_exists('mb_http_output')) { function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } } if (!function_exists('mb_strwidth')) { function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); } } if (!function_exists('mb_substr_count')) { function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); } } if (!function_exists('mb_output_handler')) { function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); } } if (!function_exists('mb_http_input')) { function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } } if (!function_exists('mb_convert_variables')) { function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); } } if (!function_exists('mb_ord')) { function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } } if (!function_exists('mb_str_pad')) { function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } } if (extension_loaded('mbstring')) { return; } if (!defined('MB_CASE_UPPER')) { define('MB_CASE_UPPER', 0); } if (!defined('MB_CASE_LOWER')) { define('MB_CASE_LOWER', 1); } if (!defined('MB_CASE_TITLE')) { define('MB_CASE_TITLE', 2); } PK2A#]��H,,(vendor/symfony/polyfill-mbstring/LICENSEnu�[���Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]�P��f�f@vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.phpnu�[���<?php return array ( 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F', 'g' => 'G', 'h' => 'H', 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', 'm' => 'M', 'n' => 'N', 'o' => 'O', 'p' => 'P', 'q' => 'Q', 'r' => 'R', 's' => 'S', 't' => 'T', 'u' => 'U', 'v' => 'V', 'w' => 'W', 'x' => 'X', 'y' => 'Y', 'z' => 'Z', 'µ' => 'Μ', 'à' => 'À', 'á' => 'Á', 'â' => 'Â', 'ã' => 'Ã', 'ä' => 'Ä', 'å' => 'Å', 'æ' => 'Æ', 'ç' => 'Ç', 'è' => 'È', 'é' => 'É', 'ê' => 'Ê', 'ë' => 'Ë', 'ì' => 'Ì', 'í' => 'Í', 'î' => 'Î', 'ï' => 'Ï', 'ð' => 'Ð', 'ñ' => 'Ñ', 'ò' => 'Ò', 'ó' => 'Ó', 'ô' => 'Ô', 'õ' => 'Õ', 'ö' => 'Ö', 'ø' => 'Ø', 'ù' => 'Ù', 'ú' => 'Ú', 'û' => 'Û', 'ü' => 'Ü', 'ý' => 'Ý', 'þ' => 'Þ', 'ÿ' => 'Ÿ', 'ā' => 'Ā', 'ă' => 'Ă', 'ą' => 'Ą', 'ć' => 'Ć', 'ĉ' => 'Ĉ', 'ċ' => 'Ċ', 'č' => 'Č', 'ď' => 'Ď', 'đ' => 'Đ', 'ē' => 'Ē', 'ĕ' => 'Ĕ', 'ė' => 'Ė', 'ę' => 'Ę', 'ě' => 'Ě', 'ĝ' => 'Ĝ', 'ğ' => 'Ğ', 'ġ' => 'Ġ', 'ģ' => 'Ģ', 'ĥ' => 'Ĥ', 'ħ' => 'Ħ', 'ĩ' => 'Ĩ', 'ī' => 'Ī', 'ĭ' => 'Ĭ', 'į' => 'Į', 'ı' => 'I', 'ij' => 'IJ', 'ĵ' => 'Ĵ', 'ķ' => 'Ķ', 'ĺ' => 'Ĺ', 'ļ' => 'Ļ', 'ľ' => 'Ľ', 'ŀ' => 'Ŀ', 'ł' => 'Ł', 'ń' => 'Ń', 'ņ' => 'Ņ', 'ň' => 'Ň', 'ŋ' => 'Ŋ', 'ō' => 'Ō', 'ŏ' => 'Ŏ', 'ő' => 'Ő', 'œ' => 'Œ', 'ŕ' => 'Ŕ', 'ŗ' => 'Ŗ', 'ř' => 'Ř', 'ś' => 'Ś', 'ŝ' => 'Ŝ', 'ş' => 'Ş', 'š' => 'Š', 'ţ' => 'Ţ', 'ť' => 'Ť', 'ŧ' => 'Ŧ', 'ũ' => 'Ũ', 'ū' => 'Ū', 'ŭ' => 'Ŭ', 'ů' => 'Ů', 'ű' => 'Ű', 'ų' => 'Ų', 'ŵ' => 'Ŵ', 'ŷ' => 'Ŷ', 'ź' => 'Ź', 'ż' => 'Ż', 'ž' => 'Ž', 'ſ' => 'S', 'ƀ' => 'Ƀ', 'ƃ' => 'Ƃ', 'ƅ' => 'Ƅ', 'ƈ' => 'Ƈ', 'ƌ' => 'Ƌ', 'ƒ' => 'Ƒ', 'ƕ' => 'Ƕ', 'ƙ' => 'Ƙ', 'ƚ' => 'Ƚ', 'ƞ' => 'Ƞ', 'ơ' => 'Ơ', 'ƣ' => 'Ƣ', 'ƥ' => 'Ƥ', 'ƨ' => 'Ƨ', 'ƭ' => 'Ƭ', 'ư' => 'Ư', 'ƴ' => 'Ƴ', 'ƶ' => 'Ƶ', 'ƹ' => 'Ƹ', 'ƽ' => 'Ƽ', 'ƿ' => 'Ƿ', 'Dž' => 'DŽ', 'dž' => 'DŽ', 'Lj' => 'LJ', 'lj' => 'LJ', 'Nj' => 'NJ', 'nj' => 'NJ', 'ǎ' => 'Ǎ', 'ǐ' => 'Ǐ', 'ǒ' => 'Ǒ', 'ǔ' => 'Ǔ', 'ǖ' => 'Ǖ', 'ǘ' => 'Ǘ', 'ǚ' => 'Ǚ', 'ǜ' => 'Ǜ', 'ǝ' => 'Ǝ', 'ǟ' => 'Ǟ', 'ǡ' => 'Ǡ', 'ǣ' => 'Ǣ', 'ǥ' => 'Ǥ', 'ǧ' => 'Ǧ', 'ǩ' => 'Ǩ', 'ǫ' => 'Ǫ', 'ǭ' => 'Ǭ', 'ǯ' => 'Ǯ', 'Dz' => 'DZ', 'dz' => 'DZ', 'ǵ' => 'Ǵ', 'ǹ' => 'Ǹ', 'ǻ' => 'Ǻ', 'ǽ' => 'Ǽ', 'ǿ' => 'Ǿ', 'ȁ' => 'Ȁ', 'ȃ' => 'Ȃ', 'ȅ' => 'Ȅ', 'ȇ' => 'Ȇ', 'ȉ' => 'Ȉ', 'ȋ' => 'Ȋ', 'ȍ' => 'Ȍ', 'ȏ' => 'Ȏ', 'ȑ' => 'Ȑ', 'ȓ' => 'Ȓ', 'ȕ' => 'Ȕ', 'ȗ' => 'Ȗ', 'ș' => 'Ș', 'ț' => 'Ț', 'ȝ' => 'Ȝ', 'ȟ' => 'Ȟ', 'ȣ' => 'Ȣ', 'ȥ' => 'Ȥ', 'ȧ' => 'Ȧ', 'ȩ' => 'Ȩ', 'ȫ' => 'Ȫ', 'ȭ' => 'Ȭ', 'ȯ' => 'Ȯ', 'ȱ' => 'Ȱ', 'ȳ' => 'Ȳ', 'ȼ' => 'Ȼ', 'ȿ' => 'Ȿ', 'ɀ' => 'Ɀ', 'ɂ' => 'Ɂ', 'ɇ' => 'Ɇ', 'ɉ' => 'Ɉ', 'ɋ' => 'Ɋ', 'ɍ' => 'Ɍ', 'ɏ' => 'Ɏ', 'ɐ' => 'Ɐ', 'ɑ' => 'Ɑ', 'ɒ' => 'Ɒ', 'ɓ' => 'Ɓ', 'ɔ' => 'Ɔ', 'ɖ' => 'Ɖ', 'ɗ' => 'Ɗ', 'ə' => 'Ə', 'ɛ' => 'Ɛ', 'ɜ' => 'Ɜ', 'ɠ' => 'Ɠ', 'ɡ' => 'Ɡ', 'ɣ' => 'Ɣ', 'ɥ' => 'Ɥ', 'ɦ' => 'Ɦ', 'ɨ' => 'Ɨ', 'ɩ' => 'Ɩ', 'ɪ' => 'Ɪ', 'ɫ' => 'Ɫ', 'ɬ' => 'Ɬ', 'ɯ' => 'Ɯ', 'ɱ' => 'Ɱ', 'ɲ' => 'Ɲ', 'ɵ' => 'Ɵ', 'ɽ' => 'Ɽ', 'ʀ' => 'Ʀ', 'ʂ' => 'Ʂ', 'ʃ' => 'Ʃ', 'ʇ' => 'Ʇ', 'ʈ' => 'Ʈ', 'ʉ' => 'Ʉ', 'ʊ' => 'Ʊ', 'ʋ' => 'Ʋ', 'ʌ' => 'Ʌ', 'ʒ' => 'Ʒ', 'ʝ' => 'Ʝ', 'ʞ' => 'Ʞ', 'ͅ' => 'Ι', 'ͱ' => 'Ͱ', 'ͳ' => 'Ͳ', 'ͷ' => 'Ͷ', 'ͻ' => 'Ͻ', 'ͼ' => 'Ͼ', 'ͽ' => 'Ͽ', 'ά' => 'Ά', 'έ' => 'Έ', 'ή' => 'Ή', 'ί' => 'Ί', 'α' => 'Α', 'β' => 'Β', 'γ' => 'Γ', 'δ' => 'Δ', 'ε' => 'Ε', 'ζ' => 'Ζ', 'η' => 'Η', 'θ' => 'Θ', 'ι' => 'Ι', 'κ' => 'Κ', 'λ' => 'Λ', 'μ' => 'Μ', 'ν' => 'Ν', 'ξ' => 'Ξ', 'ο' => 'Ο', 'π' => 'Π', 'ρ' => 'Ρ', 'ς' => 'Σ', 'σ' => 'Σ', 'τ' => 'Τ', 'υ' => 'Υ', 'φ' => 'Φ', 'χ' => 'Χ', 'ψ' => 'Ψ', 'ω' => 'Ω', 'ϊ' => 'Ϊ', 'ϋ' => 'Ϋ', 'ό' => 'Ό', 'ύ' => 'Ύ', 'ώ' => 'Ώ', 'ϐ' => 'Β', 'ϑ' => 'Θ', 'ϕ' => 'Φ', 'ϖ' => 'Π', 'ϗ' => 'Ϗ', 'ϙ' => 'Ϙ', 'ϛ' => 'Ϛ', 'ϝ' => 'Ϝ', 'ϟ' => 'Ϟ', 'ϡ' => 'Ϡ', 'ϣ' => 'Ϣ', 'ϥ' => 'Ϥ', 'ϧ' => 'Ϧ', 'ϩ' => 'Ϩ', 'ϫ' => 'Ϫ', 'ϭ' => 'Ϭ', 'ϯ' => 'Ϯ', 'ϰ' => 'Κ', 'ϱ' => 'Ρ', 'ϲ' => 'Ϲ', 'ϳ' => 'Ϳ', 'ϵ' => 'Ε', 'ϸ' => 'Ϸ', 'ϻ' => 'Ϻ', 'а' => 'А', 'б' => 'Б', 'в' => 'В', 'г' => 'Г', 'д' => 'Д', 'е' => 'Е', 'ж' => 'Ж', 'з' => 'З', 'и' => 'И', 'й' => 'Й', 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Н', 'о' => 'О', 'п' => 'П', 'р' => 'Р', 'с' => 'С', 'т' => 'Т', 'у' => 'У', 'ф' => 'Ф', 'х' => 'Х', 'ц' => 'Ц', 'ч' => 'Ч', 'ш' => 'Ш', 'щ' => 'Щ', 'ъ' => 'Ъ', 'ы' => 'Ы', 'ь' => 'Ь', 'э' => 'Э', 'ю' => 'Ю', 'я' => 'Я', 'ѐ' => 'Ѐ', 'ё' => 'Ё', 'ђ' => 'Ђ', 'ѓ' => 'Ѓ', 'є' => 'Є', 'ѕ' => 'Ѕ', 'і' => 'І', 'ї' => 'Ї', 'ј' => 'Ј', 'љ' => 'Љ', 'њ' => 'Њ', 'ћ' => 'Ћ', 'ќ' => 'Ќ', 'ѝ' => 'Ѝ', 'ў' => 'Ў', 'џ' => 'Џ', 'ѡ' => 'Ѡ', 'ѣ' => 'Ѣ', 'ѥ' => 'Ѥ', 'ѧ' => 'Ѧ', 'ѩ' => 'Ѩ', 'ѫ' => 'Ѫ', 'ѭ' => 'Ѭ', 'ѯ' => 'Ѯ', 'ѱ' => 'Ѱ', 'ѳ' => 'Ѳ', 'ѵ' => 'Ѵ', 'ѷ' => 'Ѷ', 'ѹ' => 'Ѹ', 'ѻ' => 'Ѻ', 'ѽ' => 'Ѽ', 'ѿ' => 'Ѿ', 'ҁ' => 'Ҁ', 'ҋ' => 'Ҋ', 'ҍ' => 'Ҍ', 'ҏ' => 'Ҏ', 'ґ' => 'Ґ', 'ғ' => 'Ғ', 'ҕ' => 'Ҕ', 'җ' => 'Җ', 'ҙ' => 'Ҙ', 'қ' => 'Қ', 'ҝ' => 'Ҝ', 'ҟ' => 'Ҟ', 'ҡ' => 'Ҡ', 'ң' => 'Ң', 'ҥ' => 'Ҥ', 'ҧ' => 'Ҧ', 'ҩ' => 'Ҩ', 'ҫ' => 'Ҫ', 'ҭ' => 'Ҭ', 'ү' => 'Ү', 'ұ' => 'Ұ', 'ҳ' => 'Ҳ', 'ҵ' => 'Ҵ', 'ҷ' => 'Ҷ', 'ҹ' => 'Ҹ', 'һ' => 'Һ', 'ҽ' => 'Ҽ', 'ҿ' => 'Ҿ', 'ӂ' => 'Ӂ', 'ӄ' => 'Ӄ', 'ӆ' => 'Ӆ', 'ӈ' => 'Ӈ', 'ӊ' => 'Ӊ', 'ӌ' => 'Ӌ', 'ӎ' => 'Ӎ', 'ӏ' => 'Ӏ', 'ӑ' => 'Ӑ', 'ӓ' => 'Ӓ', 'ӕ' => 'Ӕ', 'ӗ' => 'Ӗ', 'ә' => 'Ә', 'ӛ' => 'Ӛ', 'ӝ' => 'Ӝ', 'ӟ' => 'Ӟ', 'ӡ' => 'Ӡ', 'ӣ' => 'Ӣ', 'ӥ' => 'Ӥ', 'ӧ' => 'Ӧ', 'ө' => 'Ө', 'ӫ' => 'Ӫ', 'ӭ' => 'Ӭ', 'ӯ' => 'Ӯ', 'ӱ' => 'Ӱ', 'ӳ' => 'Ӳ', 'ӵ' => 'Ӵ', 'ӷ' => 'Ӷ', 'ӹ' => 'Ӹ', 'ӻ' => 'Ӻ', 'ӽ' => 'Ӽ', 'ӿ' => 'Ӿ', 'ԁ' => 'Ԁ', 'ԃ' => 'Ԃ', 'ԅ' => 'Ԅ', 'ԇ' => 'Ԇ', 'ԉ' => 'Ԉ', 'ԋ' => 'Ԋ', 'ԍ' => 'Ԍ', 'ԏ' => 'Ԏ', 'ԑ' => 'Ԑ', 'ԓ' => 'Ԓ', 'ԕ' => 'Ԕ', 'ԗ' => 'Ԗ', 'ԙ' => 'Ԙ', 'ԛ' => 'Ԛ', 'ԝ' => 'Ԝ', 'ԟ' => 'Ԟ', 'ԡ' => 'Ԡ', 'ԣ' => 'Ԣ', 'ԥ' => 'Ԥ', 'ԧ' => 'Ԧ', 'ԩ' => 'Ԩ', 'ԫ' => 'Ԫ', 'ԭ' => 'Ԭ', 'ԯ' => 'Ԯ', 'ա' => 'Ա', 'բ' => 'Բ', 'գ' => 'Գ', 'դ' => 'Դ', 'ե' => 'Ե', 'զ' => 'Զ', 'է' => 'Է', 'ը' => 'Ը', 'թ' => 'Թ', 'ժ' => 'Ժ', 'ի' => 'Ի', 'լ' => 'Լ', 'խ' => 'Խ', 'ծ' => 'Ծ', 'կ' => 'Կ', 'հ' => 'Հ', 'ձ' => 'Ձ', 'ղ' => 'Ղ', 'ճ' => 'Ճ', 'մ' => 'Մ', 'յ' => 'Յ', 'ն' => 'Ն', 'շ' => 'Շ', 'ո' => 'Ո', 'չ' => 'Չ', 'պ' => 'Պ', 'ջ' => 'Ջ', 'ռ' => 'Ռ', 'ս' => 'Ս', 'վ' => 'Վ', 'տ' => 'Տ', 'ր' => 'Ր', 'ց' => 'Ց', 'ւ' => 'Ւ', 'փ' => 'Փ', 'ք' => 'Ք', 'օ' => 'Օ', 'ֆ' => 'Ֆ', 'ა' => 'Ა', 'ბ' => 'Ბ', 'გ' => 'Გ', 'დ' => 'Დ', 'ე' => 'Ე', 'ვ' => 'Ვ', 'ზ' => 'Ზ', 'თ' => 'Თ', 'ი' => 'Ი', 'კ' => 'Კ', 'ლ' => 'Ლ', 'მ' => 'Მ', 'ნ' => 'Ნ', 'ო' => 'Ო', 'პ' => 'Პ', 'ჟ' => 'Ჟ', 'რ' => 'Რ', 'ს' => 'Ს', 'ტ' => 'Ტ', 'უ' => 'Უ', 'ფ' => 'Ფ', 'ქ' => 'Ქ', 'ღ' => 'Ღ', 'ყ' => 'Ყ', 'შ' => 'Შ', 'ჩ' => 'Ჩ', 'ც' => 'Ც', 'ძ' => 'Ძ', 'წ' => 'Წ', 'ჭ' => 'Ჭ', 'ხ' => 'Ხ', 'ჯ' => 'Ჯ', 'ჰ' => 'Ჰ', 'ჱ' => 'Ჱ', 'ჲ' => 'Ჲ', 'ჳ' => 'Ჳ', 'ჴ' => 'Ჴ', 'ჵ' => 'Ჵ', 'ჶ' => 'Ჶ', 'ჷ' => 'Ჷ', 'ჸ' => 'Ჸ', 'ჹ' => 'Ჹ', 'ჺ' => 'Ჺ', 'ჽ' => 'Ჽ', 'ჾ' => 'Ჾ', 'ჿ' => 'Ჿ', 'ᏸ' => 'Ᏸ', 'ᏹ' => 'Ᏹ', 'ᏺ' => 'Ᏺ', 'ᏻ' => 'Ᏻ', 'ᏼ' => 'Ᏼ', 'ᏽ' => 'Ᏽ', 'ᲀ' => 'В', 'ᲁ' => 'Д', 'ᲂ' => 'О', 'ᲃ' => 'С', 'ᲄ' => 'Т', 'ᲅ' => 'Т', 'ᲆ' => 'Ъ', 'ᲇ' => 'Ѣ', 'ᲈ' => 'Ꙋ', 'ᵹ' => 'Ᵹ', 'ᵽ' => 'Ᵽ', 'ᶎ' => 'Ᶎ', 'ḁ' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', 'ḇ' => 'Ḇ', 'ḉ' => 'Ḉ', 'ḋ' => 'Ḋ', 'ḍ' => 'Ḍ', 'ḏ' => 'Ḏ', 'ḑ' => 'Ḑ', 'ḓ' => 'Ḓ', 'ḕ' => 'Ḕ', 'ḗ' => 'Ḗ', 'ḙ' => 'Ḙ', 'ḛ' => 'Ḛ', 'ḝ' => 'Ḝ', 'ḟ' => 'Ḟ', 'ḡ' => 'Ḡ', 'ḣ' => 'Ḣ', 'ḥ' => 'Ḥ', 'ḧ' => 'Ḧ', 'ḩ' => 'Ḩ', 'ḫ' => 'Ḫ', 'ḭ' => 'Ḭ', 'ḯ' => 'Ḯ', 'ḱ' => 'Ḱ', 'ḳ' => 'Ḳ', 'ḵ' => 'Ḵ', 'ḷ' => 'Ḷ', 'ḹ' => 'Ḹ', 'ḻ' => 'Ḻ', 'ḽ' => 'Ḽ', 'ḿ' => 'Ḿ', 'ṁ' => 'Ṁ', 'ṃ' => 'Ṃ', 'ṅ' => 'Ṅ', 'ṇ' => 'Ṇ', 'ṉ' => 'Ṉ', 'ṋ' => 'Ṋ', 'ṍ' => 'Ṍ', 'ṏ' => 'Ṏ', 'ṑ' => 'Ṑ', 'ṓ' => 'Ṓ', 'ṕ' => 'Ṕ', 'ṗ' => 'Ṗ', 'ṙ' => 'Ṙ', 'ṛ' => 'Ṛ', 'ṝ' => 'Ṝ', 'ṟ' => 'Ṟ', 'ṡ' => 'Ṡ', 'ṣ' => 'Ṣ', 'ṥ' => 'Ṥ', 'ṧ' => 'Ṧ', 'ṩ' => 'Ṩ', 'ṫ' => 'Ṫ', 'ṭ' => 'Ṭ', 'ṯ' => 'Ṯ', 'ṱ' => 'Ṱ', 'ṳ' => 'Ṳ', 'ṵ' => 'Ṵ', 'ṷ' => 'Ṷ', 'ṹ' => 'Ṹ', 'ṻ' => 'Ṻ', 'ṽ' => 'Ṽ', 'ṿ' => 'Ṿ', 'ẁ' => 'Ẁ', 'ẃ' => 'Ẃ', 'ẅ' => 'Ẅ', 'ẇ' => 'Ẇ', 'ẉ' => 'Ẉ', 'ẋ' => 'Ẋ', 'ẍ' => 'Ẍ', 'ẏ' => 'Ẏ', 'ẑ' => 'Ẑ', 'ẓ' => 'Ẓ', 'ẕ' => 'Ẕ', 'ẛ' => 'Ṡ', 'ạ' => 'Ạ', 'ả' => 'Ả', 'ấ' => 'Ấ', 'ầ' => 'Ầ', 'ẩ' => 'Ẩ', 'ẫ' => 'Ẫ', 'ậ' => 'Ậ', 'ắ' => 'Ắ', 'ằ' => 'Ằ', 'ẳ' => 'Ẳ', 'ẵ' => 'Ẵ', 'ặ' => 'Ặ', 'ẹ' => 'Ẹ', 'ẻ' => 'Ẻ', 'ẽ' => 'Ẽ', 'ế' => 'Ế', 'ề' => 'Ề', 'ể' => 'Ể', 'ễ' => 'Ễ', 'ệ' => 'Ệ', 'ỉ' => 'Ỉ', 'ị' => 'Ị', 'ọ' => 'Ọ', 'ỏ' => 'Ỏ', 'ố' => 'Ố', 'ồ' => 'Ồ', 'ổ' => 'Ổ', 'ỗ' => 'Ỗ', 'ộ' => 'Ộ', 'ớ' => 'Ớ', 'ờ' => 'Ờ', 'ở' => 'Ở', 'ỡ' => 'Ỡ', 'ợ' => 'Ợ', 'ụ' => 'Ụ', 'ủ' => 'Ủ', 'ứ' => 'Ứ', 'ừ' => 'Ừ', 'ử' => 'Ử', 'ữ' => 'Ữ', 'ự' => 'Ự', 'ỳ' => 'Ỳ', 'ỵ' => 'Ỵ', 'ỷ' => 'Ỷ', 'ỹ' => 'Ỹ', 'ỻ' => 'Ỻ', 'ỽ' => 'Ỽ', 'ỿ' => 'Ỿ', 'ἀ' => 'Ἀ', 'ἁ' => 'Ἁ', 'ἂ' => 'Ἂ', 'ἃ' => 'Ἃ', 'ἄ' => 'Ἄ', 'ἅ' => 'Ἅ', 'ἆ' => 'Ἆ', 'ἇ' => 'Ἇ', 'ἐ' => 'Ἐ', 'ἑ' => 'Ἑ', 'ἒ' => 'Ἒ', 'ἓ' => 'Ἓ', 'ἔ' => 'Ἔ', 'ἕ' => 'Ἕ', 'ἠ' => 'Ἠ', 'ἡ' => 'Ἡ', 'ἢ' => 'Ἢ', 'ἣ' => 'Ἣ', 'ἤ' => 'Ἤ', 'ἥ' => 'Ἥ', 'ἦ' => 'Ἦ', 'ἧ' => 'Ἧ', 'ἰ' => 'Ἰ', 'ἱ' => 'Ἱ', 'ἲ' => 'Ἲ', 'ἳ' => 'Ἳ', 'ἴ' => 'Ἴ', 'ἵ' => 'Ἵ', 'ἶ' => 'Ἶ', 'ἷ' => 'Ἷ', 'ὀ' => 'Ὀ', 'ὁ' => 'Ὁ', 'ὂ' => 'Ὂ', 'ὃ' => 'Ὃ', 'ὄ' => 'Ὄ', 'ὅ' => 'Ὅ', 'ὑ' => 'Ὑ', 'ὓ' => 'Ὓ', 'ὕ' => 'Ὕ', 'ὗ' => 'Ὗ', 'ὠ' => 'Ὠ', 'ὡ' => 'Ὡ', 'ὢ' => 'Ὢ', 'ὣ' => 'Ὣ', 'ὤ' => 'Ὤ', 'ὥ' => 'Ὥ', 'ὦ' => 'Ὦ', 'ὧ' => 'Ὧ', 'ὰ' => 'Ὰ', 'ά' => 'Ά', 'ὲ' => 'Ὲ', 'έ' => 'Έ', 'ὴ' => 'Ὴ', 'ή' => 'Ή', 'ὶ' => 'Ὶ', 'ί' => 'Ί', 'ὸ' => 'Ὸ', 'ό' => 'Ό', 'ὺ' => 'Ὺ', 'ύ' => 'Ύ', 'ὼ' => 'Ὼ', 'ώ' => 'Ώ', 'ᾀ' => 'ἈΙ', 'ᾁ' => 'ἉΙ', 'ᾂ' => 'ἊΙ', 'ᾃ' => 'ἋΙ', 'ᾄ' => 'ἌΙ', 'ᾅ' => 'ἍΙ', 'ᾆ' => 'ἎΙ', 'ᾇ' => 'ἏΙ', 'ᾐ' => 'ἨΙ', 'ᾑ' => 'ἩΙ', 'ᾒ' => 'ἪΙ', 'ᾓ' => 'ἫΙ', 'ᾔ' => 'ἬΙ', 'ᾕ' => 'ἭΙ', 'ᾖ' => 'ἮΙ', 'ᾗ' => 'ἯΙ', 'ᾠ' => 'ὨΙ', 'ᾡ' => 'ὩΙ', 'ᾢ' => 'ὪΙ', 'ᾣ' => 'ὫΙ', 'ᾤ' => 'ὬΙ', 'ᾥ' => 'ὭΙ', 'ᾦ' => 'ὮΙ', 'ᾧ' => 'ὯΙ', 'ᾰ' => 'Ᾰ', 'ᾱ' => 'Ᾱ', 'ᾳ' => 'ΑΙ', 'ι' => 'Ι', 'ῃ' => 'ΗΙ', 'ῐ' => 'Ῐ', 'ῑ' => 'Ῑ', 'ῠ' => 'Ῠ', 'ῡ' => 'Ῡ', 'ῥ' => 'Ῥ', 'ῳ' => 'ΩΙ', 'ⅎ' => 'Ⅎ', 'ⅰ' => 'Ⅰ', 'ⅱ' => 'Ⅱ', 'ⅲ' => 'Ⅲ', 'ⅳ' => 'Ⅳ', 'ⅴ' => 'Ⅴ', 'ⅵ' => 'Ⅵ', 'ⅶ' => 'Ⅶ', 'ⅷ' => 'Ⅷ', 'ⅸ' => 'Ⅸ', 'ⅹ' => 'Ⅹ', 'ⅺ' => 'Ⅺ', 'ⅻ' => 'Ⅻ', 'ⅼ' => 'Ⅼ', 'ⅽ' => 'Ⅽ', 'ⅾ' => 'Ⅾ', 'ⅿ' => 'Ⅿ', 'ↄ' => 'Ↄ', 'ⓐ' => 'Ⓐ', 'ⓑ' => 'Ⓑ', 'ⓒ' => 'Ⓒ', 'ⓓ' => 'Ⓓ', 'ⓔ' => 'Ⓔ', 'ⓕ' => 'Ⓕ', 'ⓖ' => 'Ⓖ', 'ⓗ' => 'Ⓗ', 'ⓘ' => 'Ⓘ', 'ⓙ' => 'Ⓙ', 'ⓚ' => 'Ⓚ', 'ⓛ' => 'Ⓛ', 'ⓜ' => 'Ⓜ', 'ⓝ' => 'Ⓝ', 'ⓞ' => 'Ⓞ', 'ⓟ' => 'Ⓟ', 'ⓠ' => 'Ⓠ', 'ⓡ' => 'Ⓡ', 'ⓢ' => 'Ⓢ', 'ⓣ' => 'Ⓣ', 'ⓤ' => 'Ⓤ', 'ⓥ' => 'Ⓥ', 'ⓦ' => 'Ⓦ', 'ⓧ' => 'Ⓧ', 'ⓨ' => 'Ⓨ', 'ⓩ' => 'Ⓩ', 'ⰰ' => 'Ⰰ', 'ⰱ' => 'Ⰱ', 'ⰲ' => 'Ⰲ', 'ⰳ' => 'Ⰳ', 'ⰴ' => 'Ⰴ', 'ⰵ' => 'Ⰵ', 'ⰶ' => 'Ⰶ', 'ⰷ' => 'Ⰷ', 'ⰸ' => 'Ⰸ', 'ⰹ' => 'Ⰹ', 'ⰺ' => 'Ⰺ', 'ⰻ' => 'Ⰻ', 'ⰼ' => 'Ⰼ', 'ⰽ' => 'Ⰽ', 'ⰾ' => 'Ⰾ', 'ⰿ' => 'Ⰿ', 'ⱀ' => 'Ⱀ', 'ⱁ' => 'Ⱁ', 'ⱂ' => 'Ⱂ', 'ⱃ' => 'Ⱃ', 'ⱄ' => 'Ⱄ', 'ⱅ' => 'Ⱅ', 'ⱆ' => 'Ⱆ', 'ⱇ' => 'Ⱇ', 'ⱈ' => 'Ⱈ', 'ⱉ' => 'Ⱉ', 'ⱊ' => 'Ⱊ', 'ⱋ' => 'Ⱋ', 'ⱌ' => 'Ⱌ', 'ⱍ' => 'Ⱍ', 'ⱎ' => 'Ⱎ', 'ⱏ' => 'Ⱏ', 'ⱐ' => 'Ⱐ', 'ⱑ' => 'Ⱑ', 'ⱒ' => 'Ⱒ', 'ⱓ' => 'Ⱓ', 'ⱔ' => 'Ⱔ', 'ⱕ' => 'Ⱕ', 'ⱖ' => 'Ⱖ', 'ⱗ' => 'Ⱗ', 'ⱘ' => 'Ⱘ', 'ⱙ' => 'Ⱙ', 'ⱚ' => 'Ⱚ', 'ⱛ' => 'Ⱛ', 'ⱜ' => 'Ⱜ', 'ⱝ' => 'Ⱝ', 'ⱞ' => 'Ⱞ', 'ⱡ' => 'Ⱡ', 'ⱥ' => 'Ⱥ', 'ⱦ' => 'Ⱦ', 'ⱨ' => 'Ⱨ', 'ⱪ' => 'Ⱪ', 'ⱬ' => 'Ⱬ', 'ⱳ' => 'Ⱳ', 'ⱶ' => 'Ⱶ', 'ⲁ' => 'Ⲁ', 'ⲃ' => 'Ⲃ', 'ⲅ' => 'Ⲅ', 'ⲇ' => 'Ⲇ', 'ⲉ' => 'Ⲉ', 'ⲋ' => 'Ⲋ', 'ⲍ' => 'Ⲍ', 'ⲏ' => 'Ⲏ', 'ⲑ' => 'Ⲑ', 'ⲓ' => 'Ⲓ', 'ⲕ' => 'Ⲕ', 'ⲗ' => 'Ⲗ', 'ⲙ' => 'Ⲙ', 'ⲛ' => 'Ⲛ', 'ⲝ' => 'Ⲝ', 'ⲟ' => 'Ⲟ', 'ⲡ' => 'Ⲡ', 'ⲣ' => 'Ⲣ', 'ⲥ' => 'Ⲥ', 'ⲧ' => 'Ⲧ', 'ⲩ' => 'Ⲩ', 'ⲫ' => 'Ⲫ', 'ⲭ' => 'Ⲭ', 'ⲯ' => 'Ⲯ', 'ⲱ' => 'Ⲱ', 'ⲳ' => 'Ⲳ', 'ⲵ' => 'Ⲵ', 'ⲷ' => 'Ⲷ', 'ⲹ' => 'Ⲹ', 'ⲻ' => 'Ⲻ', 'ⲽ' => 'Ⲽ', 'ⲿ' => 'Ⲿ', 'ⳁ' => 'Ⳁ', 'ⳃ' => 'Ⳃ', 'ⳅ' => 'Ⳅ', 'ⳇ' => 'Ⳇ', 'ⳉ' => 'Ⳉ', 'ⳋ' => 'Ⳋ', 'ⳍ' => 'Ⳍ', 'ⳏ' => 'Ⳏ', 'ⳑ' => 'Ⳑ', 'ⳓ' => 'Ⳓ', 'ⳕ' => 'Ⳕ', 'ⳗ' => 'Ⳗ', 'ⳙ' => 'Ⳙ', 'ⳛ' => 'Ⳛ', 'ⳝ' => 'Ⳝ', 'ⳟ' => 'Ⳟ', 'ⳡ' => 'Ⳡ', 'ⳣ' => 'Ⳣ', 'ⳬ' => 'Ⳬ', 'ⳮ' => 'Ⳮ', 'ⳳ' => 'Ⳳ', 'ⴀ' => 'Ⴀ', 'ⴁ' => 'Ⴁ', 'ⴂ' => 'Ⴂ', 'ⴃ' => 'Ⴃ', 'ⴄ' => 'Ⴄ', 'ⴅ' => 'Ⴅ', 'ⴆ' => 'Ⴆ', 'ⴇ' => 'Ⴇ', 'ⴈ' => 'Ⴈ', 'ⴉ' => 'Ⴉ', 'ⴊ' => 'Ⴊ', 'ⴋ' => 'Ⴋ', 'ⴌ' => 'Ⴌ', 'ⴍ' => 'Ⴍ', 'ⴎ' => 'Ⴎ', 'ⴏ' => 'Ⴏ', 'ⴐ' => 'Ⴐ', 'ⴑ' => 'Ⴑ', 'ⴒ' => 'Ⴒ', 'ⴓ' => 'Ⴓ', 'ⴔ' => 'Ⴔ', 'ⴕ' => 'Ⴕ', 'ⴖ' => 'Ⴖ', 'ⴗ' => 'Ⴗ', 'ⴘ' => 'Ⴘ', 'ⴙ' => 'Ⴙ', 'ⴚ' => 'Ⴚ', 'ⴛ' => 'Ⴛ', 'ⴜ' => 'Ⴜ', 'ⴝ' => 'Ⴝ', 'ⴞ' => 'Ⴞ', 'ⴟ' => 'Ⴟ', 'ⴠ' => 'Ⴠ', 'ⴡ' => 'Ⴡ', 'ⴢ' => 'Ⴢ', 'ⴣ' => 'Ⴣ', 'ⴤ' => 'Ⴤ', 'ⴥ' => 'Ⴥ', 'ⴧ' => 'Ⴧ', 'ⴭ' => 'Ⴭ', 'ꙁ' => 'Ꙁ', 'ꙃ' => 'Ꙃ', 'ꙅ' => 'Ꙅ', 'ꙇ' => 'Ꙇ', 'ꙉ' => 'Ꙉ', 'ꙋ' => 'Ꙋ', 'ꙍ' => 'Ꙍ', 'ꙏ' => 'Ꙏ', 'ꙑ' => 'Ꙑ', 'ꙓ' => 'Ꙓ', 'ꙕ' => 'Ꙕ', 'ꙗ' => 'Ꙗ', 'ꙙ' => 'Ꙙ', 'ꙛ' => 'Ꙛ', 'ꙝ' => 'Ꙝ', 'ꙟ' => 'Ꙟ', 'ꙡ' => 'Ꙡ', 'ꙣ' => 'Ꙣ', 'ꙥ' => 'Ꙥ', 'ꙧ' => 'Ꙧ', 'ꙩ' => 'Ꙩ', 'ꙫ' => 'Ꙫ', 'ꙭ' => 'Ꙭ', 'ꚁ' => 'Ꚁ', 'ꚃ' => 'Ꚃ', 'ꚅ' => 'Ꚅ', 'ꚇ' => 'Ꚇ', 'ꚉ' => 'Ꚉ', 'ꚋ' => 'Ꚋ', 'ꚍ' => 'Ꚍ', 'ꚏ' => 'Ꚏ', 'ꚑ' => 'Ꚑ', 'ꚓ' => 'Ꚓ', 'ꚕ' => 'Ꚕ', 'ꚗ' => 'Ꚗ', 'ꚙ' => 'Ꚙ', 'ꚛ' => 'Ꚛ', 'ꜣ' => 'Ꜣ', 'ꜥ' => 'Ꜥ', 'ꜧ' => 'Ꜧ', 'ꜩ' => 'Ꜩ', 'ꜫ' => 'Ꜫ', 'ꜭ' => 'Ꜭ', 'ꜯ' => 'Ꜯ', 'ꜳ' => 'Ꜳ', 'ꜵ' => 'Ꜵ', 'ꜷ' => 'Ꜷ', 'ꜹ' => 'Ꜹ', 'ꜻ' => 'Ꜻ', 'ꜽ' => 'Ꜽ', 'ꜿ' => 'Ꜿ', 'ꝁ' => 'Ꝁ', 'ꝃ' => 'Ꝃ', 'ꝅ' => 'Ꝅ', 'ꝇ' => 'Ꝇ', 'ꝉ' => 'Ꝉ', 'ꝋ' => 'Ꝋ', 'ꝍ' => 'Ꝍ', 'ꝏ' => 'Ꝏ', 'ꝑ' => 'Ꝑ', 'ꝓ' => 'Ꝓ', 'ꝕ' => 'Ꝕ', 'ꝗ' => 'Ꝗ', 'ꝙ' => 'Ꝙ', 'ꝛ' => 'Ꝛ', 'ꝝ' => 'Ꝝ', 'ꝟ' => 'Ꝟ', 'ꝡ' => 'Ꝡ', 'ꝣ' => 'Ꝣ', 'ꝥ' => 'Ꝥ', 'ꝧ' => 'Ꝧ', 'ꝩ' => 'Ꝩ', 'ꝫ' => 'Ꝫ', 'ꝭ' => 'Ꝭ', 'ꝯ' => 'Ꝯ', 'ꝺ' => 'Ꝺ', 'ꝼ' => 'Ꝼ', 'ꝿ' => 'Ꝿ', 'ꞁ' => 'Ꞁ', 'ꞃ' => 'Ꞃ', 'ꞅ' => 'Ꞅ', 'ꞇ' => 'Ꞇ', 'ꞌ' => 'Ꞌ', 'ꞑ' => 'Ꞑ', 'ꞓ' => 'Ꞓ', 'ꞔ' => 'Ꞔ', 'ꞗ' => 'Ꞗ', 'ꞙ' => 'Ꞙ', 'ꞛ' => 'Ꞛ', 'ꞝ' => 'Ꞝ', 'ꞟ' => 'Ꞟ', 'ꞡ' => 'Ꞡ', 'ꞣ' => 'Ꞣ', 'ꞥ' => 'Ꞥ', 'ꞧ' => 'Ꞧ', 'ꞩ' => 'Ꞩ', 'ꞵ' => 'Ꞵ', 'ꞷ' => 'Ꞷ', 'ꞹ' => 'Ꞹ', 'ꞻ' => 'Ꞻ', 'ꞽ' => 'Ꞽ', 'ꞿ' => 'Ꞿ', 'ꟃ' => 'Ꟃ', 'ꟈ' => 'Ꟈ', 'ꟊ' => 'Ꟊ', 'ꟶ' => 'Ꟶ', 'ꭓ' => 'Ꭓ', 'ꭰ' => 'Ꭰ', 'ꭱ' => 'Ꭱ', 'ꭲ' => 'Ꭲ', 'ꭳ' => 'Ꭳ', 'ꭴ' => 'Ꭴ', 'ꭵ' => 'Ꭵ', 'ꭶ' => 'Ꭶ', 'ꭷ' => 'Ꭷ', 'ꭸ' => 'Ꭸ', 'ꭹ' => 'Ꭹ', 'ꭺ' => 'Ꭺ', 'ꭻ' => 'Ꭻ', 'ꭼ' => 'Ꭼ', 'ꭽ' => 'Ꭽ', 'ꭾ' => 'Ꭾ', 'ꭿ' => 'Ꭿ', 'ꮀ' => 'Ꮀ', 'ꮁ' => 'Ꮁ', 'ꮂ' => 'Ꮂ', 'ꮃ' => 'Ꮃ', 'ꮄ' => 'Ꮄ', 'ꮅ' => 'Ꮅ', 'ꮆ' => 'Ꮆ', 'ꮇ' => 'Ꮇ', 'ꮈ' => 'Ꮈ', 'ꮉ' => 'Ꮉ', 'ꮊ' => 'Ꮊ', 'ꮋ' => 'Ꮋ', 'ꮌ' => 'Ꮌ', 'ꮍ' => 'Ꮍ', 'ꮎ' => 'Ꮎ', 'ꮏ' => 'Ꮏ', 'ꮐ' => 'Ꮐ', 'ꮑ' => 'Ꮑ', 'ꮒ' => 'Ꮒ', 'ꮓ' => 'Ꮓ', 'ꮔ' => 'Ꮔ', 'ꮕ' => 'Ꮕ', 'ꮖ' => 'Ꮖ', 'ꮗ' => 'Ꮗ', 'ꮘ' => 'Ꮘ', 'ꮙ' => 'Ꮙ', 'ꮚ' => 'Ꮚ', 'ꮛ' => 'Ꮛ', 'ꮜ' => 'Ꮜ', 'ꮝ' => 'Ꮝ', 'ꮞ' => 'Ꮞ', 'ꮟ' => 'Ꮟ', 'ꮠ' => 'Ꮠ', 'ꮡ' => 'Ꮡ', 'ꮢ' => 'Ꮢ', 'ꮣ' => 'Ꮣ', 'ꮤ' => 'Ꮤ', 'ꮥ' => 'Ꮥ', 'ꮦ' => 'Ꮦ', 'ꮧ' => 'Ꮧ', 'ꮨ' => 'Ꮨ', 'ꮩ' => 'Ꮩ', 'ꮪ' => 'Ꮪ', 'ꮫ' => 'Ꮫ', 'ꮬ' => 'Ꮬ', 'ꮭ' => 'Ꮭ', 'ꮮ' => 'Ꮮ', 'ꮯ' => 'Ꮯ', 'ꮰ' => 'Ꮰ', 'ꮱ' => 'Ꮱ', 'ꮲ' => 'Ꮲ', 'ꮳ' => 'Ꮳ', 'ꮴ' => 'Ꮴ', 'ꮵ' => 'Ꮵ', 'ꮶ' => 'Ꮶ', 'ꮷ' => 'Ꮷ', 'ꮸ' => 'Ꮸ', 'ꮹ' => 'Ꮹ', 'ꮺ' => 'Ꮺ', 'ꮻ' => 'Ꮻ', 'ꮼ' => 'Ꮼ', 'ꮽ' => 'Ꮽ', 'ꮾ' => 'Ꮾ', 'ꮿ' => 'Ꮿ', 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F', 'g' => 'G', 'h' => 'H', 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', 'm' => 'M', 'n' => 'N', 'o' => 'O', 'p' => 'P', 'q' => 'Q', 'r' => 'R', 's' => 'S', 't' => 'T', 'u' => 'U', 'v' => 'V', 'w' => 'W', 'x' => 'X', 'y' => 'Y', 'z' => 'Z', '𐐨' => '𐐀', '𐐩' => '𐐁', '𐐪' => '𐐂', '𐐫' => '𐐃', '𐐬' => '𐐄', '𐐭' => '𐐅', '𐐮' => '𐐆', '𐐯' => '𐐇', '𐐰' => '𐐈', '𐐱' => '𐐉', '𐐲' => '𐐊', '𐐳' => '𐐋', '𐐴' => '𐐌', '𐐵' => '𐐍', '𐐶' => '𐐎', '𐐷' => '𐐏', '𐐸' => '𐐐', '𐐹' => '𐐑', '𐐺' => '𐐒', '𐐻' => '𐐓', '𐐼' => '𐐔', '𐐽' => '𐐕', '𐐾' => '𐐖', '𐐿' => '𐐗', '𐑀' => '𐐘', '𐑁' => '𐐙', '𐑂' => '𐐚', '𐑃' => '𐐛', '𐑄' => '𐐜', '𐑅' => '𐐝', '𐑆' => '𐐞', '𐑇' => '𐐟', '𐑈' => '𐐠', '𐑉' => '𐐡', '𐑊' => '𐐢', '𐑋' => '𐐣', '𐑌' => '𐐤', '𐑍' => '𐐥', '𐑎' => '𐐦', '𐑏' => '𐐧', '𐓘' => '𐒰', '𐓙' => '𐒱', '𐓚' => '𐒲', '𐓛' => '𐒳', '𐓜' => '𐒴', '𐓝' => '𐒵', '𐓞' => '𐒶', '𐓟' => '𐒷', '𐓠' => '𐒸', '𐓡' => '𐒹', '𐓢' => '𐒺', '𐓣' => '𐒻', '𐓤' => '𐒼', '𐓥' => '𐒽', '𐓦' => '𐒾', '𐓧' => '𐒿', '𐓨' => '𐓀', '𐓩' => '𐓁', '𐓪' => '𐓂', '𐓫' => '𐓃', '𐓬' => '𐓄', '𐓭' => '𐓅', '𐓮' => '𐓆', '𐓯' => '𐓇', '𐓰' => '𐓈', '𐓱' => '𐓉', '𐓲' => '𐓊', '𐓳' => '𐓋', '𐓴' => '𐓌', '𐓵' => '𐓍', '𐓶' => '𐓎', '𐓷' => '𐓏', '𐓸' => '𐓐', '𐓹' => '𐓑', '𐓺' => '𐓒', '𐓻' => '𐓓', '𐳀' => '𐲀', '𐳁' => '𐲁', '𐳂' => '𐲂', '𐳃' => '𐲃', '𐳄' => '𐲄', '𐳅' => '𐲅', '𐳆' => '𐲆', '𐳇' => '𐲇', '𐳈' => '𐲈', '𐳉' => '𐲉', '𐳊' => '𐲊', '𐳋' => '𐲋', '𐳌' => '𐲌', '𐳍' => '𐲍', '𐳎' => '𐲎', '𐳏' => '𐲏', '𐳐' => '𐲐', '𐳑' => '𐲑', '𐳒' => '𐲒', '𐳓' => '𐲓', '𐳔' => '𐲔', '𐳕' => '𐲕', '𐳖' => '𐲖', '𐳗' => '𐲗', '𐳘' => '𐲘', '𐳙' => '𐲙', '𐳚' => '𐲚', '𐳛' => '𐲛', '𐳜' => '𐲜', '𐳝' => '𐲝', '𐳞' => '𐲞', '𐳟' => '𐲟', '𐳠' => '𐲠', '𐳡' => '𐲡', '𐳢' => '𐲢', '𐳣' => '𐲣', '𐳤' => '𐲤', '𐳥' => '𐲥', '𐳦' => '𐲦', '𐳧' => '𐲧', '𐳨' => '𐲨', '𐳩' => '𐲩', '𐳪' => '𐲪', '𐳫' => '𐲫', '𐳬' => '𐲬', '𐳭' => '𐲭', '𐳮' => '𐲮', '𐳯' => '𐲯', '𐳰' => '𐲰', '𐳱' => '𐲱', '𐳲' => '𐲲', '𑣀' => '𑢠', '𑣁' => '𑢡', '𑣂' => '𑢢', '𑣃' => '𑢣', '𑣄' => '𑢤', '𑣅' => '𑢥', '𑣆' => '𑢦', '𑣇' => '𑢧', '𑣈' => '𑢨', '𑣉' => '𑢩', '𑣊' => '𑢪', '𑣋' => '𑢫', '𑣌' => '𑢬', '𑣍' => '𑢭', '𑣎' => '𑢮', '𑣏' => '𑢯', '𑣐' => '𑢰', '𑣑' => '𑢱', '𑣒' => '𑢲', '𑣓' => '𑢳', '𑣔' => '𑢴', '𑣕' => '𑢵', '𑣖' => '𑢶', '𑣗' => '𑢷', '𑣘' => '𑢸', '𑣙' => '𑢹', '𑣚' => '𑢺', '𑣛' => '𑢻', '𑣜' => '𑢼', '𑣝' => '𑢽', '𑣞' => '𑢾', '𑣟' => '𑢿', '𖹠' => '𖹀', '𖹡' => '𖹁', '𖹢' => '𖹂', '𖹣' => '𖹃', '𖹤' => '𖹄', '𖹥' => '𖹅', '𖹦' => '𖹆', '𖹧' => '𖹇', '𖹨' => '𖹈', '𖹩' => '𖹉', '𖹪' => '𖹊', '𖹫' => '𖹋', '𖹬' => '𖹌', '𖹭' => '𖹍', '𖹮' => '𖹎', '𖹯' => '𖹏', '𖹰' => '𖹐', '𖹱' => '𖹑', '𖹲' => '𖹒', '𖹳' => '𖹓', '𖹴' => '𖹔', '𖹵' => '𖹕', '𖹶' => '𖹖', '𖹷' => '𖹗', '𖹸' => '𖹘', '𖹹' => '𖹙', '𖹺' => '𖹚', '𖹻' => '𖹛', '𖹼' => '𖹜', '𖹽' => '𖹝', '𖹾' => '𖹞', '𖹿' => '𖹟', '𞤢' => '𞤀', '𞤣' => '𞤁', '𞤤' => '𞤂', '𞤥' => '𞤃', '𞤦' => '𞤄', '𞤧' => '𞤅', '𞤨' => '𞤆', '𞤩' => '𞤇', '𞤪' => '𞤈', '𞤫' => '𞤉', '𞤬' => '𞤊', '𞤭' => '𞤋', '𞤮' => '𞤌', '𞤯' => '𞤍', '𞤰' => '𞤎', '𞤱' => '𞤏', '𞤲' => '𞤐', '𞤳' => '𞤑', '𞤴' => '𞤒', '𞤵' => '𞤓', '𞤶' => '𞤔', '𞤷' => '𞤕', '𞤸' => '𞤖', '𞤹' => '𞤗', '𞤺' => '𞤘', '𞤻' => '𞤙', '𞤼' => '𞤚', '𞤽' => '𞤛', '𞤾' => '𞤜', '𞤿' => '𞤝', '𞥀' => '𞤞', '𞥁' => '𞤟', '𞥂' => '𞤠', '𞥃' => '𞤡', 'ß' => 'SS', 'ff' => 'FF', 'fi' => 'FI', 'fl' => 'FL', 'ffi' => 'FFI', 'ffl' => 'FFL', 'ſt' => 'ST', 'st' => 'ST', 'և' => 'ԵՒ', 'ﬓ' => 'ՄՆ', 'ﬔ' => 'ՄԵ', 'ﬕ' => 'ՄԻ', 'ﬖ' => 'ՎՆ', 'ﬗ' => 'ՄԽ', 'ʼn' => 'ʼN', 'ΐ' => 'Ϊ́', 'ΰ' => 'Ϋ́', 'ǰ' => 'J̌', 'ẖ' => 'H̱', 'ẗ' => 'T̈', 'ẘ' => 'W̊', 'ẙ' => 'Y̊', 'ẚ' => 'Aʾ', 'ὐ' => 'Υ̓', 'ὒ' => 'Υ̓̀', 'ὔ' => 'Υ̓́', 'ὖ' => 'Υ̓͂', 'ᾶ' => 'Α͂', 'ῆ' => 'Η͂', 'ῒ' => 'Ϊ̀', 'ΐ' => 'Ϊ́', 'ῖ' => 'Ι͂', 'ῗ' => 'Ϊ͂', 'ῢ' => 'Ϋ̀', 'ΰ' => 'Ϋ́', 'ῤ' => 'Ρ̓', 'ῦ' => 'Υ͂', 'ῧ' => 'Ϋ͂', 'ῶ' => 'Ω͂', 'ᾈ' => 'ἈΙ', 'ᾉ' => 'ἉΙ', 'ᾊ' => 'ἊΙ', 'ᾋ' => 'ἋΙ', 'ᾌ' => 'ἌΙ', 'ᾍ' => 'ἍΙ', 'ᾎ' => 'ἎΙ', 'ᾏ' => 'ἏΙ', 'ᾘ' => 'ἨΙ', 'ᾙ' => 'ἩΙ', 'ᾚ' => 'ἪΙ', 'ᾛ' => 'ἫΙ', 'ᾜ' => 'ἬΙ', 'ᾝ' => 'ἭΙ', 'ᾞ' => 'ἮΙ', 'ᾟ' => 'ἯΙ', 'ᾨ' => 'ὨΙ', 'ᾩ' => 'ὩΙ', 'ᾪ' => 'ὪΙ', 'ᾫ' => 'ὫΙ', 'ᾬ' => 'ὬΙ', 'ᾭ' => 'ὭΙ', 'ᾮ' => 'ὮΙ', 'ᾯ' => 'ὯΙ', 'ᾼ' => 'ΑΙ', 'ῌ' => 'ΗΙ', 'ῼ' => 'ΩΙ', 'ᾲ' => 'ᾺΙ', 'ᾴ' => 'ΆΙ', 'ῂ' => 'ῊΙ', 'ῄ' => 'ΉΙ', 'ῲ' => 'ῺΙ', 'ῴ' => 'ΏΙ', 'ᾷ' => 'Α͂Ι', 'ῇ' => 'Η͂Ι', 'ῷ' => 'Ω͂Ι', ); PK2A#]>|zK99Fvendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.phpnu�[���<?php // from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u'; PK2A#]�|�a a Bvendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.phpnu�[���<?php return [ 'İ' => 'i̇', 'µ' => 'μ', 'ſ' => 's', 'ͅ' => 'ι', 'ς' => 'σ', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϵ' => 'ε', 'ẛ' => 'ṡ', 'ι' => 'ι', 'ß' => 'ss', 'ʼn' => 'ʼn', 'ǰ' => 'ǰ', 'ΐ' => 'ΐ', 'ΰ' => 'ΰ', 'և' => 'եւ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẚ' => 'aʾ', 'ẞ' => 'ss', 'ὐ' => 'ὐ', 'ὒ' => 'ὒ', 'ὔ' => 'ὔ', 'ὖ' => 'ὖ', 'ᾀ' => 'ἀι', 'ᾁ' => 'ἁι', 'ᾂ' => 'ἂι', 'ᾃ' => 'ἃι', 'ᾄ' => 'ἄι', 'ᾅ' => 'ἅι', 'ᾆ' => 'ἆι', 'ᾇ' => 'ἇι', 'ᾈ' => 'ἀι', 'ᾉ' => 'ἁι', 'ᾊ' => 'ἂι', 'ᾋ' => 'ἃι', 'ᾌ' => 'ἄι', 'ᾍ' => 'ἅι', 'ᾎ' => 'ἆι', 'ᾏ' => 'ἇι', 'ᾐ' => 'ἠι', 'ᾑ' => 'ἡι', 'ᾒ' => 'ἢι', 'ᾓ' => 'ἣι', 'ᾔ' => 'ἤι', 'ᾕ' => 'ἥι', 'ᾖ' => 'ἦι', 'ᾗ' => 'ἧι', 'ᾘ' => 'ἠι', 'ᾙ' => 'ἡι', 'ᾚ' => 'ἢι', 'ᾛ' => 'ἣι', 'ᾜ' => 'ἤι', 'ᾝ' => 'ἥι', 'ᾞ' => 'ἦι', 'ᾟ' => 'ἧι', 'ᾠ' => 'ὠι', 'ᾡ' => 'ὡι', 'ᾢ' => 'ὢι', 'ᾣ' => 'ὣι', 'ᾤ' => 'ὤι', 'ᾥ' => 'ὥι', 'ᾦ' => 'ὦι', 'ᾧ' => 'ὧι', 'ᾨ' => 'ὠι', 'ᾩ' => 'ὡι', 'ᾪ' => 'ὢι', 'ᾫ' => 'ὣι', 'ᾬ' => 'ὤι', 'ᾭ' => 'ὥι', 'ᾮ' => 'ὦι', 'ᾯ' => 'ὧι', 'ᾲ' => 'ὰι', 'ᾳ' => 'αι', 'ᾴ' => 'άι', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾶι', 'ᾼ' => 'αι', 'ῂ' => 'ὴι', 'ῃ' => 'ηι', 'ῄ' => 'ήι', 'ῆ' => 'ῆ', 'ῇ' => 'ῆι', 'ῌ' => 'ηι', 'ῒ' => 'ῒ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'ῢ' => 'ῢ', 'ῤ' => 'ῤ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'ῲ' => 'ὼι', 'ῳ' => 'ωι', 'ῴ' => 'ώι', 'ῶ' => 'ῶ', 'ῷ' => 'ῶι', 'ῼ' => 'ωι', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ', ]; PK2A#]���d�_�_@vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpnu�[���<?php return array ( 'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e', 'F' => 'f', 'G' => 'g', 'H' => 'h', 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', 'M' => 'm', 'N' => 'n', 'O' => 'o', 'P' => 'p', 'Q' => 'q', 'R' => 'r', 'S' => 's', 'T' => 't', 'U' => 'u', 'V' => 'v', 'W' => 'w', 'X' => 'x', 'Y' => 'y', 'Z' => 'z', 'À' => 'à', 'Á' => 'á', 'Â' => 'â', 'Ã' => 'ã', 'Ä' => 'ä', 'Å' => 'å', 'Æ' => 'æ', 'Ç' => 'ç', 'È' => 'è', 'É' => 'é', 'Ê' => 'ê', 'Ë' => 'ë', 'Ì' => 'ì', 'Í' => 'í', 'Î' => 'î', 'Ï' => 'ï', 'Ð' => 'ð', 'Ñ' => 'ñ', 'Ò' => 'ò', 'Ó' => 'ó', 'Ô' => 'ô', 'Õ' => 'õ', 'Ö' => 'ö', 'Ø' => 'ø', 'Ù' => 'ù', 'Ú' => 'ú', 'Û' => 'û', 'Ü' => 'ü', 'Ý' => 'ý', 'Þ' => 'þ', 'Ā' => 'ā', 'Ă' => 'ă', 'Ą' => 'ą', 'Ć' => 'ć', 'Ĉ' => 'ĉ', 'Ċ' => 'ċ', 'Č' => 'č', 'Ď' => 'ď', 'Đ' => 'đ', 'Ē' => 'ē', 'Ĕ' => 'ĕ', 'Ė' => 'ė', 'Ę' => 'ę', 'Ě' => 'ě', 'Ĝ' => 'ĝ', 'Ğ' => 'ğ', 'Ġ' => 'ġ', 'Ģ' => 'ģ', 'Ĥ' => 'ĥ', 'Ħ' => 'ħ', 'Ĩ' => 'ĩ', 'Ī' => 'ī', 'Ĭ' => 'ĭ', 'Į' => 'į', 'İ' => 'i̇', 'IJ' => 'ij', 'Ĵ' => 'ĵ', 'Ķ' => 'ķ', 'Ĺ' => 'ĺ', 'Ļ' => 'ļ', 'Ľ' => 'ľ', 'Ŀ' => 'ŀ', 'Ł' => 'ł', 'Ń' => 'ń', 'Ņ' => 'ņ', 'Ň' => 'ň', 'Ŋ' => 'ŋ', 'Ō' => 'ō', 'Ŏ' => 'ŏ', 'Ő' => 'ő', 'Œ' => 'œ', 'Ŕ' => 'ŕ', 'Ŗ' => 'ŗ', 'Ř' => 'ř', 'Ś' => 'ś', 'Ŝ' => 'ŝ', 'Ş' => 'ş', 'Š' => 'š', 'Ţ' => 'ţ', 'Ť' => 'ť', 'Ŧ' => 'ŧ', 'Ũ' => 'ũ', 'Ū' => 'ū', 'Ŭ' => 'ŭ', 'Ů' => 'ů', 'Ű' => 'ű', 'Ų' => 'ų', 'Ŵ' => 'ŵ', 'Ŷ' => 'ŷ', 'Ÿ' => 'ÿ', 'Ź' => 'ź', 'Ż' => 'ż', 'Ž' => 'ž', 'Ɓ' => 'ɓ', 'Ƃ' => 'ƃ', 'Ƅ' => 'ƅ', 'Ɔ' => 'ɔ', 'Ƈ' => 'ƈ', 'Ɖ' => 'ɖ', 'Ɗ' => 'ɗ', 'Ƌ' => 'ƌ', 'Ǝ' => 'ǝ', 'Ə' => 'ə', 'Ɛ' => 'ɛ', 'Ƒ' => 'ƒ', 'Ɠ' => 'ɠ', 'Ɣ' => 'ɣ', 'Ɩ' => 'ɩ', 'Ɨ' => 'ɨ', 'Ƙ' => 'ƙ', 'Ɯ' => 'ɯ', 'Ɲ' => 'ɲ', 'Ɵ' => 'ɵ', 'Ơ' => 'ơ', 'Ƣ' => 'ƣ', 'Ƥ' => 'ƥ', 'Ʀ' => 'ʀ', 'Ƨ' => 'ƨ', 'Ʃ' => 'ʃ', 'Ƭ' => 'ƭ', 'Ʈ' => 'ʈ', 'Ư' => 'ư', 'Ʊ' => 'ʊ', 'Ʋ' => 'ʋ', 'Ƴ' => 'ƴ', 'Ƶ' => 'ƶ', 'Ʒ' => 'ʒ', 'Ƹ' => 'ƹ', 'Ƽ' => 'ƽ', 'DŽ' => 'dž', 'Dž' => 'dž', 'LJ' => 'lj', 'Lj' => 'lj', 'NJ' => 'nj', 'Nj' => 'nj', 'Ǎ' => 'ǎ', 'Ǐ' => 'ǐ', 'Ǒ' => 'ǒ', 'Ǔ' => 'ǔ', 'Ǖ' => 'ǖ', 'Ǘ' => 'ǘ', 'Ǚ' => 'ǚ', 'Ǜ' => 'ǜ', 'Ǟ' => 'ǟ', 'Ǡ' => 'ǡ', 'Ǣ' => 'ǣ', 'Ǥ' => 'ǥ', 'Ǧ' => 'ǧ', 'Ǩ' => 'ǩ', 'Ǫ' => 'ǫ', 'Ǭ' => 'ǭ', 'Ǯ' => 'ǯ', 'DZ' => 'dz', 'Dz' => 'dz', 'Ǵ' => 'ǵ', 'Ƕ' => 'ƕ', 'Ƿ' => 'ƿ', 'Ǹ' => 'ǹ', 'Ǻ' => 'ǻ', 'Ǽ' => 'ǽ', 'Ǿ' => 'ǿ', 'Ȁ' => 'ȁ', 'Ȃ' => 'ȃ', 'Ȅ' => 'ȅ', 'Ȇ' => 'ȇ', 'Ȉ' => 'ȉ', 'Ȋ' => 'ȋ', 'Ȍ' => 'ȍ', 'Ȏ' => 'ȏ', 'Ȑ' => 'ȑ', 'Ȓ' => 'ȓ', 'Ȕ' => 'ȕ', 'Ȗ' => 'ȗ', 'Ș' => 'ș', 'Ț' => 'ț', 'Ȝ' => 'ȝ', 'Ȟ' => 'ȟ', 'Ƞ' => 'ƞ', 'Ȣ' => 'ȣ', 'Ȥ' => 'ȥ', 'Ȧ' => 'ȧ', 'Ȩ' => 'ȩ', 'Ȫ' => 'ȫ', 'Ȭ' => 'ȭ', 'Ȯ' => 'ȯ', 'Ȱ' => 'ȱ', 'Ȳ' => 'ȳ', 'Ⱥ' => 'ⱥ', 'Ȼ' => 'ȼ', 'Ƚ' => 'ƚ', 'Ⱦ' => 'ⱦ', 'Ɂ' => 'ɂ', 'Ƀ' => 'ƀ', 'Ʉ' => 'ʉ', 'Ʌ' => 'ʌ', 'Ɇ' => 'ɇ', 'Ɉ' => 'ɉ', 'Ɋ' => 'ɋ', 'Ɍ' => 'ɍ', 'Ɏ' => 'ɏ', 'Ͱ' => 'ͱ', 'Ͳ' => 'ͳ', 'Ͷ' => 'ͷ', 'Ϳ' => 'ϳ', 'Ά' => 'ά', 'Έ' => 'έ', 'Ή' => 'ή', 'Ί' => 'ί', 'Ό' => 'ό', 'Ύ' => 'ύ', 'Ώ' => 'ώ', 'Α' => 'α', 'Β' => 'β', 'Γ' => 'γ', 'Δ' => 'δ', 'Ε' => 'ε', 'Ζ' => 'ζ', 'Η' => 'η', 'Θ' => 'θ', 'Ι' => 'ι', 'Κ' => 'κ', 'Λ' => 'λ', 'Μ' => 'μ', 'Ν' => 'ν', 'Ξ' => 'ξ', 'Ο' => 'ο', 'Π' => 'π', 'Ρ' => 'ρ', 'Σ' => 'σ', 'Τ' => 'τ', 'Υ' => 'υ', 'Φ' => 'φ', 'Χ' => 'χ', 'Ψ' => 'ψ', 'Ω' => 'ω', 'Ϊ' => 'ϊ', 'Ϋ' => 'ϋ', 'Ϗ' => 'ϗ', 'Ϙ' => 'ϙ', 'Ϛ' => 'ϛ', 'Ϝ' => 'ϝ', 'Ϟ' => 'ϟ', 'Ϡ' => 'ϡ', 'Ϣ' => 'ϣ', 'Ϥ' => 'ϥ', 'Ϧ' => 'ϧ', 'Ϩ' => 'ϩ', 'Ϫ' => 'ϫ', 'Ϭ' => 'ϭ', 'Ϯ' => 'ϯ', 'ϴ' => 'θ', 'Ϸ' => 'ϸ', 'Ϲ' => 'ϲ', 'Ϻ' => 'ϻ', 'Ͻ' => 'ͻ', 'Ͼ' => 'ͼ', 'Ͽ' => 'ͽ', 'Ѐ' => 'ѐ', 'Ё' => 'ё', 'Ђ' => 'ђ', 'Ѓ' => 'ѓ', 'Є' => 'є', 'Ѕ' => 'ѕ', 'І' => 'і', 'Ї' => 'ї', 'Ј' => 'ј', 'Љ' => 'љ', 'Њ' => 'њ', 'Ћ' => 'ћ', 'Ќ' => 'ќ', 'Ѝ' => 'ѝ', 'Ў' => 'ў', 'Џ' => 'џ', 'А' => 'а', 'Б' => 'б', 'В' => 'в', 'Г' => 'г', 'Д' => 'д', 'Е' => 'е', 'Ж' => 'ж', 'З' => 'з', 'И' => 'и', 'Й' => 'й', 'К' => 'к', 'Л' => 'л', 'М' => 'м', 'Н' => 'н', 'О' => 'о', 'П' => 'п', 'Р' => 'р', 'С' => 'с', 'Т' => 'т', 'У' => 'у', 'Ф' => 'ф', 'Х' => 'х', 'Ц' => 'ц', 'Ч' => 'ч', 'Ш' => 'ш', 'Щ' => 'щ', 'Ъ' => 'ъ', 'Ы' => 'ы', 'Ь' => 'ь', 'Э' => 'э', 'Ю' => 'ю', 'Я' => 'я', 'Ѡ' => 'ѡ', 'Ѣ' => 'ѣ', 'Ѥ' => 'ѥ', 'Ѧ' => 'ѧ', 'Ѩ' => 'ѩ', 'Ѫ' => 'ѫ', 'Ѭ' => 'ѭ', 'Ѯ' => 'ѯ', 'Ѱ' => 'ѱ', 'Ѳ' => 'ѳ', 'Ѵ' => 'ѵ', 'Ѷ' => 'ѷ', 'Ѹ' => 'ѹ', 'Ѻ' => 'ѻ', 'Ѽ' => 'ѽ', 'Ѿ' => 'ѿ', 'Ҁ' => 'ҁ', 'Ҋ' => 'ҋ', 'Ҍ' => 'ҍ', 'Ҏ' => 'ҏ', 'Ґ' => 'ґ', 'Ғ' => 'ғ', 'Ҕ' => 'ҕ', 'Җ' => 'җ', 'Ҙ' => 'ҙ', 'Қ' => 'қ', 'Ҝ' => 'ҝ', 'Ҟ' => 'ҟ', 'Ҡ' => 'ҡ', 'Ң' => 'ң', 'Ҥ' => 'ҥ', 'Ҧ' => 'ҧ', 'Ҩ' => 'ҩ', 'Ҫ' => 'ҫ', 'Ҭ' => 'ҭ', 'Ү' => 'ү', 'Ұ' => 'ұ', 'Ҳ' => 'ҳ', 'Ҵ' => 'ҵ', 'Ҷ' => 'ҷ', 'Ҹ' => 'ҹ', 'Һ' => 'һ', 'Ҽ' => 'ҽ', 'Ҿ' => 'ҿ', 'Ӏ' => 'ӏ', 'Ӂ' => 'ӂ', 'Ӄ' => 'ӄ', 'Ӆ' => 'ӆ', 'Ӈ' => 'ӈ', 'Ӊ' => 'ӊ', 'Ӌ' => 'ӌ', 'Ӎ' => 'ӎ', 'Ӑ' => 'ӑ', 'Ӓ' => 'ӓ', 'Ӕ' => 'ӕ', 'Ӗ' => 'ӗ', 'Ә' => 'ә', 'Ӛ' => 'ӛ', 'Ӝ' => 'ӝ', 'Ӟ' => 'ӟ', 'Ӡ' => 'ӡ', 'Ӣ' => 'ӣ', 'Ӥ' => 'ӥ', 'Ӧ' => 'ӧ', 'Ө' => 'ө', 'Ӫ' => 'ӫ', 'Ӭ' => 'ӭ', 'Ӯ' => 'ӯ', 'Ӱ' => 'ӱ', 'Ӳ' => 'ӳ', 'Ӵ' => 'ӵ', 'Ӷ' => 'ӷ', 'Ӹ' => 'ӹ', 'Ӻ' => 'ӻ', 'Ӽ' => 'ӽ', 'Ӿ' => 'ӿ', 'Ԁ' => 'ԁ', 'Ԃ' => 'ԃ', 'Ԅ' => 'ԅ', 'Ԇ' => 'ԇ', 'Ԉ' => 'ԉ', 'Ԋ' => 'ԋ', 'Ԍ' => 'ԍ', 'Ԏ' => 'ԏ', 'Ԑ' => 'ԑ', 'Ԓ' => 'ԓ', 'Ԕ' => 'ԕ', 'Ԗ' => 'ԗ', 'Ԙ' => 'ԙ', 'Ԛ' => 'ԛ', 'Ԝ' => 'ԝ', 'Ԟ' => 'ԟ', 'Ԡ' => 'ԡ', 'Ԣ' => 'ԣ', 'Ԥ' => 'ԥ', 'Ԧ' => 'ԧ', 'Ԩ' => 'ԩ', 'Ԫ' => 'ԫ', 'Ԭ' => 'ԭ', 'Ԯ' => 'ԯ', 'Ա' => 'ա', 'Բ' => 'բ', 'Գ' => 'գ', 'Դ' => 'դ', 'Ե' => 'ե', 'Զ' => 'զ', 'Է' => 'է', 'Ը' => 'ը', 'Թ' => 'թ', 'Ժ' => 'ժ', 'Ի' => 'ի', 'Լ' => 'լ', 'Խ' => 'խ', 'Ծ' => 'ծ', 'Կ' => 'կ', 'Հ' => 'հ', 'Ձ' => 'ձ', 'Ղ' => 'ղ', 'Ճ' => 'ճ', 'Մ' => 'մ', 'Յ' => 'յ', 'Ն' => 'ն', 'Շ' => 'շ', 'Ո' => 'ո', 'Չ' => 'չ', 'Պ' => 'պ', 'Ջ' => 'ջ', 'Ռ' => 'ռ', 'Ս' => 'ս', 'Վ' => 'վ', 'Տ' => 'տ', 'Ր' => 'ր', 'Ց' => 'ց', 'Ւ' => 'ւ', 'Փ' => 'փ', 'Ք' => 'ք', 'Օ' => 'օ', 'Ֆ' => 'ֆ', 'Ⴀ' => 'ⴀ', 'Ⴁ' => 'ⴁ', 'Ⴂ' => 'ⴂ', 'Ⴃ' => 'ⴃ', 'Ⴄ' => 'ⴄ', 'Ⴅ' => 'ⴅ', 'Ⴆ' => 'ⴆ', 'Ⴇ' => 'ⴇ', 'Ⴈ' => 'ⴈ', 'Ⴉ' => 'ⴉ', 'Ⴊ' => 'ⴊ', 'Ⴋ' => 'ⴋ', 'Ⴌ' => 'ⴌ', 'Ⴍ' => 'ⴍ', 'Ⴎ' => 'ⴎ', 'Ⴏ' => 'ⴏ', 'Ⴐ' => 'ⴐ', 'Ⴑ' => 'ⴑ', 'Ⴒ' => 'ⴒ', 'Ⴓ' => 'ⴓ', 'Ⴔ' => 'ⴔ', 'Ⴕ' => 'ⴕ', 'Ⴖ' => 'ⴖ', 'Ⴗ' => 'ⴗ', 'Ⴘ' => 'ⴘ', 'Ⴙ' => 'ⴙ', 'Ⴚ' => 'ⴚ', 'Ⴛ' => 'ⴛ', 'Ⴜ' => 'ⴜ', 'Ⴝ' => 'ⴝ', 'Ⴞ' => 'ⴞ', 'Ⴟ' => 'ⴟ', 'Ⴠ' => 'ⴠ', 'Ⴡ' => 'ⴡ', 'Ⴢ' => 'ⴢ', 'Ⴣ' => 'ⴣ', 'Ⴤ' => 'ⴤ', 'Ⴥ' => 'ⴥ', 'Ⴧ' => 'ⴧ', 'Ⴭ' => 'ⴭ', 'Ꭰ' => 'ꭰ', 'Ꭱ' => 'ꭱ', 'Ꭲ' => 'ꭲ', 'Ꭳ' => 'ꭳ', 'Ꭴ' => 'ꭴ', 'Ꭵ' => 'ꭵ', 'Ꭶ' => 'ꭶ', 'Ꭷ' => 'ꭷ', 'Ꭸ' => 'ꭸ', 'Ꭹ' => 'ꭹ', 'Ꭺ' => 'ꭺ', 'Ꭻ' => 'ꭻ', 'Ꭼ' => 'ꭼ', 'Ꭽ' => 'ꭽ', 'Ꭾ' => 'ꭾ', 'Ꭿ' => 'ꭿ', 'Ꮀ' => 'ꮀ', 'Ꮁ' => 'ꮁ', 'Ꮂ' => 'ꮂ', 'Ꮃ' => 'ꮃ', 'Ꮄ' => 'ꮄ', 'Ꮅ' => 'ꮅ', 'Ꮆ' => 'ꮆ', 'Ꮇ' => 'ꮇ', 'Ꮈ' => 'ꮈ', 'Ꮉ' => 'ꮉ', 'Ꮊ' => 'ꮊ', 'Ꮋ' => 'ꮋ', 'Ꮌ' => 'ꮌ', 'Ꮍ' => 'ꮍ', 'Ꮎ' => 'ꮎ', 'Ꮏ' => 'ꮏ', 'Ꮐ' => 'ꮐ', 'Ꮑ' => 'ꮑ', 'Ꮒ' => 'ꮒ', 'Ꮓ' => 'ꮓ', 'Ꮔ' => 'ꮔ', 'Ꮕ' => 'ꮕ', 'Ꮖ' => 'ꮖ', 'Ꮗ' => 'ꮗ', 'Ꮘ' => 'ꮘ', 'Ꮙ' => 'ꮙ', 'Ꮚ' => 'ꮚ', 'Ꮛ' => 'ꮛ', 'Ꮜ' => 'ꮜ', 'Ꮝ' => 'ꮝ', 'Ꮞ' => 'ꮞ', 'Ꮟ' => 'ꮟ', 'Ꮠ' => 'ꮠ', 'Ꮡ' => 'ꮡ', 'Ꮢ' => 'ꮢ', 'Ꮣ' => 'ꮣ', 'Ꮤ' => 'ꮤ', 'Ꮥ' => 'ꮥ', 'Ꮦ' => 'ꮦ', 'Ꮧ' => 'ꮧ', 'Ꮨ' => 'ꮨ', 'Ꮩ' => 'ꮩ', 'Ꮪ' => 'ꮪ', 'Ꮫ' => 'ꮫ', 'Ꮬ' => 'ꮬ', 'Ꮭ' => 'ꮭ', 'Ꮮ' => 'ꮮ', 'Ꮯ' => 'ꮯ', 'Ꮰ' => 'ꮰ', 'Ꮱ' => 'ꮱ', 'Ꮲ' => 'ꮲ', 'Ꮳ' => 'ꮳ', 'Ꮴ' => 'ꮴ', 'Ꮵ' => 'ꮵ', 'Ꮶ' => 'ꮶ', 'Ꮷ' => 'ꮷ', 'Ꮸ' => 'ꮸ', 'Ꮹ' => 'ꮹ', 'Ꮺ' => 'ꮺ', 'Ꮻ' => 'ꮻ', 'Ꮼ' => 'ꮼ', 'Ꮽ' => 'ꮽ', 'Ꮾ' => 'ꮾ', 'Ꮿ' => 'ꮿ', 'Ᏸ' => 'ᏸ', 'Ᏹ' => 'ᏹ', 'Ᏺ' => 'ᏺ', 'Ᏻ' => 'ᏻ', 'Ᏼ' => 'ᏼ', 'Ᏽ' => 'ᏽ', 'Ა' => 'ა', 'Ბ' => 'ბ', 'Გ' => 'გ', 'Დ' => 'დ', 'Ე' => 'ე', 'Ვ' => 'ვ', 'Ზ' => 'ზ', 'Თ' => 'თ', 'Ი' => 'ი', 'Კ' => 'კ', 'Ლ' => 'ლ', 'Მ' => 'მ', 'Ნ' => 'ნ', 'Ო' => 'ო', 'Პ' => 'პ', 'Ჟ' => 'ჟ', 'Რ' => 'რ', 'Ს' => 'ს', 'Ტ' => 'ტ', 'Უ' => 'უ', 'Ფ' => 'ფ', 'Ქ' => 'ქ', 'Ღ' => 'ღ', 'Ყ' => 'ყ', 'Შ' => 'შ', 'Ჩ' => 'ჩ', 'Ც' => 'ც', 'Ძ' => 'ძ', 'Წ' => 'წ', 'Ჭ' => 'ჭ', 'Ხ' => 'ხ', 'Ჯ' => 'ჯ', 'Ჰ' => 'ჰ', 'Ჱ' => 'ჱ', 'Ჲ' => 'ჲ', 'Ჳ' => 'ჳ', 'Ჴ' => 'ჴ', 'Ჵ' => 'ჵ', 'Ჶ' => 'ჶ', 'Ჷ' => 'ჷ', 'Ჸ' => 'ჸ', 'Ჹ' => 'ჹ', 'Ჺ' => 'ჺ', 'Ჽ' => 'ჽ', 'Ჾ' => 'ჾ', 'Ჿ' => 'ჿ', 'Ḁ' => 'ḁ', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', 'Ḇ' => 'ḇ', 'Ḉ' => 'ḉ', 'Ḋ' => 'ḋ', 'Ḍ' => 'ḍ', 'Ḏ' => 'ḏ', 'Ḑ' => 'ḑ', 'Ḓ' => 'ḓ', 'Ḕ' => 'ḕ', 'Ḗ' => 'ḗ', 'Ḙ' => 'ḙ', 'Ḛ' => 'ḛ', 'Ḝ' => 'ḝ', 'Ḟ' => 'ḟ', 'Ḡ' => 'ḡ', 'Ḣ' => 'ḣ', 'Ḥ' => 'ḥ', 'Ḧ' => 'ḧ', 'Ḩ' => 'ḩ', 'Ḫ' => 'ḫ', 'Ḭ' => 'ḭ', 'Ḯ' => 'ḯ', 'Ḱ' => 'ḱ', 'Ḳ' => 'ḳ', 'Ḵ' => 'ḵ', 'Ḷ' => 'ḷ', 'Ḹ' => 'ḹ', 'Ḻ' => 'ḻ', 'Ḽ' => 'ḽ', 'Ḿ' => 'ḿ', 'Ṁ' => 'ṁ', 'Ṃ' => 'ṃ', 'Ṅ' => 'ṅ', 'Ṇ' => 'ṇ', 'Ṉ' => 'ṉ', 'Ṋ' => 'ṋ', 'Ṍ' => 'ṍ', 'Ṏ' => 'ṏ', 'Ṑ' => 'ṑ', 'Ṓ' => 'ṓ', 'Ṕ' => 'ṕ', 'Ṗ' => 'ṗ', 'Ṙ' => 'ṙ', 'Ṛ' => 'ṛ', 'Ṝ' => 'ṝ', 'Ṟ' => 'ṟ', 'Ṡ' => 'ṡ', 'Ṣ' => 'ṣ', 'Ṥ' => 'ṥ', 'Ṧ' => 'ṧ', 'Ṩ' => 'ṩ', 'Ṫ' => 'ṫ', 'Ṭ' => 'ṭ', 'Ṯ' => 'ṯ', 'Ṱ' => 'ṱ', 'Ṳ' => 'ṳ', 'Ṵ' => 'ṵ', 'Ṷ' => 'ṷ', 'Ṹ' => 'ṹ', 'Ṻ' => 'ṻ', 'Ṽ' => 'ṽ', 'Ṿ' => 'ṿ', 'Ẁ' => 'ẁ', 'Ẃ' => 'ẃ', 'Ẅ' => 'ẅ', 'Ẇ' => 'ẇ', 'Ẉ' => 'ẉ', 'Ẋ' => 'ẋ', 'Ẍ' => 'ẍ', 'Ẏ' => 'ẏ', 'Ẑ' => 'ẑ', 'Ẓ' => 'ẓ', 'Ẕ' => 'ẕ', 'ẞ' => 'ß', 'Ạ' => 'ạ', 'Ả' => 'ả', 'Ấ' => 'ấ', 'Ầ' => 'ầ', 'Ẩ' => 'ẩ', 'Ẫ' => 'ẫ', 'Ậ' => 'ậ', 'Ắ' => 'ắ', 'Ằ' => 'ằ', 'Ẳ' => 'ẳ', 'Ẵ' => 'ẵ', 'Ặ' => 'ặ', 'Ẹ' => 'ẹ', 'Ẻ' => 'ẻ', 'Ẽ' => 'ẽ', 'Ế' => 'ế', 'Ề' => 'ề', 'Ể' => 'ể', 'Ễ' => 'ễ', 'Ệ' => 'ệ', 'Ỉ' => 'ỉ', 'Ị' => 'ị', 'Ọ' => 'ọ', 'Ỏ' => 'ỏ', 'Ố' => 'ố', 'Ồ' => 'ồ', 'Ổ' => 'ổ', 'Ỗ' => 'ỗ', 'Ộ' => 'ộ', 'Ớ' => 'ớ', 'Ờ' => 'ờ', 'Ở' => 'ở', 'Ỡ' => 'ỡ', 'Ợ' => 'ợ', 'Ụ' => 'ụ', 'Ủ' => 'ủ', 'Ứ' => 'ứ', 'Ừ' => 'ừ', 'Ử' => 'ử', 'Ữ' => 'ữ', 'Ự' => 'ự', 'Ỳ' => 'ỳ', 'Ỵ' => 'ỵ', 'Ỷ' => 'ỷ', 'Ỹ' => 'ỹ', 'Ỻ' => 'ỻ', 'Ỽ' => 'ỽ', 'Ỿ' => 'ỿ', 'Ἀ' => 'ἀ', 'Ἁ' => 'ἁ', 'Ἂ' => 'ἂ', 'Ἃ' => 'ἃ', 'Ἄ' => 'ἄ', 'Ἅ' => 'ἅ', 'Ἆ' => 'ἆ', 'Ἇ' => 'ἇ', 'Ἐ' => 'ἐ', 'Ἑ' => 'ἑ', 'Ἒ' => 'ἒ', 'Ἓ' => 'ἓ', 'Ἔ' => 'ἔ', 'Ἕ' => 'ἕ', 'Ἠ' => 'ἠ', 'Ἡ' => 'ἡ', 'Ἢ' => 'ἢ', 'Ἣ' => 'ἣ', 'Ἤ' => 'ἤ', 'Ἥ' => 'ἥ', 'Ἦ' => 'ἦ', 'Ἧ' => 'ἧ', 'Ἰ' => 'ἰ', 'Ἱ' => 'ἱ', 'Ἲ' => 'ἲ', 'Ἳ' => 'ἳ', 'Ἴ' => 'ἴ', 'Ἵ' => 'ἵ', 'Ἶ' => 'ἶ', 'Ἷ' => 'ἷ', 'Ὀ' => 'ὀ', 'Ὁ' => 'ὁ', 'Ὂ' => 'ὂ', 'Ὃ' => 'ὃ', 'Ὄ' => 'ὄ', 'Ὅ' => 'ὅ', 'Ὑ' => 'ὑ', 'Ὓ' => 'ὓ', 'Ὕ' => 'ὕ', 'Ὗ' => 'ὗ', 'Ὠ' => 'ὠ', 'Ὡ' => 'ὡ', 'Ὢ' => 'ὢ', 'Ὣ' => 'ὣ', 'Ὤ' => 'ὤ', 'Ὥ' => 'ὥ', 'Ὦ' => 'ὦ', 'Ὧ' => 'ὧ', 'ᾈ' => 'ᾀ', 'ᾉ' => 'ᾁ', 'ᾊ' => 'ᾂ', 'ᾋ' => 'ᾃ', 'ᾌ' => 'ᾄ', 'ᾍ' => 'ᾅ', 'ᾎ' => 'ᾆ', 'ᾏ' => 'ᾇ', 'ᾘ' => 'ᾐ', 'ᾙ' => 'ᾑ', 'ᾚ' => 'ᾒ', 'ᾛ' => 'ᾓ', 'ᾜ' => 'ᾔ', 'ᾝ' => 'ᾕ', 'ᾞ' => 'ᾖ', 'ᾟ' => 'ᾗ', 'ᾨ' => 'ᾠ', 'ᾩ' => 'ᾡ', 'ᾪ' => 'ᾢ', 'ᾫ' => 'ᾣ', 'ᾬ' => 'ᾤ', 'ᾭ' => 'ᾥ', 'ᾮ' => 'ᾦ', 'ᾯ' => 'ᾧ', 'Ᾰ' => 'ᾰ', 'Ᾱ' => 'ᾱ', 'Ὰ' => 'ὰ', 'Ά' => 'ά', 'ᾼ' => 'ᾳ', 'Ὲ' => 'ὲ', 'Έ' => 'έ', 'Ὴ' => 'ὴ', 'Ή' => 'ή', 'ῌ' => 'ῃ', 'Ῐ' => 'ῐ', 'Ῑ' => 'ῑ', 'Ὶ' => 'ὶ', 'Ί' => 'ί', 'Ῠ' => 'ῠ', 'Ῡ' => 'ῡ', 'Ὺ' => 'ὺ', 'Ύ' => 'ύ', 'Ῥ' => 'ῥ', 'Ὸ' => 'ὸ', 'Ό' => 'ό', 'Ὼ' => 'ὼ', 'Ώ' => 'ώ', 'ῼ' => 'ῳ', 'Ω' => 'ω', 'K' => 'k', 'Å' => 'å', 'Ⅎ' => 'ⅎ', 'Ⅰ' => 'ⅰ', 'Ⅱ' => 'ⅱ', 'Ⅲ' => 'ⅲ', 'Ⅳ' => 'ⅳ', 'Ⅴ' => 'ⅴ', 'Ⅵ' => 'ⅵ', 'Ⅶ' => 'ⅶ', 'Ⅷ' => 'ⅷ', 'Ⅸ' => 'ⅸ', 'Ⅹ' => 'ⅹ', 'Ⅺ' => 'ⅺ', 'Ⅻ' => 'ⅻ', 'Ⅼ' => 'ⅼ', 'Ⅽ' => 'ⅽ', 'Ⅾ' => 'ⅾ', 'Ⅿ' => 'ⅿ', 'Ↄ' => 'ↄ', 'Ⓐ' => 'ⓐ', 'Ⓑ' => 'ⓑ', 'Ⓒ' => 'ⓒ', 'Ⓓ' => 'ⓓ', 'Ⓔ' => 'ⓔ', 'Ⓕ' => 'ⓕ', 'Ⓖ' => 'ⓖ', 'Ⓗ' => 'ⓗ', 'Ⓘ' => 'ⓘ', 'Ⓙ' => 'ⓙ', 'Ⓚ' => 'ⓚ', 'Ⓛ' => 'ⓛ', 'Ⓜ' => 'ⓜ', 'Ⓝ' => 'ⓝ', 'Ⓞ' => 'ⓞ', 'Ⓟ' => 'ⓟ', 'Ⓠ' => 'ⓠ', 'Ⓡ' => 'ⓡ', 'Ⓢ' => 'ⓢ', 'Ⓣ' => 'ⓣ', 'Ⓤ' => 'ⓤ', 'Ⓥ' => 'ⓥ', 'Ⓦ' => 'ⓦ', 'Ⓧ' => 'ⓧ', 'Ⓨ' => 'ⓨ', 'Ⓩ' => 'ⓩ', 'Ⰰ' => 'ⰰ', 'Ⰱ' => 'ⰱ', 'Ⰲ' => 'ⰲ', 'Ⰳ' => 'ⰳ', 'Ⰴ' => 'ⰴ', 'Ⰵ' => 'ⰵ', 'Ⰶ' => 'ⰶ', 'Ⰷ' => 'ⰷ', 'Ⰸ' => 'ⰸ', 'Ⰹ' => 'ⰹ', 'Ⰺ' => 'ⰺ', 'Ⰻ' => 'ⰻ', 'Ⰼ' => 'ⰼ', 'Ⰽ' => 'ⰽ', 'Ⰾ' => 'ⰾ', 'Ⰿ' => 'ⰿ', 'Ⱀ' => 'ⱀ', 'Ⱁ' => 'ⱁ', 'Ⱂ' => 'ⱂ', 'Ⱃ' => 'ⱃ', 'Ⱄ' => 'ⱄ', 'Ⱅ' => 'ⱅ', 'Ⱆ' => 'ⱆ', 'Ⱇ' => 'ⱇ', 'Ⱈ' => 'ⱈ', 'Ⱉ' => 'ⱉ', 'Ⱊ' => 'ⱊ', 'Ⱋ' => 'ⱋ', 'Ⱌ' => 'ⱌ', 'Ⱍ' => 'ⱍ', 'Ⱎ' => 'ⱎ', 'Ⱏ' => 'ⱏ', 'Ⱐ' => 'ⱐ', 'Ⱑ' => 'ⱑ', 'Ⱒ' => 'ⱒ', 'Ⱓ' => 'ⱓ', 'Ⱔ' => 'ⱔ', 'Ⱕ' => 'ⱕ', 'Ⱖ' => 'ⱖ', 'Ⱗ' => 'ⱗ', 'Ⱘ' => 'ⱘ', 'Ⱙ' => 'ⱙ', 'Ⱚ' => 'ⱚ', 'Ⱛ' => 'ⱛ', 'Ⱜ' => 'ⱜ', 'Ⱝ' => 'ⱝ', 'Ⱞ' => 'ⱞ', 'Ⱡ' => 'ⱡ', 'Ɫ' => 'ɫ', 'Ᵽ' => 'ᵽ', 'Ɽ' => 'ɽ', 'Ⱨ' => 'ⱨ', 'Ⱪ' => 'ⱪ', 'Ⱬ' => 'ⱬ', 'Ɑ' => 'ɑ', 'Ɱ' => 'ɱ', 'Ɐ' => 'ɐ', 'Ɒ' => 'ɒ', 'Ⱳ' => 'ⱳ', 'Ⱶ' => 'ⱶ', 'Ȿ' => 'ȿ', 'Ɀ' => 'ɀ', 'Ⲁ' => 'ⲁ', 'Ⲃ' => 'ⲃ', 'Ⲅ' => 'ⲅ', 'Ⲇ' => 'ⲇ', 'Ⲉ' => 'ⲉ', 'Ⲋ' => 'ⲋ', 'Ⲍ' => 'ⲍ', 'Ⲏ' => 'ⲏ', 'Ⲑ' => 'ⲑ', 'Ⲓ' => 'ⲓ', 'Ⲕ' => 'ⲕ', 'Ⲗ' => 'ⲗ', 'Ⲙ' => 'ⲙ', 'Ⲛ' => 'ⲛ', 'Ⲝ' => 'ⲝ', 'Ⲟ' => 'ⲟ', 'Ⲡ' => 'ⲡ', 'Ⲣ' => 'ⲣ', 'Ⲥ' => 'ⲥ', 'Ⲧ' => 'ⲧ', 'Ⲩ' => 'ⲩ', 'Ⲫ' => 'ⲫ', 'Ⲭ' => 'ⲭ', 'Ⲯ' => 'ⲯ', 'Ⲱ' => 'ⲱ', 'Ⲳ' => 'ⲳ', 'Ⲵ' => 'ⲵ', 'Ⲷ' => 'ⲷ', 'Ⲹ' => 'ⲹ', 'Ⲻ' => 'ⲻ', 'Ⲽ' => 'ⲽ', 'Ⲿ' => 'ⲿ', 'Ⳁ' => 'ⳁ', 'Ⳃ' => 'ⳃ', 'Ⳅ' => 'ⳅ', 'Ⳇ' => 'ⳇ', 'Ⳉ' => 'ⳉ', 'Ⳋ' => 'ⳋ', 'Ⳍ' => 'ⳍ', 'Ⳏ' => 'ⳏ', 'Ⳑ' => 'ⳑ', 'Ⳓ' => 'ⳓ', 'Ⳕ' => 'ⳕ', 'Ⳗ' => 'ⳗ', 'Ⳙ' => 'ⳙ', 'Ⳛ' => 'ⳛ', 'Ⳝ' => 'ⳝ', 'Ⳟ' => 'ⳟ', 'Ⳡ' => 'ⳡ', 'Ⳣ' => 'ⳣ', 'Ⳬ' => 'ⳬ', 'Ⳮ' => 'ⳮ', 'Ⳳ' => 'ⳳ', 'Ꙁ' => 'ꙁ', 'Ꙃ' => 'ꙃ', 'Ꙅ' => 'ꙅ', 'Ꙇ' => 'ꙇ', 'Ꙉ' => 'ꙉ', 'Ꙋ' => 'ꙋ', 'Ꙍ' => 'ꙍ', 'Ꙏ' => 'ꙏ', 'Ꙑ' => 'ꙑ', 'Ꙓ' => 'ꙓ', 'Ꙕ' => 'ꙕ', 'Ꙗ' => 'ꙗ', 'Ꙙ' => 'ꙙ', 'Ꙛ' => 'ꙛ', 'Ꙝ' => 'ꙝ', 'Ꙟ' => 'ꙟ', 'Ꙡ' => 'ꙡ', 'Ꙣ' => 'ꙣ', 'Ꙥ' => 'ꙥ', 'Ꙧ' => 'ꙧ', 'Ꙩ' => 'ꙩ', 'Ꙫ' => 'ꙫ', 'Ꙭ' => 'ꙭ', 'Ꚁ' => 'ꚁ', 'Ꚃ' => 'ꚃ', 'Ꚅ' => 'ꚅ', 'Ꚇ' => 'ꚇ', 'Ꚉ' => 'ꚉ', 'Ꚋ' => 'ꚋ', 'Ꚍ' => 'ꚍ', 'Ꚏ' => 'ꚏ', 'Ꚑ' => 'ꚑ', 'Ꚓ' => 'ꚓ', 'Ꚕ' => 'ꚕ', 'Ꚗ' => 'ꚗ', 'Ꚙ' => 'ꚙ', 'Ꚛ' => 'ꚛ', 'Ꜣ' => 'ꜣ', 'Ꜥ' => 'ꜥ', 'Ꜧ' => 'ꜧ', 'Ꜩ' => 'ꜩ', 'Ꜫ' => 'ꜫ', 'Ꜭ' => 'ꜭ', 'Ꜯ' => 'ꜯ', 'Ꜳ' => 'ꜳ', 'Ꜵ' => 'ꜵ', 'Ꜷ' => 'ꜷ', 'Ꜹ' => 'ꜹ', 'Ꜻ' => 'ꜻ', 'Ꜽ' => 'ꜽ', 'Ꜿ' => 'ꜿ', 'Ꝁ' => 'ꝁ', 'Ꝃ' => 'ꝃ', 'Ꝅ' => 'ꝅ', 'Ꝇ' => 'ꝇ', 'Ꝉ' => 'ꝉ', 'Ꝋ' => 'ꝋ', 'Ꝍ' => 'ꝍ', 'Ꝏ' => 'ꝏ', 'Ꝑ' => 'ꝑ', 'Ꝓ' => 'ꝓ', 'Ꝕ' => 'ꝕ', 'Ꝗ' => 'ꝗ', 'Ꝙ' => 'ꝙ', 'Ꝛ' => 'ꝛ', 'Ꝝ' => 'ꝝ', 'Ꝟ' => 'ꝟ', 'Ꝡ' => 'ꝡ', 'Ꝣ' => 'ꝣ', 'Ꝥ' => 'ꝥ', 'Ꝧ' => 'ꝧ', 'Ꝩ' => 'ꝩ', 'Ꝫ' => 'ꝫ', 'Ꝭ' => 'ꝭ', 'Ꝯ' => 'ꝯ', 'Ꝺ' => 'ꝺ', 'Ꝼ' => 'ꝼ', 'Ᵹ' => 'ᵹ', 'Ꝿ' => 'ꝿ', 'Ꞁ' => 'ꞁ', 'Ꞃ' => 'ꞃ', 'Ꞅ' => 'ꞅ', 'Ꞇ' => 'ꞇ', 'Ꞌ' => 'ꞌ', 'Ɥ' => 'ɥ', 'Ꞑ' => 'ꞑ', 'Ꞓ' => 'ꞓ', 'Ꞗ' => 'ꞗ', 'Ꞙ' => 'ꞙ', 'Ꞛ' => 'ꞛ', 'Ꞝ' => 'ꞝ', 'Ꞟ' => 'ꞟ', 'Ꞡ' => 'ꞡ', 'Ꞣ' => 'ꞣ', 'Ꞥ' => 'ꞥ', 'Ꞧ' => 'ꞧ', 'Ꞩ' => 'ꞩ', 'Ɦ' => 'ɦ', 'Ɜ' => 'ɜ', 'Ɡ' => 'ɡ', 'Ɬ' => 'ɬ', 'Ɪ' => 'ɪ', 'Ʞ' => 'ʞ', 'Ʇ' => 'ʇ', 'Ʝ' => 'ʝ', 'Ꭓ' => 'ꭓ', 'Ꞵ' => 'ꞵ', 'Ꞷ' => 'ꞷ', 'Ꞹ' => 'ꞹ', 'Ꞻ' => 'ꞻ', 'Ꞽ' => 'ꞽ', 'Ꞿ' => 'ꞿ', 'Ꟃ' => 'ꟃ', 'Ꞔ' => 'ꞔ', 'Ʂ' => 'ʂ', 'Ᶎ' => 'ᶎ', 'Ꟈ' => 'ꟈ', 'Ꟊ' => 'ꟊ', 'Ꟶ' => 'ꟶ', 'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e', 'F' => 'f', 'G' => 'g', 'H' => 'h', 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', 'M' => 'm', 'N' => 'n', 'O' => 'o', 'P' => 'p', 'Q' => 'q', 'R' => 'r', 'S' => 's', 'T' => 't', 'U' => 'u', 'V' => 'v', 'W' => 'w', 'X' => 'x', 'Y' => 'y', 'Z' => 'z', '𐐀' => '𐐨', '𐐁' => '𐐩', '𐐂' => '𐐪', '𐐃' => '𐐫', '𐐄' => '𐐬', '𐐅' => '𐐭', '𐐆' => '𐐮', '𐐇' => '𐐯', '𐐈' => '𐐰', '𐐉' => '𐐱', '𐐊' => '𐐲', '𐐋' => '𐐳', '𐐌' => '𐐴', '𐐍' => '𐐵', '𐐎' => '𐐶', '𐐏' => '𐐷', '𐐐' => '𐐸', '𐐑' => '𐐹', '𐐒' => '𐐺', '𐐓' => '𐐻', '𐐔' => '𐐼', '𐐕' => '𐐽', '𐐖' => '𐐾', '𐐗' => '𐐿', '𐐘' => '𐑀', '𐐙' => '𐑁', '𐐚' => '𐑂', '𐐛' => '𐑃', '𐐜' => '𐑄', '𐐝' => '𐑅', '𐐞' => '𐑆', '𐐟' => '𐑇', '𐐠' => '𐑈', '𐐡' => '𐑉', '𐐢' => '𐑊', '𐐣' => '𐑋', '𐐤' => '𐑌', '𐐥' => '𐑍', '𐐦' => '𐑎', '𐐧' => '𐑏', '𐒰' => '𐓘', '𐒱' => '𐓙', '𐒲' => '𐓚', '𐒳' => '𐓛', '𐒴' => '𐓜', '𐒵' => '𐓝', '𐒶' => '𐓞', '𐒷' => '𐓟', '𐒸' => '𐓠', '𐒹' => '𐓡', '𐒺' => '𐓢', '𐒻' => '𐓣', '𐒼' => '𐓤', '𐒽' => '𐓥', '𐒾' => '𐓦', '𐒿' => '𐓧', '𐓀' => '𐓨', '𐓁' => '𐓩', '𐓂' => '𐓪', '𐓃' => '𐓫', '𐓄' => '𐓬', '𐓅' => '𐓭', '𐓆' => '𐓮', '𐓇' => '𐓯', '𐓈' => '𐓰', '𐓉' => '𐓱', '𐓊' => '𐓲', '𐓋' => '𐓳', '𐓌' => '𐓴', '𐓍' => '𐓵', '𐓎' => '𐓶', '𐓏' => '𐓷', '𐓐' => '𐓸', '𐓑' => '𐓹', '𐓒' => '𐓺', '𐓓' => '𐓻', '𐲀' => '𐳀', '𐲁' => '𐳁', '𐲂' => '𐳂', '𐲃' => '𐳃', '𐲄' => '𐳄', '𐲅' => '𐳅', '𐲆' => '𐳆', '𐲇' => '𐳇', '𐲈' => '𐳈', '𐲉' => '𐳉', '𐲊' => '𐳊', '𐲋' => '𐳋', '𐲌' => '𐳌', '𐲍' => '𐳍', '𐲎' => '𐳎', '𐲏' => '𐳏', '𐲐' => '𐳐', '𐲑' => '𐳑', '𐲒' => '𐳒', '𐲓' => '𐳓', '𐲔' => '𐳔', '𐲕' => '𐳕', '𐲖' => '𐳖', '𐲗' => '𐳗', '𐲘' => '𐳘', '𐲙' => '𐳙', '𐲚' => '𐳚', '𐲛' => '𐳛', '𐲜' => '𐳜', '𐲝' => '𐳝', '𐲞' => '𐳞', '𐲟' => '𐳟', '𐲠' => '𐳠', '𐲡' => '𐳡', '𐲢' => '𐳢', '𐲣' => '𐳣', '𐲤' => '𐳤', '𐲥' => '𐳥', '𐲦' => '𐳦', '𐲧' => '𐳧', '𐲨' => '𐳨', '𐲩' => '𐳩', '𐲪' => '𐳪', '𐲫' => '𐳫', '𐲬' => '𐳬', '𐲭' => '𐳭', '𐲮' => '𐳮', '𐲯' => '𐳯', '𐲰' => '𐳰', '𐲱' => '𐳱', '𐲲' => '𐳲', '𑢠' => '𑣀', '𑢡' => '𑣁', '𑢢' => '𑣂', '𑢣' => '𑣃', '𑢤' => '𑣄', '𑢥' => '𑣅', '𑢦' => '𑣆', '𑢧' => '𑣇', '𑢨' => '𑣈', '𑢩' => '𑣉', '𑢪' => '𑣊', '𑢫' => '𑣋', '𑢬' => '𑣌', '𑢭' => '𑣍', '𑢮' => '𑣎', '𑢯' => '𑣏', '𑢰' => '𑣐', '𑢱' => '𑣑', '𑢲' => '𑣒', '𑢳' => '𑣓', '𑢴' => '𑣔', '𑢵' => '𑣕', '𑢶' => '𑣖', '𑢷' => '𑣗', '𑢸' => '𑣘', '𑢹' => '𑣙', '𑢺' => '𑣚', '𑢻' => '𑣛', '𑢼' => '𑣜', '𑢽' => '𑣝', '𑢾' => '𑣞', '𑢿' => '𑣟', '𖹀' => '𖹠', '𖹁' => '𖹡', '𖹂' => '𖹢', '𖹃' => '𖹣', '𖹄' => '𖹤', '𖹅' => '𖹥', '𖹆' => '𖹦', '𖹇' => '𖹧', '𖹈' => '𖹨', '𖹉' => '𖹩', '𖹊' => '𖹪', '𖹋' => '𖹫', '𖹌' => '𖹬', '𖹍' => '𖹭', '𖹎' => '𖹮', '𖹏' => '𖹯', '𖹐' => '𖹰', '𖹑' => '𖹱', '𖹒' => '𖹲', '𖹓' => '𖹳', '𖹔' => '𖹴', '𖹕' => '𖹵', '𖹖' => '𖹶', '𖹗' => '𖹷', '𖹘' => '𖹸', '𖹙' => '𖹹', '𖹚' => '𖹺', '𖹛' => '𖹻', '𖹜' => '𖹼', '𖹝' => '𖹽', '𖹞' => '𖹾', '𖹟' => '𖹿', '𞤀' => '𞤢', '𞤁' => '𞤣', '𞤂' => '𞤤', '𞤃' => '𞤥', '𞤄' => '𞤦', '𞤅' => '𞤧', '𞤆' => '𞤨', '𞤇' => '𞤩', '𞤈' => '𞤪', '𞤉' => '𞤫', '𞤊' => '𞤬', '𞤋' => '𞤭', '𞤌' => '𞤮', '𞤍' => '𞤯', '𞤎' => '𞤰', '𞤏' => '𞤱', '𞤐' => '𞤲', '𞤑' => '𞤳', '𞤒' => '𞤴', '𞤓' => '𞤵', '𞤔' => '𞤶', '𞤕' => '𞤷', '𞤖' => '𞤸', '𞤗' => '𞤹', '𞤘' => '𞤺', '𞤙' => '𞤻', '𞤚' => '𞤼', '𞤛' => '𞤽', '𞤜' => '𞤾', '𞤝' => '𞤿', '𞤞' => '𞥀', '𞤟' => '𞥁', '𞤠' => '𞥂', '𞤡' => '𞥃', ); PK2A#]{.L�}�}-vendor/symfony/polyfill-mbstring/Mbstring.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Mbstring; /** * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. * * Implemented: * - mb_chr - Returns a specific character from its Unicode code point * - mb_convert_encoding - Convert character encoding * - mb_convert_variables - Convert character code in variable(s) * - mb_decode_mimeheader - Decode string in MIME header field * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED * - mb_decode_numericentity - Decode HTML numeric string reference to character * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_convert_case - Perform case folding on a string * - mb_detect_encoding - Detect character encoding * - mb_get_info - Get internal settings of mbstring * - mb_http_input - Detect HTTP input character encoding * - mb_http_output - Set/Get HTTP output character encoding * - mb_internal_encoding - Set/Get internal character encoding * - mb_list_encodings - Returns an array of all supported encodings * - mb_ord - Returns the Unicode code point of a character * - mb_output_handler - Callback function converts character encoding in output buffer * - mb_scrub - Replaces ill-formed byte sequences with substitute characters * - mb_strlen - Get string length * - mb_strpos - Find position of first occurrence of string in a string * - mb_strrpos - Find position of last occurrence of a string in a string * - mb_str_split - Convert a string to an array * - mb_strtolower - Make a string lowercase * - mb_strtoupper - Make a string uppercase * - mb_substitute_character - Set/Get substitution character * - mb_substr - Get part of string * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive * - mb_stristr - Finds first occurrence of a string within another, case insensitive * - mb_strrchr - Finds the last occurrence of a character in a string within another * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive * - mb_strstr - Finds first occurrence of a string within another * - mb_strwidth - Return width of string * - mb_substr_count - Count the number of substring occurrences * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) * - mb_ereg_* - Regular expression with multibyte support * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable * - mb_preferred_mime_name - Get MIME charset string * - mb_regex_encoding - Returns current encoding for multibyte regex as string * - mb_regex_set_options - Set/Get the default options for mbregex functions * - mb_send_mail - Send encoded mail * - mb_split - Split multibyte string using regular expression * - mb_strcut - Get part of string * - mb_strimwidth - Get truncated string with specified width * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Mbstring { public const MB_CASE_FOLD = \PHP_INT_MAX; private const SIMPLE_CASE_FOLD = [ ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], ]; private static $encodingList = ['ASCII', 'UTF-8']; private static $language = 'neutral'; private static $internalEncoding = 'UTF-8'; public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); } $toEncoding = self::getEncoding($toEncoding); if ('BASE64' === $fromEncoding) { $s = base64_decode($s); $fromEncoding = $toEncoding; } if ('BASE64' === $toEncoding) { return base64_encode($s); } if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); } if ('HTML-ENTITIES' === $fromEncoding) { $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); $fromEncoding = 'UTF-8'; } return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) { $ok = true; array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { $ok = false; } }); return $ok ? $fromEncoding : false; } public static function mb_decode_mimeheader($s) { return iconv_mime_decode($s, 2, self::$internalEncoding); } public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) { trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); } public static function mb_decode_numericentity($s, $convmap, $encoding = null) { if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return ''; // Instead of null (cf. mb_encode_numericentity). } $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $cnt = floor(\count($convmap) / 4) * 4; for ($i = 0; $i < $cnt; $i += 4) { // collector_decode_htmlnumericentity ignores $convmap[$i + 3] $convmap[$i] += $convmap[$i + 2]; $convmap[$i + 1] += $convmap[$i + 2]; } $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; for ($i = 0; $i < $cnt; $i += 4) { if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { return self::mb_chr($c - $convmap[$i + 2]); } } return $m[0]; }, $s); if (null === $encoding) { return $s; } return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) { if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; // Instead of '' (cf. mb_decode_numericentity). } if (null !== $is_hex && !\is_scalar($is_hex)) { trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); return null; } $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $cnt = floor(\count($convmap) / 4) * 4; $i = 0; $len = \strlen($s); $result = ''; while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; $c = self::mb_ord($uchr); for ($j = 0; $j < $cnt; $j += 4) { if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; continue 2; } } $result .= $uchr; } if (null === $encoding) { return $result; } return iconv('UTF-8', $encoding.'//IGNORE', $result); } public static function mb_convert_case($s, $mode, $encoding = null) { $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } if (\MB_CASE_TITLE == $mode) { static $titleRegexp = null; if (null === $titleRegexp) { $titleRegexp = self::getData('titleCaseRegexp'); } $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); } else { if (\MB_CASE_UPPER == $mode) { static $upper = null; if (null === $upper) { $upper = self::getData('upperCase'); } $map = $upper; } else { if (self::MB_CASE_FOLD === $mode) { static $caseFolding = null; if (null === $caseFolding) { $caseFolding = self::getData('caseFolding'); } $s = strtr($s, $caseFolding); } static $lower = null; if (null === $lower) { $lower = self::getData('lowerCase'); } $map = $lower; } static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $i = 0; $len = \strlen($s); while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; if (isset($map[$uchr])) { $uchr = $map[$uchr]; $nlen = \strlen($uchr); if ($nlen == $ulen) { $nlen = $i; do { $s[--$nlen] = $uchr[--$ulen]; } while ($ulen); } else { $s = substr_replace($s, $uchr, $i - $ulen, $ulen); $len += $nlen - $ulen; $i += $nlen - $ulen; } } } } if (null === $encoding) { return $s; } return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) { if (null === $encoding) { return self::$internalEncoding; } $normalizedEncoding = self::getEncoding($encoding); if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { self::$internalEncoding = $normalizedEncoding; return true; } if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); } public static function mb_language($lang = null) { if (null === $lang) { return self::$language; } switch ($normalizedLang = strtolower($lang)) { case 'uni': case 'neutral': self::$language = $normalizedLang; return true; } if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); } public static function mb_list_encodings() { return ['UTF-8']; } public static function mb_encoding_aliases($encoding) { switch (strtoupper($encoding)) { case 'UTF8': case 'UTF-8': return ['utf8']; } return false; } public static function mb_check_encoding($var = null, $encoding = null) { if (PHP_VERSION_ID < 70200 && \is_array($var)) { trigger_error('mb_check_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING); return null; } if (null === $encoding) { if (null === $var) { return false; } $encoding = self::$internalEncoding; } if (!\is_array($var)) { return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); } foreach ($var as $key => $value) { if (!self::mb_check_encoding($key, $encoding)) { return false; } if (!self::mb_check_encoding($value, $encoding)) { return false; } } return true; } public static function mb_detect_encoding($str, $encodingList = null, $strict = false) { if (null === $encodingList) { $encodingList = self::$encodingList; } else { if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); } foreach ($encodingList as $enc) { switch ($enc) { case 'ASCII': if (!preg_match('/[\x80-\xFF]/', $str)) { return $enc; } break; case 'UTF8': case 'UTF-8': if (preg_match('//u', $str)) { return 'UTF-8'; } break; default: if (0 === strncmp($enc, 'ISO-8859-', 9)) { return $enc; } } } return false; } public static function mb_detect_order($encodingList = null) { if (null === $encodingList) { return self::$encodingList; } if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); foreach ($encodingList as $enc) { switch ($enc) { default: if (strncmp($enc, 'ISO-8859-', 9)) { return false; } // no break case 'ASCII': case 'UTF8': case 'UTF-8': } } self::$encodingList = $encodingList; return true; } public static function mb_strlen($s, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return \strlen($s); } return @iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return strpos($haystack, $needle, $offset); } $needle = (string) $needle; if ('' === $needle) { if (80000 > \PHP_VERSION_ID) { trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); return false; } return 0; } return iconv_strpos($haystack, $needle, $offset, $encoding); } public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return strrpos($haystack, $needle, $offset); } if ($offset != (int) $offset) { $offset = 0; } elseif ($offset = (int) $offset) { if ($offset < 0) { if (0 > $offset += self::mb_strlen($needle)) { $haystack = self::mb_substr($haystack, 0, $offset, $encoding); } $offset = 0; } else { $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); } } $pos = '' !== $needle || 80000 > \PHP_VERSION_ID ? iconv_strrpos($haystack, $needle, $encoding) : self::mb_strlen($haystack, $encoding); return false !== $pos ? $offset + $pos : false; } public static function mb_str_split($string, $split_length = 1, $encoding = null) { if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); return null; } if (1 > $split_length = (int) $split_length) { if (80000 > \PHP_VERSION_ID) { trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); return false; } throw new \ValueError('Argument #2 ($length) must be greater than 0'); } if (null === $encoding) { $encoding = mb_internal_encoding(); } if ('UTF-8' === $encoding = self::getEncoding($encoding)) { $rx = '/('; while (65535 < $split_length) { $rx .= '.{65535}'; $split_length -= 65535; } $rx .= '.{'.$split_length.'})/us'; return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); } $result = []; $length = mb_strlen($string, $encoding); for ($i = 0; $i < $length; $i += $split_length) { $result[] = mb_substr($string, $i, $split_length, $encoding); } return $result; } public static function mb_strtolower($s, $encoding = null) { return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding); } public static function mb_strtoupper($s, $encoding = null) { return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding); } public static function mb_substitute_character($c = null) { if (null === $c) { return 'none'; } if (0 === strcasecmp($c, 'none')) { return true; } if (80000 > \PHP_VERSION_ID) { return false; } if (\is_int($c) || 'long' === $c || 'entity' === $c) { return false; } throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); } public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return (string) substr($s, $start, null === $length ? 2147483647 : $length); } if ($start < 0) { $start = iconv_strlen($s, $encoding) + $start; if ($start < 0) { $start = 0; } } if (null === $length) { $length = 2147483647; } elseif ($length < 0) { $length = iconv_strlen($s, $encoding) + $length - $start; if ($length < 0) { return ''; } } return (string) iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [ self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding), self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding), ]); return self::mb_strpos($haystack, $needle, $offset, $encoding); } public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) { $pos = self::mb_stripos($haystack, $needle, 0, $encoding); return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { $pos = strrpos($haystack, $needle); } else { $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = iconv_strrpos($haystack, $needle, $encoding); } return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) { $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = self::mb_strripos($haystack, $needle, $encoding); return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding); $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding); $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack); $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle); return self::mb_strrpos($haystack, $needle, $offset, $encoding); } public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) { $pos = strpos($haystack, $needle); if (false === $pos) { return false; } if ($part) { return substr($haystack, 0, $pos); } return substr($haystack, $pos); } public static function mb_get_info($type = 'all') { $info = [ 'internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', 'func_overload' => 0, 'func_overload_list' => 'no overload', 'mail_charset' => 'UTF-8', 'mail_header_encoding' => 'BASE64', 'mail_body_encoding' => 'BASE64', 'illegal_chars' => 0, 'encoding_translation' => 'Off', 'language' => self::$language, 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off', ]; if ('all' === $type) { return $info; } if (isset($info[$type])) { return $info[$type]; } return false; } public static function mb_http_input($type = '') { return false; } public static function mb_http_output($encoding = null) { return null !== $encoding ? 'pass' === $encoding : 'pass'; } public static function mb_strwidth($s, $encoding = null) { $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); return ($wide << 1) + iconv_strlen($s, 'UTF-8'); } public static function mb_substr_count($haystack, $needle, $encoding = null) { return substr_count($haystack, $needle); } public static function mb_output_handler($contents, $status) { return $contents; } public static function mb_chr($code, $encoding = null) { if (0x80 > $code %= 0x200000) { $s = \chr($code); } elseif (0x800 > $code) { $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { $s = mb_convert_encoding($s, $encoding, 'UTF-8'); } return $s; } public static function mb_ord($s, $encoding = null) { if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { $s = mb_convert_encoding($s, 'UTF-8', $encoding); } if (1 === \strlen($s)) { return \ord($s); } $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; if (0xF0 <= $code) { return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; } if (0xE0 <= $code) { return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; } if (0xC0 <= $code) { return (($code - 0xC0) << 6) + $s[2] - 0x80; } return $code; } public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string { if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) { throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH'); } if (null === $encoding) { $encoding = self::mb_internal_encoding(); } try { $validEncoding = @self::mb_check_encoding('', $encoding); } catch (\ValueError $e) { throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); } // BC for PHP 7.3 and lower if (!$validEncoding) { throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); } if (self::mb_strlen($pad_string, $encoding) <= 0) { throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string'); } $paddingRequired = $length - self::mb_strlen($string, $encoding); if ($paddingRequired < 1) { return $string; } switch ($pad_type) { case \STR_PAD_LEFT: return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string; case \STR_PAD_RIGHT: return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding); default: $leftPaddingLength = floor($paddingRequired / 2); $rightPaddingLength = $paddingRequired - $leftPaddingLength; return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding); } } private static function getSubpart($pos, $part, $haystack, $encoding) { if (false === $pos) { return false; } if ($part) { return self::mb_substr($haystack, 0, $pos, $encoding); } return self::mb_substr($haystack, $pos, null, $encoding); } private static function html_encoding_callback(array $m) { $i = 1; $entities = ''; $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); while (isset($m[$i])) { if (0x80 > $m[$i]) { $entities .= \chr($m[$i++]); continue; } if (0xF0 <= $m[$i]) { $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } elseif (0xE0 <= $m[$i]) { $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } else { $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; } $entities .= '&#'.$c.';'; } return $entities; } private static function title_case(array $s) { return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8'); } private static function getData($file) { if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { return require $file; } return false; } private static function getEncoding($encoding) { if (null === $encoding) { return self::$internalEncoding; } if ('UTF-8' === $encoding) { return 'UTF-8'; } $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } if ('UTF8' === $encoding) { return 'UTF-8'; } return $encoding; } } PK2A#]��m<VV.vendor/symfony/polyfill-mbstring/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Mbstring as p; if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } } if (!function_exists('mb_decode_mimeheader')) { function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } } if (!function_exists('mb_encode_mimeheader')) { function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } } if (!function_exists('mb_decode_numericentity')) { function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } } if (!function_exists('mb_encode_numericentity')) { function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } } if (!function_exists('mb_convert_case')) { function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } } if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } } if (!function_exists('mb_language')) { function mb_language($language = null) { return p\Mbstring::mb_language($language); } } if (!function_exists('mb_list_encodings')) { function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } } if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } } if (!function_exists('mb_check_encoding')) { function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } } if (!function_exists('mb_detect_encoding')) { function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } } if (!function_exists('mb_detect_order')) { function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } } if (!function_exists('mb_parse_str')) { function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; } } if (!function_exists('mb_strlen')) { function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } } if (!function_exists('mb_strpos')) { function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strtolower')) { function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } } if (!function_exists('mb_strtoupper')) { function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } } if (!function_exists('mb_substitute_character')) { function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } } if (!function_exists('mb_substr')) { function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } } if (!function_exists('mb_stripos')) { function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_stristr')) { function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strrchr')) { function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strrichr')) { function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strripos')) { function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strrpos')) { function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strstr')) { function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_get_info')) { function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } } if (!function_exists('mb_http_output')) { function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } } if (!function_exists('mb_strwidth')) { function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } } if (!function_exists('mb_substr_count')) { function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } } if (!function_exists('mb_output_handler')) { function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } } if (!function_exists('mb_http_input')) { function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } } if (!function_exists('mb_convert_variables')) { function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } } if (!function_exists('mb_ord')) { function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } } if (!function_exists('mb_str_pad')) { function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } } if (extension_loaded('mbstring')) { return; } if (!defined('MB_CASE_UPPER')) { define('MB_CASE_UPPER', 0); } if (!defined('MB_CASE_LOWER')) { define('MB_CASE_LOWER', 1); } if (!defined('MB_CASE_TITLE')) { define('MB_CASE_TITLE', 2); } PK2A#]F�f��+vendor/symfony/polyfill-php72/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php72 as p; if (\PHP_VERSION_ID >= 70200) { return; } if (!defined('PHP_FLOAT_DIG')) { define('PHP_FLOAT_DIG', 15); } if (!defined('PHP_FLOAT_EPSILON')) { define('PHP_FLOAT_EPSILON', 2.2204460492503E-16); } if (!defined('PHP_FLOAT_MIN')) { define('PHP_FLOAT_MIN', 2.2250738585072E-308); } if (!defined('PHP_FLOAT_MAX')) { define('PHP_FLOAT_MAX', 1.7976931348623157E+308); } if (!defined('PHP_OS_FAMILY')) { define('PHP_OS_FAMILY', p\Php72::php_os_family()); } if ('\\' === \DIRECTORY_SEPARATOR && !function_exists('sapi_windows_vt100_support')) { function sapi_windows_vt100_support($stream, $enable = null) { return p\Php72::sapi_windows_vt100_support($stream, $enable); } } if (!function_exists('stream_isatty')) { function stream_isatty($stream) { return p\Php72::stream_isatty($stream); } } if (!function_exists('utf8_encode')) { function utf8_encode($string) { return p\Php72::utf8_encode($string); } } if (!function_exists('utf8_decode')) { function utf8_decode($string) { return p\Php72::utf8_decode($string); } } if (!function_exists('spl_object_id')) { function spl_object_id($object) { return p\Php72::spl_object_id($object); } } if (!function_exists('mb_ord')) { function mb_ord($string, $encoding = null) { return p\Php72::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr($codepoint, $encoding = null) { return p\Php72::mb_chr($codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } } PK2A#]��� 'vendor/symfony/polyfill-php72/Php72.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php72; /** * @author Nicolas Grekas <p@tchwork.com> * @author Dariusz Rumiński <dariusz.ruminski@gmail.com> * * @internal */ final class Php72 { private static $hashMask; public static function utf8_encode($s) { $s .= $s; $len = \strlen($s); for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) { switch (true) { case $s[$i] < "\x80": $s[$j] = $s[$i]; break; case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break; default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break; } } return substr($s, 0, $j); } public static function utf8_decode($s) { $s = (string) $s; $len = \strlen($s); for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { switch ($s[$i] & "\xF0") { case "\xC0": case "\xD0": $c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F"); $s[$j] = $c < 256 ? \chr($c) : '?'; break; case "\xF0": ++$i; // no break case "\xE0": $s[$j] = '?'; $i += 2; break; default: $s[$j] = $s[$i]; } } return substr($s, 0, $j); } public static function php_os_family() { if ('\\' === \DIRECTORY_SEPARATOR) { return 'Windows'; } $map = [ 'Darwin' => 'Darwin', 'DragonFly' => 'BSD', 'FreeBSD' => 'BSD', 'NetBSD' => 'BSD', 'OpenBSD' => 'BSD', 'Linux' => 'Linux', 'SunOS' => 'Solaris', ]; return $map[\PHP_OS] ?? 'Unknown'; } public static function spl_object_id($object) { if (null === self::$hashMask) { self::initHashMask(); } if (null === $hash = spl_object_hash($object)) { return; } // On 32-bit systems, PHP_INT_SIZE is 4, return self::$hashMask ^ hexdec(substr($hash, 16 - (\PHP_INT_SIZE * 2 - 1), \PHP_INT_SIZE * 2 - 1)); } public static function sapi_windows_vt100_support($stream, $enable = null) { if (!\is_resource($stream)) { trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', \E_USER_WARNING); return false; } $meta = stream_get_meta_data($stream); if ('STDIO' !== $meta['stream_type']) { trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', \E_USER_WARNING); return false; } // We cannot actually disable vt100 support if it is set if (false === $enable || !self::stream_isatty($stream)) { return false; } // The native function does not apply to stdin $meta = array_map('strtolower', $meta); $stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri']; return !$stdin && (false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM') || 'Hyper' === getenv('TERM_PROGRAM')); } public static function stream_isatty($stream) { if (!\is_resource($stream)) { trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', \E_USER_WARNING); return false; } if ('\\' === \DIRECTORY_SEPARATOR) { $stat = @fstat($stream); // Check if formatted mode is S_IFCHR return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } return \function_exists('posix_isatty') && @posix_isatty($stream); } private static function initHashMask() { $obj = (object) []; self::$hashMask = -1; // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below $obFuncs = ['ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush']; foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? \DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { $frame['line'] = 0; break; } } if (!empty($frame['line'])) { ob_start(); debug_zval_dump($obj); self::$hashMask = (int) substr(ob_get_clean(), 17); } self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - (\PHP_INT_SIZE * 2 - 1), \PHP_INT_SIZE * 2 - 1)); } public static function mb_chr($code, $encoding = null) { if (0x80 > $code %= 0x200000) { $s = \chr($code); } elseif (0x800 > $code) { $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } if ('UTF-8' !== $encoding = $encoding ?? mb_internal_encoding()) { $s = mb_convert_encoding($s, $encoding, 'UTF-8'); } return $s; } public static function mb_ord($s, $encoding = null) { if (null === $encoding) { $s = mb_convert_encoding($s, 'UTF-8'); } elseif ('UTF-8' !== $encoding) { $s = mb_convert_encoding($s, 'UTF-8', $encoding); } if (1 === \strlen($s)) { return \ord($s); } $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; if (0xF0 <= $code) { return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; } if (0xE0 <= $code) { return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; } if (0xC0 <= $code) { return (($code - 0xC0) << 6) + $s[2] - 0x80; } return $code; } } PK2A#]��H,,%vendor/symfony/polyfill-php72/LICENSEnu�[���Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]0��,,%vendor/symfony/polyfill-php81/LICENSEnu�[���Copyright (c) 2021-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]5�+��Fvendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80100) { #[Attribute(Attribute::TARGET_METHOD)] final class ReturnTypeWillChange { public function __construct() { } } } PK2A#]X(P���@vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID >= 70400 && extension_loaded('curl')) { /** * @property string $data */ class CURLStringFile extends CURLFile { private $data; public function __construct(string $data, string $postname, string $mime = 'application/octet-stream') { $this->data = $data; parent::__construct('data://application/octet-stream;base64,'.base64_encode($data), $mime, $postname); } public function __set(string $name, $value): void { if ('data' !== $name) { $this->$name = $value; return; } if (is_object($value) ? !method_exists($value, '__toString') : !is_scalar($value)) { throw new \TypeError('Cannot assign '.gettype($value).' to property CURLStringFile::$data of type string'); } $this->name = 'data://application/octet-stream;base64,'.base64_encode($value); } public function __isset(string $name): bool { return isset($this->$name); } public function &__get(string $name) { return $this->$name; } } } PK2A#]<P��+vendor/symfony/polyfill-php81/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php81 as p; if (\PHP_VERSION_ID >= 80100) { return; } if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) { define('MYSQLI_REFRESH_REPLICA', 64); } if (!function_exists('array_is_list')) { function array_is_list(array $array): bool { return p\Php81::array_is_list($array); } } if (!function_exists('enum_exists')) { function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; } } PK2A#]��$��'vendor/symfony/polyfill-php81/Php81.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php81; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Php81 { public static function array_is_list(array $array): bool { if ([] === $array || $array === array_values($array)) { return true; } $nextKey = -1; foreach ($array as $k => $v) { if ($k !== ++$nextKey) { return false; } } return true; } } PK2A#]���,,%vendor/symfony/polyfill-ctype/LICENSEnu�[���Copyright (c) 2018-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]j�Q9@@+vendor/symfony/polyfill-ctype/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Ctype as p; if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!function_exists('ctype_alnum')) { function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } } if (!function_exists('ctype_alpha')) { function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } } if (!function_exists('ctype_cntrl')) { function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } } if (!function_exists('ctype_digit')) { function ctype_digit($text) { return p\Ctype::ctype_digit($text); } } if (!function_exists('ctype_graph')) { function ctype_graph($text) { return p\Ctype::ctype_graph($text); } } if (!function_exists('ctype_lower')) { function ctype_lower($text) { return p\Ctype::ctype_lower($text); } } if (!function_exists('ctype_print')) { function ctype_print($text) { return p\Ctype::ctype_print($text); } } if (!function_exists('ctype_punct')) { function ctype_punct($text) { return p\Ctype::ctype_punct($text); } } if (!function_exists('ctype_space')) { function ctype_space($text) { return p\Ctype::ctype_space($text); } } if (!function_exists('ctype_upper')) { function ctype_upper($text) { return p\Ctype::ctype_upper($text); } } if (!function_exists('ctype_xdigit')) { function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } } PK2A#]xs���'vendor/symfony/polyfill-ctype/Ctype.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Ctype; /** * Ctype implementation through regex. * * @internal * * @author Gert de Pagter <BackEndTea@gmail.com> */ final class Ctype { /** * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. * * @see https://php.net/ctype-alnum * * @param mixed $text * * @return bool */ public static function ctype_alnum($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); } /** * Returns TRUE if every character in text is a letter, FALSE otherwise. * * @see https://php.net/ctype-alpha * * @param mixed $text * * @return bool */ public static function ctype_alpha($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); } /** * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. * * @see https://php.net/ctype-cntrl * * @param mixed $text * * @return bool */ public static function ctype_cntrl($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); } /** * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. * * @see https://php.net/ctype-digit * * @param mixed $text * * @return bool */ public static function ctype_digit($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); } /** * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. * * @see https://php.net/ctype-graph * * @param mixed $text * * @return bool */ public static function ctype_graph($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); } /** * Returns TRUE if every character in text is a lowercase letter. * * @see https://php.net/ctype-lower * * @param mixed $text * * @return bool */ public static function ctype_lower($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); } /** * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. * * @see https://php.net/ctype-print * * @param mixed $text * * @return bool */ public static function ctype_print($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); } /** * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. * * @see https://php.net/ctype-punct * * @param mixed $text * * @return bool */ public static function ctype_punct($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); } /** * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. * * @see https://php.net/ctype-space * * @param mixed $text * * @return bool */ public static function ctype_space($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); } /** * Returns TRUE if every character in text is an uppercase letter. * * @see https://php.net/ctype-upper * * @param mixed $text * * @return bool */ public static function ctype_upper($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); } /** * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. * * @see https://php.net/ctype-xdigit * * @param mixed $text * * @return bool */ public static function ctype_xdigit($text) { $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); } /** * Converts integers to their char versions according to normal ctype behaviour, if needed. * * If an integer between -128 and 255 inclusive is provided, * it is interpreted as the ASCII value of a single character * (negative values have 256 added in order to allow characters in the Extended ASCII range). * Any other integer is interpreted as a string containing the decimal digits of the integer. * * @param mixed $int * @param string $function * * @return mixed */ private static function convert_int_to_char_for_ctype($int, $function) { if (!\is_int($int)) { return $int; } if ($int < -128 || $int > 255) { return (string) $int; } if (\PHP_VERSION_ID >= 80100) { @trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); } if ($int < 0) { $int += 256; } return \chr($int); } } PK2A#]�F)�rr-vendor/symfony/polyfill-ctype/bootstrap80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Ctype as p; if (!function_exists('ctype_alnum')) { function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } } if (!function_exists('ctype_alpha')) { function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } } if (!function_exists('ctype_cntrl')) { function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } } if (!function_exists('ctype_digit')) { function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } } if (!function_exists('ctype_graph')) { function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } } if (!function_exists('ctype_lower')) { function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } } if (!function_exists('ctype_print')) { function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } } if (!function_exists('ctype_punct')) { function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } } if (!function_exists('ctype_space')) { function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } } if (!function_exists('ctype_upper')) { function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } } if (!function_exists('ctype_xdigit')) { function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } } PK2A#]��փ� � /vendor/symfony/var-dumper/Server/Connection.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * Forwards serialized Data clones to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class Connection { private $host; private $contextProviders; /** * @var resource|null */ private $socket; /** * @param string $host The server host * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct(string $host, array $contextProviders = []) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; $this->contextProviders = $contextProviders; } public function getContextProviders(): array { return $this->contextProviders; } public function write(Data $data): bool { $socketIsFresh = !$this->socket; if (!$this->socket = $this->socket ?: $this->createSocket()) { return false; } $context = ['timestamp' => microtime(true)]; foreach ($this->contextProviders as $name => $provider) { $context[$name] = $provider->getContext(); } $context = array_filter($context); $encodedPayload = base64_encode(serialize([$data, $context]))."\n"; set_error_handler([self::class, 'nullErrorHandler']); try { if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } if (!$socketIsFresh) { stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR); fclose($this->socket); $this->socket = $this->createSocket(); } if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } } finally { restore_error_handler(); } return false; } private static function nullErrorHandler(int $t, string $m) { // no-op } private function createSocket() { set_error_handler([self::class, 'nullErrorHandler']); try { return stream_socket_client($this->host, $errno, $errstr, 3); } finally { restore_error_handler(); } } } PK2A#]1����/vendor/symfony/var-dumper/Server/DumpServer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Psr\Log\LoggerInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; /** * A server collecting Data clones sent by a ServerDumper. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class DumpServer { private $host; private $logger; /** * @var resource|null */ private $socket; public function __construct(string $host, LoggerInterface $logger = null) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; $this->logger = $logger; } public function start(): void { if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { throw new \RuntimeException(sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno); } } public function listen(callable $callback): void { if (null === $this->socket) { $this->start(); } foreach ($this->getMessages() as $clientId => $message) { if ($this->logger) { $this->logger->info('Received a payload from client {clientId}', ['clientId' => $clientId]); } $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]); // Impossible to decode the message, give up. if (false === $payload) { if ($this->logger) { $this->logger->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]); } continue; } if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) { if ($this->logger) { $this->logger->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]); } continue; } [$data, $context] = $payload; $callback($data, $context, $clientId); } } public function getHost(): string { return $this->host; } private function getMessages(): iterable { $sockets = [(int) $this->socket => $this->socket]; $write = []; while (true) { $read = $sockets; stream_select($read, $write, $write, null); foreach ($read as $stream) { if ($this->socket === $stream) { $stream = stream_socket_accept($this->socket); $sockets[(int) $stream] = $stream; } elseif (feof($stream)) { unset($sockets[(int) $stream]); fclose($stream); } else { yield (int) $stream => fgets($stream); } } } } } PK2A#]�33vendor/symfony/var-dumper/Dumper/AbstractDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\DumperInterface; /** * Abstract mechanism for dumping a Data object. * * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractDumper implements DataDumperInterface, DumperInterface { public const DUMP_LIGHT_ARRAY = 1; public const DUMP_STRING_LENGTH = 2; public const DUMP_COMMA_SEPARATOR = 4; public const DUMP_TRAILING_COMMA = 8; public static $defaultOutput = 'php://output'; protected $line = ''; protected $lineDumper; protected $outputStream; protected $decimalPoint; // This is locale dependent protected $indentPad = ' '; protected $flags; private $charset = ''; /** * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput * @param string|null $charset The default character encoding to use for non-UTF8 strings * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation */ public function __construct($output = null, string $charset = null, int $flags = 0) { $this->flags = $flags; $this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8'); $this->decimalPoint = \PHP_VERSION_ID >= 80000 ? '.' : localeconv()['decimal_point']; $this->setOutput($output ?: static::$defaultOutput); if (!$output && \is_string(static::$defaultOutput)) { static::$defaultOutput = $this->outputStream; } } /** * Sets the output destination of the dumps. * * @param callable|resource|string $output A line dumper callable, an opened stream or an output path * * @return callable|resource|string The previous output destination */ public function setOutput($output) { $prev = $this->outputStream ?? $this->lineDumper; if (\is_callable($output)) { $this->outputStream = null; $this->lineDumper = $output; } else { if (\is_string($output)) { $output = fopen($output, 'w'); } $this->outputStream = $output; $this->lineDumper = [$this, 'echoLine']; } return $prev; } /** * Sets the default character encoding to use for non-UTF8 strings. * * @return string The previous charset */ public function setCharset(string $charset) { $prev = $this->charset; $charset = strtoupper($charset); $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; $this->charset = $charset; return $prev; } /** * Sets the indentation pad string. * * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level * * @return string The previous indent pad */ public function setIndentPad(string $pad) { $prev = $this->indentPad; $this->indentPad = $pad; return $prev; } /** * Dumps a Data object. * * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump * * @return string|null The dump as string when $output is true */ public function dump(Data $data, $output = null) { $this->decimalPoint = \PHP_VERSION_ID >= 80000 ? '.' : localeconv()['decimal_point']; if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) { setlocale(\LC_NUMERIC, 'C'); } if ($returnDump = true === $output) { $output = fopen('php://memory', 'r+'); } if ($output) { $prevOutput = $this->setOutput($output); } try { $data->dump($this); $this->dumpLine(-1); if ($returnDump) { $result = stream_get_contents($output, -1, 0); fclose($output); return $result; } } finally { if ($output) { $this->setOutput($prevOutput); } if ($locale) { setlocale(\LC_NUMERIC, $locale); } } return null; } /** * Dumps the current line. * * @param int $depth The recursive depth in the dumped structure for the line being dumped, * or -1 to signal the end-of-dump to the line dumper callable */ protected function dumpLine(int $depth) { ($this->lineDumper)($this->line, $depth, $this->indentPad); $this->line = ''; } /** * Generic line dumper callback. */ protected function echoLine(string $line, int $depth, string $indentPad) { if (-1 !== $depth) { fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); } } /** * Converts a non-UTF-8 string to UTF-8. * * @return string|null */ protected function utf8Encode(?string $s) { if (null === $s || preg_match('//u', $s)) { return $s; } if (!\function_exists('iconv')) { throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); } if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { return $c; } if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { return $c; } return iconv('CP850', 'UTF-8', $s); } } PK2A#]M�@߄߄/vendor/symfony/var-dumper/Dumper/HtmlDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Data; /** * HtmlDumper dumps variables as HTML. * * @author Nicolas Grekas <p@tchwork.com> */ class HtmlDumper extends CliDumper { public static $defaultOutput = 'php://output'; protected static $themes = [ 'dark' => [ 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#56DB3A', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', 'private' => 'color:#FFFFFF', 'meta' => 'color:#B729D9', 'key' => 'color:#56DB3A', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#FF8400', 'ns' => 'user-select:none;', ], 'light' => [ 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#629755;', 'note' => 'color:#6897BB', 'ref' => 'color:#6E6E6E', 'public' => 'color:#262626', 'protected' => 'color:#262626', 'private' => 'color:#262626', 'meta' => 'color:#B729D9', 'key' => 'color:#789339', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#CC7832', 'ns' => 'user-select:none;', ], ]; protected $dumpHeader; protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">'; protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>'; protected $dumpId = 'sf-dump'; protected $colors = true; protected $headerIsDumped = false; protected $lastDepth = -1; protected $styles; private $displayOptions = [ 'maxDepth' => 1, 'maxStringLength' => 160, 'fileLinkFormat' => null, ]; private $extraDisplayOptions = []; /** * {@inheritdoc} */ public function __construct($output = null, string $charset = null, int $flags = 0) { AbstractDumper::__construct($output, $charset, $flags); $this->dumpId = 'sf-dump-'.mt_rand(); $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->styles = static::$themes['dark'] ?? self::$themes['dark']; } /** * {@inheritdoc} */ public function setStyles(array $styles) { $this->headerIsDumped = false; $this->styles = $styles + $this->styles; } public function setTheme(string $themeName) { if (!isset(static::$themes[$themeName])) { throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); } $this->setStyles(static::$themes[$themeName]); } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions) { $this->headerIsDumped = false; $this->displayOptions = $displayOptions + $this->displayOptions; } /** * Sets an HTML header that will be dumped once in the output stream. */ public function setDumpHeader(?string $header) { $this->dumpHeader = $header; } /** * Sets an HTML prefix and suffix that will encapse every single dump. */ public function setDumpBoundaries(string $prefix, string $suffix) { $this->dumpPrefix = $prefix; $this->dumpSuffix = $suffix; } /** * {@inheritdoc} */ public function dump(Data $data, $output = null, array $extraDisplayOptions = []) { $this->extraDisplayOptions = $extraDisplayOptions; $result = parent::dump($data, $output); $this->dumpId = 'sf-dump-'.mt_rand(); return $result; } /** * Dumps the HTML header. */ protected function getDumpHeader() { $this->headerIsDumped = $this->outputStream ?? $this->lineDumper; if (null !== $this->dumpHeader) { return $this->dumpHeader; } $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' <script> Sfdump = window.Sfdump || (function (doc) { var refStyle = doc.createElement('style'), rxEsc = /([.*+?^${}()|\[\]\/\\])/g, idRx = /\bsf-dump-\d+-ref[012]\w+\b/, keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl', addEventListener = function (e, n, cb) { e.addEventListener(n, cb, false); }; refStyle.innerHTML = 'pre.sf-dump .sf-dump-compact, .sf-dump-str-collapse .sf-dump-str-collapse, .sf-dump-str-expand .sf-dump-str-expand { display: none; }'; doc.head.appendChild(refStyle); refStyle = doc.createElement('style'); doc.head.appendChild(refStyle); if (!doc.addEventListener) { addEventListener = function (element, eventName, callback) { element.attachEvent('on' + eventName, function (e) { e.preventDefault = function () {e.returnValue = false;}; e.target = e.srcElement; callback(e); }); }; } function toggle(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass; if (/\bsf-dump-compact\b/.test(oldClass)) { arrow = '▼'; newClass = 'sf-dump-expanded'; } else if (/\bsf-dump-expanded\b/.test(oldClass)) { arrow = '▶'; newClass = 'sf-dump-compact'; } else { return false; } if (doc.createEvent && s.dispatchEvent) { var event = doc.createEvent('Event'); event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false); s.dispatchEvent(event); } a.lastChild.innerHTML = arrow; s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass); if (recursive) { try { a = s.querySelectorAll('.'+oldClass); for (s = 0; s < a.length; ++s) { if (-1 == a[s].className.indexOf(newClass)) { a[s].className = newClass; a[s].previousSibling.lastChild.innerHTML = arrow; } } } catch (e) { } } return true; }; function collapse(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; if (/\bsf-dump-expanded\b/.test(oldClass)) { toggle(a, recursive); return true; } return false; }; function expand(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; if (/\bsf-dump-compact\b/.test(oldClass)) { toggle(a, recursive); return true; } return false; }; function collapseAll(root) { var a = root.querySelector('a.sf-dump-toggle'); if (a) { collapse(a, true); expand(a); return true; } return false; } function reveal(node) { var previous, parents = []; while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) { parents.push(previous); } if (0 !== parents.length) { parents.forEach(function (parent) { expand(parent); }); return true; } return false; } function highlight(root, activeNode, nodes) { resetHighlightedNodes(root); Array.from(nodes||[]).forEach(function (node) { if (!/\bsf-dump-highlight\b/.test(node.className)) { node.className = node.className + ' sf-dump-highlight'; } }); if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) { activeNode.className = activeNode.className + ' sf-dump-highlight-active'; } } function resetHighlightedNodes(root) { Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) { strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, ''); strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, ''); }); } return function (root, x) { root = doc.getElementById(root); var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'), options = {$options}, elt = root.getElementsByTagName('A'), len = elt.length, i = 0, s, h, t = []; while (i < len) t.push(elt[i++]); for (i in x) { options[i] = x[i]; } function a(e, f) { addEventListener(root, e, function (e, n) { if ('A' == e.target.tagName) { f(e.target, e); } else if ('A' == e.target.parentNode.tagName) { f(e.target.parentNode, e); } else { n = /\bsf-dump-ellipsis\b/.test(e.target.className) ? e.target.parentNode : e.target; if ((n = n.nextElementSibling) && 'A' == n.tagName) { if (!/\bsf-dump-toggle\b/.test(n.className)) { n = n.nextElementSibling || n; } f(n, e, true); } } }); }; function isCtrlKey(e) { return e.ctrlKey || e.metaKey; } function xpathString(str) { var parts = str.match(/[^'"]+|['"]/g).map(function (part) { if ("'" == part) { return '"\'"'; } if ('"' == part) { return "'\"'"; } return "'" + part + "'"; }); return "concat(" + parts.join(",") + ", '')"; } function xpathHasClass(className) { return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')"; } addEventListener(root, 'mouseover', function (e) { if ('' != refStyle.innerHTML) { refStyle.innerHTML = ''; } }); a('mouseover', function (a, e, c) { if (c) { e.target.style.cursor = "pointer"; } else if (a = idRx.exec(a.className)) { try { refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}'; } catch (e) { } } }); a('click', function (a, e, c) { if (/\bsf-dump-toggle\b/.test(a.className)) { e.preventDefault(); if (!toggle(a, isCtrlKey(e))) { var r = doc.getElementById(a.getAttribute('href').slice(1)), s = r.previousSibling, f = r.parentNode, t = a.parentNode; t.replaceChild(r, a); f.replaceChild(a, s); t.insertBefore(s, r); f = f.firstChild.nodeValue.match(indentRx); t = t.firstChild.nodeValue.match(indentRx); if (f && t && f[0] !== t[0]) { r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]); } if (/\bsf-dump-compact\b/.test(r.className)) { toggle(s, isCtrlKey(e)); } } if (c) { } else if (doc.getSelection) { try { doc.getSelection().removeAllRanges(); } catch (e) { doc.getSelection().empty(); } } else { doc.selection.empty(); } } else if (/\bsf-dump-str-toggle\b/.test(a.className)) { e.preventDefault(); e = a.parentNode.parentNode; e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className); } }); elt = root.getElementsByTagName('SAMP'); len = elt.length; i = 0; while (i < len) t.push(elt[i++]); len = t.length; for (i = 0; i < len; ++i) { elt = t[i]; if ('SAMP' == elt.tagName) { a = elt.previousSibling || {}; if ('A' != a.tagName) { a = doc.createElement('A'); a.className = 'sf-dump-ref'; elt.parentNode.insertBefore(a, elt); } else { a.innerHTML += ' '; } a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children'; a.innerHTML += elt.className == 'sf-dump-compact' ? '<span>▶</span>' : '<span>▼</span>'; a.className += ' sf-dump-toggle'; x = 1; if ('sf-dump' != elt.parentNode.className) { x += elt.parentNode.getAttribute('data-depth')/1; } } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) { a = a.slice(1); elt.className += ' '+a; if (/[\[{]$/.test(elt.previousSibling.nodeValue)) { a = a != elt.nextSibling.id && doc.getElementById(a); try { s = a.nextSibling; elt.appendChild(a); s.parentNode.insertBefore(a, s); if (/^[@#]/.test(elt.innerHTML)) { elt.innerHTML += ' <span>▶</span>'; } else { elt.innerHTML = '<span>▶</span>'; elt.className = 'sf-dump-ref'; } elt.className += ' sf-dump-toggle'; } catch (e) { if ('&' == elt.innerHTML.charAt(0)) { elt.innerHTML = '…'; elt.className = 'sf-dump-ref'; } } } } } if (doc.evaluate && Array.from && root.children.length > 1) { root.setAttribute('tabindex', 0); SearchState = function () { this.nodes = []; this.idx = 0; }; SearchState.prototype = { next: function () { if (this.isEmpty()) { return this.current(); } this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0; return this.current(); }, previous: function () { if (this.isEmpty()) { return this.current(); } this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1); return this.current(); }, isEmpty: function () { return 0 === this.count(); }, current: function () { if (this.isEmpty()) { return null; } return this.nodes[this.idx]; }, reset: function () { this.nodes = []; this.idx = 0; }, count: function () { return this.nodes.length; }, }; function showCurrent(state) { var currentNode = state.current(), currentRect, searchRect; if (currentNode) { reveal(currentNode); highlight(root, currentNode, state.nodes); if ('scrollIntoView' in currentNode) { currentNode.scrollIntoView(true); currentRect = currentNode.getBoundingClientRect(); searchRect = search.getBoundingClientRect(); if (currentRect.top < (searchRect.top + searchRect.height)) { window.scrollBy(0, -(searchRect.top + searchRect.height + 5)); } } } counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count(); } var search = doc.createElement('div'); search.className = 'sf-dump-search-wrapper sf-dump-search-hidden'; search.innerHTML = ' <input type="text" class="sf-dump-search-input"> <span class="sf-dump-search-count">0 of 0<\/span> <button type="button" class="sf-dump-search-input-previous" tabindex="-1"> <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg> <\/button> <button type="button" class="sf-dump-search-input-next" tabindex="-1"> <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg> <\/button> '; root.insertBefore(search, root.firstChild); var state = new SearchState(); var searchInput = search.querySelector('.sf-dump-search-input'); var counter = search.querySelector('.sf-dump-search-count'); var searchInputTimer = 0; var previousSearchQuery = ''; addEventListener(searchInput, 'keyup', function (e) { var searchQuery = e.target.value; /* Don't perform anything if the pressed key didn't change the query */ if (searchQuery === previousSearchQuery) { return; } previousSearchQuery = searchQuery; clearTimeout(searchInputTimer); searchInputTimer = setTimeout(function () { state.reset(); collapseAll(root); resetHighlightedNodes(root); if ('' === searchQuery) { counter.textContent = '0 of 0'; return; } var classMatches = [ "sf-dump-str", "sf-dump-key", "sf-dump-public", "sf-dump-protected", "sf-dump-private", ].map(xpathHasClass).join(' or '); var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); while (node = xpathResult.iterateNext()) state.nodes.push(node); showCurrent(state); }, 400); }); Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) { addEventListener(btn, 'click', function (e) { e.preventDefault(); -1 !== e.target.className.indexOf('next') ? state.next() : state.previous(); searchInput.focus(); collapseAll(root); showCurrent(state); }) }); addEventListener(root, 'keydown', function (e) { var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className); if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) { /* F3 or CMD/CTRL + F */ if (70 === e.keyCode && document.activeElement === searchInput) { /* * If CMD/CTRL + F is hit while having focus on search input, * the user probably meant to trigger browser search instead. * Let the browser execute its behavior: */ return; } e.preventDefault(); search.className = search.className.replace(/\bsf-dump-search-hidden\b/, ''); searchInput.focus(); } else if (isSearchActive) { if (27 === e.keyCode) { /* ESC key */ search.className += ' sf-dump-search-hidden'; e.preventDefault(); resetHighlightedNodes(root); searchInput.value = ''; } else if ( (isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */ || 13 === e.keyCode /* Enter */ || 114 === e.keyCode /* F3 */ ) { e.preventDefault(); e.shiftKey ? state.previous() : state.next(); collapseAll(root); showCurrent(state); } } }); } if (0 >= options.maxStringLength) { return; } try { elt = root.querySelectorAll('.sf-dump-str'); len = elt.length; i = 0; t = []; while (i < len) t.push(elt[i++]); len = t.length; for (i = 0; i < len; ++i) { elt = t[i]; s = elt.innerText || elt.textContent; x = s.length - options.maxStringLength; if (0 < x) { h = elt.innerHTML; elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength); elt.className += ' sf-dump-str-collapse'; elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+ '<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>'; } } } catch (e) { } }; })(document); </script><style> pre.sf-dump { display: block; white-space: pre; padding: 5px; overflow: initial !important; } pre.sf-dump:after { content: ""; visibility: hidden; display: block; height: 0; clear: both; } pre.sf-dump span { display: inline; } pre.sf-dump a { text-decoration: none; cursor: pointer; border: 0; outline: none; color: inherit; } pre.sf-dump img { max-width: 50em; max-height: 50em; margin: .5em 0 0 0; padding: 0; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAHUlEQVQY02O8zAABilCaiQEN0EeA8QuUcX9g3QEAAjcC5piyhyEAAAAASUVORK5CYII=) #D3D3D3; } pre.sf-dump .sf-dump-ellipsis { display: inline-block; overflow: visible; text-overflow: ellipsis; max-width: 5em; white-space: nowrap; overflow: hidden; vertical-align: top; } pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis { max-width: none; } pre.sf-dump code { display:inline; padding:0; background:none; } .sf-dump-public.sf-dump-highlight, .sf-dump-protected.sf-dump-highlight, .sf-dump-private.sf-dump-highlight, .sf-dump-str.sf-dump-highlight, .sf-dump-key.sf-dump-highlight { background: rgba(111, 172, 204, 0.3); border: 1px solid #7DA0B1; border-radius: 3px; } .sf-dump-public.sf-dump-highlight-active, .sf-dump-protected.sf-dump-highlight-active, .sf-dump-private.sf-dump-highlight-active, .sf-dump-str.sf-dump-highlight-active, .sf-dump-key.sf-dump-highlight-active { background: rgba(253, 175, 0, 0.4); border: 1px solid #ffa500; border-radius: 3px; } pre.sf-dump .sf-dump-search-hidden { display: none !important; } pre.sf-dump .sf-dump-search-wrapper { font-size: 0; white-space: nowrap; margin-bottom: 5px; display: flex; position: -webkit-sticky; position: sticky; top: 5px; } pre.sf-dump .sf-dump-search-wrapper > * { vertical-align: top; box-sizing: border-box; height: 21px; font-weight: normal; border-radius: 0; background: #FFF; color: #757575; border: 1px solid #BBB; } pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input { padding: 3px; height: 21px; font-size: 12px; border-right: none; border-top-left-radius: 3px; border-bottom-left-radius: 3px; color: #000; min-width: 15px; width: 100%; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next, pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous { background: #F2F2F2; outline: none; border-left: none; font-size: 0; line-height: 0; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg, pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg { pointer-events: none; width: 12px; height: 12px; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count { display: inline-block; padding: 0 5px; margin: 0; border-left: none; line-height: 21px; font-size: 12px; } EOHTML ); foreach ($this->styles as $class => $style) { $line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}'; } $line .= 'pre.sf-dump .sf-dump-ellipsis-note{'.$this->styles['note'].'}'; return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader; } /** * {@inheritdoc} */ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) { if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { $this->dumpKey($cursor); $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []); $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' <samp class=sf-dump-compact>' : ' <samp class=sf-dump-expanded>'; $this->endValue($cursor); $this->line .= $this->indentPad; $this->line .= sprintf('<img src="data:%s;base64,%s" /></samp>', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); $this->endValue($cursor); } else { parent::dumpString($cursor, $str, $bin, $cut); } } /** * {@inheritdoc} */ public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild) { if (Cursor::HASH_OBJECT === $type) { $cursor->attr['depth'] = $cursor->depth; } parent::enterHash($cursor, $type, $class, false); if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) { $cursor->skipChildren = false; $eol = ' class=sf-dump-compact>'; } else { $this->expandNextHash = false; $eol = ' class=sf-dump-expanded>'; } if ($hasChild) { $this->line .= '<samp data-depth='.($cursor->depth + 1); if ($cursor->refIndex) { $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2; $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex; $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r); } $this->line .= $eol; $this->dumpLine($cursor->depth); } } /** * {@inheritdoc} */ public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut) { $this->dumpEllipsis($cursor, $hasChild, $cut); if ($hasChild) { $this->line .= '</samp>'; } parent::leaveHash($cursor, $type, $class, $hasChild, 0); } /** * {@inheritdoc} */ protected function style(string $style, string $value, array $attr = []) { if ('' === $value) { return ''; } $v = esc($value); if ('ref' === $style) { if (empty($attr['count'])) { return sprintf('<a class=sf-dump-ref>%s</a>', $v); } $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v); } if ('const' === $style && isset($attr['value'])) { $style .= sprintf(' title="%s"', esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value']))); } elseif ('public' === $style) { $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); } elseif ('str' === $style && 1 < $attr['length']) { $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { $style .= ' title=""'; $attr += [ 'ellipsis' => \strlen($value) - $c, 'ellipsis-type' => 'note', 'ellipsis-tail' => 1, ]; } elseif ('protected' === $style) { $style .= ' title="Protected property"'; } elseif ('meta' === $style && isset($attr['title'])) { $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title']))); } elseif ('private' === $style) { $style .= sprintf(' title="Private property defined in class: `%s`"', esc($this->utf8Encode($attr['class']))); } $map = static::$controlCharsMap; if (isset($attr['ellipsis'])) { $class = 'sf-dump-ellipsis'; if (isset($attr['ellipsis-type'])) { $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']); } $label = esc(substr($value, -$attr['ellipsis'])); $style = str_replace(' title="', " title=\"$v\n", $style); $v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label))); if (!empty($attr['ellipsis-tail'])) { $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); $v .= sprintf('<span class=%s>%s</span>%s', $class, substr($label, 0, $tail), substr($label, $tail)); } else { $v .= $label; } } $v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { $s = $b = '<span class="sf-dump-default'; $c = $c[$i = 0]; if ($ns = "\r" === $c[$i] || "\n" === $c[$i]) { $s .= ' sf-dump-ns'; } $s .= '">'; do { if (("\r" === $c[$i] || "\n" === $c[$i]) !== $ns) { $s .= '</span>'.$b; if ($ns = !$ns) { $s .= ' sf-dump-ns'; } $s .= '">'; } $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); } while (isset($c[++$i])); return $s.'</span>'; }, $v).'</span>'; if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { $attr['href'] = $href; } if (isset($attr['href'])) { $target = isset($attr['file']) ? '' : ' target="_blank"'; $v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v); } if (isset($attr['lang'])) { $v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v); } return $v; } /** * {@inheritdoc} */ protected function dumpLine(int $depth, bool $endOfValue = false) { if (-1 === $this->lastDepth) { $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; } if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { $args = ['"'.$this->dumpId.'"']; if ($this->extraDisplayOptions) { $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT); } // Replace is for BC $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); } $this->lastDepth = $depth; $this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); if (-1 === $depth) { AbstractDumper::dumpLine(0); } AbstractDumper::dumpLine($depth); } private function getSourceLink(string $file, int $line) { $options = $this->extraDisplayOptions + $this->displayOptions; if ($fmt = $options['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); } return false; } } function esc(string $str) { return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8'); } PK2A#]����Mvendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Interface to provide contextual data about dump data clones sent to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ interface ContextProviderInterface { public function getContext(): ?array; } PK2A#]�#��Kvendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Tries to provide context from a request. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class RequestContextProvider implements ContextProviderInterface { private $requestStack; private $cloner; public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; $this->cloner = new VarCloner(); $this->cloner->setMaxItems(0); $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); } public function getContext(): ?array { if (null === $request = $this->requestStack->getCurrentRequest()) { return null; } $controller = $request->attributes->get('_controller'); return [ 'uri' => $request->getUri(), 'method' => $request->getMethod(), 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, 'identifier' => spl_object_hash($request), ]; } } PK2A#]>��@&&Gvendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Tries to provide context on CLI. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class CliContextProvider implements ContextProviderInterface { public function getContext(): ?array { if ('cli' !== \PHP_SAPI) { return null; } return [ 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), 'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), ]; } } PK2A#]��mmJvendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\VarDumper; use Twig\Template; /** * Tries to provide context from sources (class name, file, line, code excerpt, ...). * * @author Nicolas Grekas <p@tchwork.com> * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class SourceContextProvider implements ContextProviderInterface { private $limit; private $charset; private $projectDir; private $fileLinkFormatter; public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9) { $this->charset = $charset; $this->projectDir = $projectDir; $this->fileLinkFormatter = $fileLinkFormatter; $this->limit = $limit; } public function getContext(): ?array { $trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); $file = $trace[1]['file']; $line = $trace[1]['line']; $name = '-' === $file || 'Standard input code' === $file ? 'Standard input code' : false; $fileExcerpt = false; for ($i = 2; $i < $this->limit; ++$i) { if (isset($trace[$i]['class'], $trace[$i]['function']) && 'dump' === $trace[$i]['function'] && VarDumper::class === $trace[$i]['class'] ) { $file = $trace[$i]['file'] ?? $file; $line = $trace[$i]['line'] ?? $line; while (++$i < $this->limit) { if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($trace[$i]['function'], 'call_user_func')) { $file = $trace[$i]['file']; $line = $trace[$i]['line']; break; } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { $template = $trace[$i]['object']; $name = $template->getTemplateName(); $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); $info = $template->getDebugInfo(); if (isset($info[$trace[$i - 1]['line']])) { $line = $info[$trace[$i - 1]['line']]; $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; if ($src) { $src = explode("\n", $src); $fileExcerpt = []; for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { $fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>'; } $fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>'; } } break; } } break; } } if (false === $name) { $name = str_replace('\\', '/', $file); $name = substr($name, strrpos($name, '/') + 1); } $context = ['name' => $name, 'file' => $file, 'line' => $line]; $context['file_excerpt'] = $fileExcerpt; if (null !== $this->projectDir) { $context['project_dir'] = $this->projectDir; if (str_starts_with($file, $this->projectDir)) { $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); } } if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { $context['file_link'] = $fileLink; } return $context; } private function htmlEncode(string $s): string { $html = ''; $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar($s)); return substr(strip_tags($html), 1, -1); } } PK2A#]L�ƁEE1vendor/symfony/var-dumper/Dumper/ServerDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; use Symfony\Component\VarDumper\Server\Connection; /** * ServerDumper forwards serialized Data clones to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class ServerDumper implements DataDumperInterface { private $connection; private $wrappedDumper; /** * @param string $host The server host * @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = []) { $this->connection = new Connection($host, $contextProviders); $this->wrappedDumper = $wrappedDumper; } public function getContextProviders(): array { return $this->connection->getContextProviders(); } /** * {@inheritdoc} */ public function dump(Data $data) { if (!$this->connection->write($data) && $this->wrappedDumper) { $this->wrappedDumper->dump($data); } } } PK2A#]әn��9vendor/symfony/var-dumper/Dumper/ContextualizedDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * @author Kévin Thérage <therage.kevin@gmail.com> */ class ContextualizedDumper implements DataDumperInterface { private $wrappedDumper; private $contextProviders; /** * @param ContextProviderInterface[] $contextProviders */ public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) { $this->wrappedDumper = $wrappedDumper; $this->contextProviders = $contextProviders; } public function dump(Data $data) { $context = []; foreach ($this->contextProviders as $contextProvider) { $context[\get_class($contextProvider)] = $contextProvider->getContext(); } $this->wrappedDumper->dump($data->withContext($context)); } } PK2A#]�K���8vendor/symfony/var-dumper/Dumper/DataDumperInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; /** * DataDumperInterface for dumping Data objects. * * @author Nicolas Grekas <p@tchwork.com> */ interface DataDumperInterface { public function dump(Data $data); } PK2A#]�0"b�V�V.vendor/symfony/var-dumper/Dumper/CliDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Stub; /** * CliDumper dumps variables for command line output. * * @author Nicolas Grekas <p@tchwork.com> */ class CliDumper extends AbstractDumper { public static $defaultColors; public static $defaultOutput = 'php://stdout'; protected $colors; protected $maxStringWidth = 0; protected $styles = [ // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', 'str' => '1;38;5;113', 'note' => '38;5;38', 'ref' => '38;5;247', 'public' => '', 'protected' => '', 'private' => '', 'meta' => '38;5;170', 'key' => '38;5;113', 'index' => '38;5;38', ]; protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; protected static $controlCharsMap = [ "\t" => '\t', "\n" => '\n', "\v" => '\v', "\f" => '\f', "\r" => '\r', "\033" => '\e', ]; protected $collapseNextHash = false; protected $expandNextHash = false; private $displayOptions = [ 'fileLinkFormat' => null, ]; private $handlesHrefGracefully; /** * {@inheritdoc} */ public function __construct($output = null, string $charset = null, int $flags = 0) { parent::__construct($output, $charset, $flags); if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles([ 'default' => '31', 'num' => '1;34', 'const' => '1;31', 'str' => '1;32', 'note' => '34', 'ref' => '1;30', 'meta' => '35', 'key' => '32', 'index' => '34', ]); } $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; } /** * Enables/disables colored output. */ public function setColors(bool $colors) { $this->colors = $colors; } /** * Sets the maximum number of characters per line for dumped strings. */ public function setMaxStringWidth(int $maxStringWidth) { $this->maxStringWidth = $maxStringWidth; } /** * Configures styles. * * @param array $styles A map of style names to style definitions */ public function setStyles(array $styles) { $this->styles = $styles + $this->styles; } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions) { $this->displayOptions = $displayOptions + $this->displayOptions; } /** * {@inheritdoc} */ public function dumpScalar(Cursor $cursor, string $type, $value) { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $style = 'const'; $attr = $cursor->attr; switch ($type) { case 'default': $style = 'default'; break; case 'integer': $style = 'num'; if (isset($this->styles['integer'])) { $style = 'integer'; } break; case 'double': $style = 'num'; if (isset($this->styles['float'])) { $style = 'float'; } switch (true) { case \INF === $value: $value = 'INF'; break; case -\INF === $value: $value = '-INF'; break; case is_nan($value): $value = 'NAN'; break; default: $value = (string) $value; if (!str_contains($value, $this->decimalPoint)) { $value .= $this->decimalPoint.'0'; } break; } break; case 'NULL': $value = 'null'; break; case 'boolean': $value = $value ? 'true' : 'false'; break; default: $attr += ['value' => $this->utf8Encode($value)]; $value = $this->utf8Encode($type); break; } $this->line .= $this->style($style, $value, $attr); $this->endValue($cursor); } /** * {@inheritdoc} */ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $attr = $cursor->attr; if ($bin) { $str = $this->utf8Encode($str); } if ('' === $str) { $this->line .= '""'; if ($cut) { $this->line .= '…'.$cut; } $this->endValue($cursor); } else { $attr += [ 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, 'binary' => $bin, ]; $str = $bin && false !== strpos($str, "\0") ? [$str] : explode("\n", $str); if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { unset($str[1]); $str[0] .= "\n"; } $m = \count($str) - 1; $i = $lineCut = 0; if (self::DUMP_STRING_LENGTH & $this->flags) { $this->line .= '('.$attr['length'].') '; } if ($bin) { $this->line .= 'b'; } if ($m) { $this->line .= '"""'; $this->dumpLine($cursor->depth); } else { $this->line .= '"'; } foreach ($str as $str) { if ($i < $m) { $str .= "\n"; } if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); $lineCut = $len - $this->maxStringWidth; } if ($m && 0 < $cursor->depth) { $this->line .= $this->indentPad; } if ('' !== $str) { $this->line .= $this->style('str', $str, $attr); } if ($i++ == $m) { if ($m) { if ('' !== $str) { $this->dumpLine($cursor->depth); if (0 < $cursor->depth) { $this->line .= $this->indentPad; } } $this->line .= '"""'; } else { $this->line .= '"'; } if ($cut < 0) { $this->line .= '…'; $lineCut = 0; } elseif ($cut) { $lineCut += $cut; } } if ($lineCut) { $this->line .= '…'.$lineCut; $lineCut = 0; } if ($i > $m) { $this->endValue($cursor); } else { $this->dumpLine($cursor->depth); } } } } /** * {@inheritdoc} */ public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild) { if (null === $this->colors) { $this->colors = $this->supportsColors(); } $this->dumpKey($cursor); $this->expandNextHash = false; $attr = $cursor->attr; if ($this->collapseNextHash) { $cursor->skipChildren = true; $this->collapseNextHash = $hasChild = false; } $class = $this->utf8Encode($class); if (Cursor::HASH_OBJECT === $type) { $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; } elseif (Cursor::HASH_RESOURCE === $type) { $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); } else { $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; } if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { $prefix = substr($prefix, 0, -1); } $this->line .= $prefix; if ($hasChild) { $this->dumpLine($cursor->depth); } } /** * {@inheritdoc} */ public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut) { if (empty($cursor->attr['cut_hash'])) { $this->dumpEllipsis($cursor, $hasChild, $cut); $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); } $this->endValue($cursor); } /** * Dumps an ellipsis for cut children. * * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut) { if ($cut) { $this->line .= ' …'; if (0 < $cut) { $this->line .= $cut; } if ($hasChild) { $this->dumpLine($cursor->depth + 1); } } } /** * Dumps a key in a hash structure. */ protected function dumpKey(Cursor $cursor) { if (null !== $key = $cursor->hashKey) { if ($cursor->hashKeyIsBinary) { $key = $this->utf8Encode($key); } $attr = ['binary' => $cursor->hashKeyIsBinary]; $bin = $cursor->hashKeyIsBinary ? 'b' : ''; $style = 'key'; switch ($cursor->hashType) { default: case Cursor::HASH_INDEXED: if (self::DUMP_LIGHT_ARRAY & $this->flags) { break; } $style = 'index'; // no break case Cursor::HASH_ASSOC: if (\is_int($key)) { $this->line .= $this->style($style, $key).' => '; } else { $this->line .= $bin.'"'.$this->style($style, $key).'" => '; } break; case Cursor::HASH_RESOURCE: $key = "\0~\0".$key; // no break case Cursor::HASH_OBJECT: if (!isset($key[0]) || "\0" !== $key[0]) { $this->line .= '+'.$bin.$this->style('public', $key).': '; } elseif (0 < strpos($key, "\0", 1)) { $key = explode("\0", substr($key, 1), 2); switch ($key[0][0]) { case '+': // User inserted keys $attr['dynamic'] = true; $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; break 2; case '~': $style = 'meta'; if (isset($key[0][1])) { parse_str(substr($key[0], 1), $attr); $attr += ['binary' => $cursor->hashKeyIsBinary]; } break; case '*': $style = 'protected'; $bin = '#'.$bin; break; default: $attr['class'] = $key[0]; $style = 'private'; $bin = '-'.$bin; break; } if (isset($attr['collapse'])) { if ($attr['collapse']) { $this->collapseNextHash = true; } else { $this->expandNextHash = true; } } $this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': '); } else { // This case should not happen $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; } break; } if ($cursor->hardRefTo) { $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; } } } /** * Decorates a value with some style. * * @param string $style The type of style being applied * @param string $value The value being styled * @param array $attr Optional context information * * @return string */ protected function style(string $style, string $value, array $attr = []) { if (null === $this->colors) { $this->colors = $this->supportsColors(); } if (null === $this->handlesHrefGracefully) { $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); } if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { $prefix = substr($value, 0, -$attr['ellipsis']); if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) { $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); } if (!empty($attr['ellipsis-tail'])) { $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); } else { $value = substr($value, -$attr['ellipsis']); } $value = $this->style('default', $prefix).$this->style($style, $value); goto href; } $map = static::$controlCharsMap; $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { $s = $startCchr; $c = $c[$i = 0]; do { $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); } while (isset($c[++$i])); return $s.$endCchr; }, $value, -1, $cchrCount); if ($this->colors) { if ($cchrCount && "\033" === $value[0]) { $value = substr($value, \strlen($startCchr)); } else { $value = "\033[{$this->styles[$style]}m".$value; } if ($cchrCount && str_ends_with($value, $endCchr)) { $value = substr($value, 0, -\strlen($endCchr)); } else { $value .= "\033[{$this->styles['default']}m"; } } href: if ($this->colors && $this->handlesHrefGracefully) { if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { if ('note' === $style) { $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; } else { $attr['href'] = $href; } } if (isset($attr['href'])) { $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; } } elseif ($attr['if_links'] ?? false) { return ''; } return $value; } /** * @return bool */ protected function supportsColors() { if ($this->outputStream !== static::$defaultOutput) { return $this->hasColorSupport($this->outputStream); } if (null !== static::$defaultColors) { return static::$defaultColors; } if (isset($_SERVER['argv'][1])) { $colors = $_SERVER['argv']; $i = \count($colors); while (--$i > 0) { if (isset($colors[$i][5])) { switch ($colors[$i]) { case '--ansi': case '--color': case '--color=yes': case '--color=force': case '--color=always': case '--colors=always': return static::$defaultColors = true; case '--no-ansi': case '--color=no': case '--color=none': case '--color=never': case '--colors=never': return static::$defaultColors = false; } } } } $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream; return static::$defaultColors = $this->hasColorSupport($h); } /** * {@inheritdoc} */ protected function dumpLine(int $depth, bool $endOfValue = false) { if ($this->colors) { $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); } parent::dumpLine($depth); } protected function endValue(Cursor $cursor) { if (-1 === $cursor->hashType) { return; } if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { $this->line .= ','; } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { $this->line .= ','; } } $this->dumpLine($cursor->depth, true); } /** * Returns true if the stream supports colorization. * * Reference: Composer\XdebugHandler\Process::supportsColor * https://github.com/composer/xdebug-handler * * @param mixed $stream A CLI output stream */ private function hasColorSupport($stream): bool { if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { return false; } // Follow https://no-color.org/ if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { return false; } if ('Hyper' === getenv('TERM_PROGRAM')) { return true; } if (\DIRECTORY_SEPARATOR === '\\') { return (\function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support($stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } return stream_isatty($stream); } /** * Returns true if the Windows terminal supports true color. * * Note that this does not check an output stream, but relies on environment * variables from known implementations, or a PHP and Windows version that * supports true color. */ private function isWindowsTrueColor(): bool { $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM') || 'Hyper' === getenv('TERM_PROGRAM'); if (!$result) { $version = sprintf( '%s.%s.%s', PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR, PHP_WINDOWS_VERSION_BUILD ); $result = $version >= '10.0.15063'; } return $result; } private function getSourceLink(string $file, int $line) { if ($fmt = $this->displayOptions['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); } return false; } } PK2A#]��#�ll?vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * Describe collected data clones for html output. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class HtmlDescriptor implements DumpDescriptorInterface { private $dumper; private $initialized = false; public function __construct(HtmlDumper $dumper) { $this->dumper = $dumper; } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { if (!$this->initialized) { $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); $output->writeln("<style>$styles</style><script>$scripts</script>"); $this->initialized = true; } $title = '-'; if (isset($context['request'])) { $request = $context['request']; $controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}</span>"; $title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri); $dedupIdentifier = $request['identifier']; } elseif (isset($context['cli'])) { $title = '<code>$ </code>'.$context['cli']['command_line']; $dedupIdentifier = $context['cli']['identifier']; } else { $dedupIdentifier = uniqid('', true); } $sourceDescription = ''; if (isset($context['source'])) { $source = $context['source']; $projectDir = $source['project_dir'] ?? null; $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); if (isset($source['file_link'])) { $sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription); } } $isoDate = $this->extractDate($context, 'c'); $tags = array_filter([ 'controller' => $controller ?? null, 'project dir' => $projectDir ?? null, ]); $output->writeln(<<<HTML <article data-dedup-id="$dedupIdentifier"> <header> <div class="row"> <h2 class="col">$title</h2> <time class="col text-small" title="$isoDate" datetime="$isoDate"> {$this->extractDate($context)} </time> </div> {$this->renderTags($tags)} </header> <section class="body"> <p class="text-small"> $sourceDescription </p> {$this->dumper->dump($data, true)} </section> </article> HTML ); } private function extractDate(array $context, string $format = 'r'): string { return date($format, (int) $context['timestamp']); } private function renderTags(array $tags): string { if (!$tags) { return ''; } $renderedTags = ''; foreach ($tags as $key => $value) { $renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value); } return <<<HTML <div class="row"> <ul class="tags"> $renderedTags </ul> </div> HTML; } } PK2A#]��bbHvendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; /** * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ interface DumpDescriptorInterface { public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; } PK2A#]a�MM M >vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * Describe collected data clones for cli output. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class CliDescriptor implements DumpDescriptorInterface { private $dumper; private $lastIdentifier; public function __construct(CliDumper $dumper) { $this->dumper = $dumper; } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); $this->dumper->setColors($output->isDecorated()); $rows = [['date', date('r', (int) $context['timestamp'])]]; $lastIdentifier = $this->lastIdentifier; $this->lastIdentifier = $clientId; $section = "Received from client #$clientId"; if (isset($context['request'])) { $request = $context['request']; $this->lastIdentifier = $request['identifier']; $section = sprintf('%s %s', $request['method'], $request['uri']); if ($controller = $request['controller']) { $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; } } elseif (isset($context['cli'])) { $this->lastIdentifier = $context['cli']['identifier']; $section = '$ '.$context['cli']['command_line']; } if ($this->lastIdentifier !== $lastIdentifier) { $io->section($section); } if (isset($context['source'])) { $source = $context['source']; $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']); if ($fileLink = $source['file_link'] ?? null) { $sourceInfo = sprintf('<href=%s>%s</>', $fileLink, $sourceInfo); } $rows[] = ['source', $sourceInfo]; $file = $source['file_relative'] ?? $source['file']; $rows[] = ['file', $file]; } $io->table([], $rows); $this->dumper->dump($data); $io->newLine(); } } PK2A#]���447vendor/symfony/var-dumper/Command/ServerDumpCommand.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Server\DumpServer; /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class ServerDumpCommand extends Command { protected static $defaultName = 'server:dump'; protected static $defaultDescription = 'Start a dump server that collects and displays dumps in a single place'; private $server; /** @var DumpDescriptorInterface[] */ private $descriptors; public function __construct(DumpServer $server, array $descriptors = []) { $this->server = $server; $this->descriptors = $descriptors + [ 'cli' => new CliDescriptor(new CliDumper()), 'html' => new HtmlDescriptor(new HtmlDumper()), ]; parent::__construct(); } protected function configure() { $this ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli') ->setDescription(self::$defaultDescription) ->setHelp(<<<'EOF' <info>%command.name%</info> starts a dump server that collects and displays dumps in a single place for debugging you application: <info>php %command.full_name%</info> You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option and redirecting the output to a file: <info>php %command.full_name% --format="html" > dump.html</info> EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $format = $input->getOption('format'); if (!$descriptor = $this->descriptors[$format] ?? null) { throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); } $errorIo = $io->getErrorStyle(); $errorIo->title('Symfony Var Dumper Server'); $this->server->start(); $errorIo->success(sprintf('Server listening on %s', $this->server->getHost())); $errorIo->comment('Quit the server with CONTROL-C.'); $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { $descriptor->describe($io, $data, $context, $clientId); }); return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues($this->getAvailableFormats()); } } private function getAvailableFormats(): array { return array_keys($this->descriptors); } } PK2A#]�e,�R�R3vendor/symfony/var-dumper/Cloner/AbstractCloner.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * AbstractCloner implements a generic caster mechanism for objects and resources. * * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractCloner implements ClonerInterface { public static $defaultCasters = [ '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], 'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'], 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], 'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'], 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], 'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'], 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], 'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'], 'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'], 'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'], 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], 'Symfony\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], 'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'], 'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'], 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], 'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'], 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], ':curl' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], 'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'], ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], 'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], 'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'], 'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'], 'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'], 'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'], 'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'], 'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'], 'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'], 'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'], 'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'], 'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'], ]; protected $maxItems = 2500; protected $maxString = -1; protected $minDepth = 1; /** * @var array<string, list<callable>> */ private $casters = []; /** * @var callable|null */ private $prevErrorHandler; private $classInfo = []; private $filter = 0; /** * @param callable[]|null $casters A map of casters * * @see addCasters */ public function __construct(array $casters = null) { if (null === $casters) { $casters = static::$defaultCasters; } $this->addCasters($casters); } /** * Adds casters for resources and objects. * * Maps resources or objects types to a callback. * Types are in the key, with a callable caster for value. * Resource types are to be prefixed with a `:`, * see e.g. static::$defaultCasters. * * @param callable[] $casters A map of casters */ public function addCasters(array $casters) { foreach ($casters as $type => $callback) { $this->casters[$type][] = $callback; } } /** * Sets the maximum number of items to clone past the minimum depth in nested structures. */ public function setMaxItems(int $maxItems) { $this->maxItems = $maxItems; } /** * Sets the maximum cloned length for strings. */ public function setMaxString(int $maxString) { $this->maxString = $maxString; } /** * Sets the minimum tree depth where we are guaranteed to clone all the items. After this * depth is reached, only setMaxItems items will be cloned. */ public function setMinDepth(int $minDepth) { $this->minDepth = $minDepth; } /** * Clones a PHP variable. * * @param mixed $var Any PHP variable * @param int $filter A bit field of Caster::EXCLUDE_* constants * * @return Data */ public function cloneVar($var, int $filter = 0) { $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) { // Cloner never dies throw new \ErrorException($msg, 0, $type, $file, $line); } if ($this->prevErrorHandler) { return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); } return false; }); $this->filter = $filter; if ($gc = gc_enabled()) { gc_disable(); } try { return new Data($this->doClone($var)); } finally { if ($gc) { gc_enable(); } restore_error_handler(); $this->prevErrorHandler = null; } } /** * Effectively clones the PHP variable. * * @param mixed $var Any PHP variable * * @return array */ abstract protected function doClone($var); /** * Casts an object to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure * * @return array */ protected function castObject(Stub $stub, bool $isNested) { $obj = $stub->value; $class = $stub->class; if (\PHP_VERSION_ID < 80000 ? "\0" === ($class[15] ?? null) : str_contains($class, "@anonymous\0")) { $stub->class = get_debug_type($obj); } if (isset($this->classInfo[$class])) { [$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class]; } else { $i = 2; $parents = [$class]; $hasDebugInfo = method_exists($class, '__debugInfo'); foreach (class_parents($class) as $p) { $parents[] = $p; ++$i; } foreach (class_implements($class) as $p) { $parents[] = $p; ++$i; } $parents[] = '*'; $r = new \ReflectionClass($class); $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ 'file' => $r->getFileName(), 'line' => $r->getStartLine(), ]; $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; } $stub->attr += $fileInfo; $a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class); try { while ($i--) { if (!empty($this->casters[$p = $parents[$i]])) { foreach ($this->casters[$p] as $callback) { $a = $callback($obj, $a, $stub, $isNested, $this->filter); } } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } /** * Casts a resource to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure * * @return array */ protected function castResource(Stub $stub, bool $isNested) { $a = []; $res = $stub->value; $type = $stub->class; try { if (!empty($this->casters[':'.$type])) { foreach ($this->casters[':'.$type] as $callback) { $a = $callback($res, $a, $stub, $isNested, $this->filter); } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } } PK2A#]��|)vendor/symfony/var-dumper/Cloner/Stub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * Represents the main properties of a PHP variable. * * @author Nicolas Grekas <p@tchwork.com> */ class Stub { public const TYPE_REF = 1; public const TYPE_STRING = 2; public const TYPE_ARRAY = 3; public const TYPE_OBJECT = 4; public const TYPE_RESOURCE = 5; public const STRING_BINARY = 1; public const STRING_UTF8 = 2; public const ARRAY_ASSOC = 1; public const ARRAY_INDEXED = 2; public $type = self::TYPE_REF; public $class = ''; public $value; public $cut = 0; public $handle = 0; public $refCount = 0; public $position = 0; public $attr = []; private static $defaultProperties = []; /** * @internal */ public function __sleep(): array { $properties = []; if (!isset(self::$defaultProperties[$c = static::class])) { self::$defaultProperties[$c] = get_class_vars($c); foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) { unset(self::$defaultProperties[$c][$k]); } } foreach (self::$defaultProperties[$c] as $k => $v) { if ($this->$k !== $v) { $properties[] = $k; } } return $properties; } } PK2A#]�-�a4vendor/symfony/var-dumper/Cloner/ClonerInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas <p@tchwork.com> */ interface ClonerInterface { /** * Clones a PHP variable. * * @param mixed $var Any PHP variable * * @return Data */ public function cloneVar($var); } PK2A#]a��#�5�5.vendor/symfony/var-dumper/Cloner/VarCloner.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas <p@tchwork.com> */ class VarCloner extends AbstractCloner { private static $gid; private static $arrayCache = []; /** * {@inheritdoc} */ protected function doClone($var) { $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the minimum depth $refsCounter = 0; // Hard references counter $queue = [[$var]]; // This breadth-first queue is the return value $hardRefs = []; // Map of original zval ids to stub objects $objRefs = []; // Map of original object handles to their stub object counterpart $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning $resRefs = []; // Map of original resource handles to their stub object counterpart $values = []; // Map of stub objects' ids to original values $maxItems = $this->maxItems; $maxString = $this->maxString; $minDepth = $this->minDepth; $currentDepth = 0; // Current tree depth $currentDepthFinalIndex = 0; // Final $queue index for current tree depth $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached $cookie = (object) []; // Unique object used to detect hard references $a = null; // Array cast for nested structures $stub = null; // Stub capturing the main properties of an original item value // or null if the original value is used directly if (!$gid = self::$gid) { $gid = self::$gid = md5(random_bytes(6)); // Unique string used to detect the special $GLOBALS variable } $arrayStub = new Stub(); $arrayStub->type = Stub::TYPE_ARRAY; $fromObjCast = false; for ($i = 0; $i < $len; ++$i) { // Detect when we move on to the next tree depth if ($i > $currentDepthFinalIndex) { ++$currentDepth; $currentDepthFinalIndex = $len - 1; if ($currentDepth >= $minDepth) { $minimumDepthReached = true; } } $refs = $vals = $queue[$i]; foreach ($vals as $k => $v) { // $v is the original value or a stub object in case of hard references if (\PHP_VERSION_ID >= 70400) { $zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null; } else { $refs[$k] = $cookie; $zvalRef = $vals[$k] === $cookie; } if ($zvalRef) { $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure if (\PHP_VERSION_ID >= 70400 ? null !== $vals[$k] = $hardRefs[$zvalRef] ?? null : $v instanceof Stub && isset($hardRefs[spl_object_id($v)])) { if (\PHP_VERSION_ID >= 70400) { $v = $vals[$k]; } else { $refs[$k] = $vals[$k] = $v; } if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; } ++$v->refCount; continue; } $vals[$k] = new Stub(); $vals[$k]->value = $v; $vals[$k]->handle = ++$refsCounter; if (\PHP_VERSION_ID >= 70400) { $hardRefs[$zvalRef] = $vals[$k]; } else { $refs[$k] = $vals[$k]; $h = spl_object_id($refs[$k]); $hardRefs[$h] = &$refs[$k]; $values[$h] = $v; } } // Create $stub when the original value $v cannot be used directly // If $v is a nested structure, put that structure in array $a switch (true) { case null === $v: case \is_bool($v): case \is_int($v): case \is_float($v): continue 2; case \is_string($v): if ('' === $v) { continue 2; } if (!preg_match('//u', $v)) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { $stub->cut = $cut; $stub->value = substr($v, 0, -$cut); } else { $stub->value = $v; } } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_UTF8; $stub->cut = $cut; $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); } else { continue 2; } $a = null; break; case \is_array($v): if (!$v) { continue 2; } $stub = $arrayStub; if (\PHP_VERSION_ID >= 80100) { $stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC; $a = $v; break; } $stub->class = Stub::ARRAY_INDEXED; $j = -1; foreach ($v as $gk => $gv) { if ($gk !== ++$j) { $stub->class = Stub::ARRAY_ASSOC; $a = $v; $a[$gid] = true; break; } } // Copies of $GLOBALS have very strange behavior, // let's detect them with some black magic if (isset($v[$gid])) { unset($v[$gid]); $a = []; foreach ($v as $gk => &$gv) { if ($v === $gv && (\PHP_VERSION_ID < 70400 || !isset($hardRefs[\ReflectionReference::fromArrayElement($v, $gk)->getId()]))) { unset($v); $v = new Stub(); $v->value = [$v->cut = \count($gv), Stub::TYPE_ARRAY => 0]; $v->handle = -1; if (\PHP_VERSION_ID >= 70400) { $gv = &$a[$gk]; $hardRefs[\ReflectionReference::fromArrayElement($a, $gk)->getId()] = &$gv; } else { $gv = &$hardRefs[spl_object_id($v)]; } $gv = $v; } $a[$gk] = &$gv; } unset($gv); } else { $a = $v; } break; case \is_object($v): if (empty($objRefs[$h = spl_object_id($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = \get_class($v); $stub->value = $v; $stub->handle = $h; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } $stub->handle = $h = spl_object_id($stub->value); } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($objRefs[$h])) { $objRefs[$h] = $stub; $objects[] = $v; } else { $stub = $objRefs[$h]; ++$stub->refCount; $a = null; } break; default: // resource if (empty($resRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; if ('Unknown' === $stub->class = @get_resource_type($v)) { $stub->class = 'Closed'; } $stub->value = $v; $stub->handle = $h; $a = $this->castResource($stub, 0 < $i); $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($resRefs[$h])) { $resRefs[$h] = $stub; } else { $stub = $resRefs[$h]; ++$stub->refCount; $a = null; } break; } if ($a) { if (!$minimumDepthReached || 0 > $maxItems) { $queue[$len] = $a; $stub->position = $len++; } elseif ($pos < $maxItems) { if ($maxItems < $pos += \count($a)) { $a = \array_slice($a, 0, $maxItems - $pos, true); if ($stub->cut >= 0) { $stub->cut += $pos - $maxItems; } } $queue[$len] = $a; $stub->position = $len++; } elseif ($stub->cut >= 0) { $stub->cut += \count($a); $stub->position = 0; } } if ($arrayStub === $stub) { if ($arrayStub->cut) { $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; $arrayStub->cut = 0; } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; } else { self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; } } if (!$zvalRef) { $vals[$k] = $stub; } elseif (\PHP_VERSION_ID >= 70400) { $hardRefs[$zvalRef]->value = $stub; } else { $refs[$k]->value = $stub; } } if ($fromObjCast) { $fromObjCast = false; $refs = $vals; $vals = []; $j = -1; foreach ($queue[$i] as $k => $v) { foreach ([$k => true] as $gk => $gv) { } if ($gk !== $k) { $vals = (object) $vals; $vals->{$k} = $refs[++$j]; $vals = (array) $vals; } else { $vals[$k] = $refs[++$j]; } } } $queue[$i] = $vals; } foreach ($values as $h => $v) { $hardRefs[$h] = $v; } return $queue; } } PK2A#]�Q-w99+vendor/symfony/var-dumper/Cloner/Cursor.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * Represents the current state of a dumper while dumping. * * @author Nicolas Grekas <p@tchwork.com> */ class Cursor { public const HASH_INDEXED = Stub::ARRAY_INDEXED; public const HASH_ASSOC = Stub::ARRAY_ASSOC; public const HASH_OBJECT = Stub::TYPE_OBJECT; public const HASH_RESOURCE = Stub::TYPE_RESOURCE; public $depth = 0; public $refIndex = 0; public $softRefTo = 0; public $softRefCount = 0; public $softRefHandle = 0; public $hardRefTo = 0; public $hardRefCount = 0; public $hardRefHandle = 0; public $hashType; public $hashKey; public $hashKeyIsBinary; public $hashIndex = 0; public $hashLength = 0; public $hashCut = 0; public $stop = false; public $attr = []; public $skipChildren = false; } PK2A#]3�� t6t6)vendor/symfony/var-dumper/Cloner/Data.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; /** * @author Nicolas Grekas <p@tchwork.com> */ class Data implements \ArrayAccess, \Countable, \IteratorAggregate { private $data; private $position = 0; private $key = 0; private $maxDepth = 20; private $maxItemsPerDepth = -1; private $useRefHandles = -1; private $context = []; /** * @param array $data An array as returned by ClonerInterface::cloneVar() */ public function __construct(array $data) { $this->data = $data; } /** * @return string|null */ public function getType() { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!$item instanceof Stub) { return \gettype($item); } if (Stub::TYPE_STRING === $item->type) { return 'string'; } if (Stub::TYPE_ARRAY === $item->type) { return 'array'; } if (Stub::TYPE_OBJECT === $item->type) { return $item->class; } if (Stub::TYPE_RESOURCE === $item->type) { return $item->class.' resource'; } return null; } /** * Returns a native representation of the original value. * * @param array|bool $recursive Whether values should be resolved recursively or not * * @return string|int|float|bool|array|Data[]|null */ public function getValue($recursive = false) { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub) { return $item; } if (Stub::TYPE_STRING === $item->type) { return $item->value; } $children = $item->position ? $this->data[$item->position] : []; foreach ($children as $k => $v) { if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { continue; } $children[$k] = clone $this; $children[$k]->key = $k; $children[$k]->position = $item->position; if ($recursive) { if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { $recursive = (array) $recursive; if (isset($recursive[$v->position])) { continue; } $recursive[$v->position] = true; } $children[$k] = $children[$k]->getValue($recursive); } } return $children; } /** * @return int */ #[\ReturnTypeWillChange] public function count() { return \count($this->getValue()); } /** * @return \Traversable */ #[\ReturnTypeWillChange] public function getIterator() { if (!\is_array($value = $this->getValue())) { throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value))); } yield from $value; } public function __get(string $key) { if (null !== $data = $this->seek($key)) { $item = $this->getStub($data->data[$data->position][$data->key]); return $item instanceof Stub || [] === $item ? $data : $item; } return null; } /** * @return bool */ public function __isset(string $key) { return null !== $this->seek($key); } /** * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($key) { return $this->__isset($key); } /** * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->__get($key); } /** * @return void */ #[\ReturnTypeWillChange] public function offsetSet($key, $value) { throw new \BadMethodCallException(self::class.' objects are immutable.'); } /** * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($key) { throw new \BadMethodCallException(self::class.' objects are immutable.'); } /** * @return string */ public function __toString() { $value = $this->getValue(); if (!\is_array($value)) { return (string) $value; } return sprintf('%s (count=%d)', $this->getType(), \count($value)); } /** * Returns a depth limited clone of $this. * * @return static */ public function withMaxDepth(int $maxDepth) { $data = clone $this; $data->maxDepth = $maxDepth; return $data; } /** * Limits the number of elements per depth level. * * @return static */ public function withMaxItemsPerDepth(int $maxItemsPerDepth) { $data = clone $this; $data->maxItemsPerDepth = $maxItemsPerDepth; return $data; } /** * Enables/disables objects' identifiers tracking. * * @param bool $useRefHandles False to hide global ref. handles * * @return static */ public function withRefHandles(bool $useRefHandles) { $data = clone $this; $data->useRefHandles = $useRefHandles ? -1 : 0; return $data; } /** * @return static */ public function withContext(array $context) { $data = clone $this; $data->context = $context; return $data; } /** * Seeks to a specific key in nested data structures. * * @param string|int $key The key to seek to * * @return static|null */ public function seek($key) { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { return null; } $keys = [$key]; switch ($item->type) { case Stub::TYPE_OBJECT: $keys[] = Caster::PREFIX_DYNAMIC.$key; $keys[] = Caster::PREFIX_PROTECTED.$key; $keys[] = Caster::PREFIX_VIRTUAL.$key; $keys[] = "\0$item->class\0$key"; // no break case Stub::TYPE_ARRAY: case Stub::TYPE_RESOURCE: break; default: return null; } $data = null; $children = $this->data[$item->position]; foreach ($keys as $key) { if (isset($children[$key]) || \array_key_exists($key, $children)) { $data = clone $this; $data->key = $key; $data->position = $item->position; break; } } return $data; } /** * Dumps data with a DumperInterface dumper. */ public function dump(DumperInterface $dumper) { $refs = [0]; $cursor = new Cursor(); if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) { $cursor->attr['if_links'] = true; $cursor->hashType = -1; $dumper->dumpScalar($cursor, 'default', '^'); $cursor->attr = ['if_links' => true]; $dumper->dumpScalar($cursor, 'default', ' '); $cursor->hashType = 0; } $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); } /** * Depth-first dumping of items. * * @param mixed $item A Stub object or the original value being dumped */ private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, $item) { $cursor->refIndex = 0; $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; $firstSeen = true; if (!$item instanceof Stub) { $cursor->attr = []; $type = \gettype($item); if ($item && 'array' === $type) { $item = $this->getStub($item); } } elseif (Stub::TYPE_REF === $item->type) { if ($item->handle) { if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->hardRefTo = $refs[$r]; $cursor->hardRefHandle = $this->useRefHandles & $item->handle; $cursor->hardRefCount = 0 < $item->handle ? $item->refCount : 0; } $cursor->attr = $item->attr; $type = $item->class ?: \gettype($item->value); $item = $this->getStub($item->value); } if ($item instanceof Stub) { if ($item->refCount) { if (!isset($refs[$r = $item->handle])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->softRefTo = $refs[$r]; } $cursor->softRefHandle = $this->useRefHandles & $item->handle; $cursor->softRefCount = $item->refCount; $cursor->attr = $item->attr; $cut = $item->cut; if ($item->position && $firstSeen) { $children = $this->data[$item->position]; if ($cursor->stop) { if ($cut >= 0) { $cut += \count($children); } $children = []; } } else { $children = []; } switch ($item->type) { case Stub::TYPE_STRING: $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); break; case Stub::TYPE_ARRAY: $item = clone $item; $item->type = $item->class; $item->class = $item->value; // no break case Stub::TYPE_OBJECT: case Stub::TYPE_RESOURCE: $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); if ($withChildren) { if ($cursor->skipChildren) { $withChildren = false; $cut = -1; } else { $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); } } elseif ($children && 0 <= $cut) { $cut += \count($children); } $cursor->skipChildren = false; $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); break; default: throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type)); } } elseif ('array' === $type) { $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); } elseif ('string' === $type) { $dumper->dumpString($cursor, $item, false, 0); } else { $dumper->dumpScalar($cursor, $type, $item); } } /** * Dumps children of hash structures. * * @return int The final number of removed items */ private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int { $cursor = clone $parentCursor; ++$cursor->depth; $cursor->hashType = $hashType; $cursor->hashIndex = 0; $cursor->hashLength = \count($children); $cursor->hashCut = $hashCut; foreach ($children as $key => $child) { $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); $cursor->hashKey = $dumpKeys ? $key : null; $this->dumpItem($dumper, $cursor, $refs, $child); if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { $parentCursor->stop = true; return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; } } return $hashCut; } private function getStub($item) { if (!$item || !\is_array($item)) { return $item; } $stub = new Stub(); $stub->type = Stub::TYPE_ARRAY; foreach ($item as $stub->class => $stub->position) { } if (isset($item[0])) { $stub->cut = $item[0]; } $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); return $stub; } } PK2A#].�<<4vendor/symfony/var-dumper/Cloner/DumperInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * DumperInterface used by Data objects. * * @author Nicolas Grekas <p@tchwork.com> */ interface DumperInterface { /** * Dumps a scalar value. * * @param string $type The PHP type of the value being dumped * @param string|int|float|bool $value The scalar value being dumped */ public function dumpScalar(Cursor $cursor, string $type, $value); /** * Dumps a string. * * @param string $str The string being dumped * @param bool $bin Whether $str is UTF-8 or binary encoded * @param int $cut The number of characters $str has been cut by */ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut); /** * Dumps while entering an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item */ public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild); /** * Dumps while leaving an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut); } PK2A#]�Z~b 5vendor/symfony/var-dumper/Test/VarDumperTestTrait.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Test; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * @author Nicolas Grekas <p@tchwork.com> */ trait VarDumperTestTrait { /** * @internal */ private $varDumperConfig = [ 'casters' => [], 'flags' => null, ]; protected function setUpVarDumper(array $casters, int $flags = null): void { $this->varDumperConfig['casters'] = $casters; $this->varDumperConfig['flags'] = $flags; } /** * @after */ protected function tearDownVarDumper(): void { $this->varDumperConfig['casters'] = []; $this->varDumperConfig['flags'] = null; } public function assertDumpEquals($expected, $data, int $filter = 0, string $message = '') { $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } public function assertDumpMatchesFormat($expected, $data, int $filter = 0, string $message = '') { $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } protected function getDump($data, $key = null, int $filter = 0): ?string { if (null === $flags = $this->varDumperConfig['flags']) { $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0; } $cloner = new VarCloner(); $cloner->addCasters($this->varDumperConfig['casters']); $cloner->setMaxItems(-1); $dumper = new CliDumper(null, null, $flags); $dumper->setColors(false); $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); if (null !== $key && null === $data = $data->seek($key)) { return null; } return rtrim($dumper->dump($data, true)); } private function prepareExpectation($expected, int $filter): string { if (!\is_string($expected)) { $expected = $this->getDump($expected, null, $filter); } return rtrim($expected); } } PK2A#]Dva��:vendor/symfony/var-dumper/Resources/css/htmlDescriptor.cssnu�[���body { display: flex; flex-direction: column-reverse; justify-content: flex-end; max-width: 1140px; margin: auto; padding: 15px; word-wrap: break-word; background-color: #F9F9F9; color: #222; font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.4; } p { margin: 0; } a { color: #218BC3; text-decoration: none; } a:hover { text-decoration: underline; } .text-small { font-size: 12px !important; } article { margin: 5px; margin-bottom: 10px; } article > header > .row { display: flex; flex-direction: row; align-items: baseline; margin-bottom: 10px; } article > header > .row > .col { flex: 1; display: flex; align-items: baseline; } article > header > .row > h2 { font-size: 14px; color: #222; font-weight: normal; font-family: "Lucida Console", monospace, sans-serif; word-break: break-all; margin: 20px 5px 0 0; user-select: all; } article > header > .row > h2 > code { white-space: nowrap; user-select: none; color: #cc2255; background-color: #f7f7f9; border: 1px solid #e1e1e8; border-radius: 3px; margin-right: 5px; padding: 0 3px; } article > header > .row > time.col { flex: 0; text-align: right; white-space: nowrap; color: #999; font-style: italic; } article > header ul.tags { list-style: none; padding: 0; margin: 0; font-size: 12px; } article > header ul.tags > li { user-select: all; margin-bottom: 2px; } article > header ul.tags > li > span.badge { display: inline-block; padding: .25em .4em; margin-right: 5px; border-radius: 4px; background-color: #6c757d3b; color: #524d4d; font-size: 12px; text-align: center; font-weight: 700; line-height: 1; white-space: nowrap; vertical-align: baseline; user-select: none; } article > section.body { border: 1px solid #d8d8d8; background: #FFF; padding: 10px; border-radius: 3px; } pre.sf-dump { border-radius: 3px; margin-bottom: 0; } .hidden { display: none !important; } .dumped-tag > .sf-dump { display: inline-block; margin: 0; padding: 1px 5px; line-height: 1.4; vertical-align: top; background-color: transparent; user-select: auto; } .dumped-tag > pre.sf-dump, .dumped-tag > .sf-dump-default { color: #CC7832; background: none; } .dumped-tag > .sf-dump .sf-dump-str { color: #629755; } .dumped-tag > .sf-dump .sf-dump-private, .dumped-tag > .sf-dump .sf-dump-protected, .dumped-tag > .sf-dump .sf-dump-public { color: #262626; } .dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; } .dumped-tag > .sf-dump .sf-dump-key { color: #789339; } .dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; } .dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } .dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; } .dumped-tag > .sf-dump .sf-dump-ns { user-select: none; } PK2A#]j����6vendor/symfony/var-dumper/Resources/functions/dump.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Component\VarDumper\VarDumper; if (!function_exists('dump')) { /** * @author Nicolas Grekas <p@tchwork.com> */ function dump($var, ...$moreVars) { VarDumper::dump($var); foreach ($moreVars as $v) { VarDumper::dump($v); } if (1 < func_num_args()) { return func_get_args(); } return $var; } } if (!function_exists('dd')) { /** * @return never */ function dd(...$vars) { if (!in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } foreach ($vars as $v) { VarDumper::dump($v); } exit(1); } } PK2A#]N��J^^7vendor/symfony/var-dumper/Resources/bin/var-dump-servernu�[���#!/usr/bin/env php <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\VarDumper\Command\ServerDumpCommand; use Symfony\Component\VarDumper\Server\DumpServer; function includeIfExists(string $file): bool { return file_exists($file) && include $file; } if ( !includeIfExists(__DIR__ . '/../../../../autoload.php') && !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') ) { fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); exit(1); } if (!class_exists(Application::class)) { fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL); exit(1); } $input = new ArgvInput(); $output = new ConsoleOutput(); $defaultHost = '127.0.0.1:9912'; $host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true); $logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null; $app = new Application(); $app->getDefinition()->addOption( new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost) ); $app->add($command = new ServerDumpCommand(new DumpServer($host, $logger))) ->getApplication() ->setDefaultCommand($command->getName(), true) ->run($input, $output) ; PK2A#]h��|bb8vendor/symfony/var-dumper/Resources/js/htmlDescriptor.jsnu�[���document.addEventListener('DOMContentLoaded', function() { let prev = null; Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) { const dedupId = article.dataset.dedupId; if (dedupId === prev) { article.getElementsByTagName('header')[0].classList.add('hidden'); } prev = dedupId; }); }); PK2A#]�(Y 'vendor/symfony/var-dumper/VarDumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Dumper\ServerDumper; // Load the global dump() function require_once __DIR__.'/Resources/functions/dump.php'; /** * @author Nicolas Grekas <p@tchwork.com> */ class VarDumper { /** * @var callable|null */ private static $handler; public static function dump($var) { if (null === self::$handler) { self::register(); } return (self::$handler)($var); } /** * @return callable|null */ public static function setHandler(callable $callable = null) { $prevHandler = self::$handler; // Prevent replacing the handler with expected format as soon as the env var was set: if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { return $prevHandler; } self::$handler = $callable; return $prevHandler; } private static function register(): void { $cloner = new VarCloner(); $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null; switch (true) { case 'html' === $format: $dumper = new HtmlDumper(); break; case 'cli' === $format: $dumper = new CliDumper(); break; case 'server' === $format: case $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME): $host = 'server' === $format ? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912' : $format; $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper(); $dumper = new ServerDumper($host, $dumper, self::getDefaultContextProviders()); break; default: $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper(); } if (!$dumper instanceof ServerDumper) { $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); } self::$handler = function ($var) use ($cloner, $dumper) { $dumper->dump($cloner->cloneVar($var)); }; } private static function getDefaultContextProviders(): array { $contextProviders = []; if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && class_exists(Request::class)) { $requestStack = new RequestStack(); $requestStack->push(Request::createFromGlobals()); $contextProviders['request'] = new RequestContextProvider($requestStack); } $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null; return $contextProviders + [ 'cli' => new CliContextProvider(), 'source' => new SourceContextProvider(null, null, $fileLinkFormatter), ]; } } PK2A#]�.�z,,!vendor/symfony/var-dumper/LICENSEnu�[���Copyright (c) 2014-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]]��'�'.vendor/symfony/var-dumper/Caster/DOMCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts DOM related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class DOMCaster { private const ERROR_CODES = [ \DOM_PHP_ERR => 'DOM_PHP_ERR', \DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', \DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', \DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', \DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', \DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', \DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', \DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', \DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', \DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', \DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', \DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', \DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', \DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', \DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', \DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', \DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', ]; private const NODE_TYPES = [ \XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', \XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', \XML_TEXT_NODE => 'XML_TEXT_NODE', \XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', \XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', \XML_ENTITY_NODE => 'XML_ENTITY_NODE', \XML_PI_NODE => 'XML_PI_NODE', \XML_COMMENT_NODE => 'XML_COMMENT_NODE', \XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', \XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', \XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', \XML_NOTATION_NODE => 'XML_NOTATION_NODE', \XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', \XML_DTD_NODE => 'XML_DTD_NODE', \XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', \XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', \XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', \XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', ]; public static function castException(\DOMException $e, array $a, Stub $stub, bool $isNested) { $k = Caster::PREFIX_PROTECTED.'code'; if (isset($a[$k], self::ERROR_CODES[$a[$k]])) { $a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]); } return $a; } public static function castLength($dom, array $a, Stub $stub, bool $isNested) { $a += [ 'length' => $dom->length, ]; return $a; } public static function castImplementation(\DOMImplementation $dom, array $a, Stub $stub, bool $isNested) { $a += [ Caster::PREFIX_VIRTUAL.'Core' => '1.0', Caster::PREFIX_VIRTUAL.'XML' => '2.0', ]; return $a; } public static function castNode(\DOMNode $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), 'parentNode' => new CutStub($dom->parentNode), 'childNodes' => $dom->childNodes, 'firstChild' => new CutStub($dom->firstChild), 'lastChild' => new CutStub($dom->lastChild), 'previousSibling' => new CutStub($dom->previousSibling), 'nextSibling' => new CutStub($dom->nextSibling), 'attributes' => $dom->attributes, 'ownerDocument' => new CutStub($dom->ownerDocument), 'namespaceURI' => $dom->namespaceURI, 'prefix' => $dom->prefix, 'localName' => $dom->localName, 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, 'textContent' => new CutStub($dom->textContent), ]; return $a; } public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), 'prefix' => $dom->prefix, 'localName' => $dom->localName, 'namespaceURI' => $dom->namespaceURI, 'ownerDocument' => new CutStub($dom->ownerDocument), 'parentNode' => new CutStub($dom->parentNode), ]; return $a; } public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0) { $a += [ 'doctype' => $dom->doctype, 'implementation' => $dom->implementation, 'documentElement' => new CutStub($dom->documentElement), 'actualEncoding' => $dom->actualEncoding, 'encoding' => $dom->encoding, 'xmlEncoding' => $dom->xmlEncoding, 'standalone' => $dom->standalone, 'xmlStandalone' => $dom->xmlStandalone, 'version' => $dom->version, 'xmlVersion' => $dom->xmlVersion, 'strictErrorChecking' => $dom->strictErrorChecking, 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, 'config' => $dom->config, 'formatOutput' => $dom->formatOutput, 'validateOnParse' => $dom->validateOnParse, 'resolveExternals' => $dom->resolveExternals, 'preserveWhiteSpace' => $dom->preserveWhiteSpace, 'recover' => $dom->recover, 'substituteEntities' => $dom->substituteEntities, ]; if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } return $a; } public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'data' => $dom->data, 'length' => $dom->length, ]; return $a; } public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'name' => $dom->name, 'specified' => $dom->specified, 'value' => $dom->value, 'ownerElement' => $dom->ownerElement, 'schemaTypeInfo' => $dom->schemaTypeInfo, ]; return $a; } public static function castElement(\DOMElement $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'tagName' => $dom->tagName, 'schemaTypeInfo' => $dom->schemaTypeInfo, ]; return $a; } public static function castText(\DOMText $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'wholeText' => $dom->wholeText, ]; return $a; } public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'typeName' => $dom->typeName, 'typeNamespace' => $dom->typeNamespace, ]; return $a; } public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'severity' => $dom->severity, 'message' => $dom->message, 'type' => $dom->type, 'relatedException' => $dom->relatedException, 'related_data' => $dom->related_data, 'location' => $dom->location, ]; return $a; } public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'lineNumber' => $dom->lineNumber, 'columnNumber' => $dom->columnNumber, 'offset' => $dom->offset, 'relatedNode' => $dom->relatedNode, 'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri, ]; return $a; } public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'name' => $dom->name, 'entities' => $dom->entities, 'notations' => $dom->notations, 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'internalSubset' => $dom->internalSubset, ]; return $a; } public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, ]; return $a; } public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'notationName' => $dom->notationName, 'actualEncoding' => $dom->actualEncoding, 'encoding' => $dom->encoding, 'version' => $dom->version, ]; return $a; } public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'target' => $dom->target, 'data' => $dom->data, ]; return $a; } public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, bool $isNested) { $a += [ 'document' => $dom->document, ]; return $a; } } PK2A#]�_�ll0vendor/symfony/var-dumper/Caster/PgSqlCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts pqsql resources to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class PgSqlCaster { private const PARAM_CODES = [ 'server_encoding', 'client_encoding', 'is_superuser', 'session_authorization', 'DateStyle', 'TimeZone', 'IntervalStyle', 'integer_datetimes', 'application_name', 'standard_conforming_strings', ]; private const TRANSACTION_STATUS = [ \PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', \PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', \PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', \PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', \PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', ]; private const RESULT_STATUS = [ \PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', \PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', \PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', \PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', \PGSQL_COPY_IN => 'PGSQL_COPY_IN', \PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', \PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', \PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', ]; private const DIAG_CODES = [ 'severity' => \PGSQL_DIAG_SEVERITY, 'sqlstate' => \PGSQL_DIAG_SQLSTATE, 'message' => \PGSQL_DIAG_MESSAGE_PRIMARY, 'detail' => \PGSQL_DIAG_MESSAGE_DETAIL, 'hint' => \PGSQL_DIAG_MESSAGE_HINT, 'statement position' => \PGSQL_DIAG_STATEMENT_POSITION, 'internal position' => \PGSQL_DIAG_INTERNAL_POSITION, 'internal query' => \PGSQL_DIAG_INTERNAL_QUERY, 'context' => \PGSQL_DIAG_CONTEXT, 'file' => \PGSQL_DIAG_SOURCE_FILE, 'line' => \PGSQL_DIAG_SOURCE_LINE, 'function' => \PGSQL_DIAG_SOURCE_FUNCTION, ]; public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested) { $a['seek position'] = pg_lo_tell($lo); return $a; } public static function castLink($link, array $a, Stub $stub, bool $isNested) { $a['status'] = pg_connection_status($link); $a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); $a['busy'] = pg_connection_busy($link); $a['transaction'] = pg_transaction_status($link); if (isset(self::TRANSACTION_STATUS[$a['transaction']])) { $a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$a['transaction']], $a['transaction']); } $a['pid'] = pg_get_pid($link); $a['last error'] = pg_last_error($link); $a['last notice'] = pg_last_notice($link); $a['host'] = pg_host($link); $a['port'] = pg_port($link); $a['dbname'] = pg_dbname($link); $a['options'] = pg_options($link); $a['version'] = pg_version($link); foreach (self::PARAM_CODES as $v) { if (false !== $s = pg_parameter_status($link, $v)) { $a['param'][$v] = $s; } } $a['param']['client_encoding'] = pg_client_encoding($link); $a['param'] = new EnumStub($a['param']); return $a; } public static function castResult($result, array $a, Stub $stub, bool $isNested) { $a['num rows'] = pg_num_rows($result); $a['status'] = pg_result_status($result); if (isset(self::RESULT_STATUS[$a['status']])) { $a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']); } $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); if (-1 === $a['num rows']) { foreach (self::DIAG_CODES as $k => $v) { $a['error'][$k] = pg_result_error_field($result, $v); } } $a['affected rows'] = pg_affected_rows($result); $a['last OID'] = pg_last_oid($result); $fields = pg_num_fields($result); for ($i = 0; $i < $fields; ++$i) { $field = [ 'name' => pg_field_name($result, $i), 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), 'nullable' => (bool) pg_field_is_null($result, $i), 'storage' => pg_field_size($result, $i).' bytes', 'display' => pg_field_prtlen($result, $i).' chars', ]; if (' (OID: )' === $field['table']) { $field['table'] = null; } if ('-1 bytes' === $field['storage']) { $field['storage'] = 'variable size'; } elseif ('1 bytes' === $field['storage']) { $field['storage'] = '1 byte'; } if ('1 chars' === $field['display']) { $field['display'] = '1 char'; } $a['fields'][] = new EnumStub($field); } return $a; } } PK2A#]�w�)}}-vendor/symfony/var-dumper/Caster/EnumStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents an enumeration of values. * * @author Nicolas Grekas <p@tchwork.com> */ class EnumStub extends Stub { public $dumpKeys = true; public function __construct(array $values, bool $dumpKeys = true) { $this->value = $values; $this->dumpKeys = $dumpKeys; } } PK2A#]�6���/vendor/symfony/var-dumper/Caster/UuidCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Ramsey\Uuid\UuidInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class UuidCaster { public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, ]; return $a; } } PK2A#]�1�\\/vendor/symfony/var-dumper/Caster/StubCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts a caster's Stub. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class StubCaster { public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested) { if ($isNested) { $stub->type = $c->type; $stub->class = $c->class; $stub->value = $c->value; $stub->handle = $c->handle; $stub->cut = $c->cut; $stub->attr = $c->attr; if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; } $a = []; } return $a; } public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested) { return $isNested ? $c->preservedSubset : $a; } public static function cutInternals($obj, array $a, Stub $stub, bool $isNested) { if ($isNested) { $stub->cut += \count($a); return []; } return $a; } public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested) { if ($isNested) { $stub->class = $c->dumpKeys ? '' : null; $stub->handle = 0; $stub->value = null; $stub->cut = $c->cut; $stub->attr = $c->attr; $a = []; if ($c->value) { foreach (array_keys($c->value) as $k) { $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; } // Preserve references with array_combine() $a = array_combine($keys, $c->value); } } return $a; } } PK2A#]u,Gy?y?4vendor/symfony/var-dumper/Caster/ExceptionCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * Casts common Exception classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ExceptionCaster { public static $srcContext = 1; public static $traceArgs = true; public static $errorTypes = [ \E_DEPRECATED => 'E_DEPRECATED', \E_USER_DEPRECATED => 'E_USER_DEPRECATED', \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', \E_ERROR => 'E_ERROR', \E_WARNING => 'E_WARNING', \E_PARSE => 'E_PARSE', \E_NOTICE => 'E_NOTICE', \E_CORE_ERROR => 'E_CORE_ERROR', \E_CORE_WARNING => 'E_CORE_WARNING', \E_COMPILE_ERROR => 'E_COMPILE_ERROR', \E_COMPILE_WARNING => 'E_COMPILE_WARNING', \E_USER_ERROR => 'E_USER_ERROR', \E_USER_WARNING => 'E_USER_WARNING', \E_USER_NOTICE => 'E_USER_NOTICE', \E_STRICT => 'E_STRICT', ]; private static $framesCache = []; public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0) { return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); } public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0) { return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); } public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested) { if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } return $a; } public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested) { $trace = Caster::PREFIX_VIRTUAL.'trace'; $prefix = Caster::PREFIX_PROTECTED; $xPrefix = "\0Exception\0"; if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { $b = (array) $a[$xPrefix.'previous']; $class = get_debug_type($a[$xPrefix.'previous']); self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); } unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); return $a; } public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested) { $sPrefix = "\0".SilencedErrorContext::class."\0"; if (!isset($a[$s = $sPrefix.'severity'])) { return $a; } if (isset(self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } $trace = [[ 'file' => $a[$sPrefix.'file'], 'line' => $a[$sPrefix.'line'], ]]; if (isset($a[$sPrefix.'trace'])) { $trace = array_merge($trace, $a[$sPrefix.'trace']); } unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); return $a; } public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested) { if (!$isNested) { return $a; } $stub->class = ''; $stub->handle = 0; $frames = $trace->value; $prefix = Caster::PREFIX_VIRTUAL; $a = []; $j = \count($frames); if (0 > $i = $trace->sliceOffset) { $i = max(0, $j + $i); } if (!isset($trace->value[$i])) { return []; } $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; $frames[] = ['function' => '']; $collapse = false; for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { $f = $frames[$i]; $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; $frame = new FrameStub( [ 'object' => $f['object'] ?? null, 'class' => $f['class'] ?? null, 'type' => $f['type'] ?? null, 'function' => $f['function'] ?? null, ] + $frames[$i - 1], false, true ); $f = self::castFrameStub($frame, [], $frame, true); if (isset($f[$prefix.'src'])) { foreach ($f[$prefix.'src']->value as $label => $frame) { if (str_starts_with($label, "\0~collapse=0")) { if ($collapse) { $label = substr_replace($label, '1', 11, 1); } else { $collapse = true; } } $label = substr_replace($label, "title=Stack level $j.&", 2, 0); } $f = $frames[$i - 1]; if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { $frame->value['arguments'] = new ArgsStub($f['args'], $f['function'] ?? null, $f['class'] ?? null); } } elseif ('???' !== $lastCall) { $label = new ClassStub($lastCall); if (isset($label->attr['ellipsis'])) { $label->attr['ellipsis'] += 2; $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; } } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; } $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; $lastCall = $call; } if (null !== $trace->sliceLength) { $a = \array_slice($a, 0, $trace->sliceLength, true); } return $a; } public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested) { if (!$isNested) { return $a; } $f = $frame->value; $prefix = Caster::PREFIX_VIRTUAL; if (isset($f['file'], $f['line'])) { $cacheKey = $f; unset($cacheKey['object'], $cacheKey['args']); $cacheKey[] = self::$srcContext; $cacheKey = implode('-', $cacheKey); if (isset(self::$framesCache[$cacheKey])) { $a[$prefix.'src'] = self::$framesCache[$cacheKey]; } else { if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { $f['file'] = substr($f['file'], 0, -\strlen($match[0])); $f['line'] = (int) $match[1]; } $src = $f['line']; $srcKey = $f['file']; $ellipsis = new LinkStub($srcKey, 0); $srcAttr = 'collapse='.(int) $ellipsis->inVendor; $ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0; $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; if (is_file($f['file']) && 0 <= self::$srcContext) { if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) { $template = null; if (isset($f['object'])) { $template = $f['object']; } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { $template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); } if (null !== $template) { $ellipsis = 0; $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); $templateInfo = $template->getDebugInfo(); if (isset($templateInfo[$f['line']])) { if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { $templatePath = null; } if ($templateSrc) { $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; } } } } if ($srcKey == $f['file']) { $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); $srcKey .= ':'.$f['line']; if ($ellipsis) { $ellipsis += 1 + \strlen($f['line']); } } $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); } else { $srcAttr .= '&separator=:'; } $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); } } unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); if ($frame->inTraceStub) { unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); } foreach ($a as $k => $v) { if (!$v) { unset($a[$k]); } } if ($frame->keepArgs && !empty($f['args'])) { $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); } return $a; } private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array { if (isset($a[$xPrefix.'trace'])) { $trace = $a[$xPrefix.'trace']; unset($a[$xPrefix.'trace']); // Ensures the trace is always last } else { $trace = []; } if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); } if (empty($a[$xPrefix.'previous'])) { unset($a[$xPrefix.'previous']); } unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']); if (isset($a[Caster::PREFIX_PROTECTED.'message']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) { $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; }, $a[Caster::PREFIX_PROTECTED.'message']); } if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } return $a; } private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void { if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { return; } array_unshift($trace, [ 'function' => $class ? 'new '.$class : null, 'file' => $file, 'line' => $line, ]); } private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub { $srcLines = explode("\n", $srcLines); $src = []; for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { $src[] = ($srcLines[$i] ?? '')."\n"; } if ($frame['function'] ?? false) { $stub = new CutStub(new \stdClass()); $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; $stub->type = Stub::TYPE_OBJECT; $stub->attr['cut_hash'] = true; $stub->attr['file'] = $frame['file']; $stub->attr['line'] = $frame['line']; try { $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); if ($f = $caller->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $caller->getStartLine(); } } catch (\ReflectionException $e) { // ignore fake class/function } $srcLines = ["\0~separator=\0" => $stub]; } else { $stub = null; $srcLines = []; } $ltrim = 0; do { $pad = null; for ($i = $srcContext << 1; $i >= 0; --$i) { if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { if (null === $pad) { $pad = $c; } if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { break; } } } ++$ltrim; } while (0 > $i && null !== $pad); --$ltrim; foreach ($src as $i => $c) { if ($ltrim) { $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); } $c = substr($c, 0, -1); if ($i !== $srcContext) { $c = new ConstStub('default', $c); } else { $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); if (null !== $file) { $c->attr['file'] = $file; $c->attr['line'] = $line; } } $c->attr['lang'] = $lang; $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; } return new EnumStub($srcLines); } } PK2A#]cfy�@ @ -vendor/symfony/var-dumper/Caster/LinkStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a file or a URL. * * @author Nicolas Grekas <p@tchwork.com> */ class LinkStub extends ConstStub { public $inVendor = false; private static $vendorRoots; private static $composerRoots; public function __construct(string $label, int $line = 0, string $href = null) { $this->value = $label; if (null === $href) { $href = $label; } if (!\is_string($href)) { return; } if (str_starts_with($href, 'file://')) { if ($href === $label) { $label = substr($label, 7); } $href = substr($href, 7); } elseif (str_contains($href, '://')) { $this->attr['href'] = $href; return; } if (!is_file($href)) { return; } if ($line) { $this->attr['line'] = $line; } if ($label !== $this->attr['file'] = realpath($href) ?: $href) { return; } if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1; } } private function getComposerRoot(string $file, bool &$inVendor) { if (null === self::$vendorRoots) { self::$vendorRoots = []; foreach (get_declared_classes() as $class) { if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { $r = new \ReflectionClass($class); $v = \dirname($r->getFileName(), 2); if (is_file($v.'/composer/installed.json')) { self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; } } } } $inVendor = false; if (isset(self::$composerRoots[$dir = \dirname($file)])) { return self::$composerRoots[$dir]; } foreach (self::$vendorRoots as $root) { if ($inVendor = str_starts_with($file, $root)) { return $root; } } $parent = $dir; while (!@is_file($parent.'/composer.json')) { if (!@file_exists($parent)) { // open_basedir restriction in effect break; } if ($parent === \dirname($parent)) { return self::$composerRoots[$dir] = false; } $parent = \dirname($parent); } return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; } } PK2A#]+L� ��1vendor/symfony/var-dumper/Caster/CutArrayStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a cut array. * * @author Nicolas Grekas <p@tchwork.com> */ class CutArrayStub extends CutStub { public $preservedSubset; public function __construct(array $value, array $preservedKeys) { parent::__construct($value); $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); $this->cut -= \count($this->preservedSubset); } } PK2A#]_�Vz�"�"/vendor/symfony/var-dumper/Caster/IntlCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * @author Jan Schädlich <jan.schaedlich@sensiolabs.de> * * @final */ class IntlCaster { public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested) { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; return self::castError($c, $a); } public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += 3; return self::castError($c, $a); } $a += [ Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( [ 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), ] ), Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( [ 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), ] ), Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( [ 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), ] ), ]; return self::castError($c, $a); } public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested) { $a += [ Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), Caster::PREFIX_VIRTUAL.'id' => $c->getID(), Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), ]; if ($c->useDaylightTime()) { $a += [ Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), ]; } return self::castError($c, $a); } public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $a += [ Caster::PREFIX_VIRTUAL.'type' => $c->getType(), Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } private static function castError(object $c, array $a): array { if ($errorCode = $c->getErrorCode()) { $a += [ Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), ]; } return $a; } } PK2A#]��c��-vendor/symfony/var-dumper/Caster/ArgsStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a list of function arguments. * * @author Nicolas Grekas <p@tchwork.com> */ class ArgsStub extends EnumStub { private static $parameters = []; public function __construct(array $args, string $function, ?string $class) { [$variadic, $params] = self::getParameters($function, $class); $values = []; foreach ($args as $k => $v) { $values[$k] = !\is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; } if (null === $params) { parent::__construct($values, false); return; } if (\count($values) < \count($params)) { $params = \array_slice($params, 0, \count($values)); } elseif (\count($values) > \count($params)) { $values[] = new EnumStub(array_splice($values, \count($params)), false); $params[] = $variadic; } if (['...'] === $params) { $this->dumpKeys = false; $this->value = $values[0]->value; } else { $this->value = array_combine($params, $values); } } private static function getParameters(string $function, ?string $class): array { if (isset(self::$parameters[$k = $class.'::'.$function])) { return self::$parameters[$k]; } try { $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); } catch (\ReflectionException $e) { return [null, null]; } $variadic = '...'; $params = []; foreach ($r->getParameters() as $v) { $k = '$'.$v->name; if ($v->isPassedByReference()) { $k = '&'.$k; } if ($v->isVariadic()) { $variadic .= $k; } else { $params[] = $k; } } return self::$parameters[$k] = [$variadic, $params]; } } PK2A#]����}},vendor/symfony/var-dumper/Caster/ImgStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class ImgStub extends ConstStub { public function __construct(string $data, string $contentType, string $size = '') { $this->value = ''; $this->attr['img-data'] = $data; $this->attr['img-size'] = $size; $this->attr['content-type'] = $contentType; } } PK2A#]���&&/vendor/symfony/var-dumper/Caster/AmqpCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Amqp related classes to array representation. * * @author Grégoire Pineau <lyrixx@lyrixx.info> * * @final */ class AmqpCaster { private const FLAGS = [ \AMQP_DURABLE => 'AMQP_DURABLE', \AMQP_PASSIVE => 'AMQP_PASSIVE', \AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', \AMQP_AUTODELETE => 'AMQP_AUTODELETE', \AMQP_INTERNAL => 'AMQP_INTERNAL', \AMQP_NOLOCAL => 'AMQP_NOLOCAL', \AMQP_AUTOACK => 'AMQP_AUTOACK', \AMQP_IFEMPTY => 'AMQP_IFEMPTY', \AMQP_IFUNUSED => 'AMQP_IFUNUSED', \AMQP_MANDATORY => 'AMQP_MANDATORY', \AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', \AMQP_MULTIPLE => 'AMQP_MULTIPLE', \AMQP_NOWAIT => 'AMQP_NOWAIT', \AMQP_REQUEUE => 'AMQP_REQUEUE', ]; private const EXCHANGE_TYPES = [ \AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', \AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', \AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', \AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', ]; public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPConnection\x00login"])) { return $a; } // BC layer in the amqp lib if (method_exists($c, 'getReadTimeout')) { $timeout = $c->getReadTimeout(); } else { $timeout = $c->getTimeout(); } $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'login' => $c->getLogin(), $prefix.'password' => $c->getPassword(), $prefix.'host' => $c->getHost(), $prefix.'vhost' => $c->getVhost(), $prefix.'port' => $c->getPort(), $prefix.'read_timeout' => $timeout, ]; return $a; } public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'channel_id' => $c->getChannelId(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPChannel\x00connection"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'prefetch_size' => $c->getPrefetchSize(), $prefix.'prefetch_count' => $c->getPrefetchCount(), ]; return $a; } public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPQueue\x00name"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; $type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$c->getType()], $c->getType()) : $c->getType(); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPExchange\x00name"])) { $a["\x00AMQPExchange\x00type"] = $type; return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'type' => $type, $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPEnvelope\x00body"])) { $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; return $a; } if (!($filter & Caster::EXCLUDE_VERBOSE)) { $a += [$prefix.'body' => $c->getBody()]; } $a += [ $prefix.'delivery_tag' => $c->getDeliveryTag(), $prefix.'is_redelivery' => $c->isRedelivery(), $prefix.'exchange_name' => $c->getExchangeName(), $prefix.'routing_key' => $c->getRoutingKey(), $prefix.'content_type' => $c->getContentType(), $prefix.'content_encoding' => $c->getContentEncoding(), $prefix.'headers' => $c->getHeaders(), $prefix.'delivery_mode' => $deliveryMode, $prefix.'priority' => $c->getPriority(), $prefix.'correlation_id' => $c->getCorrelationId(), $prefix.'reply_to' => $c->getReplyTo(), $prefix.'expiration' => $c->getExpiration(), $prefix.'message_id' => $c->getMessageId(), $prefix.'timestamp' => $c->getTimeStamp(), $prefix.'type' => $c->getType(), $prefix.'user_id' => $c->getUserId(), $prefix.'app_id' => $c->getAppId(), ]; return $a; } private static function extractFlags(int $flags): ConstStub { $flagsArray = []; foreach (self::FLAGS as $value => $name) { if ($flags & $value) { $flagsArray[] = $name; } } if (!$flagsArray) { $flagsArray = ['AMQP_NOPARAM']; } return new ConstStub(implode('|', $flagsArray), $flags); } } PK2A#]��/vendor/symfony/var-dumper/Caster/DateCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts DateTimeInterface related classes to array representation. * * @author Dany Maillard <danymaillard93b@gmail.com> * * @final */ class DateCaster { private const PERIOD_LIMIT = 3; public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter) { $prefix = Caster::PREFIX_VIRTUAL; $location = $d->getTimezone() ? $d->getTimezone()->getLocation() : null; $fromNow = (new \DateTime())->diff($d); $title = $d->format('l, F j, Y') ."\n".self::formatInterval($fromNow).' from now' .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') ; unset( $a[Caster::PREFIX_DYNAMIC.'date'], $a[Caster::PREFIX_DYNAMIC.'timezone'], $a[Caster::PREFIX_DYNAMIC.'timezone_type'] ); $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); $stub->class .= $d->format(' @U'); return $a; } public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter) { $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; } private static function formatInterval(\DateInterval $i): string { $format = '%R '; if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $i = $d->diff($d->add($i)); // recalculate carry over points $format .= 0 < $i->days ? '%ad ' : ''; } else { $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); } $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; $format = '%R ' === $format ? '0s' : $format; return $i->format(rtrim($format)); } public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter) { $location = $timeZone->getLocation(); $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; } public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter) { $dates = []; foreach (clone $p as $i => $d) { if (self::PERIOD_LIMIT === $i) { $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i ); break; } $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); } $period = sprintf( 'every %s, from %s%s %s', self::formatInterval($p->getDateInterval()), $p->include_start_date ? '[' : ']', self::formatDateTime($p->getStartDate()), ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).(\PHP_VERSION_ID >= 80200 && $p->include_end_date ? ']' : '[') : 'recurring '.$p->recurrences.' time/s' ); $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; } private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string { return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); } private static function formatSeconds(string $s, string $us): string { return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); } } PK2A#]ɛt� � 6vendor/symfony/var-dumper/Caster/XmlResourceCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts XML resources to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class XmlResourceCaster { private const XML_ERRORS = [ \XML_ERROR_NONE => 'XML_ERROR_NONE', \XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', \XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', \XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', \XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', \XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', \XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', \XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', \XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', \XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', \XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', \XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', \XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', \XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', \XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', \XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', \XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', \XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', \XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', \XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', \XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', \XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', ]; public static function castXml($h, array $a, Stub $stub, bool $isNested) { $a['current_byte_index'] = xml_get_current_byte_index($h); $a['current_column_number'] = xml_get_current_column_number($h); $a['current_line_number'] = xml_get_current_line_number($h); $a['error_code'] = xml_get_error_code($h); if (isset(self::XML_ERRORS[$a['error_code']])) { $a['error_code'] = new ConstStub(self::XML_ERRORS[$a['error_code']], $a['error_code']); } return $a; } } PK2A#]˚���.vendor/symfony/var-dumper/Caster/FrameStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas <p@tchwork.com> */ class FrameStub extends EnumStub { public $keepArgs; public $inTraceStub; public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) { $this->value = $frame; $this->keepArgs = $keepArgs; $this->inTraceStub = $inTraceStub; } } PK2A#]i�ѳ��7vendor/symfony/var-dumper/Caster/ProxyManagerCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use ProxyManager\Proxy\ProxyInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ProxyManagerCaster { public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested) { if ($parent = get_parent_class($c)) { $stub->class .= ' - '.$parent; } $stub->class .= '@proxy'; return $a; } } PK2A#]�r�c��1vendor/symfony/var-dumper/Caster/MysqliCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class MysqliCaster { public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array { foreach ($a as $k => $v) { if (isset($c->$k)) { $a[$k] = $c->$k; } } return $a; } } PK2A#]�ׄ� � .vendor/symfony/var-dumper/Caster/PdoCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts PDO related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class PdoCaster { private const PDO_ATTRIBUTES = [ 'CASE' => [ \PDO::CASE_LOWER => 'LOWER', \PDO::CASE_NATURAL => 'NATURAL', \PDO::CASE_UPPER => 'UPPER', ], 'ERRMODE' => [ \PDO::ERRMODE_SILENT => 'SILENT', \PDO::ERRMODE_WARNING => 'WARNING', \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', ], 'TIMEOUT', 'PREFETCH', 'AUTOCOMMIT', 'PERSISTENT', 'DRIVER_NAME', 'SERVER_INFO', 'ORACLE_NULLS' => [ \PDO::NULL_NATURAL => 'NATURAL', \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', \PDO::NULL_TO_STRING => 'TO_STRING', ], 'CLIENT_VERSION', 'SERVER_VERSION', 'STATEMENT_CLASS', 'EMULATE_PREPARES', 'CONNECTION_STATUS', 'STRINGIFY_FETCHES', 'DEFAULT_FETCH_MODE' => [ \PDO::FETCH_ASSOC => 'ASSOC', \PDO::FETCH_BOTH => 'BOTH', \PDO::FETCH_LAZY => 'LAZY', \PDO::FETCH_NUM => 'NUM', \PDO::FETCH_OBJ => 'OBJ', ], ]; public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested) { $attr = []; $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); foreach (self::PDO_ATTRIBUTES as $k => $v) { if (!isset($k[0])) { $k = $v; $v = []; } try { $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); if ($v && isset($v[$attr[$k]])) { $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); } } catch (\Exception $e) { } } if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { if ($attr[$k][1]) { $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); } $attr[$k][0] = new ClassStub($attr[$k][0]); } $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'inTransaction' => method_exists($c, 'inTransaction'), $prefix.'errorInfo' => $c->errorInfo(), $prefix.'attributes' => new EnumStub($attr), ]; if ($a[$prefix.'inTransaction']) { $a[$prefix.'inTransaction'] = $c->inTransaction(); } else { unset($a[$prefix.'inTransaction']); } if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); return $a; } public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a[$prefix.'errorInfo'] = $c->errorInfo(); if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } return $a; } } PK2A#]�ˊ��4vendor/symfony/var-dumper/Caster/XmlReaderCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts XmlReader class to array representation. * * @author Baptiste Clavié <clavie.b@gmail.com> * * @final */ class XmlReaderCaster { private const NODE_TYPES = [ \XMLReader::NONE => 'NONE', \XMLReader::ELEMENT => 'ELEMENT', \XMLReader::ATTRIBUTE => 'ATTRIBUTE', \XMLReader::TEXT => 'TEXT', \XMLReader::CDATA => 'CDATA', \XMLReader::ENTITY_REF => 'ENTITY_REF', \XMLReader::ENTITY => 'ENTITY', \XMLReader::PI => 'PI (Processing Instruction)', \XMLReader::COMMENT => 'COMMENT', \XMLReader::DOC => 'DOC', \XMLReader::DOC_TYPE => 'DOC_TYPE', \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', \XMLReader::NOTATION => 'NOTATION', \XMLReader::WHITESPACE => 'WHITESPACE', \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', \XMLReader::END_ELEMENT => 'END_ELEMENT', \XMLReader::END_ENTITY => 'END_ENTITY', \XMLReader::XML_DECLARATION => 'XML_DECLARATION', ]; public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested) { try { $properties = [ 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), ]; } catch (\Error $e) { $properties = [ 'LOADDTD' => false, 'DEFAULTATTRS' => false, 'VALIDATE' => false, 'SUBST_ENTITIES' => false, ]; } $props = Caster::PREFIX_VIRTUAL.'parserProperties'; $info = [ 'localName' => $reader->localName, 'prefix' => $reader->prefix, 'nodeType' => new ConstStub(self::NODE_TYPES[$reader->nodeType], $reader->nodeType), 'depth' => $reader->depth, 'isDefault' => $reader->isDefault, 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, 'xmlLang' => $reader->xmlLang, 'attributeCount' => $reader->attributeCount, 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, $props => $properties, ]; if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { $info[$props] = new EnumStub($info[$props]); $info[$props]->cut = $count; } $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); // +2 because hasValue and hasAttributes are always filtered $stub->cut += $count + 2; return $a + $info; } } PK2A#]j���.vendor/symfony/var-dumper/Caster/ClassStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a PHP class identifier. * * @author Nicolas Grekas <p@tchwork.com> */ class ClassStub extends ConstStub { /** * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier */ public function __construct(string $identifier, $callable = null) { $this->value = $identifier; try { if (null !== $callable) { if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); } elseif (\is_object($callable)) { $r = [$callable, '__invoke']; } elseif (\is_array($callable)) { $r = $callable; } elseif (false !== $i = strpos($callable, '::')) { $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; } else { $r = new \ReflectionFunction($callable); } } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; } else { $r = new \ReflectionClass($identifier); } if (\is_array($r)) { try { $r = new \ReflectionMethod($r[0], $r[1]); } catch (\ReflectionException $e) { $r = new \ReflectionClass($r[0]); } } if (str_contains($identifier, "@anonymous\0")) { $this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; }, $identifier); } if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); $s = ReflectionCaster::getSignature($s); if (str_ends_with($identifier, '()')) { $this->value = substr_replace($identifier, $s, -2); } else { $this->value .= $s; } } } catch (\ReflectionException $e) { return; } finally { if (0 < $i = strrpos($this->value, '\\')) { $this->attr['ellipsis'] = \strlen($this->value) - $i; $this->attr['ellipsis-type'] = 'class'; $this->attr['ellipsis-tail'] = 1; } } if ($f = $r->getFileName()) { $this->attr['file'] = $f; $this->attr['line'] = $r->getStartLine(); } } public static function wrapCallable($callable) { if (\is_object($callable) || !\is_callable($callable)) { return $callable; } if (!\is_array($callable)) { $callable = new static($callable, $callable); } elseif (\is_string($callable[0])) { $callable[0] = new static($callable[0], $callable); } else { $callable[1] = new static($callable[1], $callable); } return $callable; } } PK2A#]��Ə�0vendor/symfony/var-dumper/Caster/RedisCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Redis class from ext-redis to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class RedisCaster { private const SERIALIZERS = [ \Redis::SERIALIZER_NONE => 'NONE', \Redis::SERIALIZER_PHP => 'PHP', 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY ]; private const MODES = [ \Redis::ATOMIC => 'ATOMIC', \Redis::MULTI => 'MULTI', \Redis::PIPELINE => 'PIPELINE', ]; private const COMPRESSION_MODES = [ 0 => 'NONE', // Redis::COMPRESSION_NONE 1 => 'LZF', // Redis::COMPRESSION_LZF ]; private const FAILOVER_OPTIONS = [ \RedisCluster::FAILOVER_NONE => 'NONE', \RedisCluster::FAILOVER_ERROR => 'ERROR', \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', ]; public static function castRedis(\Redis $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; if (!$connected = $c->isConnected()) { return $a + [ $prefix.'isConnected' => $connected, ]; } $mode = $c->getMode(); return $a + [ $prefix.'isConnected' => $connected, $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), $prefix.'auth' => $c->getAuth(), $prefix.'mode' => isset(self::MODES[$mode]) ? new ConstStub(self::MODES[$mode], $mode) : $mode, $prefix.'dbNum' => $c->getDbNum(), $prefix.'timeout' => $c->getTimeout(), $prefix.'lastError' => $c->getLastError(), $prefix.'persistentId' => $c->getPersistentID(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; return $a + [ $prefix.'hosts' => $c->_hosts(), $prefix.'function' => ClassStub::wrapCallable($c->_function()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); $a += [ $prefix.'_masters' => $c->_masters(), $prefix.'_redir' => $c->_redir(), $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c, [ 'SLAVE_FAILOVER' => isset(self::FAILOVER_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover, ]), ]; return $a; } /** * @param \Redis|\RedisArray|\RedisCluster $redis */ private static function getRedisOptions($redis, array $options = []): EnumStub { $serializer = $redis->getOption(\Redis::OPT_SERIALIZER); if (\is_array($serializer)) { foreach ($serializer as &$v) { if (isset(self::SERIALIZERS[$v])) { $v = new ConstStub(self::SERIALIZERS[$v], $v); } } } elseif (isset(self::SERIALIZERS[$serializer])) { $serializer = new ConstStub(self::SERIALIZERS[$serializer], $serializer); } $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; if (\is_array($compression)) { foreach ($compression as &$v) { if (isset(self::COMPRESSION_MODES[$v])) { $v = new ConstStub(self::COMPRESSION_MODES[$v], $v); } } } elseif (isset(self::COMPRESSION_MODES[$compression])) { $compression = new ConstStub(self::COMPRESSION_MODES[$compression], $compression); } $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; if (\is_array($retry)) { foreach ($retry as &$v) { $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); } } else { $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); } $options += [ 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : 0, 'READ_TIMEOUT' => $redis->getOption(\Redis::OPT_READ_TIMEOUT), 'COMPRESSION' => $compression, 'SERIALIZER' => $serializer, 'PREFIX' => $redis->getOption(\Redis::OPT_PREFIX), 'SCAN' => $retry, ]; return new EnumStub($options); } } PK2A#]V/��uu+vendor/symfony/var-dumper/Caster/Caster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Helper for filtering out properties in casters. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class Caster { public const EXCLUDE_VERBOSE = 1; public const EXCLUDE_VIRTUAL = 2; public const EXCLUDE_DYNAMIC = 4; public const EXCLUDE_PUBLIC = 8; public const EXCLUDE_PROTECTED = 16; public const EXCLUDE_PRIVATE = 32; public const EXCLUDE_NULL = 64; public const EXCLUDE_EMPTY = 128; public const EXCLUDE_NOT_IMPORTANT = 256; public const EXCLUDE_STRICT = 512; public const PREFIX_VIRTUAL = "\0~\0"; public const PREFIX_DYNAMIC = "\0+\0"; public const PREFIX_PROTECTED = "\0*\0"; /** * Casts objects to arrays and adds the dynamic property prefix. * * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not */ public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, string $debugClass = null): array { if ($hasDebugInfo) { try { $debugInfo = $obj->__debugInfo(); } catch (\Throwable $e) { // ignore failing __debugInfo() $hasDebugInfo = false; } } $a = $obj instanceof \Closure ? [] : (array) $obj; if ($obj instanceof \__PHP_Incomplete_Class) { return $a; } if ($a) { static $publicProperties = []; $debugClass = $debugClass ?? get_debug_type($obj); $i = 0; $prefixedKeys = []; foreach ($a as $k => $v) { if ("\0" !== ($k[0] ?? '')) { if (!isset($publicProperties[$class])) { foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { $publicProperties[$class][$prop->name] = true; } } if (!isset($publicProperties[$class][$k])) { $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; } } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); } ++$i; } if ($prefixedKeys) { $keys = array_keys($a); foreach ($prefixedKeys as $i => $k) { $keys[$i] = $k; } $a = array_combine($keys, $a); } } if ($hasDebugInfo && \is_array($debugInfo)) { foreach ($debugInfo as $k => $v) { if (!isset($k[0]) || "\0" !== $k[0]) { if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { continue; } $k = self::PREFIX_VIRTUAL.$k; } unset($a[$k]); $a[$k] = $v; } } return $a; } /** * Filters out the specified properties. * * By default, a single match in the $filter bit field filters properties out, following an "or" logic. * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. * * @param array $a The array containing the properties to filter * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set * @param int|null &$count Set to the number of removed properties */ public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array { $count = 0; foreach ($a as $k => $v) { $type = self::EXCLUDE_STRICT & $filter; if (null === $v) { $type |= self::EXCLUDE_NULL & $filter; $type |= self::EXCLUDE_EMPTY & $filter; } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { $type |= self::EXCLUDE_EMPTY & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_NOT_IMPORTANT; } if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_VERBOSE; } if (!isset($k[1]) || "\0" !== $k[0]) { $type |= self::EXCLUDE_PUBLIC & $filter; } elseif ('~' === $k[1]) { $type |= self::EXCLUDE_VIRTUAL & $filter; } elseif ('+' === $k[1]) { $type |= self::EXCLUDE_DYNAMIC & $filter; } elseif ('*' === $k[1]) { $type |= self::EXCLUDE_PROTECTED & $filter; } else { $type |= self::EXCLUDE_PRIVATE & $filter; } if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { unset($a[$k]); ++$count; } } return $a; } public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array { if (isset($a['__PHP_Incomplete_Class_Name'])) { $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; unset($a['__PHP_Incomplete_Class_Name']); } return $a; } } PK2A#]b�=2vendor/symfony/var-dumper/Caster/RdKafkaCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use RdKafka\Conf; use RdKafka\Exception as RdKafkaException; use RdKafka\KafkaConsumer; use RdKafka\Message; use RdKafka\Metadata\Broker as BrokerMetadata; use RdKafka\Metadata\Collection as CollectionMetadata; use RdKafka\Metadata\Partition as PartitionMetadata; use RdKafka\Metadata\Topic as TopicMetadata; use RdKafka\Topic; use RdKafka\TopicConf; use RdKafka\TopicPartition; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts RdKafka related classes to array representation. * * @author Romain Neutron <imprec@gmail.com> */ class RdKafkaCaster { public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; try { $assignment = $c->getAssignment(); } catch (RdKafkaException $e) { $assignment = []; } $a += [ $prefix.'subscription' => $c->getSubscription(), $prefix.'assignment' => $assignment, ]; $a += self::extractMetadata($c); return $a; } public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getName(), ]; return $a; } public static function castTopicPartition(TopicPartition $c, array $a) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'offset' => $c->getOffset(), $prefix.'partition' => $c->getPartition(), $prefix.'topic' => $c->getTopic(), ]; return $a; } public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'errstr' => $c->errstr(), ]; return $a; } public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'out_q_len' => $c->getOutQLen(), ]; $a += self::extractMetadata($c); return $a; } public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested) { $a += iterator_to_array($c); return $a; } public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getTopic(), $prefix.'partitions' => $c->getPartitions(), ]; return $a; } public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'err' => $c->getErr(), $prefix.'leader' => $c->getLeader(), ]; return $a; } public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), ]; return $a; } private static function extractMetadata($c) { $prefix = Caster::PREFIX_VIRTUAL; try { $m = $c->getMetadata(true, null, 500); } catch (RdKafkaException $e) { return []; } return [ $prefix.'orig_broker_id' => $m->getOrigBrokerId(), $prefix.'orig_broker_name' => $m->getOrigBrokerName(), $prefix.'brokers' => $m->getBrokers(), $prefix.'topics' => $m->getTopics(), ]; } } PK2A#]q���� � 2vendor/symfony/var-dumper/Caster/SymfonyCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; use Symfony\Component\VarDumper\Cloner\Stub; /** * @final */ class SymfonyCaster { private const REQUEST_GETTERS = [ 'pathInfo' => 'getPathInfo', 'requestUri' => 'getRequestUri', 'baseUrl' => 'getBaseUrl', 'basePath' => 'getBasePath', 'method' => 'getMethod', 'format' => 'getRequestFormat', ]; public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested) { $clone = null; foreach (self::REQUEST_GETTERS as $prop => $getter) { $key = Caster::PREFIX_PROTECTED.$prop; if (\array_key_exists($key, $a) && null === $a[$key]) { if (null === $clone) { $clone = clone $request; } $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); } } return $a; } public static function castHttpClient($client, array $a, Stub $stub, bool $isNested) { $multiKey = sprintf("\0%s\0multi", \get_class($client)); if (isset($a[$multiKey])) { $a[$multiKey] = new CutStub($a[$multiKey]); } return $a; } public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested) { $stub->cut += \count($a); $a = []; foreach ($response->getInfo() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32(); // symfony/uid >= 5.3 if (method_exists($uuid, 'getDateTime')) { $a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C'); } return $a; } public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122(); // symfony/uid >= 5.3 if (method_exists($ulid, 'getDateTime')) { $a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C'); } return $a; } } PK2A#]����.vendor/symfony/var-dumper/Caster/GmpCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts GMP objects to array representation. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> * @author Nicolas Grekas <p@tchwork.com> * * @final */ class GmpCaster { public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array { $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); return $a; } } PK2A#]h���66-vendor/symfony/var-dumper/Caster/DsCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Ds\Collection; use Ds\Map; use Ds\Pair; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Ds extension classes to array representation. * * @author Jáchym Toušek <enumag@gmail.com> * * @final */ class DsCaster { public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); if (!$c instanceof Map) { $a += $c->toArray(); } return $a; } public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array { foreach ($c as $k => $v) { $a[] = new DsPairStub($k, $v); } return $a; } public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array { foreach ($c->toArray() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->class = Pair::class; $stub->value = null; $stub->handle = 0; $a = $c->value; } return $a; } } PK2A#]���!<;<;5vendor/symfony/var-dumper/Caster/ReflectionCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Reflector related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ReflectionCaster { public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; private const EXTRA_MAP = [ 'docComment' => 'getDocComment', 'extension' => 'getExtensionName', 'isDisabled' => 'isDisabled', 'isDeprecated' => 'isDeprecated', 'isInternal' => 'isInternal', 'isUserDefined' => 'isUserDefined', 'isGenerator' => 'isGenerator', 'isVariadic' => 'isVariadic', ]; public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; $c = new \ReflectionFunction($c); $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); if (!str_contains($c->name, '{closure}')) { $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; unset($a[$prefix.'class']); } unset($a[$prefix.'extra']); $stub->class .= self::getSignature($a); if ($f = $c->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $c->getStartLine(); } unset($a[$prefix.'parameters']); if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); return []; } if ($f) { $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); } return $a; } public static function unsetClosureFileInfo(\Closure $c, array $a) { unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); return $a; } public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested) { // Cannot create ReflectionGenerator based on a terminated Generator try { $reflectionGenerator = new \ReflectionGenerator($c); } catch (\Exception $e) { $a[Caster::PREFIX_VIRTUAL.'closed'] = true; return $a; } return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); } public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; if ($c instanceof \ReflectionNamedType || \PHP_VERSION_ID < 80000) { $a += [ $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ]; } elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) { $a[$prefix.'allowsNull'] = $c->allowsNull(); self::addMap($a, $c, [ 'types' => 'getTypes', ]); } else { $a[$prefix.'allowsNull'] = $c->allowsNull(); } return $a; } public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested) { self::addMap($a, $c, [ 'name' => 'getName', 'arguments' => 'getArguments', ]); return $a; } public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; if ($c->getThis()) { $a[$prefix.'this'] = new CutStub($c->getThis()); } $function = $c->getFunction(); $frame = [ 'class' => $function->class ?? null, 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, 'function' => $function->name, 'file' => $c->getExecutingFile(), 'line' => $c->getExecutingLine(), ]; if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) { $function = new \ReflectionGenerator($c->getExecutingGenerator()); array_unshift($trace, [ 'function' => 'yield', 'file' => $function->getExecutingFile(), 'line' => $function->getExecutingLine() - (int) (\PHP_VERSION_ID < 80100), ]); $trace[] = $frame; $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); } else { $function = new FrameStub($frame, false, true); $function = ExceptionCaster::castFrameStub($function, [], $function, true); $a[$prefix.'executing'] = $function[$prefix.'src']; } $a[Caster::PREFIX_VIRTUAL.'closed'] = false; return $a; } public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; if ($n = \Reflection::getModifierNames($c->getModifiers())) { $a[$prefix.'modifiers'] = implode(' ', $n); } self::addMap($a, $c, [ 'extends' => 'getParentClass', 'implements' => 'getInterfaceNames', 'constants' => 'getReflectionConstants', ]); foreach ($c->getProperties() as $n) { $a[$prefix.'properties'][$n->name] = $n; } foreach ($c->getMethods() as $n) { $a[$prefix.'methods'][$n->name] = $n; } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'returnsReference' => 'returnsReference', 'returnType' => 'getReturnType', 'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass', 'this' => 'getClosureThis', ]); if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && 'mixed' !== $v ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); } if (isset($a[$prefix.'this'])) { $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); } foreach ($c->getParameters() as $v) { $k = '$'.$v->name; if ($v->isVariadic()) { $k = '...'.$k; } if ($v->isPassedByReference()) { $k = '&'.$k; } $a[$prefix.'parameters'][$k] = $v; } if (isset($a[$prefix.'parameters'])) { $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { foreach ($v as $k => &$v) { if (\is_object($v)) { $a[$prefix.'use']['$'.$k] = new CutStub($v); } else { $a[$prefix.'use']['$'.$k] = &$v; } } unset($v); $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); } if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); $a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue(); self::addAttributes($a, $c); return $a; } public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); return $a; } public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'position' => 'getPosition', 'isVariadic' => 'isVariadic', 'byReference' => 'isPassedByReference', 'allowsNull' => 'allowsNull', ]); self::addAttributes($a, $c, $prefix); if ($v = $c->getType()) { $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; } if (isset($a[$prefix.'typeHint'])) { $v = $a[$prefix.'typeHint']; $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } else { unset($a[$prefix.'allowsNull']); } if ($c->isOptional()) { try { $a[$prefix.'default'] = $v = $c->getDefaultValue(); if ($c->isDefaultValueConstant() && !\is_object($v)) { $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); } if (null === $v) { unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException $e) { } } return $a; } public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); self::addAttributes($a, $c); self::addExtra($a, $c); return $a; } public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); return $a; } public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested) { self::addMap($a, $c, [ 'version' => 'getVersion', 'dependencies' => 'getDependencies', 'iniEntries' => 'getIniEntries', 'isPersistent' => 'isPersistent', 'isTemporary' => 'isTemporary', 'constants' => 'getConstants', 'functions' => 'getFunctions', 'classes' => 'getClasses', ]); return $a; } public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested) { self::addMap($a, $c, [ 'version' => 'getVersion', 'author' => 'getAuthor', 'copyright' => 'getCopyright', 'url' => 'getURL', ]); return $a; } public static function getSignature(array $a) { $prefix = Caster::PREFIX_VIRTUAL; $signature = ''; if (isset($a[$prefix.'parameters'])) { foreach ($a[$prefix.'parameters']->value as $k => $param) { $signature .= ', '; if ($type = $param->getType()) { if (!$type instanceof \ReflectionNamedType) { $signature .= $type.' '; } else { if (!$param->isOptional() && $param->allowsNull() && 'mixed' !== $type->getName()) { $signature .= '?'; } $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; } } $signature .= $k; if (!$param->isDefaultValueAvailable()) { continue; } $v = $param->getDefaultValue(); $signature .= ' = '; if ($param->isDefaultValueConstant()) { $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); } elseif (null === $v) { $signature .= 'null'; } elseif (\is_array($v)) { $signature .= $v ? '[…'.\count($v).']' : '[]'; } elseif (\is_string($v)) { $signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; } elseif (\is_bool($v)) { $signature .= $v ? 'true' : 'false'; } elseif (\is_object($v)) { $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); } else { $signature .= $v; } } } $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; if (isset($a[$prefix.'returnType'])) { $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); } return $signature; } private static function addExtra(array &$a, \Reflector $c) { $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { $x['file'] = new LinkStub($m, $c->getStartLine()); $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); } self::addMap($x, $c, self::EXTRA_MAP, ''); if ($x) { $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); } } private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) { foreach ($map as $k => $m) { if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) { continue; } if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; } } } private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void { if (\PHP_VERSION_ID >= 80000) { foreach ($c->getAttributes() as $n) { $a[$prefix.'attributes'][] = $n; } } } } PK2A#]JAo���,vendor/symfony/var-dumper/Caster/CutStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents the main properties of a PHP variable, pre-casted by a caster. * * @author Nicolas Grekas <p@tchwork.com> */ class CutStub extends Stub { public function __construct($value) { $this->value = $value; switch (\gettype($value)) { case 'object': $this->type = self::TYPE_OBJECT; $this->class = \get_class($value); if ($value instanceof \Closure) { ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); } $this->cut = -1; break; case 'array': $this->type = self::TYPE_ARRAY; $this->class = self::ARRAY_ASSOC; $this->cut = $this->value = \count($value); break; case 'resource': case 'unknown type': case 'resource (closed)': $this->type = self::TYPE_RESOURCE; $this->handle = (int) $value; if ('Unknown' === $this->class = @get_resource_type($value)) { $this->class = 'Closed'; } $this->cut = -1; break; case 'string': $this->type = self::TYPE_STRING; $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); $this->value = ''; break; } } } PK2A#]YսV&&0vendor/symfony/var-dumper/Caster/FiberCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Fiber related classes to array representation. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class FiberCaster { public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; if ($fiber->isTerminated()) { $status = 'terminated'; } elseif ($fiber->isRunning()) { $status = 'running'; } elseif ($fiber->isSuspended()) { $status = 'suspended'; } elseif ($fiber->isStarted()) { $status = 'started'; } else { $status = 'not started'; } $a[$prefix.'status'] = $status; return $a; } } PK2A#]]�͵�2vendor/symfony/var-dumper/Caster/ImagineCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Imagine\Image\ImageInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class ImagineCaster { public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array { $imgData = $c->get('png'); if (\strlen($imgData) > 1 * 1000 * 1000) { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), ]; } else { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), ]; } return $a; } } PK2A#]�~z���3vendor/symfony/var-dumper/Caster/ResourceCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts common resource types to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ResourceCaster { /** * @param \CurlHandle|resource $h */ public static function castCurl($h, array $a, Stub $stub, bool $isNested): array { return curl_getinfo($h); } public static function castDba($dba, array $a, Stub $stub, bool $isNested) { $list = dba_list(); $a['file'] = $list[(int) $dba]; return $a; } public static function castProcess($process, array $a, Stub $stub, bool $isNested) { return proc_get_status($process); } public static function castStream($stream, array $a, Stub $stub, bool $isNested) { $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); if ($a['uri'] ?? false) { $a['uri'] = new LinkStub($a['uri']); } return $a; } public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested) { return @stream_context_get_params($stream) ?: $a; } public static function castGd($gd, array $a, Stub $stub, bool $isNested) { $a['size'] = imagesx($gd).'x'.imagesy($gd); $a['trueColor'] = imageistruecolor($gd); return $a; } public static function castMysqlLink($h, array $a, Stub $stub, bool $isNested) { $a['host'] = mysql_get_host_info($h); $a['protocol'] = mysql_get_proto_info($h); $a['server'] = mysql_get_server_info($h); return $a; } public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested) { $stub->cut = -1; $info = openssl_x509_parse($h, false); $pin = openssl_pkey_get_public($h); $pin = openssl_pkey_get_details($pin)['key']; $pin = \array_slice(explode("\n", $pin), 1, -2); $pin = base64_decode(implode('', $pin)); $pin = base64_encode(hash('sha256', $pin, true)); $a += [ 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), 'expiry' => new ConstStub(date(\DateTime::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), 'fingerprint' => new EnumStub([ 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)), 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)), 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)), 'pin-sha256' => new ConstStub($pin), ]), ]; return $a; } } PK2A#]��a�) ) 4vendor/symfony/var-dumper/Caster/MemcachedCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Jan Schädlich <jan.schaedlich@sensiolabs.de> * * @final */ class MemcachedCaster { private static $optionConstants; private static $defaultOptions; public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested) { $a += [ Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), Caster::PREFIX_VIRTUAL.'options' => new EnumStub( self::getNonDefaultOptions($c) ), ]; return $a; } private static function getNonDefaultOptions(\Memcached $c): array { self::$defaultOptions = self::$defaultOptions ?? self::discoverDefaultOptions(); self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); $nonDefaultOptions = []; foreach (self::$optionConstants as $constantKey => $value) { if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { $nonDefaultOptions[$constantKey] = $option; } } return $nonDefaultOptions; } private static function discoverDefaultOptions(): array { $defaultMemcached = new \Memcached(); $defaultMemcached->addServer('127.0.0.1', 11211); $defaultOptions = []; self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); foreach (self::$optionConstants as $constantKey => $value) { $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); } return $defaultOptions; } private static function getOptionConstants(): array { $reflectedMemcached = new \ReflectionClass(\Memcached::class); $optionConstants = []; foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { if (str_starts_with($constantKey, 'OPT_')) { $optionConstants[$constantKey] = $value; } } return $optionConstants; } } PK2A#]{ ���.vendor/symfony/var-dumper/Caster/SplCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts SPL related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class SplCaster { private const SPL_FILE_OBJECT_FLAGS = [ \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', \SplFileObject::READ_AHEAD => 'READ_AHEAD', \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', \SplFileObject::READ_CSV => 'READ_CSV', ]; public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested) { return self::castSplArray($c, $a, $stub, $isNested); } public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested) { return self::castSplArray($c, $a, $stub, $isNested); } public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested) { $a += [ Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), ]; return $a; } public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested) { $prefix = Caster::PREFIX_VIRTUAL; $mode = $c->getIteratorMode(); $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); $a += [ $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), $prefix.'dllist' => iterator_to_array($c), ]; $c->setIteratorMode($mode); return $a; } public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested) { static $map = [ 'path' => 'getPath', 'filename' => 'getFilename', 'basename' => 'getBasename', 'pathname' => 'getPathname', 'extension' => 'getExtension', 'realPath' => 'getRealPath', 'aTime' => 'getATime', 'mTime' => 'getMTime', 'cTime' => 'getCTime', 'inode' => 'getInode', 'size' => 'getSize', 'perms' => 'getPerms', 'owner' => 'getOwner', 'group' => 'getGroup', 'type' => 'getType', 'writable' => 'isWritable', 'readable' => 'isReadable', 'executable' => 'isExecutable', 'file' => 'isFile', 'dir' => 'isDir', 'link' => 'isLink', 'linkTarget' => 'getLinkTarget', ]; $prefix = Caster::PREFIX_VIRTUAL; unset($a["\0SplFileInfo\0fileName"]); unset($a["\0SplFileInfo\0pathName"]); if (\PHP_VERSION_ID < 80000) { if (false === $c->getPathname()) { $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } } else { try { $c->isReadable(); } catch (\RuntimeException $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } catch (\Error $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } } foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception $e) { } } if ($a[$prefix.'realPath'] ?? false) { $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); } if (isset($a[$prefix.'perms'])) { $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); } static $mapDate = ['aTime', 'mTime', 'cTime']; foreach ($mapDate as $key) { if (isset($a[$prefix.$key])) { $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); } } return $a; } public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested) { static $map = [ 'csvControl' => 'getCsvControl', 'flags' => 'getFlags', 'maxLineLen' => 'getMaxLineLen', 'fstat' => 'fstat', 'eof' => 'eof', 'key' => 'key', ]; $prefix = Caster::PREFIX_VIRTUAL; foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception $e) { } } if (isset($a[$prefix.'flags'])) { $flagsArray = []; foreach (self::SPL_FILE_OBJECT_FLAGS as $value => $name) { if ($a[$prefix.'flags'] & $value) { $flagsArray[] = $name; } } $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); } if (isset($a[$prefix.'fstat'])) { $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); } return $a; } public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested) { $storage = []; unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 unset($a["\0SplObjectStorage\0storage"]); $clone = clone $c; foreach ($clone as $obj) { $storage[] = [ 'object' => $obj, 'info' => $clone->getInfo(), ]; } $a += [ Caster::PREFIX_VIRTUAL.'storage' => $storage, ]; return $a; } public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); return $a; } public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested) { $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); return $a; } private static function castSplArray($c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $flags = $c->getFlags(); if (!($flags & \ArrayObject::STD_PROP_LIST)) { $c->setFlags(\ArrayObject::STD_PROP_LIST); $a = Caster::castObject($c, \get_class($c), method_exists($c, '__debugInfo'), $stub->class); $c->setFlags($flags); } unset($a["\0ArrayObject\0storage"], $a["\0ArrayIterator\0storage"]); $a += [ $prefix.'storage' => $c->getArrayCopy(), $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), ]; if ($c instanceof \ArrayObject) { $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); } return $a; } } PK2A#]?�4ˈ�3vendor/symfony/var-dumper/Caster/DoctrineCaster.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Doctrine\Common\Proxy\Proxy as CommonProxy; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Proxy\Proxy as OrmProxy; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Doctrine related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class DoctrineCaster { public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested) { foreach (['__cloner__', '__initializer__'] as $k) { if (\array_key_exists($k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested) { foreach (['_entityPersister', '_identifier'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested) { foreach (['snapshot', 'association', 'typeClass'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { $a[$k] = new CutStub($a[$k]); } } return $a; } } PK2A#]�U}���.vendor/symfony/var-dumper/Caster/ConstStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a PHP constant and its value. * * @author Nicolas Grekas <p@tchwork.com> */ class ConstStub extends Stub { public function __construct(string $name, $value = null) { $this->class = $name; $this->value = 1 < \func_num_args() ? $value : $name; } /** * @return string */ public function __toString() { return (string) $this->value; } } PK2A#]�E�^ee/vendor/symfony/var-dumper/Caster/DsPairStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> */ class DsPairStub extends Stub { public function __construct($key, $value) { $this->value = [ Caster::PREFIX_VIRTUAL.'key' => $key, Caster::PREFIX_VIRTUAL.'value' => $value, ]; } } PK2A#]�e ��.vendor/symfony/var-dumper/Caster/TraceStub.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas <p@tchwork.com> */ class TraceStub extends Stub { public $keepArgs; public $sliceOffset; public $sliceLength; public $numberingOffset; public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0) { $this->value = $trace; $this->keepArgs = $keepArgs; $this->sliceOffset = $sliceOffset; $this->sliceLength = $sliceLength; $this->numberingOffset = $numberingOffset; } } PK2A#]��fo��?vendor/symfony/var-dumper/Exception/ThrowingCasterException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Exception; /** * @author Nicolas Grekas <p@tchwork.com> */ class ThrowingCasterException extends \Exception { /** * @param \Throwable $prev The exception thrown from the caster */ public function __construct(\Throwable $prev) { parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev); } } PK2A#]J�<bb'vendor/symfony/polyfill-php73/Php73.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php73; /** * @author Gabriel Caruso <carusogabriel34@gmail.com> * @author Ion Bazan <ion.bazan@gmail.com> * * @internal */ final class Php73 { public static $startAt = 1533462603; /** * @param bool $asNum * * @return array|float|int */ public static function hrtime($asNum = false) { $ns = microtime(false); $s = substr($ns, 11) - self::$startAt; $ns = 1E9 * (float) $ns; if ($asNum) { $ns += $s * 1E9; return \PHP_INT_SIZE === 4 ? $ns : (int) $ns; } return [$s, (int) $ns]; } } PK2A#]���,,%vendor/symfony/polyfill-php73/LICENSEnu�[���Copyright (c) 2018-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]8S��EE?vendor/symfony/polyfill-php73/Resources/stubs/JsonException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 70300) { class JsonException extends Exception { } } PK2A#]��|��+vendor/symfony/polyfill-php73/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php73 as p; if (\PHP_VERSION_ID >= 70300) { return; } if (!function_exists('is_countable')) { function is_countable($value) { return is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXmlElement; } } if (!function_exists('hrtime')) { require_once __DIR__.'/Php73.php'; p\Php73::$startAt = (int) microtime(true); function hrtime($as_number = false) { return p\Php73::hrtime($as_number); } } if (!function_exists('array_key_first')) { function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } } } if (!function_exists('array_key_last')) { function array_key_last(array $array) { return key(array_slice($array, -1, 1, true)); } } PK2A#]y�̻44Evendor/symfony/options-resolver/Exception/InvalidOptionsException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Thrown when the value of an option does not match its validation rules. * * You should make sure a valid value is passed to the option. * * @author Bernhard Schussek <bschussek@gmail.com> */ class InvalidOptionsException extends InvalidArgumentException { } PK2A#]�䐅??Cvendor/symfony/options-resolver/Exception/NoSuchOptionException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Thrown when trying to read an option that has no value set. * * When accessing optional options from within a lazy option or normalizer you should first * check whether the optional option is set. You can do this with `isset($options['optional'])`. * In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can * occur when evaluating lazy options. * * @author Tobias Schultze <http://tobion.de> */ class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface { } PK2A#]��1���Fvendor/symfony/options-resolver/Exception/InvalidArgumentException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Thrown when an argument is invalid. * * @author Bernhard Schussek <bschussek@gmail.com> */ class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } PK2A#]�,�Evendor/symfony/options-resolver/Exception/MissingOptionsException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Exception thrown when a required option is missing. * * Add the option to the passed options array. * * @author Bernhard Schussek <bschussek@gmail.com> */ class MissingOptionsException extends InvalidArgumentException { } PK2A#]�F����@vendor/symfony/options-resolver/Exception/ExceptionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Marker interface for all exceptions thrown by the OptionsResolver component. * * @author Bernhard Schussek <bschussek@gmail.com> */ interface ExceptionInterface extends \Throwable { } PK2A#]��c��Gvendor/symfony/options-resolver/Exception/OptionDefinitionException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Thrown when two lazy options have a cyclic dependency. * * @author Bernhard Schussek <bschussek@gmail.com> */ class OptionDefinitionException extends \LogicException implements ExceptionInterface { } PK2A#]��>>Gvendor/symfony/options-resolver/Exception/UndefinedOptionsException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Exception thrown when an undefined option is passed. * * You should remove the options in question from your code or define them * beforehand. * * @author Bernhard Schussek <bschussek@gmail.com> */ class UndefinedOptionsException extends InvalidArgumentException { } PK2A#]N+�??=vendor/symfony/options-resolver/Exception/AccessException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; /** * Thrown when trying to read an option outside of or write it inside of * {@link \Symfony\Component\OptionsResolver\Options::resolve()}. * * @author Bernhard Schussek <bschussek@gmail.com> */ class AccessException extends \LogicException implements ExceptionInterface { } PK2A#]Y��>��Fvendor/symfony/options-resolver/Exception/NoConfigurationException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Exception; use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector; /** * Thrown when trying to introspect an option definition property * for which no value was configured inside the OptionsResolver instance. * * @see OptionsResolverIntrospector * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class NoConfigurationException extends \RuntimeException implements ExceptionInterface { } PK2A#]����+vendor/symfony/options-resolver/Options.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver; /** * Contains resolved option values. * * @author Bernhard Schussek <bschussek@gmail.com> * @author Tobias Schultze <http://tobion.de> */ interface Options extends \ArrayAccess, \Countable { } PK2A#]���U,,'vendor/symfony/options-resolver/LICENSEnu�[���Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]��Ig�g�3vendor/symfony/options-resolver/OptionsResolver.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver; use Symfony\Component\OptionsResolver\Exception\AccessException; use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\OptionsResolver\Exception\MissingOptionsException; use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException; use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; /** * Validates options and merges them with default values. * * @author Bernhard Schussek <bschussek@gmail.com> * @author Tobias Schultze <http://tobion.de> */ class OptionsResolver implements Options { private const VALIDATION_FUNCTIONS = [ 'bool' => 'is_bool', 'boolean' => 'is_bool', 'int' => 'is_int', 'integer' => 'is_int', 'long' => 'is_int', 'float' => 'is_float', 'double' => 'is_float', 'real' => 'is_float', 'numeric' => 'is_numeric', 'string' => 'is_string', 'scalar' => 'is_scalar', 'array' => 'is_array', 'iterable' => 'is_iterable', 'countable' => 'is_countable', 'callable' => 'is_callable', 'object' => 'is_object', 'resource' => 'is_resource', ]; /** * The names of all defined options. */ private $defined = []; /** * The default option values. */ private $defaults = []; /** * A list of closure for nested options. * * @var \Closure[][] */ private $nested = []; /** * The names of required options. */ private $required = []; /** * The resolved option values. */ private $resolved = []; /** * A list of normalizer closures. * * @var \Closure[][] */ private $normalizers = []; /** * A list of accepted values for each option. */ private $allowedValues = []; /** * A list of accepted types for each option. */ private $allowedTypes = []; /** * A list of info messages for each option. */ private $info = []; /** * A list of closures for evaluating lazy options. */ private $lazy = []; /** * A list of lazy options whose closure is currently being called. * * This list helps detecting circular dependencies between lazy options. */ private $calling = []; /** * A list of deprecated options. */ private $deprecated = []; /** * The list of options provided by the user. */ private $given = []; /** * Whether the instance is locked for reading. * * Once locked, the options cannot be changed anymore. This is * necessary in order to avoid inconsistencies during the resolving * process. If any option is changed after being read, all evaluated * lazy options that depend on this option would become invalid. */ private $locked = false; private $parentsOptions = []; /** * Whether the whole options definition is marked as array prototype. */ private $prototype; /** * The prototype array's index that is being read. */ private $prototypeIndex; /** * Sets the default value of a given option. * * If the default value should be set based on other options, you can pass * a closure with the following signature: * * function (Options $options) { * // ... * } * * The closure will be evaluated when {@link resolve()} is called. The * closure has access to the resolved values of other options through the * passed {@link Options} instance: * * function (Options $options) { * if (isset($options['port'])) { * // ... * } * } * * If you want to access the previously set default value, add a second * argument to the closure's signature: * * $options->setDefault('name', 'Default Name'); * * $options->setDefault('name', function (Options $options, $previousValue) { * // 'Default Name' === $previousValue * }); * * This is mostly useful if the configuration of the {@link Options} object * is spread across different locations of your code, such as base and * sub-classes. * * If you want to define nested options, you can pass a closure with the * following signature: * * $options->setDefault('database', function (OptionsResolver $resolver) { * $resolver->setDefined(['dbname', 'host', 'port', 'user', 'pass']); * } * * To get access to the parent options, add a second argument to the closure's * signature: * * function (OptionsResolver $resolver, Options $parent) { * // 'default' === $parent['connection'] * } * * @param string $option The name of the option * @param mixed $value The default value of the option * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function setDefault(string $option, $value) { // Setting is not possible once resolving starts, because then lazy // options could manipulate the state of the object, leading to // inconsistent results. if ($this->locked) { throw new AccessException('Default values cannot be set from a lazy option or normalizer.'); } // If an option is a closure that should be evaluated lazily, store it // in the "lazy" property. if ($value instanceof \Closure) { $reflClosure = new \ReflectionFunction($value); $params = $reflClosure->getParameters(); if (isset($params[0]) && Options::class === $this->getParameterClassName($params[0])) { // Initialize the option if no previous value exists if (!isset($this->defaults[$option])) { $this->defaults[$option] = null; } // Ignore previous lazy options if the closure has no second parameter if (!isset($this->lazy[$option]) || !isset($params[1])) { $this->lazy[$option] = []; } // Store closure for later evaluation $this->lazy[$option][] = $value; $this->defined[$option] = true; // Make sure the option is processed and is not nested anymore unset($this->resolved[$option], $this->nested[$option]); return $this; } if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (($type = $params[1]->getType()) instanceof \ReflectionNamedType && Options::class === $type->getName()))) { // Store closure for later evaluation $this->nested[$option][] = $value; $this->defaults[$option] = []; $this->defined[$option] = true; // Make sure the option is processed and is not lazy anymore unset($this->resolved[$option], $this->lazy[$option]); return $this; } } // This option is not lazy nor nested anymore unset($this->lazy[$option], $this->nested[$option]); // Yet undefined options can be marked as resolved, because we only need // to resolve options with lazy closures, normalizers or validation // rules, none of which can exist for undefined options // If the option was resolved before, update the resolved value if (!isset($this->defined[$option]) || \array_key_exists($option, $this->resolved)) { $this->resolved[$option] = $value; } $this->defaults[$option] = $value; $this->defined[$option] = true; return $this; } /** * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function setDefaults(array $defaults) { foreach ($defaults as $option => $value) { $this->setDefault($option, $value); } return $this; } /** * Returns whether a default value is set for an option. * * Returns true if {@link setDefault()} was called for this option. * An option is also considered set if it was set to null. * * @return bool */ public function hasDefault(string $option) { return \array_key_exists($option, $this->defaults); } /** * Marks one or more options as required. * * @param string|string[] $optionNames One or more option names * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function setRequired($optionNames) { if ($this->locked) { throw new AccessException('Options cannot be made required from a lazy option or normalizer.'); } foreach ((array) $optionNames as $option) { $this->defined[$option] = true; $this->required[$option] = true; } return $this; } /** * Returns whether an option is required. * * An option is required if it was passed to {@link setRequired()}. * * @return bool */ public function isRequired(string $option) { return isset($this->required[$option]); } /** * Returns the names of all required options. * * @return string[] * * @see isRequired() */ public function getRequiredOptions() { return array_keys($this->required); } /** * Returns whether an option is missing a default value. * * An option is missing if it was passed to {@link setRequired()}, but not * to {@link setDefault()}. This option must be passed explicitly to * {@link resolve()}, otherwise an exception will be thrown. * * @return bool */ public function isMissing(string $option) { return isset($this->required[$option]) && !\array_key_exists($option, $this->defaults); } /** * Returns the names of all options missing a default value. * * @return string[] */ public function getMissingOptions() { return array_keys(array_diff_key($this->required, $this->defaults)); } /** * Defines a valid option name. * * Defines an option name without setting a default value. The option will * be accepted when passed to {@link resolve()}. When not passed, the * option will not be included in the resolved options. * * @param string|string[] $optionNames One or more option names * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function setDefined($optionNames) { if ($this->locked) { throw new AccessException('Options cannot be defined from a lazy option or normalizer.'); } foreach ((array) $optionNames as $option) { $this->defined[$option] = true; } return $this; } /** * Returns whether an option is defined. * * Returns true for any option passed to {@link setDefault()}, * {@link setRequired()} or {@link setDefined()}. * * @return bool */ public function isDefined(string $option) { return isset($this->defined[$option]); } /** * Returns the names of all defined options. * * @return string[] * * @see isDefined() */ public function getDefinedOptions() { return array_keys($this->defined); } public function isNested(string $option): bool { return isset($this->nested[$option]); } /** * Deprecates an option, allowed types or values. * * Instead of passing the message, you may also pass a closure with the * following signature: * * function (Options $options, $value): string { * // ... * } * * The closure receives the value as argument and should return a string. * Return an empty string to ignore the option deprecation. * * The closure is invoked when {@link resolve()} is called. The parameter * passed to the closure is the value of the option after validating it * and before normalizing it. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string|\Closure $message The deprecation message to use * * @return $this */ public function setDeprecated(string $option/* , string $package, string $version, $message = 'The option "%name%" is deprecated.' */): self { if ($this->locked) { throw new AccessException('Options cannot be deprecated from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } $args = \func_get_args(); if (\func_num_args() < 3) { trigger_deprecation('symfony/options-resolver', '5.1', 'The signature of method "%s()" requires 2 new arguments: "string $package, string $version", not defining them is deprecated.', __METHOD__); $message = $args[1] ?? 'The option "%name%" is deprecated.'; $package = $version = ''; } else { $package = $args[1]; $version = $args[2]; $message = $args[3] ?? 'The option "%name%" is deprecated.'; } if (!\is_string($message) && !$message instanceof \Closure) { throw new InvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', get_debug_type($message))); } // ignore if empty string if ('' === $message) { return $this; } $this->deprecated[$option] = [ 'package' => $package, 'version' => $version, 'message' => $message, ]; // Make sure the option is processed unset($this->resolved[$option]); return $this; } public function isDeprecated(string $option): bool { return isset($this->deprecated[$option]); } /** * Sets the normalizer for an option. * * The normalizer should be a closure with the following signature: * * function (Options $options, $value) { * // ... * } * * The closure is invoked when {@link resolve()} is called. The closure * has access to the resolved values of other options through the passed * {@link Options} instance. * * The second parameter passed to the closure is the value of * the option. * * The resolved option value is set to the return value of the closure. * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function setNormalizer(string $option, \Closure $normalizer) { if ($this->locked) { throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } $this->normalizers[$option] = [$normalizer]; // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Adds a normalizer for an option. * * The normalizer should be a closure with the following signature: * * function (Options $options, $value): mixed { * // ... * } * * The closure is invoked when {@link resolve()} is called. The closure * has access to the resolved values of other options through the passed * {@link Options} instance. * * The second parameter passed to the closure is the value of * the option. * * The resolved option value is set to the return value of the closure. * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function addNormalizer(string $option, \Closure $normalizer, bool $forcePrepend = false): self { if ($this->locked) { throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } if ($forcePrepend) { $this->normalizers[$option] = $this->normalizers[$option] ?? []; array_unshift($this->normalizers[$option], $normalizer); } else { $this->normalizers[$option][] = $normalizer; } // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Sets allowed values for an option. * * Instead of passing values, you may also pass a closures with the * following signature: * * function ($value) { * // return true or false * } * * The closure receives the value as argument and should return true to * accept the value and false to reject the value. * * @param string $option The option name * @param mixed $allowedValues One or more acceptable values/closures * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function setAllowedValues(string $option, $allowedValues) { if ($this->locked) { throw new AccessException('Allowed values cannot be set from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : [$allowedValues]; // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Adds allowed values for an option. * * The values are merged with the allowed values defined previously. * * Instead of passing values, you may also pass a closures with the * following signature: * * function ($value) { * // return true or false * } * * The closure receives the value as argument and should return true to * accept the value and false to reject the value. * * @param string $option The option name * @param mixed $allowedValues One or more acceptable values/closures * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function addAllowedValues(string $option, $allowedValues) { if ($this->locked) { throw new AccessException('Allowed values cannot be added from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } if (!\is_array($allowedValues)) { $allowedValues = [$allowedValues]; } if (!isset($this->allowedValues[$option])) { $this->allowedValues[$option] = $allowedValues; } else { $this->allowedValues[$option] = array_merge($this->allowedValues[$option], $allowedValues); } // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Sets allowed types for an option. * * Any type for which a corresponding is_<type>() function exists is * acceptable. Additionally, fully-qualified class or interface names may * be passed. * * @param string|string[] $allowedTypes One or more accepted types * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function setAllowedTypes(string $option, $allowedTypes) { if ($this->locked) { throw new AccessException('Allowed types cannot be set from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } $this->allowedTypes[$option] = (array) $allowedTypes; // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Adds allowed types for an option. * * The types are merged with the allowed types defined previously. * * Any type for which a corresponding is_<type>() function exists is * acceptable. Additionally, fully-qualified class or interface names may * be passed. * * @param string|string[] $allowedTypes One or more accepted types * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function addAllowedTypes(string $option, $allowedTypes) { if ($this->locked) { throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } if (!isset($this->allowedTypes[$option])) { $this->allowedTypes[$option] = (array) $allowedTypes; } else { $this->allowedTypes[$option] = array_merge($this->allowedTypes[$option], (array) $allowedTypes); } // Make sure the option is processed unset($this->resolved[$option]); return $this; } /** * Defines an option configurator with the given name. */ public function define(string $option): OptionConfigurator { if (isset($this->defined[$option])) { throw new OptionDefinitionException(sprintf('The option "%s" is already defined.', $option)); } return new OptionConfigurator($option, $this); } /** * Sets an info message for an option. * * @return $this * * @throws UndefinedOptionsException If the option is undefined * @throws AccessException If called from a lazy option or normalizer */ public function setInfo(string $option, string $info): self { if ($this->locked) { throw new AccessException('The Info message cannot be set from a lazy option or normalizer.'); } if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } $this->info[$option] = $info; return $this; } /** * Gets the info message for an option. */ public function getInfo(string $option): ?string { if (!isset($this->defined[$option])) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } return $this->info[$option] ?? null; } /** * Marks the whole options definition as array prototype. * * @return $this * * @throws AccessException If called from a lazy option, a normalizer or a root definition */ public function setPrototype(bool $prototype): self { if ($this->locked) { throw new AccessException('The prototype property cannot be set from a lazy option or normalizer.'); } if (null === $this->prototype && $prototype) { throw new AccessException('The prototype property cannot be set from a root definition.'); } $this->prototype = $prototype; return $this; } public function isPrototype(): bool { return $this->prototype ?? false; } /** * Removes the option with the given name. * * Undefined options are ignored. * * @param string|string[] $optionNames One or more option names * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function remove($optionNames) { if ($this->locked) { throw new AccessException('Options cannot be removed from a lazy option or normalizer.'); } foreach ((array) $optionNames as $option) { unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]); unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option], $this->info[$option]); } return $this; } /** * Removes all options. * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function clear() { if ($this->locked) { throw new AccessException('Options cannot be cleared from a lazy option or normalizer.'); } $this->defined = []; $this->defaults = []; $this->nested = []; $this->required = []; $this->resolved = []; $this->lazy = []; $this->normalizers = []; $this->allowedTypes = []; $this->allowedValues = []; $this->deprecated = []; $this->info = []; return $this; } /** * Merges options with the default values stored in the container and * validates them. * * Exceptions are thrown if: * * - Undefined options are passed; * - Required options are missing; * - Options have invalid types; * - Options have invalid values. * * @return array * * @throws UndefinedOptionsException If an option name is undefined * @throws InvalidOptionsException If an option doesn't fulfill the * specified validation rules * @throws MissingOptionsException If a required option is missing * @throws OptionDefinitionException If there is a cyclic dependency between * lazy options and/or normalizers * @throws NoSuchOptionException If a lazy option reads an unavailable option * @throws AccessException If called from a lazy option or normalizer */ public function resolve(array $options = []) { if ($this->locked) { throw new AccessException('Options cannot be resolved from a lazy option or normalizer.'); } // Allow this method to be called multiple times $clone = clone $this; // Make sure that no unknown options are passed $diff = array_diff_key($options, $clone->defined); if (\count($diff) > 0) { ksort($clone->defined); ksort($diff); throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', $this->formatOptions(array_keys($diff)), implode('", "', array_keys($clone->defined)))); } // Override options set by the user foreach ($options as $option => $value) { $clone->given[$option] = true; $clone->defaults[$option] = $value; unset($clone->resolved[$option], $clone->lazy[$option]); } // Check whether any required option is missing $diff = array_diff_key($clone->required, $clone->defaults); if (\count($diff) > 0) { ksort($diff); throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', $this->formatOptions(array_keys($diff)))); } // Lock the container $clone->locked = true; // Now process the individual options. Use offsetGet(), which resolves // the option itself and any options that the option depends on foreach ($clone->defaults as $option => $_) { $clone->offsetGet($option); } return $clone->resolved; } /** * Returns the resolved value of an option. * * @param bool $triggerDeprecation Whether to trigger the deprecation or not (true by default) * * @return mixed * * @throws AccessException If accessing this method outside of * {@link resolve()} * @throws NoSuchOptionException If the option is not set * @throws InvalidOptionsException If the option doesn't fulfill the * specified validation rules * @throws OptionDefinitionException If there is a cyclic dependency between * lazy options and/or normalizers */ #[\ReturnTypeWillChange] public function offsetGet($option, bool $triggerDeprecation = true) { if (!$this->locked) { throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); } // Shortcut for resolved options if (isset($this->resolved[$option]) || \array_key_exists($option, $this->resolved)) { if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option]['message'])) { trigger_deprecation($this->deprecated[$option]['package'], $this->deprecated[$option]['version'], strtr($this->deprecated[$option]['message'], ['%name%' => $option])); } return $this->resolved[$option]; } // Check whether the option is set at all if (!isset($this->defaults[$option]) && !\array_key_exists($option, $this->defaults)) { if (!isset($this->defined[$option])) { throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $this->formatOptions([$option]))); } $value = $this->defaults[$option]; // Resolve the option if it is a nested definition if (isset($this->nested[$option])) { // If the closure is already being called, we have a cyclic dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } if (!\is_array($value)) { throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), get_debug_type($value))); } // The following section must be protected from cyclic calls. $this->calling[$option] = true; try { $resolver = new self(); $resolver->prototype = false; $resolver->parentsOptions = $this->parentsOptions; $resolver->parentsOptions[] = $option; foreach ($this->nested[$option] as $closure) { $closure($resolver, $this); } if ($resolver->prototype) { $values = []; foreach ($value as $index => $prototypeValue) { if (!\is_array($prototypeValue)) { throw new InvalidOptionsException(sprintf('The value of the option "%s" is expected to be of type array of array, but is of type array of "%s".', $this->formatOptions([$option]), get_debug_type($prototypeValue))); } $resolver->prototypeIndex = $index; $values[$index] = $resolver->resolve($prototypeValue); } $value = $values; } else { $value = $resolver->resolve($value); } } finally { $resolver->prototypeIndex = null; unset($this->calling[$option]); } } // Resolve the option if the default value is lazily evaluated if (isset($this->lazy[$option])) { // If the closure is already being called, we have a cyclic // dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } // The following section must be protected from cyclic // calls. Set $calling for the current $option to detect a cyclic // dependency // BEGIN $this->calling[$option] = true; try { foreach ($this->lazy[$option] as $closure) { $value = $closure($this, $value); } } finally { unset($this->calling[$option]); } // END } // Validate the type of the resolved option if (isset($this->allowedTypes[$option])) { $valid = true; $invalidTypes = []; foreach ($this->allowedTypes[$option] as $type) { if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) { break; } } if (!$valid) { $fmtActualValue = $this->formatValue($value); $fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]); $fmtProvidedTypes = implode('|', array_keys($invalidTypes)); $allowedContainsArrayType = \count(array_filter($this->allowedTypes[$option], static function ($item) { return str_ends_with($item, '[]'); })) > 0; if (\is_array($value) && $allowedContainsArrayType) { throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } } // Validate the value of the resolved option if (isset($this->allowedValues[$option])) { $success = false; $printableAllowedValues = []; foreach ($this->allowedValues[$option] as $allowedValue) { if ($allowedValue instanceof \Closure) { if ($allowedValue($value)) { $success = true; break; } // Don't include closures in the exception message continue; } if ($value === $allowedValue) { $success = true; break; } $printableAllowedValues[] = $allowedValue; } if (!$success) { $message = sprintf( 'The option "%s" with value %s is invalid.', $option, $this->formatValue($value) ); if (\count($printableAllowedValues) > 0) { $message .= sprintf( ' Accepted values are: %s.', $this->formatValues($printableAllowedValues) ); } if (isset($this->info[$option])) { $message .= sprintf(' Info: %s.', $this->info[$option]); } throw new InvalidOptionsException($message); } } // Check whether the option is deprecated // and it is provided by the user or is being called from a lazy evaluation if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || ($this->calling && \is_string($this->deprecated[$option]['message'])))) { $deprecation = $this->deprecated[$option]; $message = $this->deprecated[$option]['message']; if ($message instanceof \Closure) { // If the closure is already being called, we have a cyclic dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } $this->calling[$option] = true; try { if (!\is_string($message = $message($this, $value))) { throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', get_debug_type($message))); } } finally { unset($this->calling[$option]); } } if ('' !== $message) { trigger_deprecation($deprecation['package'], $deprecation['version'], strtr($message, ['%name%' => $option])); } } // Normalize the validated option if (isset($this->normalizers[$option])) { // If the closure is already being called, we have a cyclic // dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } // The following section must be protected from cyclic // calls. Set $calling for the current $option to detect a cyclic // dependency // BEGIN $this->calling[$option] = true; try { foreach ($this->normalizers[$option] as $normalizer) { $value = $normalizer($this, $value); } } finally { unset($this->calling[$option]); } // END } // Mark as resolved $this->resolved[$option] = $value; return $value; } private function verifyTypes(string $type, $value, array &$invalidTypes, int $level = 0): bool { if (\is_array($value) && '[]' === substr($type, -2)) { $type = substr($type, 0, -2); $valid = true; foreach ($value as $val) { if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) { $valid = false; } } return $valid; } if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) { return true; } if (!$invalidTypes || $level > 0) { $invalidTypes[get_debug_type($value)] = true; } return false; } /** * Returns whether a resolved option with the given name exists. * * @param string $option The option name * * @return bool * * @throws AccessException If accessing this method outside of {@link resolve()} * * @see \ArrayAccess::offsetExists() */ #[\ReturnTypeWillChange] public function offsetExists($option) { if (!$this->locked) { throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); } return \array_key_exists($option, $this->defaults); } /** * Not supported. * * @return void * * @throws AccessException */ #[\ReturnTypeWillChange] public function offsetSet($option, $value) { throw new AccessException('Setting options via array access is not supported. Use setDefault() instead.'); } /** * Not supported. * * @return void * * @throws AccessException */ #[\ReturnTypeWillChange] public function offsetUnset($option) { throw new AccessException('Removing options via array access is not supported. Use remove() instead.'); } /** * Returns the number of set options. * * This may be only a subset of the defined options. * * @return int * * @throws AccessException If accessing this method outside of {@link resolve()} * * @see \Countable::count() */ #[\ReturnTypeWillChange] public function count() { if (!$this->locked) { throw new AccessException('Counting is only supported within closures of lazy options and normalizers.'); } return \count($this->defaults); } /** * Returns a string representation of the value. * * This method returns the equivalent PHP tokens for most scalar types * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped * in double quotes ("). * * @param mixed $value The value to format as string */ private function formatValue($value): string { if (\is_object($value)) { return \get_class($value); } if (\is_array($value)) { return 'array'; } if (\is_string($value)) { return '"'.$value.'"'; } if (\is_resource($value)) { return 'resource'; } if (null === $value) { return 'null'; } if (false === $value) { return 'false'; } if (true === $value) { return 'true'; } return (string) $value; } /** * Returns a string representation of a list of values. * * Each of the values is converted to a string using * {@link formatValue()}. The values are then concatenated with commas. * * @see formatValue() */ private function formatValues(array $values): string { foreach ($values as $key => $value) { $values[$key] = $this->formatValue($value); } return implode(', ', $values); } private function formatOptions(array $options): string { if ($this->parentsOptions) { $prefix = array_shift($this->parentsOptions); if ($this->parentsOptions) { $prefix .= sprintf('[%s]', implode('][', $this->parentsOptions)); } if ($this->prototype && null !== $this->prototypeIndex) { $prefix .= sprintf('[%s]', $this->prototypeIndex); } $options = array_map(static function (string $option) use ($prefix): string { return sprintf('%s[%s]', $prefix, $option); }, $options); } return implode('", "', $options); } private function getParameterClassName(\ReflectionParameter $parameter): ?string { if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) { return null; } return $type->getName(); } } PK2A#]w��f f 6vendor/symfony/options-resolver/OptionConfigurator.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver; use Symfony\Component\OptionsResolver\Exception\AccessException; final class OptionConfigurator { private $name; private $resolver; public function __construct(string $name, OptionsResolver $resolver) { $this->name = $name; $this->resolver = $resolver; $this->resolver->setDefined($name); } /** * Adds allowed types for this option. * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function allowedTypes(string ...$types): self { $this->resolver->setAllowedTypes($this->name, $types); return $this; } /** * Sets allowed values for this option. * * @param mixed ...$values One or more acceptable values/closures * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function allowedValues(...$values): self { $this->resolver->setAllowedValues($this->name, $values); return $this; } /** * Sets the default value for this option. * * @param mixed $value The default value of the option * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function default($value): self { $this->resolver->setDefault($this->name, $value); return $this; } /** * Defines an option configurator with the given name. */ public function define(string $option): self { return $this->resolver->define($option); } /** * Marks this option as deprecated. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string|\Closure $message The deprecation message to use * * @return $this */ public function deprecated(string $package, string $version, $message = 'The option "%name%" is deprecated.'): self { $this->resolver->setDeprecated($this->name, $package, $version, $message); return $this; } /** * Sets the normalizer for this option. * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function normalize(\Closure $normalizer): self { $this->resolver->setNormalizer($this->name, $normalizer); return $this; } /** * Marks this option as required. * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function required(): self { $this->resolver->setRequired($this->name); return $this; } /** * Sets an info message for an option. * * @return $this * * @throws AccessException If called from a lazy option or normalizer */ public function info(string $info): self { $this->resolver->setInfo($this->name, $info); return $this; } } PK2A#]���^^^Evendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\OptionsResolver\Debug; use Symfony\Component\OptionsResolver\Exception\NoConfigurationException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; use Symfony\Component\OptionsResolver\OptionsResolver; /** * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class OptionsResolverIntrospector { private $get; public function __construct(OptionsResolver $optionsResolver) { $this->get = \Closure::bind(function ($property, $option, $message) { /** @var OptionsResolver $this */ if (!$this->isDefined($option)) { throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option)); } if (!\array_key_exists($option, $this->{$property})) { throw new NoConfigurationException($message); } return $this->{$property}[$option]; }, $optionsResolver, $optionsResolver); } /** * @return mixed * * @throws NoConfigurationException on no configured value */ public function getDefault(string $option) { return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); } /** * @return \Closure[] * * @throws NoConfigurationException on no configured closures */ public function getLazyClosures(string $option): array { return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); } /** * @return string[] * * @throws NoConfigurationException on no configured types */ public function getAllowedTypes(string $option): array { return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); } /** * @return mixed[] * * @throws NoConfigurationException on no configured values */ public function getAllowedValues(string $option): array { return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); } /** * @throws NoConfigurationException on no configured normalizer */ public function getNormalizer(string $option): \Closure { return current($this->getNormalizers($option)); } /** * @throws NoConfigurationException when no normalizer is configured */ public function getNormalizers(string $option): array { return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); } /** * @return string|\Closure * * @throws NoConfigurationException on no configured deprecation * * @deprecated since Symfony 5.1, use "getDeprecation()" instead. */ public function getDeprecationMessage(string $option) { trigger_deprecation('symfony/options-resolver', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); return $this->getDeprecation($option)['message']; } /** * @throws NoConfigurationException on no configured deprecation */ public function getDeprecation(string $option): array { return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option)); } } PK2A#]3��eaXaX'vendor/symfony/polyfill-iconv/Iconv.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Iconv; /** * iconv implementation in pure PHP, UTF-8 centric. * * Implemented: * - iconv - Convert string to requested character encoding * - iconv_mime_decode - Decodes a MIME header field * - iconv_mime_decode_headers - Decodes multiple MIME header fields at once * - iconv_get_encoding - Retrieve internal configuration variables of iconv extension * - iconv_set_encoding - Set current setting for character encoding conversion * - iconv_mime_encode - Composes a MIME header field * - iconv_strlen - Returns the character count of string * - iconv_strpos - Finds position of first occurrence of a needle within a haystack * - iconv_strrpos - Finds the last occurrence of a needle within a haystack * - iconv_substr - Cut out part of a string * * Charsets available for conversion are defined by files * in the charset/ directory and by Iconv::$alias below. * You're welcome to send back any addition you make. * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Iconv { public const ERROR_ILLEGAL_CHARACTER = 'iconv(): Detected an illegal character in input string'; public const ERROR_WRONG_CHARSET = 'iconv(): Wrong charset, conversion from `%s\' to `%s\' is not allowed'; public static $inputEncoding = 'utf-8'; public static $outputEncoding = 'utf-8'; public static $internalEncoding = 'utf-8'; private static $alias = [ 'utf8' => 'utf-8', 'ascii' => 'us-ascii', 'tis-620' => 'iso-8859-11', 'cp1250' => 'windows-1250', 'cp1251' => 'windows-1251', 'cp1252' => 'windows-1252', 'cp1253' => 'windows-1253', 'cp1254' => 'windows-1254', 'cp1255' => 'windows-1255', 'cp1256' => 'windows-1256', 'cp1257' => 'windows-1257', 'cp1258' => 'windows-1258', 'shift-jis' => 'cp932', 'shift_jis' => 'cp932', 'latin1' => 'iso-8859-1', 'latin2' => 'iso-8859-2', 'latin3' => 'iso-8859-3', 'latin4' => 'iso-8859-4', 'latin5' => 'iso-8859-9', 'latin6' => 'iso-8859-10', 'latin7' => 'iso-8859-13', 'latin8' => 'iso-8859-14', 'latin9' => 'iso-8859-15', 'latin10' => 'iso-8859-16', 'iso8859-1' => 'iso-8859-1', 'iso8859-2' => 'iso-8859-2', 'iso8859-3' => 'iso-8859-3', 'iso8859-4' => 'iso-8859-4', 'iso8859-5' => 'iso-8859-5', 'iso8859-6' => 'iso-8859-6', 'iso8859-7' => 'iso-8859-7', 'iso8859-8' => 'iso-8859-8', 'iso8859-9' => 'iso-8859-9', 'iso8859-10' => 'iso-8859-10', 'iso8859-11' => 'iso-8859-11', 'iso8859-12' => 'iso-8859-12', 'iso8859-13' => 'iso-8859-13', 'iso8859-14' => 'iso-8859-14', 'iso8859-15' => 'iso-8859-15', 'iso8859-16' => 'iso-8859-16', 'iso_8859-1' => 'iso-8859-1', 'iso_8859-2' => 'iso-8859-2', 'iso_8859-3' => 'iso-8859-3', 'iso_8859-4' => 'iso-8859-4', 'iso_8859-5' => 'iso-8859-5', 'iso_8859-6' => 'iso-8859-6', 'iso_8859-7' => 'iso-8859-7', 'iso_8859-8' => 'iso-8859-8', 'iso_8859-9' => 'iso-8859-9', 'iso_8859-10' => 'iso-8859-10', 'iso_8859-11' => 'iso-8859-11', 'iso_8859-12' => 'iso-8859-12', 'iso_8859-13' => 'iso-8859-13', 'iso_8859-14' => 'iso-8859-14', 'iso_8859-15' => 'iso-8859-15', 'iso_8859-16' => 'iso-8859-16', 'iso88591' => 'iso-8859-1', 'iso88592' => 'iso-8859-2', 'iso88593' => 'iso-8859-3', 'iso88594' => 'iso-8859-4', 'iso88595' => 'iso-8859-5', 'iso88596' => 'iso-8859-6', 'iso88597' => 'iso-8859-7', 'iso88598' => 'iso-8859-8', 'iso88599' => 'iso-8859-9', 'iso885910' => 'iso-8859-10', 'iso885911' => 'iso-8859-11', 'iso885912' => 'iso-8859-12', 'iso885913' => 'iso-8859-13', 'iso885914' => 'iso-8859-14', 'iso885915' => 'iso-8859-15', 'iso885916' => 'iso-8859-16', ]; private static $translitMap = []; private static $convertMap = []; private static $errorHandler; private static $lastError; private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; private static $isValidUtf8; public static function iconv($inCharset, $outCharset, $str) { $str = (string) $str; if ('' === $str) { return ''; } // Prepare for //IGNORE and //TRANSLIT $translit = $ignore = ''; $outCharset = strtolower($outCharset); $inCharset = strtolower($inCharset); if ('' === $outCharset) { $outCharset = 'iso-8859-1'; } if ('' === $inCharset) { $inCharset = 'iso-8859-1'; } do { $loop = false; if ('//translit' === substr($outCharset, -10)) { $loop = $translit = true; $outCharset = substr($outCharset, 0, -10); } if ('//ignore' === substr($outCharset, -8)) { $loop = $ignore = true; $outCharset = substr($outCharset, 0, -8); } } while ($loop); do { $loop = false; if ('//translit' === substr($inCharset, -10)) { $loop = true; $inCharset = substr($inCharset, 0, -10); } if ('//ignore' === substr($inCharset, -8)) { $loop = true; $inCharset = substr($inCharset, 0, -8); } } while ($loop); if (isset(self::$alias[$inCharset])) { $inCharset = self::$alias[$inCharset]; } if (isset(self::$alias[$outCharset])) { $outCharset = self::$alias[$outCharset]; } // Load charset maps if (('utf-8' !== $inCharset && !self::loadMap('from.', $inCharset, $inMap)) || ('utf-8' !== $outCharset && !self::loadMap('to.', $outCharset, $outMap))) { trigger_error(sprintf(self::ERROR_WRONG_CHARSET, $inCharset, $outCharset)); return false; } if ('utf-8' !== $inCharset) { // Convert input to UTF-8 $result = ''; if (self::mapToUtf8($result, $inMap, $str, $ignore)) { $str = $result; } else { $str = false; } self::$isValidUtf8 = true; } else { self::$isValidUtf8 = preg_match('//u', $str); if (!self::$isValidUtf8 && !$ignore) { trigger_error(self::ERROR_ILLEGAL_CHARACTER); return false; } if ('utf-8' === $outCharset) { // UTF-8 validation $str = self::utf8ToUtf8($str, $ignore); } } if ('utf-8' !== $outCharset && false !== $str) { // Convert output to UTF-8 $result = ''; if (self::mapFromUtf8($result, $outMap, $str, $ignore, $translit)) { return $result; } return false; } return $str; } public static function iconv_mime_decode_headers($str, $mode = 0, $charset = null) { if (null === $charset) { $charset = self::$internalEncoding; } if (false !== strpos($str, "\r")) { $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n"); } $str = explode("\n\n", $str, 2); $headers = []; $str = preg_split('/\n(?![ \t])/', $str[0]); foreach ($str as $str) { $str = self::iconv_mime_decode($str, $mode, $charset); if (false === $str) { return false; } $str = explode(':', $str, 2); if (2 === \count($str)) { if (isset($headers[$str[0]])) { if (!\is_array($headers[$str[0]])) { $headers[$str[0]] = [$headers[$str[0]]]; } $headers[$str[0]][] = ltrim($str[1]); } else { $headers[$str[0]] = ltrim($str[1]); } } } return $headers; } public static function iconv_mime_decode($str, $mode = 0, $charset = null) { if (null === $charset) { $charset = self::$internalEncoding; } if (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) { $charset .= '//IGNORE'; } if (false !== strpos($str, "\r")) { $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n"); } $str = preg_split('/\n(?![ \t])/', rtrim($str), 2); $str = preg_replace('/[ \t]*\n[ \t]+/', ' ', rtrim($str[0])); $str = preg_split('/=\?([^?]+)\?([bqBQ])\?(.*?)\?=/', $str, -1, \PREG_SPLIT_DELIM_CAPTURE); $result = self::iconv('utf-8', $charset, $str[0]); if (false === $result) { return false; } $i = 1; $len = \count($str); while ($i < $len) { $c = strtolower($str[$i]); if ((\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) && 'utf-8' !== $c && !isset(self::$alias[$c]) && !self::loadMap('from.', $c, $d)) { $d = false; } elseif ('B' === strtoupper($str[$i + 1])) { $d = base64_decode($str[$i + 2]); } else { $d = rawurldecode(strtr(str_replace('%', '%25', $str[$i + 2]), '=_', '% ')); } if (false !== $d) { if ('' !== $d) { if ('' === $d = self::iconv($c, $charset, $d)) { $str[$i + 3] = substr($str[$i + 3], 1); } else { $result .= $d; } } $d = self::iconv('utf-8', $charset, $str[$i + 3]); if ('' !== trim($d)) { $result .= $d; } } elseif (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) { $result .= "=?{$str[$i]}?{$str[$i + 1]}?{$str[$i + 2]}?={$str[$i + 3]}"; } else { $result = false; break; } $i += 4; } return $result; } public static function iconv_get_encoding($type = 'all') { switch ($type) { case 'input_encoding': return self::$inputEncoding; case 'output_encoding': return self::$outputEncoding; case 'internal_encoding': return self::$internalEncoding; } return [ 'input_encoding' => self::$inputEncoding, 'output_encoding' => self::$outputEncoding, 'internal_encoding' => self::$internalEncoding, ]; } public static function iconv_set_encoding($type, $charset) { switch ($type) { case 'input_encoding': self::$inputEncoding = $charset; break; case 'output_encoding': self::$outputEncoding = $charset; break; case 'internal_encoding': self::$internalEncoding = $charset; break; default: return false; } return true; } public static function iconv_mime_encode($fieldName, $fieldValue, $pref = null) { if (!\is_array($pref)) { $pref = []; } $pref += [ 'scheme' => 'B', 'input-charset' => self::$internalEncoding, 'output-charset' => self::$internalEncoding, 'line-length' => 76, 'line-break-chars' => "\r\n", ]; if (preg_match('/[\x80-\xFF]/', $fieldName)) { $fieldName = ''; } $scheme = strtoupper(substr($pref['scheme'], 0, 1)); $in = strtolower($pref['input-charset']); $out = strtolower($pref['output-charset']); if ('utf-8' !== $in && false === $fieldValue = self::iconv($in, 'utf-8', $fieldValue)) { return false; } preg_match_all('/./us', $fieldValue, $chars); $chars = $chars[0] ?? []; $lineBreak = (int) $pref['line-length']; $lineStart = "=?{$pref['output-charset']}?{$scheme}?"; $lineLength = \strlen($fieldName) + 2 + \strlen($lineStart) + 2; $lineOffset = \strlen($lineStart) + 3; $lineData = ''; $fieldValue = []; $Q = 'Q' === $scheme; foreach ($chars as $c) { if ('utf-8' !== $out && false === $c = self::iconv('utf-8', $out, $c)) { return false; } $o = $Q ? $c = preg_replace_callback( '/[=_\?\x00-\x1F\x80-\xFF]/', [__CLASS__, 'qpByteCallback'], $c ) : base64_encode($lineData.$c); if (isset($o[$lineBreak - $lineLength])) { if (!$Q) { $lineData = base64_encode($lineData); } $fieldValue[] = $lineStart.$lineData.'?='; $lineLength = $lineOffset; $lineData = ''; } $lineData .= $c; $Q && $lineLength += \strlen($c); } if ('' !== $lineData) { if (!$Q) { $lineData = base64_encode($lineData); } $fieldValue[] = $lineStart.$lineData.'?='; } return $fieldName.': '.implode($pref['line-break-chars'].' ', $fieldValue); } public static function iconv_strlen($s, $encoding = null) { static $hasXml = null; if (null === $hasXml) { $hasXml = \extension_loaded('xml'); } if ($hasXml) { return self::strlen1($s, $encoding); } return self::strlen2($s, $encoding); } public static function strlen1($s, $encoding = null) { if (null === $encoding) { $encoding = self::$internalEncoding; } if (0 !== stripos($encoding, 'utf-8') && false === $s = self::iconv($encoding, 'utf-8', $s)) { return false; } return \strlen(utf8_decode($s)); } public static function strlen2($s, $encoding = null) { if (null === $encoding) { $encoding = self::$internalEncoding; } if (0 !== stripos($encoding, 'utf-8') && false === $s = self::iconv($encoding, 'utf-8', $s)) { return false; } $ulenMask = self::$ulenMask; $i = 0; $j = 0; $len = \strlen($s); while ($i < $len) { $u = $s[$i] & "\xF0"; $i += $ulenMask[$u] ?? 1; ++$j; } return $j; } public static function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null) { if (null === $encoding) { $encoding = self::$internalEncoding; } if (0 !== stripos($encoding, 'utf-8')) { if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) { return false; } if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) { return false; } } if ($offset = (int) $offset) { $haystack = self::iconv_substr($haystack, $offset, 2147483647, 'utf-8'); } $pos = strpos($haystack, $needle); return false === $pos ? false : ($offset + ($pos ? self::iconv_strlen(substr($haystack, 0, $pos), 'utf-8') : 0)); } public static function iconv_strrpos($haystack, $needle, $encoding = null) { if (null === $encoding) { $encoding = self::$internalEncoding; } if (0 !== stripos($encoding, 'utf-8')) { if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) { return false; } if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) { return false; } } $pos = isset($needle[0]) ? strrpos($haystack, $needle) : false; return false === $pos ? false : self::iconv_strlen($pos ? substr($haystack, 0, $pos) : $haystack, 'utf-8'); } public static function iconv_substr($s, $start, $length = 2147483647, $encoding = null) { if (null === $encoding) { $encoding = self::$internalEncoding; } if (0 !== stripos($encoding, 'utf-8')) { $encoding = null; } elseif (false === $s = self::iconv($encoding, 'utf-8', $s)) { return false; } $s = (string) $s; $slen = self::iconv_strlen($s, 'utf-8'); $start = (int) $start; if (0 > $start) { $start += $slen; } if (0 > $start) { if (\PHP_VERSION_ID < 80000) { return false; } $start = 0; } if ($start >= $slen) { return \PHP_VERSION_ID >= 80000 ? '' : false; } $rx = $slen - $start; if (0 > $length) { $length += $rx; } if (0 === $length) { return ''; } if (0 > $length) { return \PHP_VERSION_ID >= 80000 ? '' : false; } if ($length > $rx) { $length = $rx; } $rx = '/^'.($start ? self::pregOffset($start) : '').'('.self::pregOffset($length).')/u'; $s = preg_match($rx, $s, $s) ? $s[1] : ''; if (null === $encoding) { return $s; } return self::iconv('utf-8', $encoding, $s); } private static function loadMap($type, $charset, &$map) { if (!isset(self::$convertMap[$type.$charset])) { if (false === $map = self::getData($type.$charset)) { if ('to.' === $type && self::loadMap('from.', $charset, $map)) { $map = array_flip($map); } else { return false; } } self::$convertMap[$type.$charset] = $map; } else { $map = self::$convertMap[$type.$charset]; } return true; } private static function utf8ToUtf8($str, $ignore) { $ulenMask = self::$ulenMask; $valid = self::$isValidUtf8; $u = $str; $i = $j = 0; $len = \strlen($str); while ($i < $len) { if ($str[$i] < "\x80") { $u[$j++] = $str[$i++]; } else { $ulen = $str[$i] & "\xF0"; $ulen = $ulenMask[$ulen] ?? 1; $uchr = substr($str, $i, $ulen); if (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr))) { if ($ignore) { ++$i; continue; } trigger_error(self::ERROR_ILLEGAL_CHARACTER); return false; } $i += $ulen; $u[$j++] = $uchr[0]; isset($uchr[1]) && 0 !== ($u[$j++] = $uchr[1]) && isset($uchr[2]) && 0 !== ($u[$j++] = $uchr[2]) && isset($uchr[3]) && 0 !== ($u[$j++] = $uchr[3]); } } return substr($u, 0, $j); } private static function mapToUtf8(&$result, array $map, $str, $ignore) { $len = \strlen($str); for ($i = 0; $i < $len; ++$i) { if (isset($str[$i + 1], $map[$str[$i].$str[$i + 1]])) { $result .= $map[$str[$i].$str[++$i]]; } elseif (isset($map[$str[$i]])) { $result .= $map[$str[$i]]; } elseif (!$ignore) { trigger_error(self::ERROR_ILLEGAL_CHARACTER); return false; } } return true; } private static function mapFromUtf8(&$result, array $map, $str, $ignore, $translit) { $ulenMask = self::$ulenMask; $valid = self::$isValidUtf8; if ($translit && !self::$translitMap) { self::$translitMap = self::getData('translit'); } $i = 0; $len = \strlen($str); while ($i < $len) { if ($str[$i] < "\x80") { $uchr = $str[$i++]; } else { $ulen = $str[$i] & "\xF0"; $ulen = $ulenMask[$ulen] ?? 1; $uchr = substr($str, $i, $ulen); if ($ignore && (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr)))) { ++$i; continue; } $i += $ulen; } if (isset($map[$uchr])) { $result .= $map[$uchr]; } elseif ($translit) { if (isset(self::$translitMap[$uchr])) { $uchr = self::$translitMap[$uchr]; } elseif ($uchr >= "\xC3\x80") { $uchr = \Normalizer::normalize($uchr, \Normalizer::NFD); if ($uchr[0] < "\x80") { $uchr = $uchr[0]; } elseif ($ignore) { continue; } else { return false; } } elseif ($ignore) { continue; } else { return false; } $str = $uchr.substr($str, $i); $len = \strlen($str); $i = 0; } elseif (!$ignore) { return false; } } return true; } private static function qpByteCallback(array $m) { return '='.strtoupper(dechex(\ord($m[0]))); } private static function pregOffset($offset) { $rx = []; $offset = (int) $offset; while ($offset > 65535) { $rx[] = '.{65535}'; $offset -= 65535; } return implode('', $rx).'.{'.$offset.'}'; } private static function getData($file) { if (file_exists($file = __DIR__.'/Resources/charset/'.$file.'.php')) { return require $file; } return false; } } PK2A#]��H,,%vendor/symfony/polyfill-iconv/LICENSEnu�[���Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]|�2���-vendor/symfony/polyfill-iconv/bootstrap80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Iconv as p; if (!defined('ICONV_IMPL')) { define('ICONV_IMPL', 'Symfony'); } if (!defined('ICONV_VERSION')) { define('ICONV_VERSION', '1.0'); } if (!defined('ICONV_MIME_DECODE_STRICT')) { define('ICONV_MIME_DECODE_STRICT', 1); } if (!defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR')) { define('ICONV_MIME_DECODE_CONTINUE_ON_ERROR', 2); } if (!function_exists('iconv')) { function iconv(?string $from_encoding, ?string $to_encoding, ?string $string): string|false { return p\Iconv::iconv((string) $from_encoding, (string) $to_encoding, (string) $string); } } if (!function_exists('iconv_get_encoding')) { function iconv_get_encoding(?string $type = 'all'): array|string|false { return p\Iconv::iconv_get_encoding((string) $type); } } if (!function_exists('iconv_set_encoding')) { function iconv_set_encoding(?string $type, ?string $encoding): bool { return p\Iconv::iconv_set_encoding((string) $type, (string) $encoding); } } if (!function_exists('iconv_mime_encode')) { function iconv_mime_encode(?string $field_name, ?string $field_value, ?array $options = []): string|false { return p\Iconv::iconv_mime_encode((string) $field_name, (string) $field_value, (array) $options); } } if (!function_exists('iconv_mime_decode_headers')) { function iconv_mime_decode_headers(?string $headers, ?int $mode = 0, ?string $encoding = null): array|false { return p\Iconv::iconv_mime_decode_headers((string) $headers, (int) $mode, $encoding); } } if (extension_loaded('mbstring')) { if (!function_exists('iconv_strlen')) { function iconv_strlen(?string $string, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strlen((string) $string, $encoding); } } if (!function_exists('iconv_strpos')) { function iconv_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('iconv_strrpos')) { function iconv_strrpos(?string $haystack, ?string $needle, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strrpos((string) $haystack, (string) $needle, 0, $encoding); } } if (!function_exists('iconv_substr')) { function iconv_substr(?string $string, ?int $offset, ?int $length = null, ?string $encoding = null): string|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_substr((string) $string, (int) $offset, $length, $encoding); } } if (!function_exists('iconv_mime_decode')) { function iconv_mime_decode($string, $mode = 0, $encoding = null) { $currentMbEncoding = mb_internal_encoding(); null === $encoding && $encoding = p\Iconv::$internalEncoding; mb_internal_encoding($encoding); $decoded = mb_decode_mimeheader($string); mb_internal_encoding($currentMbEncoding); return $decoded; } } } else { if (!function_exists('iconv_strlen')) { if (extension_loaded('xml')) { function iconv_strlen(?string $string, ?string $encoding = null): int|false { return p\Iconv::strlen1((string) $string, $encoding); } } else { function iconv_strlen(?string $string, ?string $encoding = null): int|false { return p\Iconv::strlen2((string) $string, $encoding); } } } if (!function_exists('iconv_strpos')) { function iconv_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Iconv::iconv_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('iconv_strrpos')) { function iconv_strrpos(?string $haystack, ?string $needle, ?string $encoding = null): int|false { return p\Iconv::iconv_strrpos((string) $haystack, (string) $needle, $encoding); } } if (!function_exists('iconv_substr')) { function iconv_substr(?string $string, ?int $offset, ?int $length = null, ?string $encoding = null): string|false { return p\Iconv::iconv_substr((string) $string, (string) $offset, $length, $encoding); } } if (!function_exists('iconv_mime_decode')) { function iconv_mime_decode(?string $string, ?int $mode = 0, ?string $encoding = null): string|false { return p\Iconv::iconv_mime_decode((string) $string, (int) $mode, $encoding); } } } PK2A#]�Z+vendor/symfony/polyfill-iconv/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Iconv as p; if (extension_loaded('iconv')) { return; } if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!defined('ICONV_IMPL')) { define('ICONV_IMPL', 'Symfony'); } if (!defined('ICONV_VERSION')) { define('ICONV_VERSION', '1.0'); } if (!defined('ICONV_MIME_DECODE_STRICT')) { define('ICONV_MIME_DECODE_STRICT', 1); } if (!defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR')) { define('ICONV_MIME_DECODE_CONTINUE_ON_ERROR', 2); } if (!function_exists('iconv')) { function iconv($from_encoding, $to_encoding, $string) { return p\Iconv::iconv($from_encoding, $to_encoding, $string); } } if (!function_exists('iconv_get_encoding')) { function iconv_get_encoding($type = 'all') { return p\Iconv::iconv_get_encoding($type); } } if (!function_exists('iconv_set_encoding')) { function iconv_set_encoding($type, $encoding) { return p\Iconv::iconv_set_encoding($type, $encoding); } } if (!function_exists('iconv_mime_encode')) { function iconv_mime_encode($field_name, $field_value, $options = []) { return p\Iconv::iconv_mime_encode($field_name, $field_value, $options); } } if (!function_exists('iconv_mime_decode_headers')) { function iconv_mime_decode_headers($headers, $mode = 0, $encoding = null) { return p\Iconv::iconv_mime_decode_headers($headers, $mode, $encoding); } } if (extension_loaded('mbstring')) { if (!function_exists('iconv_strlen')) { function iconv_strlen($string, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strlen($string, $encoding); } } if (!function_exists('iconv_strpos')) { function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('iconv_strrpos')) { function iconv_strrpos($haystack, $needle, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strrpos($haystack, $needle, 0, $encoding); } } if (!function_exists('iconv_substr')) { function iconv_substr($string, $offset, $length = 2147483647, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_substr($string, $offset, $length, $encoding); } } if (!function_exists('iconv_mime_decode')) { function iconv_mime_decode($string, $mode = 0, $encoding = null) { $currentMbEncoding = mb_internal_encoding(); null === $encoding && $encoding = p\Iconv::$internalEncoding; mb_internal_encoding($encoding); $decoded = mb_decode_mimeheader($string); mb_internal_encoding($currentMbEncoding); return $decoded; } } } else { if (!function_exists('iconv_strlen')) { if (extension_loaded('xml')) { function iconv_strlen($string, $encoding = null) { return p\Iconv::strlen1($string, $encoding); } } else { function iconv_strlen($string, $encoding = null) { return p\Iconv::strlen2($string, $encoding); } } } if (!function_exists('iconv_strpos')) { function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Iconv::iconv_strpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('iconv_strrpos')) { function iconv_strrpos($haystack, $needle, $encoding = null) { return p\Iconv::iconv_strrpos($haystack, $needle, $encoding); } } if (!function_exists('iconv_substr')) { function iconv_substr($string, $offset, $length = 2147483647, $encoding = null) { return p\Iconv::iconv_substr($string, $offset, $length, $encoding); } } if (!function_exists('iconv_mime_decode')) { function iconv_mime_decode($string, $mode = 0, $encoding = null) { return p\Iconv::iconv_mime_decode($string, $mode, $encoding); } } } PK2A#],� Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1257.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => '‰', '�' => '‹', '�' => '¨', '�' => 'ˇ', '�' => '¸', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '™', '�' => '›', '�' => '¯', '�' => '˛', '�' => ' ', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¦', '�' => '§', '�' => 'Ø', '�' => '©', '�' => 'Ŗ', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => 'Æ', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => 'ø', '�' => '¹', '�' => 'ŗ', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => 'æ', '�' => 'Ą', '�' => 'Į', '�' => 'Ā', '�' => 'Ć', '�' => 'Ä', '�' => 'Å', '�' => 'Ę', '�' => 'Ē', '�' => 'Č', '�' => 'É', '�' => 'Ź', '�' => 'Ė', '�' => 'Ģ', '�' => 'Ķ', '�' => 'Ī', '�' => 'Ļ', '�' => 'Š', '�' => 'Ń', '�' => 'Ņ', '�' => 'Ó', '�' => 'Ō', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ų', '�' => 'Ł', '�' => 'Ś', '�' => 'Ū', '�' => 'Ü', '�' => 'Ż', '�' => 'Ž', '�' => 'ß', '�' => 'ą', '�' => 'į', '�' => 'ā', '�' => 'ć', '�' => 'ä', '�' => 'å', '�' => 'ę', '�' => 'ē', '�' => 'č', '�' => 'é', '�' => 'ź', '�' => 'ė', '�' => 'ģ', '�' => 'ķ', '�' => 'ī', '�' => 'ļ', '�' => 'š', '�' => 'ń', '�' => 'ņ', '�' => 'ó', '�' => 'ō', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ų', '�' => 'ł', '�' => 'ś', '�' => 'ū', '�' => 'ü', '�' => 'ż', '�' => 'ž', '�' => '˙', ); $result =& $data; unset($data); return $result; PK2A#]�R��Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-10.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ą', '�' => 'Ē', '�' => 'Ģ', '�' => 'Ī', '�' => 'Ĩ', '�' => 'Ķ', '�' => '§', '�' => 'Ļ', '�' => 'Đ', '�' => 'Š', '�' => 'Ŧ', '�' => 'Ž', '�' => '', '�' => 'Ū', '�' => 'Ŋ', '�' => '°', '�' => 'ą', '�' => 'ē', '�' => 'ģ', '�' => 'ī', '�' => 'ĩ', '�' => 'ķ', '�' => '·', '�' => 'ļ', '�' => 'đ', '�' => 'š', '�' => 'ŧ', '�' => 'ž', '�' => '―', '�' => 'ū', '�' => 'ŋ', '�' => 'Ā', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Į', '�' => 'Č', '�' => 'É', '�' => 'Ę', '�' => 'Ë', '�' => 'Ė', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ð', '�' => 'Ņ', '�' => 'Ō', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => 'Ũ', '�' => 'Ø', '�' => 'Ų', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ý', '�' => 'Þ', '�' => 'ß', '�' => 'ā', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'į', '�' => 'č', '�' => 'é', '�' => 'ę', '�' => 'ë', '�' => 'ė', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ð', '�' => 'ņ', '�' => 'ō', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => 'ũ', '�' => 'ø', '�' => 'ų', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ý', '�' => 'þ', '�' => 'ĸ', ); $result =& $data; unset($data); return $result; PK2A#] �����Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1250.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => '‰', '�' => 'Š', '�' => '‹', '�' => 'Ś', '�' => 'Ť', '�' => 'Ž', '�' => 'Ź', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '™', '�' => 'š', '�' => '›', '�' => 'ś', '�' => 'ť', '�' => 'ž', '�' => 'ź', '�' => ' ', '�' => 'ˇ', '�' => '˘', '�' => 'Ł', '�' => '¤', '�' => 'Ą', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'Ş', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => 'Ż', '�' => '°', '�' => '±', '�' => '˛', '�' => 'ł', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => 'ą', '�' => 'ş', '�' => '»', '�' => 'Ľ', '�' => '˝', '�' => 'ľ', '�' => 'ż', '�' => 'Ŕ', '�' => 'Á', '�' => 'Â', '�' => 'Ă', '�' => 'Ä', '�' => 'Ĺ', '�' => 'Ć', '�' => 'Ç', '�' => 'Č', '�' => 'É', '�' => 'Ę', '�' => 'Ë', '�' => 'Ě', '�' => 'Í', '�' => 'Î', '�' => 'Ď', '�' => 'Đ', '�' => 'Ń', '�' => 'Ň', '�' => 'Ó', '�' => 'Ô', '�' => 'Ő', '�' => 'Ö', '�' => '×', '�' => 'Ř', '�' => 'Ů', '�' => 'Ú', '�' => 'Ű', '�' => 'Ü', '�' => 'Ý', '�' => 'Ţ', '�' => 'ß', '�' => 'ŕ', '�' => 'á', '�' => 'â', '�' => 'ă', '�' => 'ä', '�' => 'ĺ', '�' => 'ć', '�' => 'ç', '�' => 'č', '�' => 'é', '�' => 'ę', '�' => 'ë', '�' => 'ě', '�' => 'í', '�' => 'î', '�' => 'ď', '�' => 'đ', '�' => 'ń', '�' => 'ň', '�' => 'ó', '�' => 'ô', '�' => 'ő', '�' => 'ö', '�' => '÷', '�' => 'ř', '�' => 'ů', '�' => 'ú', '�' => 'ű', '�' => 'ü', '�' => 'ý', '�' => 'ţ', '�' => '˙', ); $result =& $data; unset($data); return $result; PK2A#]xB �?vendor/symfony/polyfill-iconv/Resources/charset/from.cp1006.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '۰', '�' => '۱', '�' => '۲', '�' => '۳', '�' => '۴', '�' => '۵', '�' => '۶', '�' => '۷', '�' => '۸', '�' => '۹', '�' => '،', '�' => '؛', '�' => '', '�' => '؟', '�' => 'ﺁ', '�' => 'ﺍ', '�' => 'ﺎ', '�' => 'ﺎ', '�' => 'ﺏ', '�' => 'ﺑ', '�' => 'ﭖ', '�' => 'ﭘ', '�' => 'ﺓ', '�' => 'ﺕ', '�' => 'ﺗ', '�' => 'ﭦ', '�' => 'ﭨ', '�' => 'ﺙ', '�' => 'ﺛ', '�' => 'ﺝ', '�' => 'ﺟ', '�' => 'ﭺ', '�' => 'ﭼ', '�' => 'ﺡ', '�' => 'ﺣ', '�' => 'ﺥ', '�' => 'ﺧ', '�' => 'ﺩ', '�' => 'ﮄ', '�' => 'ﺫ', '�' => 'ﺭ', '�' => 'ﮌ', '�' => 'ﺯ', '�' => 'ﮊ', '�' => 'ﺱ', '�' => 'ﺳ', '�' => 'ﺵ', '�' => 'ﺷ', '�' => 'ﺹ', '�' => 'ﺻ', '�' => 'ﺽ', '�' => 'ﺿ', '�' => 'ﻁ', '�' => 'ﻅ', '�' => 'ﻉ', '�' => 'ﻊ', '�' => 'ﻋ', '�' => 'ﻌ', '�' => 'ﻍ', '�' => 'ﻎ', '�' => 'ﻏ', '�' => 'ﻐ', '�' => 'ﻑ', '�' => 'ﻓ', '�' => 'ﻕ', '�' => 'ﻗ', '�' => 'ﻙ', '�' => 'ﻛ', '�' => 'ﮒ', '�' => 'ﮔ', '�' => 'ﻝ', '�' => 'ﻟ', '�' => 'ﻠ', '�' => 'ﻡ', '�' => 'ﻣ', '�' => 'ﮞ', '�' => 'ﻥ', '�' => 'ﻧ', '�' => 'ﺅ', '�' => 'ﻭ', '�' => 'ﮦ', '�' => 'ﮨ', '�' => 'ﮩ', '�' => 'ﮪ', '�' => 'ﺀ', '�' => 'ﺉ', '�' => 'ﺊ', '�' => 'ﺋ', '�' => 'ﻱ', '�' => 'ﻲ', '�' => 'ﻳ', '�' => 'ﮰ', '�' => 'ﮮ', '�' => 'ﹼ', '�' => 'ﹽ', ); $result =& $data; unset($data); return $result; PK2A#]�*��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp875.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', ' ' => '', ' ' => '', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => '', '!' => '', '"' => '', '#' => '', '$' => '', '%' => ' ', '&' => '', '\'' => '', '(' => '', ')' => '', '*' => '', '+' => '', ',' => '', '-' => '', '.' => '', '/' => '', 0 => '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', 6 => '', 7 => '', 8 => '', 9 => '', ':' => '', ';' => '', '<' => '', '=' => '', '>' => '', '?' => '', '@' => ' ', 'A' => 'Α', 'B' => 'Β', 'C' => 'Γ', 'D' => 'Δ', 'E' => 'Ε', 'F' => 'Ζ', 'G' => 'Η', 'H' => 'Θ', 'I' => 'Ι', 'J' => '[', 'K' => '.', 'L' => '<', 'M' => '(', 'N' => '+', 'O' => '!', 'P' => '&', 'Q' => 'Κ', 'R' => 'Λ', 'S' => 'Μ', 'T' => 'Ν', 'U' => 'Ξ', 'V' => 'Ο', 'W' => 'Π', 'X' => 'Ρ', 'Y' => 'Σ', 'Z' => ']', '[' => '$', '\\' => '*', ']' => ')', '^' => ';', '_' => '^', '`' => '-', 'a' => '/', 'b' => 'Τ', 'c' => 'Υ', 'd' => 'Φ', 'e' => 'Χ', 'f' => 'Ψ', 'g' => 'Ω', 'h' => 'Ϊ', 'i' => 'Ϋ', 'j' => '|', 'k' => ',', 'l' => '%', 'm' => '_', 'n' => '>', 'o' => '?', 'p' => '¨', 'q' => 'Ά', 'r' => 'Έ', 's' => 'Ή', 't' => ' ', 'u' => 'Ί', 'v' => 'Ό', 'w' => 'Ύ', 'x' => 'Ώ', 'y' => '`', 'z' => ':', '{' => '#', '|' => '@', '}' => '\'', '~' => '=', '' => '"', '�' => '΅', '�' => 'a', '�' => 'b', '�' => 'c', '�' => 'd', '�' => 'e', '�' => 'f', '�' => 'g', '�' => 'h', '�' => 'i', '�' => 'α', '�' => 'β', '�' => 'γ', '�' => 'δ', '�' => 'ε', '�' => 'ζ', '�' => '°', '�' => 'j', '�' => 'k', '�' => 'l', '�' => 'm', '�' => 'n', '�' => 'o', '�' => 'p', '�' => 'q', '�' => 'r', '�' => 'η', '�' => 'θ', '�' => 'ι', '�' => 'κ', '�' => 'λ', '�' => 'μ', '�' => '´', '�' => '~', '�' => 's', '�' => 't', '�' => 'u', '�' => 'v', '�' => 'w', '�' => 'x', '�' => 'y', '�' => 'z', '�' => 'ν', '�' => 'ξ', '�' => 'ο', '�' => 'π', '�' => 'ρ', '�' => 'σ', '�' => '£', '�' => 'ά', '�' => 'έ', '�' => 'ή', '�' => 'ϊ', '�' => 'ί', '�' => 'ό', '�' => 'ύ', '�' => 'ϋ', '�' => 'ώ', '�' => 'ς', '�' => 'τ', '�' => 'υ', '�' => 'φ', '�' => 'χ', '�' => 'ψ', '�' => '{', '�' => 'A', '�' => 'B', '�' => 'C', '�' => 'D', '�' => 'E', '�' => 'F', '�' => 'G', '�' => 'H', '�' => 'I', '�' => '', '�' => 'ω', '�' => 'ΐ', '�' => 'ΰ', '�' => '‘', '�' => '―', '�' => '}', '�' => 'J', '�' => 'K', '�' => 'L', '�' => 'M', '�' => 'N', '�' => 'O', '�' => 'P', '�' => 'Q', '�' => 'R', '�' => '±', '�' => '½', '�' => '', '�' => '·', '�' => '’', '�' => '¦', '�' => '\\', '�' => '', '�' => 'S', '�' => 'T', '�' => 'U', '�' => 'V', '�' => 'W', '�' => 'X', '�' => 'Y', '�' => 'Z', '�' => '²', '�' => '§', '�' => '', '�' => '', '�' => '«', '�' => '¬', '�' => '0', '�' => '1', '�' => '2', '�' => '3', '�' => '4', '�' => '5', '�' => '6', '�' => '7', '�' => '8', '�' => '9', '�' => '³', '�' => '©', '�' => '', '�' => '', '�' => '»', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]OP�gCCAvendor/symfony/polyfill-iconv/Resources/charset/from.us-ascii.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', ); $result =& $data; unset($data); return $result; PK2A#]��c�ZZCvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-3.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ħ', '�' => '˘', '�' => '£', '�' => '¤', '�' => 'Ĥ', '�' => '§', '�' => '¨', '�' => 'İ', '�' => 'Ş', '�' => 'Ğ', '�' => 'Ĵ', '�' => '', '�' => 'Ż', '�' => '°', '�' => 'ħ', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => 'ĥ', '�' => '·', '�' => '¸', '�' => 'ı', '�' => 'ş', '�' => 'ğ', '�' => 'ĵ', '�' => '½', '�' => 'ż', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ä', '�' => 'Ċ', '�' => 'Ĉ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Ġ', '�' => 'Ö', '�' => '×', '�' => 'Ĝ', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ŭ', '�' => 'Ŝ', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ä', '�' => 'ċ', '�' => 'ĉ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'ġ', '�' => 'ö', '�' => '÷', '�' => 'ĝ', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ŭ', '�' => 'ŝ', '�' => '˙', ); $result =& $data; unset($data); return $result; PK2A#]L�tw��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp864.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '٪', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '°', '�' => '·', '�' => '∙', '�' => '√', '�' => '▒', '�' => '─', '�' => '│', '�' => '┼', '�' => '┤', '�' => '┬', '�' => '├', '�' => '┴', '�' => '┐', '�' => '┌', '�' => '└', '�' => '┘', '�' => 'β', '�' => '∞', '�' => 'φ', '�' => '±', '�' => '½', '�' => '¼', '�' => '≈', '�' => '«', '�' => '»', '�' => 'ﻷ', '�' => 'ﻸ', '�' => 'ﻻ', '�' => 'ﻼ', '�' => ' ', '�' => '', '�' => 'ﺂ', '�' => '£', '�' => '¤', '�' => 'ﺄ', '�' => 'ﺎ', '�' => 'ﺏ', '�' => 'ﺕ', '�' => 'ﺙ', '�' => '،', '�' => 'ﺝ', '�' => 'ﺡ', '�' => 'ﺥ', '�' => '٠', '�' => '١', '�' => '٢', '�' => '٣', '�' => '٤', '�' => '٥', '�' => '٦', '�' => '٧', '�' => '٨', '�' => '٩', '�' => 'ﻑ', '�' => '؛', '�' => 'ﺱ', '�' => 'ﺵ', '�' => 'ﺹ', '�' => '؟', '�' => '¢', '�' => 'ﺀ', '�' => 'ﺁ', '�' => 'ﺃ', '�' => 'ﺅ', '�' => 'ﻊ', '�' => 'ﺋ', '�' => 'ﺍ', '�' => 'ﺑ', '�' => 'ﺓ', '�' => 'ﺗ', '�' => 'ﺛ', '�' => 'ﺟ', '�' => 'ﺣ', '�' => 'ﺧ', '�' => 'ﺩ', '�' => 'ﺫ', '�' => 'ﺭ', '�' => 'ﺯ', '�' => 'ﺳ', '�' => 'ﺷ', '�' => 'ﺻ', '�' => 'ﺿ', '�' => 'ﻁ', '�' => 'ﻅ', '�' => 'ﻋ', '�' => 'ﻏ', '�' => '¦', '�' => '¬', '�' => '÷', '�' => '×', '�' => 'ﻉ', '�' => 'ـ', '�' => 'ﻓ', '�' => 'ﻗ', '�' => 'ﻛ', '�' => 'ﻟ', '�' => 'ﻣ', '�' => 'ﻧ', '�' => 'ﻫ', '�' => 'ﻭ', '�' => 'ﻯ', '�' => 'ﻳ', '�' => 'ﺽ', '�' => 'ﻌ', '�' => 'ﻎ', '�' => 'ﻍ', '�' => 'ﻡ', '�' => 'ﹽ', '�' => 'ّ', '�' => 'ﻥ', '�' => 'ﻩ', '�' => 'ﻬ', '�' => 'ﻰ', '�' => 'ﻲ', '�' => 'ﻐ', '�' => 'ﻕ', '�' => 'ﻵ', '�' => 'ﻶ', '�' => 'ﻝ', '�' => 'ﻙ', '�' => 'ﻱ', '�' => '■', ); $result =& $data; unset($data); return $result; PK2A#]j� ���Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-4.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ą', '�' => 'ĸ', '�' => 'Ŗ', '�' => '¤', '�' => 'Ĩ', '�' => 'Ļ', '�' => '§', '�' => '¨', '�' => 'Š', '�' => 'Ē', '�' => 'Ģ', '�' => 'Ŧ', '�' => '', '�' => 'Ž', '�' => '¯', '�' => '°', '�' => 'ą', '�' => '˛', '�' => 'ŗ', '�' => '´', '�' => 'ĩ', '�' => 'ļ', '�' => 'ˇ', '�' => '¸', '�' => 'š', '�' => 'ē', '�' => 'ģ', '�' => 'ŧ', '�' => 'Ŋ', '�' => 'ž', '�' => 'ŋ', '�' => 'Ā', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Į', '�' => 'Č', '�' => 'É', '�' => 'Ę', '�' => 'Ë', '�' => 'Ė', '�' => 'Í', '�' => 'Î', '�' => 'Ī', '�' => 'Đ', '�' => 'Ņ', '�' => 'Ō', '�' => 'Ķ', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ų', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ũ', '�' => 'Ū', '�' => 'ß', '�' => 'ā', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'į', '�' => 'č', '�' => 'é', '�' => 'ę', '�' => 'ë', '�' => 'ė', '�' => 'í', '�' => 'î', '�' => 'ī', '�' => 'đ', '�' => 'ņ', '�' => 'ō', '�' => 'ķ', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ų', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ũ', '�' => 'ū', '�' => '˙', ); $result =& $data; unset($data); return $result; PK2A#]Ŭ�>vendor/symfony/polyfill-iconv/Resources/charset/from.cp863.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'Â', '�' => 'à', '�' => '¶', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'ï', '�' => 'î', '�' => '‗', '�' => 'À', '�' => '§', '�' => 'É', '�' => 'È', '�' => 'Ê', '�' => 'ô', '�' => 'Ë', '�' => 'Ï', '�' => 'û', '�' => 'ù', '�' => '¤', '�' => 'Ô', '�' => 'Ü', '�' => '¢', '�' => '£', '�' => 'Ù', '�' => 'Û', '�' => 'ƒ', '�' => '¦', '�' => '´', '�' => 'ó', '�' => 'ú', '�' => '¨', '�' => '¸', '�' => '³', '�' => '¯', '�' => 'Î', '�' => '⌐', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¾', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#] ��r�r>vendor/symfony/polyfill-iconv/Resources/charset/from.cp949.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�A' => '갂', '�B' => '갃', '�C' => '갅', '�D' => '갆', '�E' => '갋', '�F' => '갌', '�G' => '갍', '�H' => '갎', '�I' => '갏', '�J' => '갘', '�K' => '갞', '�L' => '갟', '�M' => '갡', '�N' => '갢', '�O' => '갣', '�P' => '갥', '�Q' => '갦', '�R' => '갧', '�S' => '갨', '�T' => '갩', '�U' => '갪', '�V' => '갫', '�W' => '갮', '�X' => '갲', '�Y' => '갳', '�Z' => '갴', '�a' => '갵', '�b' => '갶', '�c' => '갷', '�d' => '갺', '�e' => '갻', '�f' => '갽', '�g' => '갾', '�h' => '갿', '�i' => '걁', '�j' => '걂', '�k' => '걃', '�l' => '걄', '�m' => '걅', '�n' => '걆', '�o' => '걇', '�p' => '걈', '�q' => '걉', '�r' => '걊', '�s' => '걌', '�t' => '걎', '�u' => '걏', '�v' => '걐', '�w' => '걑', '�x' => '걒', '�y' => '걓', '�z' => '걕', '��' => '걖', '��' => '걗', '��' => '걙', '��' => '걚', '��' => '걛', '��' => '걝', '��' => '걞', '��' => '걟', '��' => '걠', '��' => '걡', '��' => '걢', '��' => '걣', '��' => '걤', '��' => '걥', '��' => '걦', '��' => '걧', '��' => '걨', '��' => '걩', '��' => '걪', '��' => '걫', '��' => '걬', '��' => '걭', '��' => '걮', '��' => '걯', '��' => '걲', '��' => '걳', '��' => '걵', '��' => '걶', '��' => '걹', '��' => '걻', '��' => '걼', '��' => '걽', '��' => '걾', '��' => '걿', '��' => '겂', '��' => '겇', '��' => '겈', '��' => '겍', '��' => '겎', '��' => '겏', '��' => '겑', '��' => '겒', '��' => '겓', '��' => '겕', '��' => '겖', '��' => '겗', '��' => '겘', '��' => '겙', '��' => '겚', '��' => '겛', '��' => '겞', '��' => '겢', '��' => '겣', '��' => '겤', '��' => '겥', '��' => '겦', '��' => '겧', '��' => '겫', '��' => '겭', '��' => '겮', '��' => '겱', '��' => '겲', '��' => '겳', '��' => '겴', '��' => '겵', '��' => '겶', '��' => '겷', '��' => '겺', '��' => '겾', '��' => '겿', '��' => '곀', '��' => '곂', '��' => '곃', '��' => '곅', '��' => '곆', '��' => '곇', '��' => '곉', '��' => '곊', '��' => '곋', '��' => '곍', '��' => '곎', '��' => '곏', '��' => '곐', '��' => '곑', '��' => '곒', '��' => '곓', '��' => '곔', '��' => '곖', '��' => '곘', '��' => '곙', '��' => '곚', '��' => '곛', '��' => '곜', '��' => '곝', '��' => '곞', '��' => '곟', '��' => '곢', '��' => '곣', '��' => '곥', '��' => '곦', '��' => '곩', '��' => '곫', '��' => '곭', '��' => '곮', '��' => '곲', '��' => '곴', '��' => '곷', '��' => '곸', '��' => '곹', '��' => '곺', '��' => '곻', '��' => '곾', '��' => '곿', '��' => '괁', '��' => '괂', '��' => '괃', '��' => '괅', '��' => '괇', '��' => '괈', '��' => '괉', '��' => '괊', '��' => '괋', '��' => '괎', '��' => '괐', '��' => '괒', '��' => '괓', '�A' => '괔', '�B' => '괕', '�C' => '괖', '�D' => '괗', '�E' => '괙', '�F' => '괚', '�G' => '괛', '�H' => '괝', '�I' => '괞', '�J' => '괟', '�K' => '괡', '�L' => '괢', '�M' => '괣', '�N' => '괤', '�O' => '괥', '�P' => '괦', '�Q' => '괧', '�R' => '괨', '�S' => '괪', '�T' => '괫', '�U' => '괮', '�V' => '괯', '�W' => '괰', '�X' => '괱', '�Y' => '괲', '�Z' => '괳', '�a' => '괶', '�b' => '괷', '�c' => '괹', '�d' => '괺', '�e' => '괻', '�f' => '괽', '�g' => '괾', '�h' => '괿', '�i' => '굀', '�j' => '굁', '�k' => '굂', '�l' => '굃', '�m' => '굆', '�n' => '굈', '�o' => '굊', '�p' => '굋', '�q' => '굌', '�r' => '굍', '�s' => '굎', '�t' => '굏', '�u' => '굑', '�v' => '굒', '�w' => '굓', '�x' => '굕', '�y' => '굖', '�z' => '굗', '��' => '굙', '��' => '굚', '��' => '굛', '��' => '굜', '��' => '굝', '��' => '굞', '��' => '굟', '��' => '굠', '��' => '굢', '��' => '굤', '��' => '굥', '��' => '굦', '��' => '굧', '��' => '굨', '��' => '굩', '��' => '굪', '��' => '굫', '��' => '굮', '��' => '굯', '��' => '굱', '��' => '굲', '��' => '굷', '��' => '굸', '��' => '굹', '��' => '굺', '��' => '굾', '��' => '궀', '��' => '궃', '��' => '궄', '��' => '궅', '��' => '궆', '��' => '궇', '��' => '궊', '��' => '궋', '��' => '궍', '��' => '궎', '��' => '궏', '��' => '궑', '��' => '궒', '��' => '궓', '��' => '궔', '��' => '궕', '��' => '궖', '��' => '궗', '��' => '궘', '��' => '궙', '��' => '궚', '��' => '궛', '��' => '궞', '��' => '궟', '��' => '궠', '��' => '궡', '��' => '궢', '��' => '궣', '��' => '궥', '��' => '궦', '��' => '궧', '��' => '궨', '��' => '궩', '��' => '궪', '��' => '궫', '��' => '궬', '��' => '궭', '��' => '궮', '��' => '궯', '��' => '궰', '��' => '궱', '��' => '궲', '��' => '궳', '��' => '궴', '��' => '궵', '��' => '궶', '��' => '궸', '��' => '궹', '��' => '궺', '��' => '궻', '��' => '궼', '��' => '궽', '��' => '궾', '��' => '궿', '��' => '귂', '��' => '귃', '��' => '귅', '��' => '귆', '��' => '귇', '��' => '귉', '��' => '귊', '��' => '귋', '��' => '귌', '��' => '귍', '��' => '귎', '��' => '귏', '��' => '귒', '��' => '귔', '��' => '귕', '��' => '귖', '��' => '귗', '��' => '귘', '��' => '귙', '��' => '귚', '��' => '귛', '��' => '귝', '��' => '귞', '��' => '귟', '��' => '귡', '��' => '귢', '��' => '귣', '��' => '귥', '��' => '귦', '��' => '귧', '��' => '귨', '��' => '귩', '��' => '귪', '��' => '귫', '��' => '귬', '��' => '귭', '��' => '귮', '��' => '귯', '��' => '귰', '��' => '귱', '��' => '귲', '��' => '귳', '��' => '귴', '��' => '귵', '��' => '귶', '��' => '귷', '�A' => '귺', '�B' => '귻', '�C' => '귽', '�D' => '귾', '�E' => '긂', '�F' => '긃', '�G' => '긄', '�H' => '긅', '�I' => '긆', '�J' => '긇', '�K' => '긊', '�L' => '긌', '�M' => '긎', '�N' => '긏', '�O' => '긐', '�P' => '긑', '�Q' => '긒', '�R' => '긓', '�S' => '긕', '�T' => '긖', '�U' => '긗', '�V' => '긘', '�W' => '긙', '�X' => '긚', '�Y' => '긛', '�Z' => '긜', '�a' => '긝', '�b' => '긞', '�c' => '긟', '�d' => '긠', '�e' => '긡', '�f' => '긢', '�g' => '긣', '�h' => '긤', '�i' => '긥', '�j' => '긦', '�k' => '긧', '�l' => '긨', '�m' => '긩', '�n' => '긪', '�o' => '긫', '�p' => '긬', '�q' => '긭', '�r' => '긮', '�s' => '긯', '�t' => '긲', '�u' => '긳', '�v' => '긵', '�w' => '긶', '�x' => '긹', '�y' => '긻', '�z' => '긼', '��' => '긽', '��' => '긾', '��' => '긿', '��' => '깂', '��' => '깄', '��' => '깇', '��' => '깈', '��' => '깉', '��' => '깋', '��' => '깏', '��' => '깑', '��' => '깒', '��' => '깓', '��' => '깕', '��' => '깗', '��' => '깘', '��' => '깙', '��' => '깚', '��' => '깛', '��' => '깞', '��' => '깢', '��' => '깣', '��' => '깤', '��' => '깦', '��' => '깧', '��' => '깪', '��' => '깫', '��' => '깭', '��' => '깮', '��' => '깯', '��' => '깱', '��' => '깲', '��' => '깳', '��' => '깴', '��' => '깵', '��' => '깶', '��' => '깷', '��' => '깺', '��' => '깾', '��' => '깿', '��' => '꺀', '��' => '꺁', '��' => '꺂', '��' => '꺃', '��' => '꺆', '��' => '꺇', '��' => '꺈', '��' => '꺉', '��' => '꺊', '��' => '꺋', '��' => '꺍', '��' => '꺎', '��' => '꺏', '��' => '꺐', '��' => '꺑', '��' => '꺒', '��' => '꺓', '��' => '꺔', '��' => '꺕', '��' => '꺖', '��' => '꺗', '��' => '꺘', '��' => '꺙', '��' => '꺚', '��' => '꺛', '��' => '꺜', '��' => '꺝', '��' => '꺞', '��' => '꺟', '��' => '꺠', '��' => '꺡', '��' => '꺢', '��' => '꺣', '��' => '꺤', '��' => '꺥', '��' => '꺦', '��' => '꺧', '��' => '꺨', '��' => '꺩', '��' => '꺪', '��' => '꺫', '��' => '꺬', '��' => '꺭', '��' => '꺮', '��' => '꺯', '��' => '꺰', '��' => '꺱', '��' => '꺲', '��' => '꺳', '��' => '꺴', '��' => '꺵', '��' => '꺶', '��' => '꺷', '��' => '꺸', '��' => '꺹', '��' => '꺺', '��' => '꺻', '��' => '꺿', '��' => '껁', '��' => '껂', '��' => '껃', '��' => '껅', '��' => '껆', '��' => '껇', '��' => '껈', '��' => '껉', '��' => '껊', '��' => '껋', '��' => '껎', '��' => '껒', '��' => '껓', '��' => '껔', '��' => '껕', '��' => '껖', '��' => '껗', '��' => '껚', '��' => '껛', '��' => '껝', '��' => '껞', '��' => '껟', '��' => '껠', '��' => '껡', '��' => '껢', '��' => '껣', '��' => '껤', '��' => '껥', '�A' => '껦', '�B' => '껧', '�C' => '껩', '�D' => '껪', '�E' => '껬', '�F' => '껮', '�G' => '껯', '�H' => '껰', '�I' => '껱', '�J' => '껲', '�K' => '껳', '�L' => '껵', '�M' => '껶', '�N' => '껷', '�O' => '껹', '�P' => '껺', '�Q' => '껻', '�R' => '껽', '�S' => '껾', '�T' => '껿', '�U' => '꼀', '�V' => '꼁', '�W' => '꼂', '�X' => '꼃', '�Y' => '꼄', '�Z' => '꼅', '�a' => '꼆', '�b' => '꼉', '�c' => '꼊', '�d' => '꼋', '�e' => '꼌', '�f' => '꼎', '�g' => '꼏', '�h' => '꼑', '�i' => '꼒', '�j' => '꼓', '�k' => '꼔', '�l' => '꼕', '�m' => '꼖', '�n' => '꼗', '�o' => '꼘', '�p' => '꼙', '�q' => '꼚', '�r' => '꼛', '�s' => '꼜', '�t' => '꼝', '�u' => '꼞', '�v' => '꼟', '�w' => '꼠', '�x' => '꼡', '�y' => '꼢', '�z' => '꼣', '��' => '꼤', '��' => '꼥', '��' => '꼦', '��' => '꼧', '��' => '꼨', '��' => '꼩', '��' => '꼪', '��' => '꼫', '��' => '꼮', '��' => '꼯', '��' => '꼱', '��' => '꼳', '��' => '꼵', '��' => '꼶', '��' => '꼷', '��' => '꼸', '��' => '꼹', '��' => '꼺', '��' => '꼻', '��' => '꼾', '��' => '꽀', '��' => '꽄', '��' => '꽅', '��' => '꽆', '��' => '꽇', '��' => '꽊', '��' => '꽋', '��' => '꽌', '��' => '꽍', '��' => '꽎', '��' => '꽏', '��' => '꽑', '��' => '꽒', '��' => '꽓', '��' => '꽔', '��' => '꽕', '��' => '꽖', '��' => '꽗', '��' => '꽘', '��' => '꽙', '��' => '꽚', '��' => '꽛', '��' => '꽞', '��' => '꽟', '��' => '꽠', '��' => '꽡', '��' => '꽢', '��' => '꽣', '��' => '꽦', '��' => '꽧', '��' => '꽨', '��' => '꽩', '��' => '꽪', '��' => '꽫', '��' => '꽬', '��' => '꽭', '��' => '꽮', '��' => '꽯', '��' => '꽰', '��' => '꽱', '��' => '꽲', '��' => '꽳', '��' => '꽴', '��' => '꽵', '��' => '꽶', '��' => '꽷', '��' => '꽸', '��' => '꽺', '��' => '꽻', '��' => '꽼', '��' => '꽽', '��' => '꽾', '��' => '꽿', '��' => '꾁', '��' => '꾂', '��' => '꾃', '��' => '꾅', '��' => '꾆', '��' => '꾇', '��' => '꾉', '��' => '꾊', '��' => '꾋', '��' => '꾌', '��' => '꾍', '��' => '꾎', '��' => '꾏', '��' => '꾒', '��' => '꾓', '��' => '꾔', '��' => '꾖', '��' => '꾗', '��' => '꾘', '��' => '꾙', '��' => '꾚', '��' => '꾛', '��' => '꾝', '��' => '꾞', '��' => '꾟', '��' => '꾠', '��' => '꾡', '��' => '꾢', '��' => '꾣', '��' => '꾤', '��' => '꾥', '��' => '꾦', '��' => '꾧', '��' => '꾨', '��' => '꾩', '��' => '꾪', '��' => '꾫', '��' => '꾬', '��' => '꾭', '��' => '꾮', '��' => '꾯', '��' => '꾰', '��' => '꾱', '��' => '꾲', '��' => '꾳', '��' => '꾴', '��' => '꾵', '��' => '꾶', '��' => '꾷', '��' => '꾺', '��' => '꾻', '��' => '꾽', '��' => '꾾', '�A' => '꾿', '�B' => '꿁', '�C' => '꿂', '�D' => '꿃', '�E' => '꿄', '�F' => '꿅', '�G' => '꿆', '�H' => '꿊', '�I' => '꿌', '�J' => '꿏', '�K' => '꿐', '�L' => '꿑', '�M' => '꿒', '�N' => '꿓', '�O' => '꿕', '�P' => '꿖', '�Q' => '꿗', '�R' => '꿘', '�S' => '꿙', '�T' => '꿚', '�U' => '꿛', '�V' => '꿝', '�W' => '꿞', '�X' => '꿟', '�Y' => '꿠', '�Z' => '꿡', '�a' => '꿢', '�b' => '꿣', '�c' => '꿤', '�d' => '꿥', '�e' => '꿦', '�f' => '꿧', '�g' => '꿪', '�h' => '꿫', '�i' => '꿬', '�j' => '꿭', '�k' => '꿮', '�l' => '꿯', '�m' => '꿲', '�n' => '꿳', '�o' => '꿵', '�p' => '꿶', '�q' => '꿷', '�r' => '꿹', '�s' => '꿺', '�t' => '꿻', '�u' => '꿼', '�v' => '꿽', '�w' => '꿾', '�x' => '꿿', '�y' => '뀂', '�z' => '뀃', '��' => '뀅', '��' => '뀆', '��' => '뀇', '��' => '뀈', '��' => '뀉', '��' => '뀊', '��' => '뀋', '��' => '뀍', '��' => '뀎', '��' => '뀏', '��' => '뀑', '��' => '뀒', '��' => '뀓', '��' => '뀕', '��' => '뀖', '��' => '뀗', '��' => '뀘', '��' => '뀙', '��' => '뀚', '��' => '뀛', '��' => '뀞', '��' => '뀟', '��' => '뀠', '��' => '뀡', '��' => '뀢', '��' => '뀣', '��' => '뀤', '��' => '뀥', '��' => '뀦', '��' => '뀧', '��' => '뀩', '��' => '뀪', '��' => '뀫', '��' => '뀬', '��' => '뀭', '��' => '뀮', '��' => '뀯', '��' => '뀰', '��' => '뀱', '��' => '뀲', '��' => '뀳', '��' => '뀴', '��' => '뀵', '��' => '뀶', '��' => '뀷', '��' => '뀸', '��' => '뀹', '��' => '뀺', '��' => '뀻', '��' => '뀼', '��' => '뀽', '��' => '뀾', '��' => '뀿', '��' => '끀', '��' => '끁', '��' => '끂', '��' => '끃', '��' => '끆', '��' => '끇', '��' => '끉', '��' => '끋', '��' => '끍', '��' => '끏', '��' => '끐', '��' => '끑', '��' => '끒', '��' => '끖', '��' => '끘', '��' => '끚', '��' => '끛', '��' => '끜', '��' => '끞', '��' => '끟', '��' => '끠', '��' => '끡', '��' => '끢', '��' => '끣', '��' => '끤', '��' => '끥', '��' => '끦', '��' => '끧', '��' => '끨', '��' => '끩', '��' => '끪', '��' => '끫', '��' => '끬', '��' => '끭', '��' => '끮', '��' => '끯', '��' => '끰', '��' => '끱', '��' => '끲', '��' => '끳', '��' => '끴', '��' => '끵', '��' => '끶', '��' => '끷', '��' => '끸', '��' => '끹', '��' => '끺', '��' => '끻', '��' => '끾', '��' => '끿', '��' => '낁', '��' => '낂', '��' => '낃', '��' => '낅', '��' => '낆', '��' => '낇', '��' => '낈', '��' => '낉', '��' => '낊', '��' => '낋', '��' => '낎', '��' => '낐', '��' => '낒', '��' => '낓', '��' => '낔', '��' => '낕', '��' => '낖', '��' => '낗', '��' => '낛', '��' => '낝', '��' => '낞', '��' => '낣', '��' => '낤', '�A' => '낥', '�B' => '낦', '�C' => '낧', '�D' => '낪', '�E' => '낰', '�F' => '낲', '�G' => '낶', '�H' => '낷', '�I' => '낹', '�J' => '낺', '�K' => '낻', '�L' => '낽', '�M' => '낾', '�N' => '낿', '�O' => '냀', '�P' => '냁', '�Q' => '냂', '�R' => '냃', '�S' => '냆', '�T' => '냊', '�U' => '냋', '�V' => '냌', '�W' => '냍', '�X' => '냎', '�Y' => '냏', '�Z' => '냒', '�a' => '냓', '�b' => '냕', '�c' => '냖', '�d' => '냗', '�e' => '냙', '�f' => '냚', '�g' => '냛', '�h' => '냜', '�i' => '냝', '�j' => '냞', '�k' => '냟', '�l' => '냡', '�m' => '냢', '�n' => '냣', '�o' => '냤', '�p' => '냦', '�q' => '냧', '�r' => '냨', '�s' => '냩', '�t' => '냪', '�u' => '냫', '�v' => '냬', '�w' => '냭', '�x' => '냮', '�y' => '냯', '�z' => '냰', '��' => '냱', '��' => '냲', '��' => '냳', '��' => '냴', '��' => '냵', '��' => '냶', '��' => '냷', '��' => '냸', '��' => '냹', '��' => '냺', '��' => '냻', '��' => '냼', '��' => '냽', '��' => '냾', '��' => '냿', '��' => '넀', '��' => '넁', '��' => '넂', '��' => '넃', '��' => '넄', '��' => '넅', '��' => '넆', '��' => '넇', '��' => '넊', '��' => '넍', '��' => '넎', '��' => '넏', '��' => '넑', '��' => '넔', '��' => '넕', '��' => '넖', '��' => '넗', '��' => '넚', '��' => '넞', '��' => '넟', '��' => '넠', '��' => '넡', '��' => '넢', '��' => '넦', '��' => '넧', '��' => '넩', '��' => '넪', '��' => '넫', '��' => '넭', '��' => '넮', '��' => '넯', '��' => '넰', '��' => '넱', '��' => '넲', '��' => '넳', '��' => '넶', '��' => '넺', '��' => '넻', '��' => '넼', '��' => '넽', '��' => '넾', '��' => '넿', '��' => '녂', '��' => '녃', '��' => '녅', '��' => '녆', '��' => '녇', '��' => '녉', '��' => '녊', '��' => '녋', '��' => '녌', '��' => '녍', '��' => '녎', '��' => '녏', '��' => '녒', '��' => '녓', '��' => '녖', '��' => '녗', '��' => '녙', '��' => '녚', '��' => '녛', '��' => '녝', '��' => '녞', '��' => '녟', '��' => '녡', '��' => '녢', '��' => '녣', '��' => '녤', '��' => '녥', '��' => '녦', '��' => '녧', '��' => '녨', '��' => '녩', '��' => '녪', '��' => '녫', '��' => '녬', '��' => '녭', '��' => '녮', '��' => '녯', '��' => '녰', '��' => '녱', '��' => '녲', '��' => '녳', '��' => '녴', '��' => '녵', '��' => '녶', '��' => '녷', '��' => '녺', '��' => '녻', '��' => '녽', '��' => '녾', '��' => '녿', '��' => '놁', '��' => '놃', '��' => '놄', '��' => '놅', '��' => '놆', '��' => '놇', '��' => '놊', '��' => '놌', '��' => '놎', '��' => '놏', '��' => '놐', '��' => '놑', '��' => '놕', '��' => '놖', '��' => '놗', '��' => '놙', '��' => '놚', '��' => '놛', '��' => '놝', '�A' => '놞', '�B' => '놟', '�C' => '놠', '�D' => '놡', '�E' => '놢', '�F' => '놣', '�G' => '놤', '�H' => '놥', '�I' => '놦', '�J' => '놧', '�K' => '놩', '�L' => '놪', '�M' => '놫', '�N' => '놬', '�O' => '놭', '�P' => '놮', '�Q' => '놯', '�R' => '놰', '�S' => '놱', '�T' => '놲', '�U' => '놳', '�V' => '놴', '�W' => '놵', '�X' => '놶', '�Y' => '놷', '�Z' => '놸', '�a' => '놹', '�b' => '놺', '�c' => '놻', '�d' => '놼', '�e' => '놽', '�f' => '놾', '�g' => '놿', '�h' => '뇀', '�i' => '뇁', '�j' => '뇂', '�k' => '뇃', '�l' => '뇄', '�m' => '뇅', '�n' => '뇆', '�o' => '뇇', '�p' => '뇈', '�q' => '뇉', '�r' => '뇊', '�s' => '뇋', '�t' => '뇍', '�u' => '뇎', '�v' => '뇏', '�w' => '뇑', '�x' => '뇒', '�y' => '뇓', '�z' => '뇕', '��' => '뇖', '��' => '뇗', '��' => '뇘', '��' => '뇙', '��' => '뇚', '��' => '뇛', '��' => '뇞', '��' => '뇠', '��' => '뇡', '��' => '뇢', '��' => '뇣', '��' => '뇤', '��' => '뇥', '��' => '뇦', '��' => '뇧', '��' => '뇪', '��' => '뇫', '��' => '뇭', '��' => '뇮', '��' => '뇯', '��' => '뇱', '��' => '뇲', '��' => '뇳', '��' => '뇴', '��' => '뇵', '��' => '뇶', '��' => '뇷', '��' => '뇸', '��' => '뇺', '��' => '뇼', '��' => '뇾', '��' => '뇿', '��' => '눀', '��' => '눁', '��' => '눂', '��' => '눃', '��' => '눆', '��' => '눇', '��' => '눉', '��' => '눊', '��' => '눍', '��' => '눎', '��' => '눏', '��' => '눐', '��' => '눑', '��' => '눒', '��' => '눓', '��' => '눖', '��' => '눘', '��' => '눚', '��' => '눛', '��' => '눜', '��' => '눝', '��' => '눞', '��' => '눟', '��' => '눡', '��' => '눢', '��' => '눣', '��' => '눤', '��' => '눥', '��' => '눦', '��' => '눧', '��' => '눨', '��' => '눩', '��' => '눪', '��' => '눫', '��' => '눬', '��' => '눭', '��' => '눮', '��' => '눯', '��' => '눰', '��' => '눱', '��' => '눲', '��' => '눳', '��' => '눵', '��' => '눶', '��' => '눷', '��' => '눸', '��' => '눹', '��' => '눺', '��' => '눻', '��' => '눽', '��' => '눾', '��' => '눿', '��' => '뉀', '��' => '뉁', '��' => '뉂', '��' => '뉃', '��' => '뉄', '��' => '뉅', '��' => '뉆', '��' => '뉇', '��' => '뉈', '��' => '뉉', '��' => '뉊', '��' => '뉋', '��' => '뉌', '��' => '뉍', '��' => '뉎', '��' => '뉏', '��' => '뉐', '��' => '뉑', '��' => '뉒', '��' => '뉓', '��' => '뉔', '��' => '뉕', '��' => '뉖', '��' => '뉗', '��' => '뉙', '��' => '뉚', '��' => '뉛', '��' => '뉝', '��' => '뉞', '��' => '뉟', '��' => '뉡', '��' => '뉢', '��' => '뉣', '��' => '뉤', '��' => '뉥', '��' => '뉦', '��' => '뉧', '��' => '뉪', '��' => '뉫', '��' => '뉬', '��' => '뉭', '��' => '뉮', '�A' => '뉯', '�B' => '뉰', '�C' => '뉱', '�D' => '뉲', '�E' => '뉳', '�F' => '뉶', '�G' => '뉷', '�H' => '뉸', '�I' => '뉹', '�J' => '뉺', '�K' => '뉻', '�L' => '뉽', '�M' => '뉾', '�N' => '뉿', '�O' => '늀', '�P' => '늁', '�Q' => '늂', '�R' => '늃', '�S' => '늆', '�T' => '늇', '�U' => '늈', '�V' => '늊', '�W' => '늋', '�X' => '늌', '�Y' => '늍', '�Z' => '늎', '�a' => '늏', '�b' => '늒', '�c' => '늓', '�d' => '늕', '�e' => '늖', '�f' => '늗', '�g' => '늛', '�h' => '늜', '�i' => '늝', '�j' => '늞', '�k' => '늟', '�l' => '늢', '�m' => '늤', '�n' => '늧', '�o' => '늨', '�p' => '늩', '�q' => '늫', '�r' => '늭', '�s' => '늮', '�t' => '늯', '�u' => '늱', '�v' => '늲', '�w' => '늳', '�x' => '늵', '�y' => '늶', '�z' => '늷', '��' => '늸', '��' => '늹', '��' => '늺', '��' => '늻', '��' => '늼', '��' => '늽', '��' => '늾', '��' => '늿', '��' => '닀', '��' => '닁', '��' => '닂', '��' => '닃', '��' => '닄', '��' => '닅', '��' => '닆', '��' => '닇', '��' => '닊', '��' => '닋', '��' => '닍', '��' => '닎', '��' => '닏', '��' => '닑', '��' => '닓', '��' => '닔', '��' => '닕', '��' => '닖', '��' => '닗', '��' => '닚', '��' => '닜', '��' => '닞', '��' => '닟', '��' => '닠', '��' => '닡', '��' => '닣', '��' => '닧', '��' => '닩', '��' => '닪', '��' => '닰', '��' => '닱', '��' => '닲', '��' => '닶', '��' => '닼', '��' => '닽', '��' => '닾', '��' => '댂', '��' => '댃', '��' => '댅', '��' => '댆', '��' => '댇', '��' => '댉', '��' => '댊', '��' => '댋', '��' => '댌', '��' => '댍', '��' => '댎', '��' => '댏', '��' => '댒', '��' => '댖', '��' => '댗', '��' => '댘', '��' => '댙', '��' => '댚', '��' => '댛', '��' => '댝', '��' => '댞', '��' => '댟', '��' => '댠', '��' => '댡', '��' => '댢', '��' => '댣', '��' => '댤', '��' => '댥', '��' => '댦', '��' => '댧', '��' => '댨', '��' => '댩', '��' => '댪', '��' => '댫', '��' => '댬', '��' => '댭', '��' => '댮', '��' => '댯', '��' => '댰', '��' => '댱', '��' => '댲', '��' => '댳', '��' => '댴', '��' => '댵', '��' => '댶', '��' => '댷', '��' => '댸', '��' => '댹', '��' => '댺', '��' => '댻', '��' => '댼', '��' => '댽', '��' => '댾', '��' => '댿', '��' => '덀', '��' => '덁', '��' => '덂', '��' => '덃', '��' => '덄', '��' => '덅', '��' => '덆', '��' => '덇', '��' => '덈', '��' => '덉', '��' => '덊', '��' => '덋', '��' => '덌', '��' => '덍', '��' => '덎', '��' => '덏', '��' => '덐', '��' => '덑', '��' => '덒', '��' => '덓', '��' => '덗', '��' => '덙', '��' => '덚', '��' => '덝', '��' => '덠', '��' => '덡', '��' => '덢', '��' => '덣', '�A' => '덦', '�B' => '덨', '�C' => '덪', '�D' => '덬', '�E' => '덭', '�F' => '덯', '�G' => '덲', '�H' => '덳', '�I' => '덵', '�J' => '덶', '�K' => '덷', '�L' => '덹', '�M' => '덺', '�N' => '덻', '�O' => '덼', '�P' => '덽', '�Q' => '덾', '�R' => '덿', '�S' => '뎂', '�T' => '뎆', '�U' => '뎇', '�V' => '뎈', '�W' => '뎉', '�X' => '뎊', '�Y' => '뎋', '�Z' => '뎍', '�a' => '뎎', '�b' => '뎏', '�c' => '뎑', '�d' => '뎒', '�e' => '뎓', '�f' => '뎕', '�g' => '뎖', '�h' => '뎗', '�i' => '뎘', '�j' => '뎙', '�k' => '뎚', '�l' => '뎛', '�m' => '뎜', '�n' => '뎝', '�o' => '뎞', '�p' => '뎟', '�q' => '뎢', '�r' => '뎣', '�s' => '뎤', '�t' => '뎥', '�u' => '뎦', '�v' => '뎧', '�w' => '뎩', '�x' => '뎪', '�y' => '뎫', '�z' => '뎭', '��' => '뎮', '��' => '뎯', '��' => '뎰', '��' => '뎱', '��' => '뎲', '��' => '뎳', '��' => '뎴', '��' => '뎵', '��' => '뎶', '��' => '뎷', '��' => '뎸', '��' => '뎹', '��' => '뎺', '��' => '뎻', '��' => '뎼', '��' => '뎽', '��' => '뎾', '��' => '뎿', '��' => '돀', '��' => '돁', '��' => '돂', '��' => '돃', '��' => '돆', '��' => '돇', '��' => '돉', '��' => '돊', '��' => '돍', '��' => '돏', '��' => '돑', '��' => '돒', '��' => '돓', '��' => '돖', '��' => '돘', '��' => '돚', '��' => '돜', '��' => '돞', '��' => '돟', '��' => '돡', '��' => '돢', '��' => '돣', '��' => '돥', '��' => '돦', '��' => '돧', '��' => '돩', '��' => '돪', '��' => '돫', '��' => '돬', '��' => '돭', '��' => '돮', '��' => '돯', '��' => '돰', '��' => '돱', '��' => '돲', '��' => '돳', '��' => '돴', '��' => '돵', '��' => '돶', '��' => '돷', '��' => '돸', '��' => '돹', '��' => '돺', '��' => '돻', '��' => '돽', '��' => '돾', '��' => '돿', '��' => '됀', '��' => '됁', '��' => '됂', '��' => '됃', '��' => '됄', '��' => '됅', '��' => '됆', '��' => '됇', '��' => '됈', '��' => '됉', '��' => '됊', '��' => '됋', '��' => '됌', '��' => '됍', '��' => '됎', '��' => '됏', '��' => '됑', '��' => '됒', '��' => '됓', '��' => '됔', '��' => '됕', '��' => '됖', '��' => '됗', '��' => '됙', '��' => '됚', '��' => '됛', '��' => '됝', '��' => '됞', '��' => '됟', '��' => '됡', '��' => '됢', '��' => '됣', '��' => '됤', '��' => '됥', '��' => '됦', '��' => '됧', '��' => '됪', '��' => '됬', '��' => '됭', '��' => '됮', '��' => '됯', '��' => '됰', '��' => '됱', '��' => '됲', '��' => '됳', '��' => '됵', '��' => '됶', '��' => '됷', '��' => '됸', '��' => '됹', '��' => '됺', '��' => '됻', '��' => '됼', '��' => '됽', '��' => '됾', '��' => '됿', '��' => '둀', '��' => '둁', '��' => '둂', '��' => '둃', '��' => '둄', '�A' => '둅', '�B' => '둆', '�C' => '둇', '�D' => '둈', '�E' => '둉', '�F' => '둊', '�G' => '둋', '�H' => '둌', '�I' => '둍', '�J' => '둎', '�K' => '둏', '�L' => '둒', '�M' => '둓', '�N' => '둕', '�O' => '둖', '�P' => '둗', '�Q' => '둙', '�R' => '둚', '�S' => '둛', '�T' => '둜', '�U' => '둝', '�V' => '둞', '�W' => '둟', '�X' => '둢', '�Y' => '둤', '�Z' => '둦', '�a' => '둧', '�b' => '둨', '�c' => '둩', '�d' => '둪', '�e' => '둫', '�f' => '둭', '�g' => '둮', '�h' => '둯', '�i' => '둰', '�j' => '둱', '�k' => '둲', '�l' => '둳', '�m' => '둴', '�n' => '둵', '�o' => '둶', '�p' => '둷', '�q' => '둸', '�r' => '둹', '�s' => '둺', '�t' => '둻', '�u' => '둼', '�v' => '둽', '�w' => '둾', '�x' => '둿', '�y' => '뒁', '�z' => '뒂', '��' => '뒃', '��' => '뒄', '��' => '뒅', '��' => '뒆', '��' => '뒇', '��' => '뒉', '��' => '뒊', '��' => '뒋', '��' => '뒌', '��' => '뒍', '��' => '뒎', '��' => '뒏', '��' => '뒐', '��' => '뒑', '��' => '뒒', '��' => '뒓', '��' => '뒔', '��' => '뒕', '��' => '뒖', '��' => '뒗', '��' => '뒘', '��' => '뒙', '��' => '뒚', '��' => '뒛', '��' => '뒜', '��' => '뒞', '��' => '뒟', '��' => '뒠', '��' => '뒡', '��' => '뒢', '��' => '뒣', '��' => '뒥', '��' => '뒦', '��' => '뒧', '��' => '뒩', '��' => '뒪', '��' => '뒫', '��' => '뒭', '��' => '뒮', '��' => '뒯', '��' => '뒰', '��' => '뒱', '��' => '뒲', '��' => '뒳', '��' => '뒴', '��' => '뒶', '��' => '뒸', '��' => '뒺', '��' => '뒻', '��' => '뒼', '��' => '뒽', '��' => '뒾', '��' => '뒿', '��' => '듁', '��' => '듂', '��' => '듃', '��' => '듅', '��' => '듆', '��' => '듇', '��' => '듉', '��' => '듊', '��' => '듋', '��' => '듌', '��' => '듍', '��' => '듎', '��' => '듏', '��' => '듑', '��' => '듒', '��' => '듓', '��' => '듔', '��' => '듖', '��' => '듗', '��' => '듘', '��' => '듙', '��' => '듚', '��' => '듛', '��' => '듞', '��' => '듟', '��' => '듡', '��' => '듢', '��' => '듥', '��' => '듧', '��' => '듨', '��' => '듩', '��' => '듪', '��' => '듫', '��' => '듮', '��' => '듰', '��' => '듲', '��' => '듳', '��' => '듴', '��' => '듵', '��' => '듶', '��' => '듷', '��' => '듹', '��' => '듺', '��' => '듻', '��' => '듼', '��' => '듽', '��' => '듾', '��' => '듿', '��' => '딀', '��' => '딁', '��' => '딂', '��' => '딃', '��' => '딄', '��' => '딅', '��' => '딆', '��' => '딇', '��' => '딈', '��' => '딉', '��' => '딊', '��' => '딋', '��' => '딌', '��' => '딍', '��' => '딎', '��' => '딏', '��' => '딐', '��' => '딑', '��' => '딒', '��' => '딓', '��' => '딖', '��' => '딗', '��' => '딙', '��' => '딚', '��' => '딝', '�A' => '딞', '�B' => '딟', '�C' => '딠', '�D' => '딡', '�E' => '딢', '�F' => '딣', '�G' => '딦', '�H' => '딫', '�I' => '딬', '�J' => '딭', '�K' => '딮', '�L' => '딯', '�M' => '딲', '�N' => '딳', '�O' => '딵', '�P' => '딶', '�Q' => '딷', '�R' => '딹', '�S' => '딺', '�T' => '딻', '�U' => '딼', '�V' => '딽', '�W' => '딾', '�X' => '딿', '�Y' => '땂', '�Z' => '땆', '�a' => '땇', '�b' => '땈', '�c' => '땉', '�d' => '땊', '�e' => '땎', '�f' => '땏', '�g' => '땑', '�h' => '땒', '�i' => '땓', '�j' => '땕', '�k' => '땖', '�l' => '땗', '�m' => '땘', '�n' => '땙', '�o' => '땚', '�p' => '땛', '�q' => '땞', '�r' => '땢', '�s' => '땣', '�t' => '땤', '�u' => '땥', '�v' => '땦', '�w' => '땧', '�x' => '땨', '�y' => '땩', '�z' => '땪', '��' => '땫', '��' => '땬', '��' => '땭', '��' => '땮', '��' => '땯', '��' => '땰', '��' => '땱', '��' => '땲', '��' => '땳', '��' => '땴', '��' => '땵', '��' => '땶', '��' => '땷', '��' => '땸', '��' => '땹', '��' => '땺', '��' => '땻', '��' => '땼', '��' => '땽', '��' => '땾', '��' => '땿', '��' => '떀', '��' => '떁', '��' => '떂', '��' => '떃', '��' => '떄', '��' => '떅', '��' => '떆', '��' => '떇', '��' => '떈', '��' => '떉', '��' => '떊', '��' => '떋', '��' => '떌', '��' => '떍', '��' => '떎', '��' => '떏', '��' => '떐', '��' => '떑', '��' => '떒', '��' => '떓', '��' => '떔', '��' => '떕', '��' => '떖', '��' => '떗', '��' => '떘', '��' => '떙', '��' => '떚', '��' => '떛', '��' => '떜', '��' => '떝', '��' => '떞', '��' => '떟', '��' => '떢', '��' => '떣', '��' => '떥', '��' => '떦', '��' => '떧', '��' => '떩', '��' => '떬', '��' => '떭', '��' => '떮', '��' => '떯', '��' => '떲', '��' => '떶', '��' => '떷', '��' => '떸', '��' => '떹', '��' => '떺', '��' => '떾', '��' => '떿', '��' => '뗁', '��' => '뗂', '��' => '뗃', '��' => '뗅', '��' => '뗆', '��' => '뗇', '��' => '뗈', '��' => '뗉', '��' => '뗊', '��' => '뗋', '��' => '뗎', '��' => '뗒', '��' => '뗓', '��' => '뗔', '��' => '뗕', '��' => '뗖', '��' => '뗗', '��' => '뗙', '��' => '뗚', '��' => '뗛', '��' => '뗜', '��' => '뗝', '��' => '뗞', '��' => '뗟', '��' => '뗠', '��' => '뗡', '��' => '뗢', '��' => '뗣', '��' => '뗤', '��' => '뗥', '��' => '뗦', '��' => '뗧', '��' => '뗨', '��' => '뗩', '��' => '뗪', '��' => '뗫', '��' => '뗭', '��' => '뗮', '��' => '뗯', '��' => '뗰', '��' => '뗱', '��' => '뗲', '��' => '뗳', '��' => '뗴', '��' => '뗵', '��' => '뗶', '��' => '뗷', '��' => '뗸', '��' => '뗹', '��' => '뗺', '��' => '뗻', '��' => '뗼', '��' => '뗽', '��' => '뗾', '��' => '뗿', '�A' => '똀', '�B' => '똁', '�C' => '똂', '�D' => '똃', '�E' => '똄', '�F' => '똅', '�G' => '똆', '�H' => '똇', '�I' => '똈', '�J' => '똉', '�K' => '똊', '�L' => '똋', '�M' => '똌', '�N' => '똍', '�O' => '똎', '�P' => '똏', '�Q' => '똒', '�R' => '똓', '�S' => '똕', '�T' => '똖', '�U' => '똗', '�V' => '똙', '�W' => '똚', '�X' => '똛', '�Y' => '똜', '�Z' => '똝', '�a' => '똞', '�b' => '똟', '�c' => '똠', '�d' => '똡', '�e' => '똢', '�f' => '똣', '�g' => '똤', '�h' => '똦', '�i' => '똧', '�j' => '똨', '�k' => '똩', '�l' => '똪', '�m' => '똫', '�n' => '똭', '�o' => '똮', '�p' => '똯', '�q' => '똰', '�r' => '똱', '�s' => '똲', '�t' => '똳', '�u' => '똵', '�v' => '똶', '�w' => '똷', '�x' => '똸', '�y' => '똹', '�z' => '똺', '��' => '똻', '��' => '똼', '��' => '똽', '��' => '똾', '��' => '똿', '��' => '뙀', '��' => '뙁', '��' => '뙂', '��' => '뙃', '��' => '뙄', '��' => '뙅', '��' => '뙆', '��' => '뙇', '��' => '뙉', '��' => '뙊', '��' => '뙋', '��' => '뙌', '��' => '뙍', '��' => '뙎', '��' => '뙏', '��' => '뙐', '��' => '뙑', '��' => '뙒', '��' => '뙓', '��' => '뙔', '��' => '뙕', '��' => '뙖', '��' => '뙗', '��' => '뙘', '��' => '뙙', '��' => '뙚', '��' => '뙛', '��' => '뙜', '��' => '뙝', '��' => '뙞', '��' => '뙟', '��' => '뙠', '��' => '뙡', '��' => '뙢', '��' => '뙣', '��' => '뙥', '��' => '뙦', '��' => '뙧', '��' => '뙩', '��' => '뙪', '��' => '뙫', '��' => '뙬', '��' => '뙭', '��' => '뙮', '��' => '뙯', '��' => '뙰', '��' => '뙱', '��' => '뙲', '��' => '뙳', '��' => '뙴', '��' => '뙵', '��' => '뙶', '��' => '뙷', '��' => '뙸', '��' => '뙹', '��' => '뙺', '��' => '뙻', '��' => '뙼', '��' => '뙽', '��' => '뙾', '��' => '뙿', '��' => '뚀', '��' => '뚁', '��' => '뚂', '��' => '뚃', '��' => '뚄', '��' => '뚅', '��' => '뚆', '��' => '뚇', '��' => '뚈', '��' => '뚉', '��' => '뚊', '��' => '뚋', '��' => '뚌', '��' => '뚍', '��' => '뚎', '��' => '뚏', '��' => '뚐', '��' => '뚑', '��' => '뚒', '��' => '뚓', '��' => '뚔', '��' => '뚕', '��' => '뚖', '��' => '뚗', '��' => '뚘', '��' => '뚙', '��' => '뚚', '��' => '뚛', '��' => '뚞', '��' => '뚟', '��' => '뚡', '��' => '뚢', '��' => '뚣', '��' => '뚥', '��' => '뚦', '��' => '뚧', '��' => '뚨', '��' => '뚩', '��' => '뚪', '��' => '뚭', '��' => '뚮', '��' => '뚯', '��' => '뚰', '��' => '뚲', '��' => '뚳', '��' => '뚴', '��' => '뚵', '��' => '뚶', '��' => '뚷', '��' => '뚸', '��' => '뚹', '��' => '뚺', '��' => '뚻', '��' => '뚼', '��' => '뚽', '��' => '뚾', '��' => '뚿', '��' => '뛀', '��' => '뛁', '��' => '뛂', '�A' => '뛃', '�B' => '뛄', '�C' => '뛅', '�D' => '뛆', '�E' => '뛇', '�F' => '뛈', '�G' => '뛉', '�H' => '뛊', '�I' => '뛋', '�J' => '뛌', '�K' => '뛍', '�L' => '뛎', '�M' => '뛏', '�N' => '뛐', '�O' => '뛑', '�P' => '뛒', '�Q' => '뛓', '�R' => '뛕', '�S' => '뛖', '�T' => '뛗', '�U' => '뛘', '�V' => '뛙', '�W' => '뛚', '�X' => '뛛', '�Y' => '뛜', '�Z' => '뛝', '�a' => '뛞', '�b' => '뛟', '�c' => '뛠', '�d' => '뛡', '�e' => '뛢', '�f' => '뛣', '�g' => '뛤', '�h' => '뛥', '�i' => '뛦', '�j' => '뛧', '�k' => '뛨', '�l' => '뛩', '�m' => '뛪', '�n' => '뛫', '�o' => '뛬', '�p' => '뛭', '�q' => '뛮', '�r' => '뛯', '�s' => '뛱', '�t' => '뛲', '�u' => '뛳', '�v' => '뛵', '�w' => '뛶', '�x' => '뛷', '�y' => '뛹', '�z' => '뛺', '��' => '뛻', '��' => '뛼', '��' => '뛽', '��' => '뛾', '��' => '뛿', '��' => '뜂', '��' => '뜃', '��' => '뜄', '��' => '뜆', '��' => '뜇', '��' => '뜈', '��' => '뜉', '��' => '뜊', '��' => '뜋', '��' => '뜌', '��' => '뜍', '��' => '뜎', '��' => '뜏', '��' => '뜐', '��' => '뜑', '��' => '뜒', '��' => '뜓', '��' => '뜔', '��' => '뜕', '��' => '뜖', '��' => '뜗', '��' => '뜘', '��' => '뜙', '��' => '뜚', '��' => '뜛', '��' => '뜜', '��' => '뜝', '��' => '뜞', '��' => '뜟', '��' => '뜠', '��' => '뜡', '��' => '뜢', '��' => '뜣', '��' => '뜤', '��' => '뜥', '��' => '뜦', '��' => '뜧', '��' => '뜪', '��' => '뜫', '��' => '뜭', '��' => '뜮', '��' => '뜱', '��' => '뜲', '��' => '뜳', '��' => '뜴', '��' => '뜵', '��' => '뜶', '��' => '뜷', '��' => '뜺', '��' => '뜼', '��' => '뜽', '��' => '뜾', '��' => '뜿', '��' => '띀', '��' => '띁', '��' => '띂', '��' => '띃', '��' => '띅', '��' => '띆', '��' => '띇', '��' => '띉', '��' => '띊', '��' => '띋', '��' => '띍', '��' => '띎', '��' => '띏', '��' => '띐', '��' => '띑', '��' => '띒', '��' => '띓', '��' => '띖', '��' => '띗', '��' => '띘', '��' => '띙', '��' => '띚', '��' => '띛', '��' => '띜', '��' => '띝', '��' => '띞', '��' => '띟', '��' => '띡', '��' => '띢', '��' => '띣', '��' => '띥', '��' => '띦', '��' => '띧', '��' => '띩', '��' => '띪', '��' => '띫', '��' => '띬', '��' => '띭', '��' => '띮', '��' => '띯', '��' => '띲', '��' => '띴', '��' => '띶', '��' => '띷', '��' => '띸', '��' => '띹', '��' => '띺', '��' => '띻', '��' => '띾', '��' => '띿', '��' => '랁', '��' => '랂', '��' => '랃', '��' => '랅', '��' => '랆', '��' => '랇', '��' => '랈', '��' => '랉', '��' => '랊', '��' => '랋', '��' => '랎', '��' => '랓', '��' => '랔', '��' => '랕', '��' => '랚', '��' => '랛', '��' => '랝', '��' => '랞', '�A' => '랟', '�B' => '랡', '�C' => '랢', '�D' => '랣', '�E' => '랤', '�F' => '랥', '�G' => '랦', '�H' => '랧', '�I' => '랪', '�J' => '랮', '�K' => '랯', '�L' => '랰', '�M' => '랱', '�N' => '랲', '�O' => '랳', '�P' => '랶', '�Q' => '랷', '�R' => '랹', '�S' => '랺', '�T' => '랻', '�U' => '랼', '�V' => '랽', '�W' => '랾', '�X' => '랿', '�Y' => '럀', '�Z' => '럁', '�a' => '럂', '�b' => '럃', '�c' => '럄', '�d' => '럅', '�e' => '럆', '�f' => '럈', '�g' => '럊', '�h' => '럋', '�i' => '럌', '�j' => '럍', '�k' => '럎', '�l' => '럏', '�m' => '럐', '�n' => '럑', '�o' => '럒', '�p' => '럓', '�q' => '럔', '�r' => '럕', '�s' => '럖', '�t' => '럗', '�u' => '럘', '�v' => '럙', '�w' => '럚', '�x' => '럛', '�y' => '럜', '�z' => '럝', '��' => '럞', '��' => '럟', '��' => '럠', '��' => '럡', '��' => '럢', '��' => '럣', '��' => '럤', '��' => '럥', '��' => '럦', '��' => '럧', '��' => '럨', '��' => '럩', '��' => '럪', '��' => '럫', '��' => '럮', '��' => '럯', '��' => '럱', '��' => '럲', '��' => '럳', '��' => '럵', '��' => '럶', '��' => '럷', '��' => '럸', '��' => '럹', '��' => '럺', '��' => '럻', '��' => '럾', '��' => '렂', '��' => '렃', '��' => '렄', '��' => '렅', '��' => '렆', '��' => '렊', '��' => '렋', '��' => '렍', '��' => '렎', '��' => '렏', '��' => '렑', '��' => '렒', '��' => '렓', '��' => '렔', '��' => '렕', '��' => '렖', '��' => '렗', '��' => '렚', '��' => '렜', '��' => '렞', '��' => '렟', '��' => '렠', '��' => '렡', '��' => '렢', '��' => '렣', '��' => '렦', '��' => '렧', '��' => '렩', '��' => '렪', '��' => '렫', '��' => '렭', '��' => '렮', '��' => '렯', '��' => '렰', '��' => '렱', '��' => '렲', '��' => '렳', '��' => '렶', '��' => '렺', '��' => '렻', '��' => '렼', '��' => '렽', '��' => '렾', '��' => '렿', '��' => '롁', '��' => '롂', '��' => '롃', '��' => '롅', '��' => '롆', '��' => '롇', '��' => '롈', '��' => '롉', '��' => '롊', '��' => '롋', '��' => '롌', '��' => '롍', '��' => '롎', '��' => '롏', '��' => '롐', '��' => '롒', '��' => '롔', '��' => '롕', '��' => '롖', '��' => '롗', '��' => '롘', '��' => '롙', '��' => '롚', '��' => '롛', '��' => '롞', '��' => '롟', '��' => '롡', '��' => '롢', '��' => '롣', '��' => '롥', '��' => '롦', '��' => '롧', '��' => '롨', '��' => '롩', '��' => '롪', '��' => '롫', '��' => '롮', '��' => '롰', '��' => '롲', '��' => '롳', '��' => '롴', '��' => '롵', '��' => '롶', '��' => '롷', '��' => '롹', '��' => '롺', '��' => '롻', '��' => '롽', '��' => '롾', '��' => '롿', '��' => '뢀', '��' => '뢁', '��' => '뢂', '��' => '뢃', '��' => '뢄', '�A' => '뢅', '�B' => '뢆', '�C' => '뢇', '�D' => '뢈', '�E' => '뢉', '�F' => '뢊', '�G' => '뢋', '�H' => '뢌', '�I' => '뢎', '�J' => '뢏', '�K' => '뢐', '�L' => '뢑', '�M' => '뢒', '�N' => '뢓', '�O' => '뢔', '�P' => '뢕', '�Q' => '뢖', '�R' => '뢗', '�S' => '뢘', '�T' => '뢙', '�U' => '뢚', '�V' => '뢛', '�W' => '뢜', '�X' => '뢝', '�Y' => '뢞', '�Z' => '뢟', '�a' => '뢠', '�b' => '뢡', '�c' => '뢢', '�d' => '뢣', '�e' => '뢤', '�f' => '뢥', '�g' => '뢦', '�h' => '뢧', '�i' => '뢩', '�j' => '뢪', '�k' => '뢫', '�l' => '뢬', '�m' => '뢭', '�n' => '뢮', '�o' => '뢯', '�p' => '뢱', '�q' => '뢲', '�r' => '뢳', '�s' => '뢵', '�t' => '뢶', '�u' => '뢷', '�v' => '뢹', '�w' => '뢺', '�x' => '뢻', '�y' => '뢼', '�z' => '뢽', '��' => '뢾', '��' => '뢿', '��' => '룂', '��' => '룄', '��' => '룆', '��' => '룇', '��' => '룈', '��' => '룉', '��' => '룊', '��' => '룋', '��' => '룍', '��' => '룎', '��' => '룏', '��' => '룑', '��' => '룒', '��' => '룓', '��' => '룕', '��' => '룖', '��' => '룗', '��' => '룘', '��' => '룙', '��' => '룚', '��' => '룛', '��' => '룜', '��' => '룞', '��' => '룠', '��' => '룢', '��' => '룣', '��' => '룤', '��' => '룥', '��' => '룦', '��' => '룧', '��' => '룪', '��' => '룫', '��' => '룭', '��' => '룮', '��' => '룯', '��' => '룱', '��' => '룲', '��' => '룳', '��' => '룴', '��' => '룵', '��' => '룶', '��' => '룷', '��' => '룺', '��' => '룼', '��' => '룾', '��' => '룿', '��' => '뤀', '��' => '뤁', '��' => '뤂', '��' => '뤃', '��' => '뤅', '��' => '뤆', '��' => '뤇', '��' => '뤈', '��' => '뤉', '��' => '뤊', '��' => '뤋', '��' => '뤌', '��' => '뤍', '��' => '뤎', '��' => '뤏', '��' => '뤐', '��' => '뤑', '��' => '뤒', '��' => '뤓', '��' => '뤔', '��' => '뤕', '��' => '뤖', '��' => '뤗', '��' => '뤙', '��' => '뤚', '��' => '뤛', '��' => '뤜', '��' => '뤝', '��' => '뤞', '��' => '뤟', '��' => '뤡', '��' => '뤢', '��' => '뤣', '��' => '뤤', '��' => '뤥', '��' => '뤦', '��' => '뤧', '��' => '뤨', '��' => '뤩', '��' => '뤪', '��' => '뤫', '��' => '뤬', '��' => '뤭', '��' => '뤮', '��' => '뤯', '��' => '뤰', '��' => '뤱', '��' => '뤲', '��' => '뤳', '��' => '뤴', '��' => '뤵', '��' => '뤶', '��' => '뤷', '��' => '뤸', '��' => '뤹', '��' => '뤺', '��' => '뤻', '��' => '뤾', '��' => '뤿', '��' => '륁', '��' => '륂', '��' => '륃', '��' => '륅', '��' => '륆', '��' => '륇', '��' => '륈', '��' => '륉', '��' => '륊', '��' => '륋', '��' => '륍', '��' => '륎', '��' => '륐', '��' => '륒', '��' => '륓', '��' => '륔', '��' => '륕', '��' => '륖', '��' => '륗', '�A' => '륚', '�B' => '륛', '�C' => '륝', '�D' => '륞', '�E' => '륟', '�F' => '륡', '�G' => '륢', '�H' => '륣', '�I' => '륤', '�J' => '륥', '�K' => '륦', '�L' => '륧', '�M' => '륪', '�N' => '륬', '�O' => '륮', '�P' => '륯', '�Q' => '륰', '�R' => '륱', '�S' => '륲', '�T' => '륳', '�U' => '륶', '�V' => '륷', '�W' => '륹', '�X' => '륺', '�Y' => '륻', '�Z' => '륽', '�a' => '륾', '�b' => '륿', '�c' => '릀', '�d' => '릁', '�e' => '릂', '�f' => '릃', '�g' => '릆', '�h' => '릈', '�i' => '릋', '�j' => '릌', '�k' => '릏', '�l' => '릐', '�m' => '릑', '�n' => '릒', '�o' => '릓', '�p' => '릔', '�q' => '릕', '�r' => '릖', '�s' => '릗', '�t' => '릘', '�u' => '릙', '�v' => '릚', '�w' => '릛', '�x' => '릜', '�y' => '릝', '�z' => '릞', '��' => '릟', '��' => '릠', '��' => '릡', '��' => '릢', '��' => '릣', '��' => '릤', '��' => '릥', '��' => '릦', '��' => '릧', '��' => '릨', '��' => '릩', '��' => '릪', '��' => '릫', '��' => '릮', '��' => '릯', '��' => '릱', '��' => '릲', '��' => '릳', '��' => '릵', '��' => '릶', '��' => '릷', '��' => '릸', '��' => '릹', '��' => '릺', '��' => '릻', '��' => '릾', '��' => '맀', '��' => '맂', '��' => '맃', '��' => '맄', '��' => '맅', '��' => '맆', '��' => '맇', '��' => '맊', '��' => '맋', '��' => '맍', '��' => '맓', '��' => '맔', '��' => '맕', '��' => '맖', '��' => '맗', '��' => '맚', '��' => '맜', '��' => '맟', '��' => '맠', '��' => '맢', '��' => '맦', '��' => '맧', '��' => '맩', '��' => '맪', '��' => '맫', '��' => '맭', '��' => '맮', '��' => '맯', '��' => '맰', '��' => '맱', '��' => '맲', '��' => '맳', '��' => '맶', '��' => '맻', '��' => '맼', '��' => '맽', '��' => '맾', '��' => '맿', '��' => '먂', '��' => '먃', '��' => '먄', '��' => '먅', '��' => '먆', '��' => '먇', '��' => '먉', '��' => '먊', '��' => '먋', '��' => '먌', '��' => '먍', '��' => '먎', '��' => '먏', '��' => '먐', '��' => '먑', '��' => '먒', '��' => '먓', '��' => '먔', '��' => '먖', '��' => '먗', '��' => '먘', '��' => '먙', '��' => '먚', '��' => '먛', '��' => '먜', '��' => '먝', '��' => '먞', '��' => '먟', '��' => '먠', '��' => '먡', '��' => '먢', '��' => '먣', '��' => '먤', '��' => '먥', '��' => '먦', '��' => '먧', '��' => '먨', '��' => '먩', '��' => '먪', '��' => '먫', '��' => '먬', '��' => '먭', '��' => '먮', '��' => '먯', '��' => '먰', '��' => '먱', '��' => '먲', '��' => '먳', '��' => '먴', '��' => '먵', '��' => '먶', '��' => '먷', '��' => '먺', '��' => '먻', '��' => '먽', '��' => '먾', '��' => '먿', '��' => '멁', '��' => '멃', '��' => '멄', '��' => '멅', '��' => '멆', '�A' => '멇', '�B' => '멊', '�C' => '멌', '�D' => '멏', '�E' => '멐', '�F' => '멑', '�G' => '멒', '�H' => '멖', '�I' => '멗', '�J' => '멙', '�K' => '멚', '�L' => '멛', '�M' => '멝', '�N' => '멞', '�O' => '멟', '�P' => '멠', '�Q' => '멡', '�R' => '멢', '�S' => '멣', '�T' => '멦', '�U' => '멪', '�V' => '멫', '�W' => '멬', '�X' => '멭', '�Y' => '멮', '�Z' => '멯', '�a' => '멲', '�b' => '멳', '�c' => '멵', '�d' => '멶', '�e' => '멷', '�f' => '멹', '�g' => '멺', '�h' => '멻', '�i' => '멼', '�j' => '멽', '�k' => '멾', '�l' => '멿', '�m' => '몀', '�n' => '몁', '�o' => '몂', '�p' => '몆', '�q' => '몈', '�r' => '몉', '�s' => '몊', '�t' => '몋', '�u' => '몍', '�v' => '몎', '�w' => '몏', '�x' => '몐', '�y' => '몑', '�z' => '몒', '��' => '몓', '��' => '몔', '��' => '몕', '��' => '몖', '��' => '몗', '��' => '몘', '��' => '몙', '��' => '몚', '��' => '몛', '��' => '몜', '��' => '몝', '��' => '몞', '��' => '몟', '��' => '몠', '��' => '몡', '��' => '몢', '��' => '몣', '��' => '몤', '��' => '몥', '��' => '몦', '��' => '몧', '��' => '몪', '��' => '몭', '��' => '몮', '��' => '몯', '��' => '몱', '��' => '몳', '��' => '몴', '��' => '몵', '��' => '몶', '��' => '몷', '��' => '몺', '��' => '몼', '��' => '몾', '��' => '몿', '��' => '뫀', '��' => '뫁', '��' => '뫂', '��' => '뫃', '��' => '뫅', '��' => '뫆', '��' => '뫇', '��' => '뫉', '��' => '뫊', '��' => '뫋', '��' => '뫌', '��' => '뫍', '��' => '뫎', '��' => '뫏', '��' => '뫐', '��' => '뫑', '��' => '뫒', '��' => '뫓', '��' => '뫔', '��' => '뫕', '��' => '뫖', '��' => '뫗', '��' => '뫚', '��' => '뫛', '��' => '뫜', '��' => '뫝', '��' => '뫞', '��' => '뫟', '��' => '뫠', '��' => '뫡', '��' => '뫢', '��' => '뫣', '��' => '뫤', '��' => '뫥', '��' => '뫦', '��' => '뫧', '��' => '뫨', '��' => '뫩', '��' => '뫪', '��' => '뫫', '��' => '뫬', '��' => '뫭', '��' => '뫮', '��' => '뫯', '��' => '뫰', '��' => '뫱', '��' => '뫲', '��' => '뫳', '��' => '뫴', '��' => '뫵', '��' => '뫶', '��' => '뫷', '��' => '뫸', '��' => '뫹', '��' => '뫺', '��' => '뫻', '��' => '뫽', '��' => '뫾', '��' => '뫿', '��' => '묁', '��' => '묂', '��' => '묃', '��' => '묅', '��' => '묆', '��' => '묇', '��' => '묈', '��' => '묉', '��' => '묊', '��' => '묋', '��' => '묌', '��' => '묎', '��' => '묐', '��' => '묒', '��' => '묓', '��' => '묔', '��' => '묕', '��' => '묖', '��' => '묗', '��' => '묙', '��' => '묚', '��' => '묛', '��' => '묝', '��' => '묞', '��' => '묟', '��' => '묡', '��' => '묢', '��' => '묣', '��' => '묤', '��' => '묥', '��' => '묦', '��' => '묧', '�A' => '묨', '�B' => '묪', '�C' => '묬', '�D' => '묭', '�E' => '묮', '�F' => '묯', '�G' => '묰', '�H' => '묱', '�I' => '묲', '�J' => '묳', '�K' => '묷', '�L' => '묹', '�M' => '묺', '�N' => '묿', '�O' => '뭀', '�P' => '뭁', '�Q' => '뭂', '�R' => '뭃', '�S' => '뭆', '�T' => '뭈', '�U' => '뭊', '�V' => '뭋', '�W' => '뭌', '�X' => '뭎', '�Y' => '뭑', '�Z' => '뭒', '�a' => '뭓', '�b' => '뭕', '�c' => '뭖', '�d' => '뭗', '�e' => '뭙', '�f' => '뭚', '�g' => '뭛', '�h' => '뭜', '�i' => '뭝', '�j' => '뭞', '�k' => '뭟', '�l' => '뭠', '�m' => '뭢', '�n' => '뭤', '�o' => '뭥', '�p' => '뭦', '�q' => '뭧', '�r' => '뭨', '�s' => '뭩', '�t' => '뭪', '�u' => '뭫', '�v' => '뭭', '�w' => '뭮', '�x' => '뭯', '�y' => '뭰', '�z' => '뭱', '��' => '뭲', '��' => '뭳', '��' => '뭴', '��' => '뭵', '��' => '뭶', '��' => '뭷', '��' => '뭸', '��' => '뭹', '��' => '뭺', '��' => '뭻', '��' => '뭼', '��' => '뭽', '��' => '뭾', '��' => '뭿', '��' => '뮀', '��' => '뮁', '��' => '뮂', '��' => '뮃', '��' => '뮄', '��' => '뮅', '��' => '뮆', '��' => '뮇', '��' => '뮉', '��' => '뮊', '��' => '뮋', '��' => '뮍', '��' => '뮎', '��' => '뮏', '��' => '뮑', '��' => '뮒', '��' => '뮓', '��' => '뮔', '��' => '뮕', '��' => '뮖', '��' => '뮗', '��' => '뮘', '��' => '뮙', '��' => '뮚', '��' => '뮛', '��' => '뮜', '��' => '뮝', '��' => '뮞', '��' => '뮟', '��' => '뮠', '��' => '뮡', '��' => '뮢', '��' => '뮣', '��' => '뮥', '��' => '뮦', '��' => '뮧', '��' => '뮩', '��' => '뮪', '��' => '뮫', '��' => '뮭', '��' => '뮮', '��' => '뮯', '��' => '뮰', '��' => '뮱', '��' => '뮲', '��' => '뮳', '��' => '뮵', '��' => '뮶', '��' => '뮸', '��' => '뮹', '��' => '뮺', '��' => '뮻', '��' => '뮼', '��' => '뮽', '��' => '뮾', '��' => '뮿', '��' => '믁', '��' => '믂', '��' => '믃', '��' => '믅', '��' => '믆', '��' => '믇', '��' => '믉', '��' => '믊', '��' => '믋', '��' => '믌', '��' => '믍', '��' => '믎', '��' => '믏', '��' => '믑', '��' => '믒', '��' => '믔', '��' => '믕', '��' => '믖', '��' => '믗', '��' => '믘', '��' => '믙', '��' => '믚', '��' => '믛', '��' => '믜', '��' => '믝', '��' => '믞', '��' => '믟', '��' => '믠', '��' => '믡', '��' => '믢', '��' => '믣', '��' => '믤', '��' => '믥', '��' => '믦', '��' => '믧', '��' => '믨', '��' => '믩', '��' => '믪', '��' => '믫', '��' => '믬', '��' => '믭', '��' => '믮', '��' => '믯', '��' => '믰', '��' => '믱', '��' => '믲', '��' => '믳', '��' => '믴', '��' => '믵', '��' => '믶', '��' => '믷', '��' => '믺', '��' => '믻', '��' => '믽', '��' => '믾', '��' => '밁', '�A' => '밃', '�B' => '밄', '�C' => '밅', '�D' => '밆', '�E' => '밇', '�F' => '밊', '�G' => '밎', '�H' => '밐', '�I' => '밒', '�J' => '밓', '�K' => '밙', '�L' => '밚', '�M' => '밠', '�N' => '밡', '�O' => '밢', '�P' => '밣', '�Q' => '밦', '�R' => '밨', '�S' => '밪', '�T' => '밫', '�U' => '밬', '�V' => '밮', '�W' => '밯', '�X' => '밲', '�Y' => '밳', '�Z' => '밵', '�a' => '밶', '�b' => '밷', '�c' => '밹', '�d' => '밺', '�e' => '밻', '�f' => '밼', '�g' => '밽', '�h' => '밾', '�i' => '밿', '�j' => '뱂', '�k' => '뱆', '�l' => '뱇', '�m' => '뱈', '�n' => '뱊', '�o' => '뱋', '�p' => '뱎', '�q' => '뱏', '�r' => '뱑', '�s' => '뱒', '�t' => '뱓', '�u' => '뱔', '�v' => '뱕', '�w' => '뱖', '�x' => '뱗', '�y' => '뱘', '�z' => '뱙', '��' => '뱚', '��' => '뱛', '��' => '뱜', '��' => '뱞', '��' => '뱟', '��' => '뱠', '��' => '뱡', '��' => '뱢', '��' => '뱣', '��' => '뱤', '��' => '뱥', '��' => '뱦', '��' => '뱧', '��' => '뱨', '��' => '뱩', '��' => '뱪', '��' => '뱫', '��' => '뱬', '��' => '뱭', '��' => '뱮', '��' => '뱯', '��' => '뱰', '��' => '뱱', '��' => '뱲', '��' => '뱳', '��' => '뱴', '��' => '뱵', '��' => '뱶', '��' => '뱷', '��' => '뱸', '��' => '뱹', '��' => '뱺', '��' => '뱻', '��' => '뱼', '��' => '뱽', '��' => '뱾', '��' => '뱿', '��' => '벀', '��' => '벁', '��' => '벂', '��' => '벃', '��' => '벆', '��' => '벇', '��' => '벉', '��' => '벊', '��' => '벍', '��' => '벏', '��' => '벐', '��' => '벑', '��' => '벒', '��' => '벓', '��' => '벖', '��' => '벘', '��' => '벛', '��' => '벜', '��' => '벝', '��' => '벞', '��' => '벟', '��' => '벢', '��' => '벣', '��' => '벥', '��' => '벦', '��' => '벩', '��' => '벪', '��' => '벫', '��' => '벬', '��' => '벭', '��' => '벮', '��' => '벯', '��' => '벲', '��' => '벶', '��' => '벷', '��' => '벸', '��' => '벹', '��' => '벺', '��' => '벻', '��' => '벾', '��' => '벿', '��' => '볁', '��' => '볂', '��' => '볃', '��' => '볅', '��' => '볆', '��' => '볇', '��' => '볈', '��' => '볉', '��' => '볊', '��' => '볋', '��' => '볌', '��' => '볎', '��' => '볒', '��' => '볓', '��' => '볔', '��' => '볖', '��' => '볗', '��' => '볙', '��' => '볚', '��' => '볛', '��' => '볝', '��' => '볞', '��' => '볟', '��' => '볠', '��' => '볡', '��' => '볢', '��' => '볣', '��' => '볤', '��' => '볥', '��' => '볦', '��' => '볧', '��' => '볨', '��' => '볩', '��' => '볪', '��' => '볫', '��' => '볬', '��' => '볭', '��' => '볮', '��' => '볯', '��' => '볰', '��' => '볱', '��' => '볲', '��' => '볳', '��' => '볷', '��' => '볹', '��' => '볺', '��' => '볻', '��' => '볽', '�A' => '볾', '�B' => '볿', '�C' => '봀', '�D' => '봁', '�E' => '봂', '�F' => '봃', '�G' => '봆', '�H' => '봈', '�I' => '봊', '�J' => '봋', '�K' => '봌', '�L' => '봍', '�M' => '봎', '�N' => '봏', '�O' => '봑', '�P' => '봒', '�Q' => '봓', '�R' => '봕', '�S' => '봖', '�T' => '봗', '�U' => '봘', '�V' => '봙', '�W' => '봚', '�X' => '봛', '�Y' => '봜', '�Z' => '봝', '�a' => '봞', '�b' => '봟', '�c' => '봠', '�d' => '봡', '�e' => '봢', '�f' => '봣', '�g' => '봥', '�h' => '봦', '�i' => '봧', '�j' => '봨', '�k' => '봩', '�l' => '봪', '�m' => '봫', '�n' => '봭', '�o' => '봮', '�p' => '봯', '�q' => '봰', '�r' => '봱', '�s' => '봲', '�t' => '봳', '�u' => '봴', '�v' => '봵', '�w' => '봶', '�x' => '봷', '�y' => '봸', '�z' => '봹', '��' => '봺', '��' => '봻', '��' => '봼', '��' => '봽', '��' => '봾', '��' => '봿', '��' => '뵁', '��' => '뵂', '��' => '뵃', '��' => '뵄', '��' => '뵅', '��' => '뵆', '��' => '뵇', '��' => '뵊', '��' => '뵋', '��' => '뵍', '��' => '뵎', '��' => '뵏', '��' => '뵑', '��' => '뵒', '��' => '뵓', '��' => '뵔', '��' => '뵕', '��' => '뵖', '��' => '뵗', '��' => '뵚', '��' => '뵛', '��' => '뵜', '��' => '뵝', '��' => '뵞', '��' => '뵟', '��' => '뵠', '��' => '뵡', '��' => '뵢', '��' => '뵣', '��' => '뵥', '��' => '뵦', '��' => '뵧', '��' => '뵩', '��' => '뵪', '��' => '뵫', '��' => '뵬', '��' => '뵭', '��' => '뵮', '��' => '뵯', '��' => '뵰', '��' => '뵱', '��' => '뵲', '��' => '뵳', '��' => '뵴', '��' => '뵵', '��' => '뵶', '��' => '뵷', '��' => '뵸', '��' => '뵹', '��' => '뵺', '��' => '뵻', '��' => '뵼', '��' => '뵽', '��' => '뵾', '��' => '뵿', '��' => '붂', '��' => '붃', '��' => '붅', '��' => '붆', '��' => '붋', '��' => '붌', '��' => '붍', '��' => '붎', '��' => '붏', '��' => '붒', '��' => '붔', '��' => '붖', '��' => '붗', '��' => '붘', '��' => '붛', '��' => '붝', '��' => '붞', '��' => '붟', '��' => '붠', '��' => '붡', '��' => '붢', '��' => '붣', '��' => '붥', '��' => '붦', '��' => '붧', '��' => '붨', '��' => '붩', '��' => '붪', '��' => '붫', '��' => '붬', '��' => '붭', '��' => '붮', '��' => '붯', '��' => '붱', '��' => '붲', '��' => '붳', '��' => '붴', '��' => '붵', '��' => '붶', '��' => '붷', '��' => '붹', '��' => '붺', '��' => '붻', '��' => '붼', '��' => '붽', '��' => '붾', '��' => '붿', '��' => '뷀', '��' => '뷁', '��' => '뷂', '��' => '뷃', '��' => '뷄', '��' => '뷅', '��' => '뷆', '��' => '뷇', '��' => '뷈', '��' => '뷉', '��' => '뷊', '��' => '뷋', '��' => '뷌', '��' => '뷍', '��' => '뷎', '��' => '뷏', '��' => '뷐', '��' => '뷑', '�A' => '뷒', '�B' => '뷓', '�C' => '뷖', '�D' => '뷗', '�E' => '뷙', '�F' => '뷚', '�G' => '뷛', '�H' => '뷝', '�I' => '뷞', '�J' => '뷟', '�K' => '뷠', '�L' => '뷡', '�M' => '뷢', '�N' => '뷣', '�O' => '뷤', '�P' => '뷥', '�Q' => '뷦', '�R' => '뷧', '�S' => '뷨', '�T' => '뷪', '�U' => '뷫', '�V' => '뷬', '�W' => '뷭', '�X' => '뷮', '�Y' => '뷯', '�Z' => '뷱', '�a' => '뷲', '�b' => '뷳', '�c' => '뷵', '�d' => '뷶', '�e' => '뷷', '�f' => '뷹', '�g' => '뷺', '�h' => '뷻', '�i' => '뷼', '�j' => '뷽', '�k' => '뷾', '�l' => '뷿', '�m' => '븁', '�n' => '븂', '�o' => '븄', '�p' => '븆', '�q' => '븇', '�r' => '븈', '�s' => '븉', '�t' => '븊', '�u' => '븋', '�v' => '븎', '�w' => '븏', '�x' => '븑', '�y' => '븒', '�z' => '븓', '��' => '븕', '��' => '븖', '��' => '븗', '��' => '븘', '��' => '븙', '��' => '븚', '��' => '븛', '��' => '븞', '��' => '븠', '��' => '븡', '��' => '븢', '��' => '븣', '��' => '븤', '��' => '븥', '��' => '븦', '��' => '븧', '��' => '븨', '��' => '븩', '��' => '븪', '��' => '븫', '��' => '븬', '��' => '븭', '��' => '븮', '��' => '븯', '��' => '븰', '��' => '븱', '��' => '븲', '��' => '븳', '��' => '븴', '��' => '븵', '��' => '븶', '��' => '븷', '��' => '븸', '��' => '븹', '��' => '븺', '��' => '븻', '��' => '븼', '��' => '븽', '��' => '븾', '��' => '븿', '��' => '빀', '��' => '빁', '��' => '빂', '��' => '빃', '��' => '빆', '��' => '빇', '��' => '빉', '��' => '빊', '��' => '빋', '��' => '빍', '��' => '빏', '��' => '빐', '��' => '빑', '��' => '빒', '��' => '빓', '��' => '빖', '��' => '빘', '��' => '빜', '��' => '빝', '��' => '빞', '��' => '빟', '��' => '빢', '��' => '빣', '��' => '빥', '��' => '빦', '��' => '빧', '��' => '빩', '��' => '빫', '��' => '빬', '��' => '빭', '��' => '빮', '��' => '빯', '��' => '빲', '��' => '빶', '��' => '빷', '��' => '빸', '��' => '빹', '��' => '빺', '��' => '빾', '��' => '빿', '��' => '뺁', '��' => '뺂', '��' => '뺃', '��' => '뺅', '��' => '뺆', '��' => '뺇', '��' => '뺈', '��' => '뺉', '��' => '뺊', '��' => '뺋', '��' => '뺎', '��' => '뺒', '��' => '뺓', '��' => '뺔', '��' => '뺕', '��' => '뺖', '��' => '뺗', '��' => '뺚', '��' => '뺛', '��' => '뺜', '��' => '뺝', '��' => '뺞', '��' => '뺟', '��' => '뺠', '��' => '뺡', '��' => '뺢', '��' => '뺣', '��' => '뺤', '��' => '뺥', '��' => '뺦', '��' => '뺧', '��' => '뺩', '��' => '뺪', '��' => '뺫', '��' => '뺬', '��' => '뺭', '��' => '뺮', '��' => '뺯', '��' => '뺰', '��' => '뺱', '��' => '뺲', '��' => '뺳', '��' => '뺴', '��' => '뺵', '��' => '뺶', '��' => '뺷', '�A' => '뺸', '�B' => '뺹', '�C' => '뺺', '�D' => '뺻', '�E' => '뺼', '�F' => '뺽', '�G' => '뺾', '�H' => '뺿', '�I' => '뻀', '�J' => '뻁', '�K' => '뻂', '�L' => '뻃', '�M' => '뻄', '�N' => '뻅', '�O' => '뻆', '�P' => '뻇', '�Q' => '뻈', '�R' => '뻉', '�S' => '뻊', '�T' => '뻋', '�U' => '뻌', '�V' => '뻍', '�W' => '뻎', '�X' => '뻏', '�Y' => '뻒', '�Z' => '뻓', '�a' => '뻕', '�b' => '뻖', '�c' => '뻙', '�d' => '뻚', '�e' => '뻛', '�f' => '뻜', '�g' => '뻝', '�h' => '뻞', '�i' => '뻟', '�j' => '뻡', '�k' => '뻢', '�l' => '뻦', '�m' => '뻧', '�n' => '뻨', '�o' => '뻩', '�p' => '뻪', '�q' => '뻫', '�r' => '뻭', '�s' => '뻮', '�t' => '뻯', '�u' => '뻰', '�v' => '뻱', '�w' => '뻲', '�x' => '뻳', '�y' => '뻴', '�z' => '뻵', '��' => '뻶', '��' => '뻷', '��' => '뻸', '��' => '뻹', '��' => '뻺', '��' => '뻻', '��' => '뻼', '��' => '뻽', '��' => '뻾', '��' => '뻿', '��' => '뼀', '��' => '뼂', '��' => '뼃', '��' => '뼄', '��' => '뼅', '��' => '뼆', '��' => '뼇', '��' => '뼊', '��' => '뼋', '��' => '뼌', '��' => '뼍', '��' => '뼎', '��' => '뼏', '��' => '뼐', '��' => '뼑', '��' => '뼒', '��' => '뼓', '��' => '뼔', '��' => '뼕', '��' => '뼖', '��' => '뼗', '��' => '뼚', '��' => '뼞', '��' => '뼟', '��' => '뼠', '��' => '뼡', '��' => '뼢', '��' => '뼣', '��' => '뼤', '��' => '뼥', '��' => '뼦', '��' => '뼧', '��' => '뼨', '��' => '뼩', '��' => '뼪', '��' => '뼫', '��' => '뼬', '��' => '뼭', '��' => '뼮', '��' => '뼯', '��' => '뼰', '��' => '뼱', '��' => '뼲', '��' => '뼳', '��' => '뼴', '��' => '뼵', '��' => '뼶', '��' => '뼷', '��' => '뼸', '��' => '뼹', '��' => '뼺', '��' => '뼻', '��' => '뼼', '��' => '뼽', '��' => '뼾', '��' => '뼿', '��' => '뽂', '��' => '뽃', '��' => '뽅', '��' => '뽆', '��' => '뽇', '��' => '뽉', '��' => '뽊', '��' => '뽋', '��' => '뽌', '��' => '뽍', '��' => '뽎', '��' => '뽏', '��' => '뽒', '��' => '뽓', '��' => '뽔', '��' => '뽖', '��' => '뽗', '��' => '뽘', '��' => '뽙', '��' => '뽚', '��' => '뽛', '��' => '뽜', '��' => '뽝', '��' => '뽞', '��' => '뽟', '��' => '뽠', '��' => '뽡', '��' => '뽢', '��' => '뽣', '��' => '뽤', '��' => '뽥', '��' => '뽦', '��' => '뽧', '��' => '뽨', '��' => '뽩', '��' => '뽪', '��' => '뽫', '��' => '뽬', '��' => '뽭', '��' => '뽮', '��' => '뽯', '��' => '뽰', '��' => '뽱', '��' => '뽲', '��' => '뽳', '��' => '뽴', '��' => '뽵', '��' => '뽶', '��' => '뽷', '��' => '뽸', '��' => '뽹', '��' => '뽺', '��' => '뽻', '��' => '뽼', '��' => '뽽', '��' => '뽾', '��' => '뽿', '��' => '뾀', '��' => '뾁', '��' => '뾂', '�A' => '뾃', '�B' => '뾄', '�C' => '뾅', '�D' => '뾆', '�E' => '뾇', '�F' => '뾈', '�G' => '뾉', '�H' => '뾊', '�I' => '뾋', '�J' => '뾌', '�K' => '뾍', '�L' => '뾎', '�M' => '뾏', '�N' => '뾐', '�O' => '뾑', '�P' => '뾒', '�Q' => '뾓', '�R' => '뾕', '�S' => '뾖', '�T' => '뾗', '�U' => '뾘', '�V' => '뾙', '�W' => '뾚', '�X' => '뾛', '�Y' => '뾜', '�Z' => '뾝', '�a' => '뾞', '�b' => '뾟', '�c' => '뾠', '�d' => '뾡', '�e' => '뾢', '�f' => '뾣', '�g' => '뾤', '�h' => '뾥', '�i' => '뾦', '�j' => '뾧', '�k' => '뾨', '�l' => '뾩', '�m' => '뾪', '�n' => '뾫', '�o' => '뾬', '�p' => '뾭', '�q' => '뾮', '�r' => '뾯', '�s' => '뾱', '�t' => '뾲', '�u' => '뾳', '�v' => '뾴', '�w' => '뾵', '�x' => '뾶', '�y' => '뾷', '�z' => '뾸', '��' => '뾹', '��' => '뾺', '��' => '뾻', '��' => '뾼', '��' => '뾽', '��' => '뾾', '��' => '뾿', '��' => '뿀', '��' => '뿁', '��' => '뿂', '��' => '뿃', '��' => '뿄', '��' => '뿆', '��' => '뿇', '��' => '뿈', '��' => '뿉', '��' => '뿊', '��' => '뿋', '��' => '뿎', '��' => '뿏', '��' => '뿑', '��' => '뿒', '��' => '뿓', '��' => '뿕', '��' => '뿖', '��' => '뿗', '��' => '뿘', '��' => '뿙', '��' => '뿚', '��' => '뿛', '��' => '뿝', '��' => '뿞', '��' => '뿠', '��' => '뿢', '��' => '뿣', '��' => '뿤', '��' => '뿥', '��' => '뿦', '��' => '뿧', '��' => '뿨', '��' => '뿩', '��' => '뿪', '��' => '뿫', '��' => '뿬', '��' => '뿭', '��' => '뿮', '��' => '뿯', '��' => '뿰', '��' => '뿱', '��' => '뿲', '��' => '뿳', '��' => '뿴', '��' => '뿵', '��' => '뿶', '��' => '뿷', '��' => '뿸', '��' => '뿹', '��' => '뿺', '��' => '뿻', '��' => '뿼', '��' => '뿽', '��' => '뿾', '��' => '뿿', '��' => '쀀', '��' => '쀁', '��' => '쀂', '��' => '쀃', '��' => '쀄', '��' => '쀅', '��' => '쀆', '��' => '쀇', '��' => '쀈', '��' => '쀉', '��' => '쀊', '��' => '쀋', '��' => '쀌', '��' => '쀍', '��' => '쀎', '��' => '쀏', '��' => '쀐', '��' => '쀑', '��' => '쀒', '��' => '쀓', '��' => '쀔', '��' => '쀕', '��' => '쀖', '��' => '쀗', '��' => '쀘', '��' => '쀙', '��' => '쀚', '��' => '쀛', '��' => '쀜', '��' => '쀝', '��' => '쀞', '��' => '쀟', '��' => '쀠', '��' => '쀡', '��' => '쀢', '��' => '쀣', '��' => '쀤', '��' => '쀥', '��' => '쀦', '��' => '쀧', '��' => '쀨', '��' => '쀩', '��' => '쀪', '��' => '쀫', '��' => '쀬', '��' => '쀭', '��' => '쀮', '��' => '쀯', '��' => '쀰', '��' => '쀱', '��' => '쀲', '��' => '쀳', '��' => '쀴', '��' => '쀵', '��' => '쀶', '��' => '쀷', '��' => '쀸', '��' => '쀹', '��' => '쀺', '��' => '쀻', '��' => '쀽', '��' => '쀾', '��' => '쀿', '�A' => '쁀', '�B' => '쁁', '�C' => '쁂', '�D' => '쁃', '�E' => '쁄', '�F' => '쁅', '�G' => '쁆', '�H' => '쁇', '�I' => '쁈', '�J' => '쁉', '�K' => '쁊', '�L' => '쁋', '�M' => '쁌', '�N' => '쁍', '�O' => '쁎', '�P' => '쁏', '�Q' => '쁐', '�R' => '쁒', '�S' => '쁓', '�T' => '쁔', '�U' => '쁕', '�V' => '쁖', '�W' => '쁗', '�X' => '쁙', '�Y' => '쁚', '�Z' => '쁛', '�a' => '쁝', '�b' => '쁞', '�c' => '쁟', '�d' => '쁡', '�e' => '쁢', '�f' => '쁣', '�g' => '쁤', '�h' => '쁥', '�i' => '쁦', '�j' => '쁧', '�k' => '쁪', '�l' => '쁫', '�m' => '쁬', '�n' => '쁭', '�o' => '쁮', '�p' => '쁯', '�q' => '쁰', '�r' => '쁱', '�s' => '쁲', '�t' => '쁳', '�u' => '쁴', '�v' => '쁵', '�w' => '쁶', '�x' => '쁷', '�y' => '쁸', '�z' => '쁹', '��' => '쁺', '��' => '쁻', '��' => '쁼', '��' => '쁽', '��' => '쁾', '��' => '쁿', '��' => '삀', '��' => '삁', '��' => '삂', '��' => '삃', '��' => '삄', '��' => '삅', '��' => '삆', '��' => '삇', '��' => '삈', '��' => '삉', '��' => '삊', '��' => '삋', '��' => '삌', '��' => '삍', '��' => '삎', '��' => '삏', '��' => '삒', '��' => '삓', '��' => '삕', '��' => '삖', '��' => '삗', '��' => '삙', '��' => '삚', '��' => '삛', '��' => '삜', '��' => '삝', '��' => '삞', '��' => '삟', '��' => '삢', '��' => '삤', '��' => '삦', '��' => '삧', '��' => '삨', '��' => '삩', '��' => '삪', '��' => '삫', '��' => '삮', '��' => '삱', '��' => '삲', '��' => '삷', '��' => '삸', '��' => '삹', '��' => '삺', '��' => '삻', '��' => '삾', '��' => '샂', '��' => '샃', '��' => '샄', '��' => '샆', '��' => '샇', '��' => '샊', '��' => '샋', '��' => '샍', '��' => '샎', '��' => '샏', '��' => '샑', '��' => '샒', '��' => '샓', '��' => '샔', '��' => '샕', '��' => '샖', '��' => '샗', '��' => '샚', '��' => '샞', '��' => '샟', '��' => '샠', '��' => '샡', '��' => '샢', '��' => '샣', '��' => '샦', '��' => '샧', '��' => '샩', '��' => '샪', '��' => '샫', '��' => '샭', '��' => '샮', '��' => '샯', '��' => '샰', '��' => '샱', '��' => '샲', '��' => '샳', '��' => '샶', '��' => '샸', '��' => '샺', '��' => '샻', '��' => '샼', '��' => '샽', '��' => '샾', '��' => '샿', '��' => '섁', '��' => '섂', '��' => '섃', '��' => '섅', '��' => '섆', '��' => '섇', '��' => '섉', '��' => '섊', '��' => '섋', '��' => '섌', '��' => '섍', '��' => '섎', '��' => '섏', '��' => '섑', '��' => '섒', '��' => '섓', '��' => '섔', '��' => '섖', '��' => '섗', '��' => '섘', '��' => '섙', '��' => '섚', '��' => '섛', '��' => '섡', '��' => '섢', '��' => '섥', '��' => '섨', '��' => '섩', '��' => '섪', '��' => '섫', '��' => '섮', '�A' => '섲', '�B' => '섳', '�C' => '섴', '�D' => '섵', '�E' => '섷', '�F' => '섺', '�G' => '섻', '�H' => '섽', '�I' => '섾', '�J' => '섿', '�K' => '셁', '�L' => '셂', '�M' => '셃', '�N' => '셄', '�O' => '셅', '�P' => '셆', '�Q' => '셇', '�R' => '셊', '�S' => '셎', '�T' => '셏', '�U' => '셐', '�V' => '셑', '�W' => '셒', '�X' => '셓', '�Y' => '셖', '�Z' => '셗', '�a' => '셙', '�b' => '셚', '�c' => '셛', '�d' => '셝', '�e' => '셞', '�f' => '셟', '�g' => '셠', '�h' => '셡', '�i' => '셢', '�j' => '셣', '�k' => '셦', '�l' => '셪', '�m' => '셫', '�n' => '셬', '�o' => '셭', '�p' => '셮', '�q' => '셯', '�r' => '셱', '�s' => '셲', '�t' => '셳', '�u' => '셵', '�v' => '셶', '�w' => '셷', '�x' => '셹', '�y' => '셺', '�z' => '셻', '��' => '셼', '��' => '셽', '��' => '셾', '��' => '셿', '��' => '솀', '��' => '솁', '��' => '솂', '��' => '솃', '��' => '솄', '��' => '솆', '��' => '솇', '��' => '솈', '��' => '솉', '��' => '솊', '��' => '솋', '��' => '솏', '��' => '솑', '��' => '솒', '��' => '솓', '��' => '솕', '��' => '솗', '��' => '솘', '��' => '솙', '��' => '솚', '��' => '솛', '��' => '솞', '��' => '솠', '��' => '솢', '��' => '솣', '��' => '솤', '��' => '솦', '��' => '솧', '��' => '솪', '��' => '솫', '��' => '솭', '��' => '솮', '��' => '솯', '��' => '솱', '��' => '솲', '��' => '솳', '��' => '솴', '��' => '솵', '��' => '솶', '��' => '솷', '��' => '솸', '��' => '솹', '��' => '솺', '��' => '솻', '��' => '솼', '��' => '솾', '��' => '솿', '��' => '쇀', '��' => '쇁', '��' => '쇂', '��' => '쇃', '��' => '쇅', '��' => '쇆', '��' => '쇇', '��' => '쇉', '��' => '쇊', '��' => '쇋', '��' => '쇍', '��' => '쇎', '��' => '쇏', '��' => '쇐', '��' => '쇑', '��' => '쇒', '��' => '쇓', '��' => '쇕', '��' => '쇖', '��' => '쇙', '��' => '쇚', '��' => '쇛', '��' => '쇜', '��' => '쇝', '��' => '쇞', '��' => '쇟', '��' => '쇡', '��' => '쇢', '��' => '쇣', '��' => '쇥', '��' => '쇦', '��' => '쇧', '��' => '쇩', '��' => '쇪', '��' => '쇫', '��' => '쇬', '��' => '쇭', '��' => '쇮', '��' => '쇯', '��' => '쇲', '��' => '쇴', '��' => '쇵', '��' => '쇶', '��' => '쇷', '��' => '쇸', '��' => '쇹', '��' => '쇺', '��' => '쇻', '��' => '쇾', '��' => '쇿', '��' => '숁', '��' => '숂', '��' => '숃', '��' => '숅', '��' => '숆', '��' => '숇', '��' => '숈', '��' => '숉', '��' => '숊', '��' => '숋', '��' => '숎', '��' => '숐', '��' => '숒', '��' => '숓', '��' => '숔', '��' => '숕', '��' => '숖', '��' => '숗', '��' => '숚', '��' => '숛', '��' => '숝', '��' => '숞', '��' => '숡', '��' => '숢', '��' => '숣', '�A' => '숤', '�B' => '숥', '�C' => '숦', '�D' => '숧', '�E' => '숪', '�F' => '숬', '�G' => '숮', '�H' => '숰', '�I' => '숳', '�J' => '숵', '�K' => '숶', '�L' => '숷', '�M' => '숸', '�N' => '숹', '�O' => '숺', '�P' => '숻', '�Q' => '숼', '�R' => '숽', '�S' => '숾', '�T' => '숿', '�U' => '쉀', '�V' => '쉁', '�W' => '쉂', '�X' => '쉃', '�Y' => '쉄', '�Z' => '쉅', '�a' => '쉆', '�b' => '쉇', '�c' => '쉉', '�d' => '쉊', '�e' => '쉋', '�f' => '쉌', '�g' => '쉍', '�h' => '쉎', '�i' => '쉏', '�j' => '쉒', '�k' => '쉓', '�l' => '쉕', '�m' => '쉖', '�n' => '쉗', '�o' => '쉙', '�p' => '쉚', '�q' => '쉛', '�r' => '쉜', '�s' => '쉝', '�t' => '쉞', '�u' => '쉟', '�v' => '쉡', '�w' => '쉢', '�x' => '쉣', '�y' => '쉤', '�z' => '쉦', '��' => '쉧', '��' => '쉨', '��' => '쉩', '��' => '쉪', '��' => '쉫', '��' => '쉮', '��' => '쉯', '��' => '쉱', '��' => '쉲', '��' => '쉳', '��' => '쉵', '��' => '쉶', '��' => '쉷', '��' => '쉸', '��' => '쉹', '��' => '쉺', '��' => '쉻', '��' => '쉾', '��' => '슀', '��' => '슂', '��' => '슃', '��' => '슄', '��' => '슅', '��' => '슆', '��' => '슇', '��' => '슊', '��' => '슋', '��' => '슌', '��' => '슍', '��' => '슎', '��' => '슏', '��' => '슑', '��' => '슒', '��' => '슓', '��' => '슔', '��' => '슕', '��' => '슖', '��' => '슗', '��' => '슙', '��' => '슚', '��' => '슜', '��' => '슞', '��' => '슟', '��' => '슠', '��' => '슡', '��' => '슢', '��' => '슣', '��' => '슦', '��' => '슧', '��' => '슩', '��' => '슪', '��' => '슫', '��' => '슮', '��' => '슯', '��' => '슰', '��' => '슱', '��' => '슲', '��' => '슳', '��' => '슶', '��' => '슸', '��' => '슺', '��' => '슻', '��' => '슼', '��' => '슽', '��' => '슾', '��' => '슿', '��' => '싀', '��' => '싁', '��' => '싂', '��' => '싃', '��' => '싄', '��' => '싅', '��' => '싆', '��' => '싇', '��' => '싈', '��' => '싉', '��' => '싊', '��' => '싋', '��' => '싌', '��' => '싍', '��' => '싎', '��' => '싏', '��' => '싐', '��' => '싑', '��' => '싒', '��' => '싓', '��' => '싔', '��' => '싕', '��' => '싖', '��' => '싗', '��' => '싘', '��' => '싙', '��' => '싚', '��' => '싛', '��' => '싞', '��' => '싟', '��' => '싡', '��' => '싢', '��' => '싥', '��' => '싦', '��' => '싧', '��' => '싨', '��' => '싩', '��' => '싪', '��' => '싮', '��' => '싰', '��' => '싲', '��' => '싳', '��' => '싴', '��' => '싵', '��' => '싷', '��' => '싺', '��' => '싽', '��' => '싾', '��' => '싿', '��' => '쌁', '��' => '쌂', '��' => '쌃', '��' => '쌄', '��' => '쌅', '��' => '쌆', '��' => '쌇', '��' => '쌊', '��' => '쌋', '��' => '쌎', '��' => '쌏', '�A' => '쌐', '�B' => '쌑', '�C' => '쌒', '�D' => '쌖', '�E' => '쌗', '�F' => '쌙', '�G' => '쌚', '�H' => '쌛', '�I' => '쌝', '�J' => '쌞', '�K' => '쌟', '�L' => '쌠', '�M' => '쌡', '�N' => '쌢', '�O' => '쌣', '�P' => '쌦', '�Q' => '쌧', '�R' => '쌪', '�S' => '쌫', '�T' => '쌬', '�U' => '쌭', '�V' => '쌮', '�W' => '쌯', '�X' => '쌰', '�Y' => '쌱', '�Z' => '쌲', '�a' => '쌳', '�b' => '쌴', '�c' => '쌵', '�d' => '쌶', '�e' => '쌷', '�f' => '쌸', '�g' => '쌹', '�h' => '쌺', '�i' => '쌻', '�j' => '쌼', '�k' => '쌽', '�l' => '쌾', '�m' => '쌿', '�n' => '썀', '�o' => '썁', '�p' => '썂', '�q' => '썃', '�r' => '썄', '�s' => '썆', '�t' => '썇', '�u' => '썈', '�v' => '썉', '�w' => '썊', '�x' => '썋', '�y' => '썌', '�z' => '썍', '��' => '썎', '��' => '썏', '��' => '썐', '��' => '썑', '��' => '썒', '��' => '썓', '��' => '썔', '��' => '썕', '��' => '썖', '��' => '썗', '��' => '썘', '��' => '썙', '��' => '썚', '��' => '썛', '��' => '썜', '��' => '썝', '��' => '썞', '��' => '썟', '��' => '썠', '��' => '썡', '��' => '썢', '��' => '썣', '��' => '썤', '��' => '썥', '��' => '썦', '��' => '썧', '��' => '썪', '��' => '썫', '��' => '썭', '��' => '썮', '��' => '썯', '��' => '썱', '��' => '썳', '��' => '썴', '��' => '썵', '��' => '썶', '��' => '썷', '��' => '썺', '��' => '썻', '��' => '썾', '��' => '썿', '��' => '쎀', '��' => '쎁', '��' => '쎂', '��' => '쎃', '��' => '쎅', '��' => '쎆', '��' => '쎇', '��' => '쎉', '��' => '쎊', '��' => '쎋', '��' => '쎍', '��' => '쎎', '��' => '쎏', '��' => '쎐', '��' => '쎑', '��' => '쎒', '��' => '쎓', '��' => '쎔', '��' => '쎕', '��' => '쎖', '��' => '쎗', '��' => '쎘', '��' => '쎙', '��' => '쎚', '��' => '쎛', '��' => '쎜', '��' => '쎝', '��' => '쎞', '��' => '쎟', '��' => '쎠', '��' => '쎡', '��' => '쎢', '��' => '쎣', '��' => '쎤', '��' => '쎥', '��' => '쎦', '��' => '쎧', '��' => '쎨', '��' => '쎩', '��' => '쎪', '��' => '쎫', '��' => '쎬', '��' => '쎭', '��' => '쎮', '��' => '쎯', '��' => '쎰', '��' => '쎱', '��' => '쎲', '��' => '쎳', '��' => '쎴', '��' => '쎵', '��' => '쎶', '��' => '쎷', '��' => '쎸', '��' => '쎹', '��' => '쎺', '��' => '쎻', '��' => '쎼', '��' => '쎽', '��' => '쎾', '��' => '쎿', '��' => '쏁', '��' => '쏂', '��' => '쏃', '��' => '쏄', '��' => '쏅', '��' => '쏆', '��' => '쏇', '��' => '쏈', '��' => '쏉', '��' => '쏊', '��' => '쏋', '��' => '쏌', '��' => '쏍', '��' => '쏎', '��' => '쏏', '��' => '쏐', '��' => '쏑', '��' => '쏒', '��' => '쏓', '��' => '쏔', '��' => '쏕', '��' => '쏖', '��' => '쏗', '��' => '쏚', '�A' => '쏛', '�B' => '쏝', '�C' => '쏞', '�D' => '쏡', '�E' => '쏣', '�F' => '쏤', '�G' => '쏥', '�H' => '쏦', '�I' => '쏧', '�J' => '쏪', '�K' => '쏫', '�L' => '쏬', '�M' => '쏮', '�N' => '쏯', '�O' => '쏰', '�P' => '쏱', '�Q' => '쏲', '�R' => '쏳', '�S' => '쏶', '�T' => '쏷', '�U' => '쏹', '�V' => '쏺', '�W' => '쏻', '�X' => '쏼', '�Y' => '쏽', '�Z' => '쏾', '�a' => '쏿', '�b' => '쐀', '�c' => '쐁', '�d' => '쐂', '�e' => '쐃', '�f' => '쐄', '�g' => '쐅', '�h' => '쐆', '�i' => '쐇', '�j' => '쐉', '�k' => '쐊', '�l' => '쐋', '�m' => '쐌', '�n' => '쐍', '�o' => '쐎', '�p' => '쐏', '�q' => '쐑', '�r' => '쐒', '�s' => '쐓', '�t' => '쐔', '�u' => '쐕', '�v' => '쐖', '�w' => '쐗', '�x' => '쐘', '�y' => '쐙', '�z' => '쐚', '��' => '쐛', '��' => '쐜', '��' => '쐝', '��' => '쐞', '��' => '쐟', '��' => '쐠', '��' => '쐡', '��' => '쐢', '��' => '쐣', '��' => '쐥', '��' => '쐦', '��' => '쐧', '��' => '쐨', '��' => '쐩', '��' => '쐪', '��' => '쐫', '��' => '쐭', '��' => '쐮', '��' => '쐯', '��' => '쐱', '��' => '쐲', '��' => '쐳', '��' => '쐵', '��' => '쐶', '��' => '쐷', '��' => '쐸', '��' => '쐹', '��' => '쐺', '��' => '쐻', '��' => '쐾', '��' => '쐿', '��' => '쑀', '��' => '쑁', '��' => '쑂', '��' => '쑃', '��' => '쑄', '��' => '쑅', '��' => '쑆', '��' => '쑇', '��' => '쑉', '��' => '쑊', '��' => '쑋', '��' => '쑌', '��' => '쑍', '��' => '쑎', '��' => '쑏', '��' => '쑐', '��' => '쑑', '��' => '쑒', '��' => '쑓', '��' => '쑔', '��' => '쑕', '��' => '쑖', '��' => '쑗', '��' => '쑘', '��' => '쑙', '��' => '쑚', '��' => '쑛', '��' => '쑜', '��' => '쑝', '��' => '쑞', '��' => '쑟', '��' => '쑠', '��' => '쑡', '��' => '쑢', '��' => '쑣', '��' => '쑦', '��' => '쑧', '��' => '쑩', '��' => '쑪', '��' => '쑫', '��' => '쑭', '��' => '쑮', '��' => '쑯', '��' => '쑰', '��' => '쑱', '��' => '쑲', '��' => '쑳', '��' => '쑶', '��' => '쑷', '��' => '쑸', '��' => '쑺', '��' => '쑻', '��' => '쑼', '��' => '쑽', '��' => '쑾', '��' => '쑿', '��' => '쒁', '��' => '쒂', '��' => '쒃', '��' => '쒄', '��' => '쒅', '��' => '쒆', '��' => '쒇', '��' => '쒈', '��' => '쒉', '��' => '쒊', '��' => '쒋', '��' => '쒌', '��' => '쒍', '��' => '쒎', '��' => '쒏', '��' => '쒐', '��' => '쒑', '��' => '쒒', '��' => '쒓', '��' => '쒕', '��' => '쒖', '��' => '쒗', '��' => '쒘', '��' => '쒙', '��' => '쒚', '��' => '쒛', '��' => '쒝', '��' => '쒞', '��' => '쒟', '��' => '쒠', '��' => '쒡', '��' => '쒢', '��' => '쒣', '��' => '쒤', '��' => '쒥', '��' => '쒦', '��' => '쒧', '��' => '쒨', '��' => '쒩', '�A' => '쒪', '�B' => '쒫', '�C' => '쒬', '�D' => '쒭', '�E' => '쒮', '�F' => '쒯', '�G' => '쒰', '�H' => '쒱', '�I' => '쒲', '�J' => '쒳', '�K' => '쒴', '�L' => '쒵', '�M' => '쒶', '�N' => '쒷', '�O' => '쒹', '�P' => '쒺', '�Q' => '쒻', '�R' => '쒽', '�S' => '쒾', '�T' => '쒿', '�U' => '쓀', '�V' => '쓁', '�W' => '쓂', '�X' => '쓃', '�Y' => '쓄', '�Z' => '쓅', '�a' => '쓆', '�b' => '쓇', '�c' => '쓈', '�d' => '쓉', '�e' => '쓊', '�f' => '쓋', '�g' => '쓌', '�h' => '쓍', '�i' => '쓎', '�j' => '쓏', '�k' => '쓐', '�l' => '쓑', '�m' => '쓒', '�n' => '쓓', '�o' => '쓔', '�p' => '쓕', '�q' => '쓖', '�r' => '쓗', '�s' => '쓘', '�t' => '쓙', '�u' => '쓚', '�v' => '쓛', '�w' => '쓜', '�x' => '쓝', '�y' => '쓞', '�z' => '쓟', '��' => '쓠', '��' => '쓡', '��' => '쓢', '��' => '쓣', '��' => '쓤', '��' => '쓥', '��' => '쓦', '��' => '쓧', '��' => '쓨', '��' => '쓪', '��' => '쓫', '��' => '쓬', '��' => '쓭', '��' => '쓮', '��' => '쓯', '��' => '쓲', '��' => '쓳', '��' => '쓵', '��' => '쓶', '��' => '쓷', '��' => '쓹', '��' => '쓻', '��' => '쓼', '��' => '쓽', '��' => '쓾', '��' => '씂', '��' => '씃', '��' => '씄', '��' => '씅', '��' => '씆', '��' => '씇', '��' => '씈', '��' => '씉', '��' => '씊', '��' => '씋', '��' => '씍', '��' => '씎', '��' => '씏', '��' => '씑', '��' => '씒', '��' => '씓', '��' => '씕', '��' => '씖', '��' => '씗', '��' => '씘', '��' => '씙', '��' => '씚', '��' => '씛', '��' => '씝', '��' => '씞', '��' => '씟', '��' => '씠', '��' => '씡', '��' => '씢', '��' => '씣', '��' => '씤', '��' => '씥', '��' => '씦', '��' => '씧', '��' => '씪', '��' => '씫', '��' => '씭', '��' => '씮', '��' => '씯', '��' => '씱', '��' => '씲', '��' => '씳', '��' => '씴', '��' => '씵', '��' => '씶', '��' => '씷', '��' => '씺', '��' => '씼', '��' => '씾', '��' => '씿', '��' => '앀', '��' => '앁', '��' => '앂', '��' => '앃', '��' => '앆', '��' => '앇', '��' => '앋', '��' => '앏', '��' => '앐', '��' => '앑', '��' => '앒', '��' => '앖', '��' => '앚', '��' => '앛', '��' => '앜', '��' => '앟', '��' => '앢', '��' => '앣', '��' => '앥', '��' => '앦', '��' => '앧', '��' => '앩', '��' => '앪', '��' => '앫', '��' => '앬', '��' => '앭', '��' => '앮', '��' => '앯', '��' => '앲', '��' => '앶', '��' => '앷', '��' => '앸', '��' => '앹', '��' => '앺', '��' => '앻', '��' => '앾', '��' => '앿', '��' => '얁', '��' => '얂', '��' => '얃', '��' => '얅', '��' => '얆', '��' => '얈', '��' => '얉', '��' => '얊', '��' => '얋', '��' => '얎', '��' => '얐', '��' => '얒', '��' => '얓', '��' => '얔', '�A' => '얖', '�B' => '얙', '�C' => '얚', '�D' => '얛', '�E' => '얝', '�F' => '얞', '�G' => '얟', '�H' => '얡', '�I' => '얢', '�J' => '얣', '�K' => '얤', '�L' => '얥', '�M' => '얦', '�N' => '얧', '�O' => '얨', '�P' => '얪', '�Q' => '얫', '�R' => '얬', '�S' => '얭', '�T' => '얮', '�U' => '얯', '�V' => '얰', '�W' => '얱', '�X' => '얲', '�Y' => '얳', '�Z' => '얶', '�a' => '얷', '�b' => '얺', '�c' => '얿', '�d' => '엀', '�e' => '엁', '�f' => '엂', '�g' => '엃', '�h' => '엋', '�i' => '엍', '�j' => '엏', '�k' => '엒', '�l' => '엓', '�m' => '엕', '�n' => '엖', '�o' => '엗', '�p' => '엙', '�q' => '엚', '�r' => '엛', '�s' => '엜', '�t' => '엝', '�u' => '엞', '�v' => '엟', '�w' => '엢', '�x' => '엤', '�y' => '엦', '�z' => '엧', '��' => '엨', '��' => '엩', '��' => '엪', '��' => '엫', '��' => '엯', '��' => '엱', '��' => '엲', '��' => '엳', '��' => '엵', '��' => '엸', '��' => '엹', '��' => '엺', '��' => '엻', '��' => '옂', '��' => '옃', '��' => '옄', '��' => '옉', '��' => '옊', '��' => '옋', '��' => '옍', '��' => '옎', '��' => '옏', '��' => '옑', '��' => '옒', '��' => '옓', '��' => '옔', '��' => '옕', '��' => '옖', '��' => '옗', '��' => '옚', '��' => '옝', '��' => '옞', '��' => '옟', '��' => '옠', '��' => '옡', '��' => '옢', '��' => '옣', '��' => '옦', '��' => '옧', '��' => '옩', '��' => '옪', '��' => '옫', '��' => '옯', '��' => '옱', '��' => '옲', '��' => '옶', '��' => '옸', '��' => '옺', '��' => '옼', '��' => '옽', '��' => '옾', '��' => '옿', '��' => '왂', '��' => '왃', '��' => '왅', '��' => '왆', '��' => '왇', '��' => '왉', '��' => '왊', '��' => '왋', '��' => '왌', '��' => '왍', '��' => '왎', '��' => '왏', '��' => '왒', '��' => '왖', '��' => '왗', '��' => '왘', '��' => '왙', '��' => '왚', '��' => '왛', '��' => '왞', '��' => '왟', '��' => '왡', '��' => '왢', '��' => '왣', '��' => '왤', '��' => '왥', '��' => '왦', '��' => '왧', '��' => '왨', '��' => '왩', '��' => '왪', '��' => '왫', '��' => '왭', '��' => '왮', '��' => '왰', '��' => '왲', '��' => '왳', '��' => '왴', '��' => '왵', '��' => '왶', '��' => '왷', '��' => '왺', '��' => '왻', '��' => '왽', '��' => '왾', '��' => '왿', '��' => '욁', '��' => '욂', '��' => '욃', '��' => '욄', '��' => '욅', '��' => '욆', '��' => '욇', '��' => '욊', '��' => '욌', '��' => '욎', '��' => '욏', '��' => '욐', '��' => '욑', '��' => '욒', '��' => '욓', '��' => '욖', '��' => '욗', '��' => '욙', '��' => '욚', '��' => '욛', '��' => '욝', '��' => '욞', '��' => '욟', '��' => '욠', '��' => '욡', '��' => '욢', '��' => '욣', '��' => '욦', '�A' => '욨', '�B' => '욪', '�C' => '욫', '�D' => '욬', '�E' => '욭', '�F' => '욮', '�G' => '욯', '�H' => '욲', '�I' => '욳', '�J' => '욵', '�K' => '욶', '�L' => '욷', '�M' => '욻', '�N' => '욼', '�O' => '욽', '�P' => '욾', '�Q' => '욿', '�R' => '웂', '�S' => '웄', '�T' => '웆', '�U' => '웇', '�V' => '웈', '�W' => '웉', '�X' => '웊', '�Y' => '웋', '�Z' => '웎', '�a' => '웏', '�b' => '웑', '�c' => '웒', '�d' => '웓', '�e' => '웕', '�f' => '웖', '�g' => '웗', '�h' => '웘', '�i' => '웙', '�j' => '웚', '�k' => '웛', '�l' => '웞', '�m' => '웟', '�n' => '웢', '�o' => '웣', '�p' => '웤', '�q' => '웥', '�r' => '웦', '�s' => '웧', '�t' => '웪', '�u' => '웫', '�v' => '웭', '�w' => '웮', '�x' => '웯', '�y' => '웱', '�z' => '웲', '��' => '웳', '��' => '웴', '��' => '웵', '��' => '웶', '��' => '웷', '��' => '웺', '��' => '웻', '��' => '웼', '��' => '웾', '��' => '웿', '��' => '윀', '��' => '윁', '��' => '윂', '��' => '윃', '��' => '윆', '��' => '윇', '��' => '윉', '��' => '윊', '��' => '윋', '��' => '윍', '��' => '윎', '��' => '윏', '��' => '윐', '��' => '윑', '��' => '윒', '��' => '윓', '��' => '윖', '��' => '윘', '��' => '윚', '��' => '윛', '��' => '윜', '��' => '윝', '��' => '윞', '��' => '윟', '��' => '윢', '��' => '윣', '��' => '윥', '��' => '윦', '��' => '윧', '��' => '윩', '��' => '윪', '��' => '윫', '��' => '윬', '��' => '윭', '��' => '윮', '��' => '윯', '��' => '윲', '��' => '윴', '��' => '윶', '��' => '윸', '��' => '윹', '��' => '윺', '��' => '윻', '��' => '윾', '��' => '윿', '��' => '읁', '��' => '읂', '��' => '읃', '��' => '읅', '��' => '읆', '��' => '읇', '��' => '읈', '��' => '읉', '��' => '읋', '��' => '읎', '��' => '읐', '��' => '읙', '��' => '읚', '��' => '읛', '��' => '읝', '��' => '읞', '��' => '읟', '��' => '읡', '��' => '읢', '��' => '읣', '��' => '읤', '��' => '읥', '��' => '읦', '��' => '읧', '��' => '읩', '��' => '읪', '��' => '읬', '��' => '읭', '��' => '읮', '��' => '읯', '��' => '읰', '��' => '읱', '��' => '읲', '��' => '읳', '��' => '읶', '��' => '읷', '��' => '읹', '��' => '읺', '��' => '읻', '��' => '읿', '��' => '잀', '��' => '잁', '��' => '잂', '��' => '잆', '��' => '잋', '��' => '잌', '��' => '잍', '��' => '잏', '��' => '잒', '��' => '잓', '��' => '잕', '��' => '잙', '��' => '잛', '��' => '잜', '��' => '잝', '��' => '잞', '��' => '잟', '��' => '잢', '��' => '잧', '��' => '잨', '��' => '잩', '��' => '잪', '��' => '잫', '��' => '잮', '��' => '잯', '��' => '잱', '��' => '잲', '��' => '잳', '��' => '잵', '��' => '잶', '��' => '잷', '�A' => '잸', '�B' => '잹', '�C' => '잺', '�D' => '잻', '�E' => '잾', '�F' => '쟂', '�G' => '쟃', '�H' => '쟄', '�I' => '쟅', '�J' => '쟆', '�K' => '쟇', '�L' => '쟊', '�M' => '쟋', '�N' => '쟍', '�O' => '쟏', '�P' => '쟑', '�Q' => '쟒', '�R' => '쟓', '�S' => '쟔', '�T' => '쟕', '�U' => '쟖', '�V' => '쟗', '�W' => '쟙', '�X' => '쟚', '�Y' => '쟛', '�Z' => '쟜', '�a' => '쟞', '�b' => '쟟', '�c' => '쟠', '�d' => '쟡', '�e' => '쟢', '�f' => '쟣', '�g' => '쟥', '�h' => '쟦', '�i' => '쟧', '�j' => '쟩', '�k' => '쟪', '�l' => '쟫', '�m' => '쟭', '�n' => '쟮', '�o' => '쟯', '�p' => '쟰', '�q' => '쟱', '�r' => '쟲', '�s' => '쟳', '�t' => '쟴', '�u' => '쟵', '�v' => '쟶', '�w' => '쟷', '�x' => '쟸', '�y' => '쟹', '�z' => '쟺', '��' => '쟻', '��' => '쟼', '��' => '쟽', '��' => '쟾', '��' => '쟿', '��' => '젂', '��' => '젃', '��' => '젅', '��' => '젆', '��' => '젇', '��' => '젉', '��' => '젋', '��' => '젌', '��' => '젍', '��' => '젎', '��' => '젏', '��' => '젒', '��' => '젔', '��' => '젗', '��' => '젘', '��' => '젙', '��' => '젚', '��' => '젛', '��' => '젞', '��' => '젟', '��' => '젡', '��' => '젢', '��' => '젣', '��' => '젥', '��' => '젦', '��' => '젧', '��' => '젨', '��' => '젩', '��' => '젪', '��' => '젫', '��' => '젮', '��' => '젰', '��' => '젲', '��' => '젳', '��' => '젴', '��' => '젵', '��' => '젶', '��' => '젷', '��' => '젹', '��' => '젺', '��' => '젻', '��' => '젽', '��' => '젾', '��' => '젿', '��' => '졁', '��' => '졂', '��' => '졃', '��' => '졄', '��' => '졅', '��' => '졆', '��' => '졇', '��' => '졊', '��' => '졋', '��' => '졎', '��' => '졏', '��' => '졐', '��' => '졑', '��' => '졒', '��' => '졓', '��' => '졕', '��' => '졖', '��' => '졗', '��' => '졘', '��' => '졙', '��' => '졚', '��' => '졛', '��' => '졜', '��' => '졝', '��' => '졞', '��' => '졟', '��' => '졠', '��' => '졡', '��' => '졢', '��' => '졣', '��' => '졤', '��' => '졥', '��' => '졦', '��' => '졧', '��' => '졨', '��' => '졩', '��' => '졪', '��' => '졫', '��' => '졬', '��' => '졭', '��' => '졮', '��' => '졯', '��' => '졲', '��' => '졳', '��' => '졵', '��' => '졶', '��' => '졷', '��' => '졹', '��' => '졻', '��' => '졼', '��' => '졽', '��' => '졾', '��' => '졿', '��' => '좂', '��' => '좄', '��' => '좈', '��' => '좉', '��' => '좊', '��' => '좎', '��' => '좏', '��' => '좐', '��' => '좑', '��' => '좒', '��' => '좓', '��' => '좕', '��' => '좖', '��' => '좗', '��' => '좘', '��' => '좙', '��' => '좚', '��' => '좛', '��' => '좜', '��' => '좞', '��' => '좠', '��' => '좢', '��' => '좣', '��' => '좤', '�A' => '좥', '�B' => '좦', '�C' => '좧', '�D' => '좩', '�E' => '좪', '�F' => '좫', '�G' => '좬', '�H' => '좭', '�I' => '좮', '�J' => '좯', '�K' => '좰', '�L' => '좱', '�M' => '좲', '�N' => '좳', '�O' => '좴', '�P' => '좵', '�Q' => '좶', '�R' => '좷', '�S' => '좸', '�T' => '좹', '�U' => '좺', '�V' => '좻', '�W' => '좾', '�X' => '좿', '�Y' => '죀', '�Z' => '죁', '�a' => '죂', '�b' => '죃', '�c' => '죅', '�d' => '죆', '�e' => '죇', '�f' => '죉', '�g' => '죊', '�h' => '죋', '�i' => '죍', '�j' => '죎', '�k' => '죏', '�l' => '죐', '�m' => '죑', '�n' => '죒', '�o' => '죓', '�p' => '죖', '�q' => '죘', '�r' => '죚', '�s' => '죛', '�t' => '죜', '�u' => '죝', '�v' => '죞', '�w' => '죟', '�x' => '죢', '�y' => '죣', '�z' => '죥', '��' => '죦', '��' => '죧', '��' => '죨', '��' => '죩', '��' => '죪', '��' => '죫', '��' => '죬', '��' => '죭', '��' => '죮', '��' => '죯', '��' => '죰', '��' => '죱', '��' => '죲', '��' => '죳', '��' => '죴', '��' => '죶', '��' => '죷', '��' => '죸', '��' => '죹', '��' => '죺', '��' => '죻', '��' => '죾', '��' => '죿', '��' => '줁', '��' => '줂', '��' => '줃', '��' => '줇', '��' => '줈', '��' => '줉', '��' => '줊', '��' => '줋', '��' => '줎', '��' => ' ', '��' => '、', '��' => '。', '��' => '·', '��' => '‥', '��' => '…', '��' => '¨', '��' => '〃', '��' => '', '��' => '―', '��' => '∥', '��' => '\', '��' => '∼', '��' => '‘', '��' => '’', '��' => '“', '��' => '”', '��' => '〔', '��' => '〕', '��' => '〈', '��' => '〉', '��' => '《', '��' => '》', '��' => '「', '��' => '」', '��' => '『', '��' => '』', '��' => '【', '��' => '】', '��' => '±', '��' => '×', '��' => '÷', '��' => '≠', '��' => '≤', '��' => '≥', '��' => '∞', '��' => '∴', '��' => '°', '��' => '′', '��' => '″', '��' => '℃', '��' => 'Å', '��' => '¢', '��' => '£', '��' => '¥', '��' => '♂', '��' => '♀', '��' => '∠', '��' => '⊥', '��' => '⌒', '��' => '∂', '��' => '∇', '��' => '≡', '��' => '≒', '��' => '§', '��' => '※', '��' => '☆', '��' => '★', '��' => '○', '��' => '●', '��' => '◎', '��' => '◇', '��' => '◆', '��' => '□', '��' => '■', '��' => '△', '��' => '▲', '��' => '▽', '��' => '▼', '��' => '→', '��' => '←', '��' => '↑', '��' => '↓', '��' => '↔', '��' => '〓', '��' => '≪', '��' => '≫', '��' => '√', '��' => '∽', '��' => '∝', '��' => '∵', '��' => '∫', '��' => '∬', '��' => '∈', '��' => '∋', '��' => '⊆', '��' => '⊇', '��' => '⊂', '��' => '⊃', '��' => '∪', '��' => '∩', '��' => '∧', '��' => '∨', '��' => '¬', '�A' => '줐', '�B' => '줒', '�C' => '줓', '�D' => '줔', '�E' => '줕', '�F' => '줖', '�G' => '줗', '�H' => '줙', '�I' => '줚', '�J' => '줛', '�K' => '줜', '�L' => '줝', '�M' => '줞', '�N' => '줟', '�O' => '줠', '�P' => '줡', '�Q' => '줢', '�R' => '줣', '�S' => '줤', '�T' => '줥', '�U' => '줦', '�V' => '줧', '�W' => '줨', '�X' => '줩', '�Y' => '줪', '�Z' => '줫', '�a' => '줭', '�b' => '줮', '�c' => '줯', '�d' => '줰', '�e' => '줱', '�f' => '줲', '�g' => '줳', '�h' => '줵', '�i' => '줶', '�j' => '줷', '�k' => '줸', '�l' => '줹', '�m' => '줺', '�n' => '줻', '�o' => '줼', '�p' => '줽', '�q' => '줾', '�r' => '줿', '�s' => '쥀', '�t' => '쥁', '�u' => '쥂', '�v' => '쥃', '�w' => '쥄', '�x' => '쥅', '�y' => '쥆', '�z' => '쥇', '��' => '쥈', '��' => '쥉', '��' => '쥊', '��' => '쥋', '��' => '쥌', '��' => '쥍', '��' => '쥎', '��' => '쥏', '��' => '쥒', '��' => '쥓', '��' => '쥕', '��' => '쥖', '��' => '쥗', '��' => '쥙', '��' => '쥚', '��' => '쥛', '��' => '쥜', '��' => '쥝', '��' => '쥞', '��' => '쥟', '��' => '쥢', '��' => '쥤', '��' => '쥥', '��' => '쥦', '��' => '쥧', '��' => '쥨', '��' => '쥩', '��' => '쥪', '��' => '쥫', '��' => '쥭', '��' => '쥮', '��' => '쥯', '��' => '⇒', '��' => '⇔', '��' => '∀', '��' => '∃', '��' => '´', '��' => '~', '��' => 'ˇ', '��' => '˘', '��' => '˝', '��' => '˚', '��' => '˙', '��' => '¸', '��' => '˛', '��' => '¡', '��' => '¿', '��' => 'ː', '��' => '∮', '��' => '∑', '��' => '∏', '��' => '¤', '��' => '℉', '��' => '‰', '��' => '◁', '��' => '◀', '��' => '▷', '��' => '▶', '��' => '♤', '��' => '♠', '��' => '♡', '��' => '♥', '��' => '♧', '��' => '♣', '��' => '⊙', '��' => '◈', '��' => '▣', '��' => '◐', '��' => '◑', '��' => '▒', '��' => '▤', '��' => '▥', '��' => '▨', '��' => '▧', '��' => '▦', '��' => '▩', '��' => '♨', '��' => '☏', '��' => '☎', '��' => '☜', '��' => '☞', '��' => '¶', '��' => '†', '��' => '‡', '��' => '↕', '��' => '↗', '��' => '↙', '��' => '↖', '��' => '↘', '��' => '♭', '��' => '♩', '��' => '♪', '��' => '♬', '��' => '㉿', '��' => '㈜', '��' => '№', '��' => '㏇', '��' => '™', '��' => '㏂', '��' => '㏘', '��' => '℡', '��' => '€', '��' => '®', '�A' => '쥱', '�B' => '쥲', '�C' => '쥳', '�D' => '쥵', '�E' => '쥶', '�F' => '쥷', '�G' => '쥸', '�H' => '쥹', '�I' => '쥺', '�J' => '쥻', '�K' => '쥽', '�L' => '쥾', '�M' => '쥿', '�N' => '즀', '�O' => '즁', '�P' => '즂', '�Q' => '즃', '�R' => '즄', '�S' => '즅', '�T' => '즆', '�U' => '즇', '�V' => '즊', '�W' => '즋', '�X' => '즍', '�Y' => '즎', '�Z' => '즏', '�a' => '즑', '�b' => '즒', '�c' => '즓', '�d' => '즔', '�e' => '즕', '�f' => '즖', '�g' => '즗', '�h' => '즚', '�i' => '즜', '�j' => '즞', '�k' => '즟', '�l' => '즠', '�m' => '즡', '�n' => '즢', '�o' => '즣', '�p' => '즤', '�q' => '즥', '�r' => '즦', '�s' => '즧', '�t' => '즨', '�u' => '즩', '�v' => '즪', '�w' => '즫', '�x' => '즬', '�y' => '즭', '�z' => '즮', '��' => '즯', '��' => '즰', '��' => '즱', '��' => '즲', '��' => '즳', '��' => '즴', '��' => '즵', '��' => '즶', '��' => '즷', '��' => '즸', '��' => '즹', '��' => '즺', '��' => '즻', '��' => '즼', '��' => '즽', '��' => '즾', '��' => '즿', '��' => '짂', '��' => '짃', '��' => '짅', '��' => '짆', '��' => '짉', '��' => '짋', '��' => '짌', '��' => '짍', '��' => '짎', '��' => '짏', '��' => '짒', '��' => '짔', '��' => '짗', '��' => '짘', '��' => '짛', '��' => '!', '��' => '"', '��' => '#', '��' => '$', '��' => '%', '��' => '&', '��' => ''', '��' => '(', '��' => ')', '��' => '*', '��' => '+', '��' => ',', '��' => '-', '��' => '.', '��' => '/', '��' => '0', '��' => '1', '��' => '2', '��' => '3', '��' => '4', '��' => '5', '��' => '6', '��' => '7', '��' => '8', '��' => '9', '��' => ':', '��' => ';', '��' => '<', '��' => '=', '��' => '>', '��' => '?', '��' => '@', '��' => 'A', '��' => 'B', '��' => 'C', '��' => 'D', '��' => 'E', '��' => 'F', '��' => 'G', '��' => 'H', '��' => 'I', '��' => 'J', '��' => 'K', '��' => 'L', '��' => 'M', '��' => 'N', '��' => 'O', '��' => 'P', '��' => 'Q', '��' => 'R', '��' => 'S', '��' => 'T', '��' => 'U', '��' => 'V', '��' => 'W', '��' => 'X', '��' => 'Y', '��' => 'Z', '��' => '[', '��' => '₩', '��' => ']', '��' => '^', '��' => '_', '��' => '`', '��' => 'a', '��' => 'b', '��' => 'c', '��' => 'd', '��' => 'e', '��' => 'f', '��' => 'g', '��' => 'h', '��' => 'i', '��' => 'j', '��' => 'k', '��' => 'l', '��' => 'm', '��' => 'n', '��' => 'o', '��' => 'p', '��' => 'q', '��' => 'r', '��' => 's', '��' => 't', '��' => 'u', '��' => 'v', '��' => 'w', '��' => 'x', '��' => 'y', '��' => 'z', '��' => '{', '��' => '|', '��' => '}', '��' => ' ̄', '�A' => '짞', '�B' => '짟', '�C' => '짡', '�D' => '짣', '�E' => '짥', '�F' => '짦', '�G' => '짨', '�H' => '짩', '�I' => '짪', '�J' => '짫', '�K' => '짮', '�L' => '짲', '�M' => '짳', '�N' => '짴', '�O' => '짵', '�P' => '짶', '�Q' => '짷', '�R' => '짺', '�S' => '짻', '�T' => '짽', '�U' => '짾', '�V' => '짿', '�W' => '쨁', '�X' => '쨂', '�Y' => '쨃', '�Z' => '쨄', '�a' => '쨅', '�b' => '쨆', '�c' => '쨇', '�d' => '쨊', '�e' => '쨎', '�f' => '쨏', '�g' => '쨐', '�h' => '쨑', '�i' => '쨒', '�j' => '쨓', '�k' => '쨕', '�l' => '쨖', '�m' => '쨗', '�n' => '쨙', '�o' => '쨚', '�p' => '쨛', '�q' => '쨜', '�r' => '쨝', '�s' => '쨞', '�t' => '쨟', '�u' => '쨠', '�v' => '쨡', '�w' => '쨢', '�x' => '쨣', '�y' => '쨤', '�z' => '쨥', '��' => '쨦', '��' => '쨧', '��' => '쨨', '��' => '쨪', '��' => '쨫', '��' => '쨬', '��' => '쨭', '��' => '쨮', '��' => '쨯', '��' => '쨰', '��' => '쨱', '��' => '쨲', '��' => '쨳', '��' => '쨴', '��' => '쨵', '��' => '쨶', '��' => '쨷', '��' => '쨸', '��' => '쨹', '��' => '쨺', '��' => '쨻', '��' => '쨼', '��' => '쨽', '��' => '쨾', '��' => '쨿', '��' => '쩀', '��' => '쩁', '��' => '쩂', '��' => '쩃', '��' => '쩄', '��' => '쩅', '��' => '쩆', '��' => 'ㄱ', '��' => 'ㄲ', '��' => 'ㄳ', '��' => 'ㄴ', '��' => 'ㄵ', '��' => 'ㄶ', '��' => 'ㄷ', '��' => 'ㄸ', '��' => 'ㄹ', '��' => 'ㄺ', '��' => 'ㄻ', '��' => 'ㄼ', '��' => 'ㄽ', '��' => 'ㄾ', '��' => 'ㄿ', '��' => 'ㅀ', '��' => 'ㅁ', '��' => 'ㅂ', '��' => 'ㅃ', '��' => 'ㅄ', '��' => 'ㅅ', '��' => 'ㅆ', '��' => 'ㅇ', '��' => 'ㅈ', '��' => 'ㅉ', '��' => 'ㅊ', '��' => 'ㅋ', '��' => 'ㅌ', '��' => 'ㅍ', '��' => 'ㅎ', '��' => 'ㅏ', '��' => 'ㅐ', '��' => 'ㅑ', '��' => 'ㅒ', '��' => 'ㅓ', '��' => 'ㅔ', '��' => 'ㅕ', '��' => 'ㅖ', '��' => 'ㅗ', '��' => 'ㅘ', '��' => 'ㅙ', '��' => 'ㅚ', '��' => 'ㅛ', '��' => 'ㅜ', '��' => 'ㅝ', '��' => 'ㅞ', '��' => 'ㅟ', '��' => 'ㅠ', '��' => 'ㅡ', '��' => 'ㅢ', '��' => 'ㅣ', '��' => 'ㅤ', '��' => 'ㅥ', '��' => 'ㅦ', '��' => 'ㅧ', '��' => 'ㅨ', '��' => 'ㅩ', '��' => 'ㅪ', '��' => 'ㅫ', '��' => 'ㅬ', '��' => 'ㅭ', '��' => 'ㅮ', '��' => 'ㅯ', '��' => 'ㅰ', '��' => 'ㅱ', '��' => 'ㅲ', '��' => 'ㅳ', '��' => 'ㅴ', '��' => 'ㅵ', '��' => 'ㅶ', '��' => 'ㅷ', '��' => 'ㅸ', '��' => 'ㅹ', '��' => 'ㅺ', '��' => 'ㅻ', '��' => 'ㅼ', '��' => 'ㅽ', '��' => 'ㅾ', '��' => 'ㅿ', '��' => 'ㆀ', '��' => 'ㆁ', '��' => 'ㆂ', '��' => 'ㆃ', '��' => 'ㆄ', '��' => 'ㆅ', '��' => 'ㆆ', '��' => 'ㆇ', '��' => 'ㆈ', '��' => 'ㆉ', '��' => 'ㆊ', '��' => 'ㆋ', '��' => 'ㆌ', '��' => 'ㆍ', '��' => 'ㆎ', '�A' => '쩇', '�B' => '쩈', '�C' => '쩉', '�D' => '쩊', '�E' => '쩋', '�F' => '쩎', '�G' => '쩏', '�H' => '쩑', '�I' => '쩒', '�J' => '쩓', '�K' => '쩕', '�L' => '쩖', '�M' => '쩗', '�N' => '쩘', '�O' => '쩙', '�P' => '쩚', '�Q' => '쩛', '�R' => '쩞', '�S' => '쩢', '�T' => '쩣', '�U' => '쩤', '�V' => '쩥', '�W' => '쩦', '�X' => '쩧', '�Y' => '쩩', '�Z' => '쩪', '�a' => '쩫', '�b' => '쩬', '�c' => '쩭', '�d' => '쩮', '�e' => '쩯', '�f' => '쩰', '�g' => '쩱', '�h' => '쩲', '�i' => '쩳', '�j' => '쩴', '�k' => '쩵', '�l' => '쩶', '�m' => '쩷', '�n' => '쩸', '�o' => '쩹', '�p' => '쩺', '�q' => '쩻', '�r' => '쩼', '�s' => '쩾', '�t' => '쩿', '�u' => '쪀', '�v' => '쪁', '�w' => '쪂', '�x' => '쪃', '�y' => '쪅', '�z' => '쪆', '��' => '쪇', '��' => '쪈', '��' => '쪉', '��' => '쪊', '��' => '쪋', '��' => '쪌', '��' => '쪍', '��' => '쪎', '��' => '쪏', '��' => '쪐', '��' => '쪑', '��' => '쪒', '��' => '쪓', '��' => '쪔', '��' => '쪕', '��' => '쪖', '��' => '쪗', '��' => '쪙', '��' => '쪚', '��' => '쪛', '��' => '쪜', '��' => '쪝', '��' => '쪞', '��' => '쪟', '��' => '쪠', '��' => '쪡', '��' => '쪢', '��' => '쪣', '��' => '쪤', '��' => '쪥', '��' => '쪦', '��' => '쪧', '��' => 'ⅰ', '��' => 'ⅱ', '��' => 'ⅲ', '��' => 'ⅳ', '��' => 'ⅴ', '��' => 'ⅵ', '��' => 'ⅶ', '��' => 'ⅷ', '��' => 'ⅸ', '��' => 'ⅹ', '��' => 'Ⅰ', '��' => 'Ⅱ', '��' => 'Ⅲ', '��' => 'Ⅳ', '��' => 'Ⅴ', '��' => 'Ⅵ', '��' => 'Ⅶ', '��' => 'Ⅷ', '��' => 'Ⅸ', '��' => 'Ⅹ', '��' => 'Α', '��' => 'Β', '��' => 'Γ', '��' => 'Δ', '��' => 'Ε', '��' => 'Ζ', '��' => 'Η', '��' => 'Θ', '��' => 'Ι', '��' => 'Κ', '��' => 'Λ', '��' => 'Μ', '��' => 'Ν', '��' => 'Ξ', '��' => 'Ο', '��' => 'Π', '��' => 'Ρ', '��' => 'Σ', '��' => 'Τ', '��' => 'Υ', '��' => 'Φ', '��' => 'Χ', '��' => 'Ψ', '��' => 'Ω', '��' => 'α', '��' => 'β', '��' => 'γ', '��' => 'δ', '��' => 'ε', '��' => 'ζ', '��' => 'η', '��' => 'θ', '��' => 'ι', '��' => 'κ', '��' => 'λ', '��' => 'μ', '��' => 'ν', '��' => 'ξ', '��' => 'ο', '��' => 'π', '��' => 'ρ', '��' => 'σ', '��' => 'τ', '��' => 'υ', '��' => 'φ', '��' => 'χ', '��' => 'ψ', '��' => 'ω', '�A' => '쪨', '�B' => '쪩', '�C' => '쪪', '�D' => '쪫', '�E' => '쪬', '�F' => '쪭', '�G' => '쪮', '�H' => '쪯', '�I' => '쪰', '�J' => '쪱', '�K' => '쪲', '�L' => '쪳', '�M' => '쪴', '�N' => '쪵', '�O' => '쪶', '�P' => '쪷', '�Q' => '쪸', '�R' => '쪹', '�S' => '쪺', '�T' => '쪻', '�U' => '쪾', '�V' => '쪿', '�W' => '쫁', '�X' => '쫂', '�Y' => '쫃', '�Z' => '쫅', '�a' => '쫆', '�b' => '쫇', '�c' => '쫈', '�d' => '쫉', '�e' => '쫊', '�f' => '쫋', '�g' => '쫎', '�h' => '쫐', '�i' => '쫒', '�j' => '쫔', '�k' => '쫕', '�l' => '쫖', '�m' => '쫗', '�n' => '쫚', '�o' => '쫛', '�p' => '쫜', '�q' => '쫝', '�r' => '쫞', '�s' => '쫟', '�t' => '쫡', '�u' => '쫢', '�v' => '쫣', '�w' => '쫤', '�x' => '쫥', '�y' => '쫦', '�z' => '쫧', '��' => '쫨', '��' => '쫩', '��' => '쫪', '��' => '쫫', '��' => '쫭', '��' => '쫮', '��' => '쫯', '��' => '쫰', '��' => '쫱', '��' => '쫲', '��' => '쫳', '��' => '쫵', '��' => '쫶', '��' => '쫷', '��' => '쫸', '��' => '쫹', '��' => '쫺', '��' => '쫻', '��' => '쫼', '��' => '쫽', '��' => '쫾', '��' => '쫿', '��' => '쬀', '��' => '쬁', '��' => '쬂', '��' => '쬃', '��' => '쬄', '��' => '쬅', '��' => '쬆', '��' => '쬇', '��' => '쬉', '��' => '쬊', '��' => '─', '��' => '│', '��' => '┌', '��' => '┐', '��' => '┘', '��' => '└', '��' => '├', '��' => '┬', '��' => '┤', '��' => '┴', '��' => '┼', '��' => '━', '��' => '┃', '��' => '┏', '��' => '┓', '��' => '┛', '��' => '┗', '��' => '┣', '��' => '┳', '��' => '┫', '��' => '┻', '��' => '╋', '��' => '┠', '��' => '┯', '��' => '┨', '��' => '┷', '��' => '┿', '��' => '┝', '��' => '┰', '��' => '┥', '��' => '┸', '��' => '╂', '��' => '┒', '��' => '┑', '��' => '┚', '��' => '┙', '��' => '┖', '��' => '┕', '��' => '┎', '��' => '┍', '��' => '┞', '��' => '┟', '��' => '┡', '��' => '┢', '��' => '┦', '��' => '┧', '��' => '┩', '��' => '┪', '��' => '┭', '��' => '┮', '��' => '┱', '��' => '┲', '��' => '┵', '��' => '┶', '��' => '┹', '��' => '┺', '��' => '┽', '��' => '┾', '��' => '╀', '��' => '╁', '��' => '╃', '��' => '╄', '��' => '╅', '��' => '╆', '��' => '╇', '��' => '╈', '��' => '╉', '��' => '╊', '�A' => '쬋', '�B' => '쬌', '�C' => '쬍', '�D' => '쬎', '�E' => '쬏', '�F' => '쬑', '�G' => '쬒', '�H' => '쬓', '�I' => '쬕', '�J' => '쬖', '�K' => '쬗', '�L' => '쬙', '�M' => '쬚', '�N' => '쬛', '�O' => '쬜', '�P' => '쬝', '�Q' => '쬞', '�R' => '쬟', '�S' => '쬢', '�T' => '쬣', '�U' => '쬤', '�V' => '쬥', '�W' => '쬦', '�X' => '쬧', '�Y' => '쬨', '�Z' => '쬩', '�a' => '쬪', '�b' => '쬫', '�c' => '쬬', '�d' => '쬭', '�e' => '쬮', '�f' => '쬯', '�g' => '쬰', '�h' => '쬱', '�i' => '쬲', '�j' => '쬳', '�k' => '쬴', '�l' => '쬵', '�m' => '쬶', '�n' => '쬷', '�o' => '쬸', '�p' => '쬹', '�q' => '쬺', '�r' => '쬻', '�s' => '쬼', '�t' => '쬽', '�u' => '쬾', '�v' => '쬿', '�w' => '쭀', '�x' => '쭂', '�y' => '쭃', '�z' => '쭄', '��' => '쭅', '��' => '쭆', '��' => '쭇', '��' => '쭊', '��' => '쭋', '��' => '쭍', '��' => '쭎', '��' => '쭏', '��' => '쭑', '��' => '쭒', '��' => '쭓', '��' => '쭔', '��' => '쭕', '��' => '쭖', '��' => '쭗', '��' => '쭚', '��' => '쭛', '��' => '쭜', '��' => '쭞', '��' => '쭟', '��' => '쭠', '��' => '쭡', '��' => '쭢', '��' => '쭣', '��' => '쭥', '��' => '쭦', '��' => '쭧', '��' => '쭨', '��' => '쭩', '��' => '쭪', '��' => '쭫', '��' => '쭬', '��' => '㎕', '��' => '㎖', '��' => '㎗', '��' => 'ℓ', '��' => '㎘', '��' => '㏄', '��' => '㎣', '��' => '㎤', '��' => '㎥', '��' => '㎦', '��' => '㎙', '��' => '㎚', '��' => '㎛', '��' => '㎜', '��' => '㎝', '��' => '㎞', '��' => '㎟', '��' => '㎠', '��' => '㎡', '��' => '㎢', '��' => '㏊', '��' => '㎍', '��' => '㎎', '��' => '㎏', '��' => '㏏', '��' => '㎈', '��' => '㎉', '��' => '㏈', '��' => '㎧', '��' => '㎨', '��' => '㎰', '��' => '㎱', '��' => '㎲', '��' => '㎳', '��' => '㎴', '��' => '㎵', '��' => '㎶', '��' => '㎷', '��' => '㎸', '��' => '㎹', '��' => '㎀', '��' => '㎁', '��' => '㎂', '��' => '㎃', '��' => '㎄', '��' => '㎺', '��' => '㎻', '��' => '㎼', '��' => '㎽', '��' => '㎾', '��' => '㎿', '��' => '㎐', '��' => '㎑', '��' => '㎒', '��' => '㎓', '��' => '㎔', '��' => 'Ω', '��' => '㏀', '��' => '㏁', '��' => '㎊', '��' => '㎋', '��' => '㎌', '��' => '㏖', '��' => '㏅', '��' => '㎭', '��' => '㎮', '��' => '㎯', '��' => '㏛', '��' => '㎩', '��' => '㎪', '��' => '㎫', '��' => '㎬', '��' => '㏝', '��' => '㏐', '��' => '㏓', '��' => '㏃', '��' => '㏉', '��' => '㏜', '��' => '㏆', '�A' => '쭭', '�B' => '쭮', '�C' => '쭯', '�D' => '쭰', '�E' => '쭱', '�F' => '쭲', '�G' => '쭳', '�H' => '쭴', '�I' => '쭵', '�J' => '쭶', '�K' => '쭷', '�L' => '쭺', '�M' => '쭻', '�N' => '쭼', '�O' => '쭽', '�P' => '쭾', '�Q' => '쭿', '�R' => '쮀', '�S' => '쮁', '�T' => '쮂', '�U' => '쮃', '�V' => '쮄', '�W' => '쮅', '�X' => '쮆', '�Y' => '쮇', '�Z' => '쮈', '�a' => '쮉', '�b' => '쮊', '�c' => '쮋', '�d' => '쮌', '�e' => '쮍', '�f' => '쮎', '�g' => '쮏', '�h' => '쮐', '�i' => '쮑', '�j' => '쮒', '�k' => '쮓', '�l' => '쮔', '�m' => '쮕', '�n' => '쮖', '�o' => '쮗', '�p' => '쮘', '�q' => '쮙', '�r' => '쮚', '�s' => '쮛', '�t' => '쮝', '�u' => '쮞', '�v' => '쮟', '�w' => '쮠', '�x' => '쮡', '�y' => '쮢', '�z' => '쮣', '��' => '쮤', '��' => '쮥', '��' => '쮦', '��' => '쮧', '��' => '쮨', '��' => '쮩', '��' => '쮪', '��' => '쮫', '��' => '쮬', '��' => '쮭', '��' => '쮮', '��' => '쮯', '��' => '쮰', '��' => '쮱', '��' => '쮲', '��' => '쮳', '��' => '쮴', '��' => '쮵', '��' => '쮶', '��' => '쮷', '��' => '쮹', '��' => '쮺', '��' => '쮻', '��' => '쮼', '��' => '쮽', '��' => '쮾', '��' => '쮿', '��' => '쯀', '��' => '쯁', '��' => '쯂', '��' => '쯃', '��' => '쯄', '��' => 'Æ', '��' => 'Ð', '��' => 'ª', '��' => 'Ħ', '��' => 'IJ', '��' => 'Ŀ', '��' => 'Ł', '��' => 'Ø', '��' => 'Œ', '��' => 'º', '��' => 'Þ', '��' => 'Ŧ', '��' => 'Ŋ', '��' => '㉠', '��' => '㉡', '��' => '㉢', '��' => '㉣', '��' => '㉤', '��' => '㉥', '��' => '㉦', '��' => '㉧', '��' => '㉨', '��' => '㉩', '��' => '㉪', '��' => '㉫', '��' => '㉬', '��' => '㉭', '��' => '㉮', '��' => '㉯', '��' => '㉰', '��' => '㉱', '��' => '㉲', '��' => '㉳', '��' => '㉴', '��' => '㉵', '��' => '㉶', '��' => '㉷', '��' => '㉸', '��' => '㉹', '��' => '㉺', '��' => '㉻', '��' => 'ⓐ', '��' => 'ⓑ', '��' => 'ⓒ', '��' => 'ⓓ', '��' => 'ⓔ', '��' => 'ⓕ', '��' => 'ⓖ', '��' => 'ⓗ', '��' => 'ⓘ', '��' => 'ⓙ', '��' => 'ⓚ', '��' => 'ⓛ', '��' => 'ⓜ', '��' => 'ⓝ', '��' => 'ⓞ', '��' => 'ⓟ', '��' => 'ⓠ', '��' => 'ⓡ', '��' => 'ⓢ', '��' => 'ⓣ', '��' => 'ⓤ', '��' => 'ⓥ', '��' => 'ⓦ', '��' => 'ⓧ', '��' => 'ⓨ', '��' => 'ⓩ', '��' => '①', '��' => '②', '��' => '③', '��' => '④', '��' => '⑤', '��' => '⑥', '��' => '⑦', '��' => '⑧', '��' => '⑨', '��' => '⑩', '��' => '⑪', '��' => '⑫', '��' => '⑬', '��' => '⑭', '��' => '⑮', '��' => '½', '��' => '⅓', '��' => '⅔', '��' => '¼', '��' => '¾', '��' => '⅛', '��' => '⅜', '��' => '⅝', '��' => '⅞', '�A' => '쯅', '�B' => '쯆', '�C' => '쯇', '�D' => '쯈', '�E' => '쯉', '�F' => '쯊', '�G' => '쯋', '�H' => '쯌', '�I' => '쯍', '�J' => '쯎', '�K' => '쯏', '�L' => '쯐', '�M' => '쯑', '�N' => '쯒', '�O' => '쯓', '�P' => '쯕', '�Q' => '쯖', '�R' => '쯗', '�S' => '쯘', '�T' => '쯙', '�U' => '쯚', '�V' => '쯛', '�W' => '쯜', '�X' => '쯝', '�Y' => '쯞', '�Z' => '쯟', '�a' => '쯠', '�b' => '쯡', '�c' => '쯢', '�d' => '쯣', '�e' => '쯥', '�f' => '쯦', '�g' => '쯨', '�h' => '쯪', '�i' => '쯫', '�j' => '쯬', '�k' => '쯭', '�l' => '쯮', '�m' => '쯯', '�n' => '쯰', '�o' => '쯱', '�p' => '쯲', '�q' => '쯳', '�r' => '쯴', '�s' => '쯵', '�t' => '쯶', '�u' => '쯷', '�v' => '쯸', '�w' => '쯹', '�x' => '쯺', '�y' => '쯻', '�z' => '쯼', '��' => '쯽', '��' => '쯾', '��' => '쯿', '��' => '찀', '��' => '찁', '��' => '찂', '��' => '찃', '��' => '찄', '��' => '찅', '��' => '찆', '��' => '찇', '��' => '찈', '��' => '찉', '��' => '찊', '��' => '찋', '��' => '찎', '��' => '찏', '��' => '찑', '��' => '찒', '��' => '찓', '��' => '찕', '��' => '찖', '��' => '찗', '��' => '찘', '��' => '찙', '��' => '찚', '��' => '찛', '��' => '찞', '��' => '찟', '��' => '찠', '��' => '찣', '��' => '찤', '��' => 'æ', '��' => 'đ', '��' => 'ð', '��' => 'ħ', '��' => 'ı', '��' => 'ij', '��' => 'ĸ', '��' => 'ŀ', '��' => 'ł', '��' => 'ø', '��' => 'œ', '��' => 'ß', '��' => 'þ', '��' => 'ŧ', '��' => 'ŋ', '��' => 'ʼn', '��' => '㈀', '��' => '㈁', '��' => '㈂', '��' => '㈃', '��' => '㈄', '��' => '㈅', '��' => '㈆', '��' => '㈇', '��' => '㈈', '��' => '㈉', '��' => '㈊', '��' => '㈋', '��' => '㈌', '��' => '㈍', '��' => '㈎', '��' => '㈏', '��' => '㈐', '��' => '㈑', '��' => '㈒', '��' => '㈓', '��' => '㈔', '��' => '㈕', '��' => '㈖', '��' => '㈗', '��' => '㈘', '��' => '㈙', '��' => '㈚', '��' => '㈛', '��' => '⒜', '��' => '⒝', '��' => '⒞', '��' => '⒟', '��' => '⒠', '��' => '⒡', '��' => '⒢', '��' => '⒣', '��' => '⒤', '��' => '⒥', '��' => '⒦', '��' => '⒧', '��' => '⒨', '��' => '⒩', '��' => '⒪', '��' => '⒫', '��' => '⒬', '��' => '⒭', '��' => '⒮', '��' => '⒯', '��' => '⒰', '��' => '⒱', '��' => '⒲', '��' => '⒳', '��' => '⒴', '��' => '⒵', '��' => '⑴', '��' => '⑵', '��' => '⑶', '��' => '⑷', '��' => '⑸', '��' => '⑹', '��' => '⑺', '��' => '⑻', '��' => '⑼', '��' => '⑽', '��' => '⑾', '��' => '⑿', '��' => '⒀', '��' => '⒁', '��' => '⒂', '��' => '¹', '��' => '²', '��' => '³', '��' => '⁴', '��' => 'ⁿ', '��' => '₁', '��' => '₂', '��' => '₃', '��' => '₄', '�A' => '찥', '�B' => '찦', '�C' => '찪', '�D' => '찫', '�E' => '찭', '�F' => '찯', '�G' => '찱', '�H' => '찲', '�I' => '찳', '�J' => '찴', '�K' => '찵', '�L' => '찶', '�M' => '찷', '�N' => '찺', '�O' => '찿', '�P' => '챀', '�Q' => '챁', '�R' => '챂', '�S' => '챃', '�T' => '챆', '�U' => '챇', '�V' => '챉', '�W' => '챊', '�X' => '챋', '�Y' => '챍', '�Z' => '챎', '�a' => '챏', '�b' => '챐', '�c' => '챑', '�d' => '챒', '�e' => '챓', '�f' => '챖', '�g' => '챚', '�h' => '챛', '�i' => '챜', '�j' => '챝', '�k' => '챞', '�l' => '챟', '�m' => '챡', '�n' => '챢', '�o' => '챣', '�p' => '챥', '�q' => '챧', '�r' => '챩', '�s' => '챪', '�t' => '챫', '�u' => '챬', '�v' => '챭', '�w' => '챮', '�x' => '챯', '�y' => '챱', '�z' => '챲', '��' => '챳', '��' => '챴', '��' => '챶', '��' => '챷', '��' => '챸', '��' => '챹', '��' => '챺', '��' => '챻', '��' => '챼', '��' => '챽', '��' => '챾', '��' => '챿', '��' => '첀', '��' => '첁', '��' => '첂', '��' => '첃', '��' => '첄', '��' => '첅', '��' => '첆', '��' => '첇', '��' => '첈', '��' => '첉', '��' => '첊', '��' => '첋', '��' => '첌', '��' => '첍', '��' => '첎', '��' => '첏', '��' => '첐', '��' => '첑', '��' => '첒', '��' => '첓', '��' => 'ぁ', '��' => 'あ', '��' => 'ぃ', '��' => 'い', '��' => 'ぅ', '��' => 'う', '��' => 'ぇ', '��' => 'え', '��' => 'ぉ', '��' => 'お', '��' => 'か', '��' => 'が', '��' => 'き', '��' => 'ぎ', '��' => 'く', '��' => 'ぐ', '��' => 'け', '��' => 'げ', '��' => 'こ', '��' => 'ご', '��' => 'さ', '��' => 'ざ', '��' => 'し', '��' => 'じ', '��' => 'す', '��' => 'ず', '��' => 'せ', '��' => 'ぜ', '��' => 'そ', '��' => 'ぞ', '��' => 'た', '��' => 'だ', '��' => 'ち', '��' => 'ぢ', '��' => 'っ', '��' => 'つ', '��' => 'づ', '��' => 'て', '��' => 'で', '��' => 'と', '��' => 'ど', '��' => 'な', '��' => 'に', '��' => 'ぬ', '��' => 'ね', '��' => 'の', '��' => 'は', '��' => 'ば', '��' => 'ぱ', '��' => 'ひ', '��' => 'び', '��' => 'ぴ', '��' => 'ふ', '��' => 'ぶ', '��' => 'ぷ', '��' => 'へ', '��' => 'べ', '��' => 'ぺ', '��' => 'ほ', '��' => 'ぼ', '��' => 'ぽ', '��' => 'ま', '��' => 'み', '��' => 'む', '��' => 'め', '��' => 'も', '��' => 'ゃ', '��' => 'や', '��' => 'ゅ', '��' => 'ゆ', '��' => 'ょ', '��' => 'よ', '��' => 'ら', '��' => 'り', '��' => 'る', '��' => 'れ', '��' => 'ろ', '��' => 'ゎ', '��' => 'わ', '��' => 'ゐ', '��' => 'ゑ', '��' => 'を', '��' => 'ん', '�A' => '첔', '�B' => '첕', '�C' => '첖', '�D' => '첗', '�E' => '첚', '�F' => '첛', '�G' => '첝', '�H' => '첞', '�I' => '첟', '�J' => '첡', '�K' => '첢', '�L' => '첣', '�M' => '첤', '�N' => '첥', '�O' => '첦', '�P' => '첧', '�Q' => '첪', '�R' => '첮', '�S' => '첯', '�T' => '첰', '�U' => '첱', '�V' => '첲', '�W' => '첳', '�X' => '첶', '�Y' => '첷', '�Z' => '첹', '�a' => '첺', '�b' => '첻', '�c' => '첽', '�d' => '첾', '�e' => '첿', '�f' => '쳀', '�g' => '쳁', '�h' => '쳂', '�i' => '쳃', '�j' => '쳆', '�k' => '쳈', '�l' => '쳊', '�m' => '쳋', '�n' => '쳌', '�o' => '쳍', '�p' => '쳎', '�q' => '쳏', '�r' => '쳑', '�s' => '쳒', '�t' => '쳓', '�u' => '쳕', '�v' => '쳖', '�w' => '쳗', '�x' => '쳘', '�y' => '쳙', '�z' => '쳚', '��' => '쳛', '��' => '쳜', '��' => '쳝', '��' => '쳞', '��' => '쳟', '��' => '쳠', '��' => '쳡', '��' => '쳢', '��' => '쳣', '��' => '쳥', '��' => '쳦', '��' => '쳧', '��' => '쳨', '��' => '쳩', '��' => '쳪', '��' => '쳫', '��' => '쳭', '��' => '쳮', '��' => '쳯', '��' => '쳱', '��' => '쳲', '��' => '쳳', '��' => '쳴', '��' => '쳵', '��' => '쳶', '��' => '쳷', '��' => '쳸', '��' => '쳹', '��' => '쳺', '��' => '쳻', '��' => '쳼', '��' => '쳽', '��' => 'ァ', '��' => 'ア', '��' => 'ィ', '��' => 'イ', '��' => 'ゥ', '��' => 'ウ', '��' => 'ェ', '��' => 'エ', '��' => 'ォ', '��' => 'オ', '��' => 'カ', '��' => 'ガ', '��' => 'キ', '��' => 'ギ', '��' => 'ク', '��' => 'グ', '��' => 'ケ', '��' => 'ゲ', '��' => 'コ', '��' => 'ゴ', '��' => 'サ', '��' => 'ザ', '��' => 'シ', '��' => 'ジ', '��' => 'ス', '��' => 'ズ', '��' => 'セ', '��' => 'ゼ', '��' => 'ソ', '��' => 'ゾ', '��' => 'タ', '��' => 'ダ', '��' => 'チ', '��' => 'ヂ', '��' => 'ッ', '��' => 'ツ', '��' => 'ヅ', '��' => 'テ', '��' => 'デ', '��' => 'ト', '��' => 'ド', '��' => 'ナ', '��' => 'ニ', '��' => 'ヌ', '��' => 'ネ', '��' => 'ノ', '��' => 'ハ', '��' => 'バ', '��' => 'パ', '��' => 'ヒ', '��' => 'ビ', '��' => 'ピ', '��' => 'フ', '��' => 'ブ', '��' => 'プ', '��' => 'ヘ', '��' => 'ベ', '��' => 'ペ', '��' => 'ホ', '��' => 'ボ', '��' => 'ポ', '��' => 'マ', '��' => 'ミ', '��' => 'ム', '��' => 'メ', '��' => 'モ', '��' => 'ャ', '��' => 'ヤ', '��' => 'ュ', '��' => 'ユ', '��' => 'ョ', '��' => 'ヨ', '��' => 'ラ', '��' => 'リ', '��' => 'ル', '��' => 'レ', '��' => 'ロ', '��' => 'ヮ', '��' => 'ワ', '��' => 'ヰ', '��' => 'ヱ', '��' => 'ヲ', '��' => 'ン', '��' => 'ヴ', '��' => 'ヵ', '��' => 'ヶ', '�A' => '쳾', '�B' => '쳿', '�C' => '촀', '�D' => '촂', '�E' => '촃', '�F' => '촄', '�G' => '촅', '�H' => '촆', '�I' => '촇', '�J' => '촊', '�K' => '촋', '�L' => '촍', '�M' => '촎', '�N' => '촏', '�O' => '촑', '�P' => '촒', '�Q' => '촓', '�R' => '촔', '�S' => '촕', '�T' => '촖', '�U' => '촗', '�V' => '촚', '�W' => '촜', '�X' => '촞', '�Y' => '촟', '�Z' => '촠', '�a' => '촡', '�b' => '촢', '�c' => '촣', '�d' => '촥', '�e' => '촦', '�f' => '촧', '�g' => '촩', '�h' => '촪', '�i' => '촫', '�j' => '촭', '�k' => '촮', '�l' => '촯', '�m' => '촰', '�n' => '촱', '�o' => '촲', '�p' => '촳', '�q' => '촴', '�r' => '촵', '�s' => '촶', '�t' => '촷', '�u' => '촸', '�v' => '촺', '�w' => '촻', '�x' => '촼', '�y' => '촽', '�z' => '촾', '��' => '촿', '��' => '쵀', '��' => '쵁', '��' => '쵂', '��' => '쵃', '��' => '쵄', '��' => '쵅', '��' => '쵆', '��' => '쵇', '��' => '쵈', '��' => '쵉', '��' => '쵊', '��' => '쵋', '��' => '쵌', '��' => '쵍', '��' => '쵎', '��' => '쵏', '��' => '쵐', '��' => '쵑', '��' => '쵒', '��' => '쵓', '��' => '쵔', '��' => '쵕', '��' => '쵖', '��' => '쵗', '��' => '쵘', '��' => '쵙', '��' => '쵚', '��' => '쵛', '��' => '쵝', '��' => '쵞', '��' => '쵟', '��' => 'А', '��' => 'Б', '��' => 'В', '��' => 'Г', '��' => 'Д', '��' => 'Е', '��' => 'Ё', '��' => 'Ж', '��' => 'З', '��' => 'И', '��' => 'Й', '��' => 'К', '��' => 'Л', '��' => 'М', '��' => 'Н', '��' => 'О', '��' => 'П', '��' => 'Р', '��' => 'С', '��' => 'Т', '��' => 'У', '��' => 'Ф', '��' => 'Х', '��' => 'Ц', '��' => 'Ч', '��' => 'Ш', '��' => 'Щ', '��' => 'Ъ', '��' => 'Ы', '��' => 'Ь', '��' => 'Э', '��' => 'Ю', '��' => 'Я', '��' => 'а', '��' => 'б', '��' => 'в', '��' => 'г', '��' => 'д', '��' => 'е', '��' => 'ё', '��' => 'ж', '��' => 'з', '��' => 'и', '��' => 'й', '��' => 'к', '��' => 'л', '��' => 'м', '��' => 'н', '��' => 'о', '��' => 'п', '��' => 'р', '��' => 'с', '��' => 'т', '��' => 'у', '��' => 'ф', '��' => 'х', '��' => 'ц', '��' => 'ч', '��' => 'ш', '��' => 'щ', '��' => 'ъ', '��' => 'ы', '��' => 'ь', '��' => 'э', '��' => 'ю', '��' => 'я', '�A' => '쵡', '�B' => '쵢', '�C' => '쵣', '�D' => '쵥', '�E' => '쵦', '�F' => '쵧', '�G' => '쵨', '�H' => '쵩', '�I' => '쵪', '�J' => '쵫', '�K' => '쵮', '�L' => '쵰', '�M' => '쵲', '�N' => '쵳', '�O' => '쵴', '�P' => '쵵', '�Q' => '쵶', '�R' => '쵷', '�S' => '쵹', '�T' => '쵺', '�U' => '쵻', '�V' => '쵼', '�W' => '쵽', '�X' => '쵾', '�Y' => '쵿', '�Z' => '춀', '�a' => '춁', '�b' => '춂', '�c' => '춃', '�d' => '춄', '�e' => '춅', '�f' => '춆', '�g' => '춇', '�h' => '춉', '�i' => '춊', '�j' => '춋', '�k' => '춌', '�l' => '춍', '�m' => '춎', '�n' => '춏', '�o' => '춐', '�p' => '춑', '�q' => '춒', '�r' => '춓', '�s' => '춖', '�t' => '춗', '�u' => '춙', '�v' => '춚', '�w' => '춛', '�x' => '춝', '�y' => '춞', '�z' => '춟', '��' => '춠', '��' => '춡', '��' => '춢', '��' => '춣', '��' => '춦', '��' => '춨', '��' => '춪', '��' => '춫', '��' => '춬', '��' => '춭', '��' => '춮', '��' => '춯', '��' => '춱', '��' => '춲', '��' => '춳', '��' => '춴', '��' => '춵', '��' => '춶', '��' => '춷', '��' => '춸', '��' => '춹', '��' => '춺', '��' => '춻', '��' => '춼', '��' => '춽', '��' => '춾', '��' => '춿', '��' => '췀', '��' => '췁', '��' => '췂', '��' => '췃', '��' => '췅', '�A' => '췆', '�B' => '췇', '�C' => '췈', '�D' => '췉', '�E' => '췊', '�F' => '췋', '�G' => '췍', '�H' => '췎', '�I' => '췏', '�J' => '췑', '�K' => '췒', '�L' => '췓', '�M' => '췔', '�N' => '췕', '�O' => '췖', '�P' => '췗', '�Q' => '췘', '�R' => '췙', '�S' => '췚', '�T' => '췛', '�U' => '췜', '�V' => '췝', '�W' => '췞', '�X' => '췟', '�Y' => '췠', '�Z' => '췡', '�a' => '췢', '�b' => '췣', '�c' => '췤', '�d' => '췥', '�e' => '췦', '�f' => '췧', '�g' => '췩', '�h' => '췪', '�i' => '췫', '�j' => '췭', '�k' => '췮', '�l' => '췯', '�m' => '췱', '�n' => '췲', '�o' => '췳', '�p' => '췴', '�q' => '췵', '�r' => '췶', '�s' => '췷', '�t' => '췺', '�u' => '췼', '�v' => '췾', '�w' => '췿', '�x' => '츀', '�y' => '츁', '�z' => '츂', '��' => '츃', '��' => '츅', '��' => '츆', '��' => '츇', '��' => '츉', '��' => '츊', '��' => '츋', '��' => '츍', '��' => '츎', '��' => '츏', '��' => '츐', '��' => '츑', '��' => '츒', '��' => '츓', '��' => '츕', '��' => '츖', '��' => '츗', '��' => '츘', '��' => '츚', '��' => '츛', '��' => '츜', '��' => '츝', '��' => '츞', '��' => '츟', '��' => '츢', '��' => '츣', '��' => '츥', '��' => '츦', '��' => '츧', '��' => '츩', '��' => '츪', '��' => '츫', '�A' => '츬', '�B' => '츭', '�C' => '츮', '�D' => '츯', '�E' => '츲', '�F' => '츴', '�G' => '츶', '�H' => '츷', '�I' => '츸', '�J' => '츹', '�K' => '츺', '�L' => '츻', '�M' => '츼', '�N' => '츽', '�O' => '츾', '�P' => '츿', '�Q' => '칀', '�R' => '칁', '�S' => '칂', '�T' => '칃', '�U' => '칄', '�V' => '칅', '�W' => '칆', '�X' => '칇', '�Y' => '칈', '�Z' => '칉', '�a' => '칊', '�b' => '칋', '�c' => '칌', '�d' => '칍', '�e' => '칎', '�f' => '칏', '�g' => '칐', '�h' => '칑', '�i' => '칒', '�j' => '칓', '�k' => '칔', '�l' => '칕', '�m' => '칖', '�n' => '칗', '�o' => '칚', '�p' => '칛', '�q' => '칝', '�r' => '칞', '�s' => '칢', '�t' => '칣', '�u' => '칤', '�v' => '칥', '�w' => '칦', '�x' => '칧', '�y' => '칪', '�z' => '칬', '��' => '칮', '��' => '칯', '��' => '칰', '��' => '칱', '��' => '칲', '��' => '칳', '��' => '칶', '��' => '칷', '��' => '칹', '��' => '칺', '��' => '칻', '��' => '칽', '��' => '칾', '��' => '칿', '��' => '캀', '��' => '캁', '��' => '캂', '��' => '캃', '��' => '캆', '��' => '캈', '��' => '캊', '��' => '캋', '��' => '캌', '��' => '캍', '��' => '캎', '��' => '캏', '��' => '캒', '��' => '캓', '��' => '캕', '��' => '캖', '��' => '캗', '��' => '캙', '�A' => '캚', '�B' => '캛', '�C' => '캜', '�D' => '캝', '�E' => '캞', '�F' => '캟', '�G' => '캢', '�H' => '캦', '�I' => '캧', '�J' => '캨', '�K' => '캩', '�L' => '캪', '�M' => '캫', '�N' => '캮', '�O' => '캯', '�P' => '캰', '�Q' => '캱', '�R' => '캲', '�S' => '캳', '�T' => '캴', '�U' => '캵', '�V' => '캶', '�W' => '캷', '�X' => '캸', '�Y' => '캹', '�Z' => '캺', '�a' => '캻', '�b' => '캼', '�c' => '캽', '�d' => '캾', '�e' => '캿', '�f' => '컀', '�g' => '컂', '�h' => '컃', '�i' => '컄', '�j' => '컅', '�k' => '컆', '�l' => '컇', '�m' => '컈', '�n' => '컉', '�o' => '컊', '�p' => '컋', '�q' => '컌', '�r' => '컍', '�s' => '컎', '�t' => '컏', '�u' => '컐', '�v' => '컑', '�w' => '컒', '�x' => '컓', '�y' => '컔', '�z' => '컕', '��' => '컖', '��' => '컗', '��' => '컘', '��' => '컙', '��' => '컚', '��' => '컛', '��' => '컜', '��' => '컝', '��' => '컞', '��' => '컟', '��' => '컠', '��' => '컡', '��' => '컢', '��' => '컣', '��' => '컦', '��' => '컧', '��' => '컩', '��' => '컪', '��' => '컭', '��' => '컮', '��' => '컯', '��' => '컰', '��' => '컱', '��' => '컲', '��' => '컳', '��' => '컶', '��' => '컺', '��' => '컻', '��' => '컼', '��' => '컽', '��' => '컾', '��' => '컿', '��' => '가', '��' => '각', '��' => '간', '��' => '갇', '��' => '갈', '��' => '갉', '��' => '갊', '��' => '감', '��' => '갑', '��' => '값', '��' => '갓', '��' => '갔', '��' => '강', '��' => '갖', '��' => '갗', '��' => '같', '��' => '갚', '��' => '갛', '��' => '개', '��' => '객', '��' => '갠', '��' => '갤', '��' => '갬', '��' => '갭', '��' => '갯', '��' => '갰', '��' => '갱', '��' => '갸', '��' => '갹', '��' => '갼', '��' => '걀', '��' => '걋', '��' => '걍', '��' => '걔', '��' => '걘', '��' => '걜', '��' => '거', '��' => '걱', '��' => '건', '��' => '걷', '��' => '걸', '��' => '걺', '��' => '검', '��' => '겁', '��' => '것', '��' => '겄', '��' => '겅', '��' => '겆', '��' => '겉', '��' => '겊', '��' => '겋', '��' => '게', '��' => '겐', '��' => '겔', '��' => '겜', '��' => '겝', '��' => '겟', '��' => '겠', '��' => '겡', '��' => '겨', '��' => '격', '��' => '겪', '��' => '견', '��' => '겯', '��' => '결', '��' => '겸', '��' => '겹', '��' => '겻', '��' => '겼', '��' => '경', '��' => '곁', '��' => '계', '��' => '곈', '��' => '곌', '��' => '곕', '��' => '곗', '��' => '고', '��' => '곡', '��' => '곤', '��' => '곧', '��' => '골', '��' => '곪', '��' => '곬', '��' => '곯', '��' => '곰', '��' => '곱', '��' => '곳', '��' => '공', '��' => '곶', '��' => '과', '��' => '곽', '��' => '관', '��' => '괄', '��' => '괆', '�A' => '켂', '�B' => '켃', '�C' => '켅', '�D' => '켆', '�E' => '켇', '�F' => '켉', '�G' => '켊', '�H' => '켋', '�I' => '켌', '�J' => '켍', '�K' => '켎', '�L' => '켏', '�M' => '켒', '�N' => '켔', '�O' => '켖', '�P' => '켗', '�Q' => '켘', '�R' => '켙', '�S' => '켚', '�T' => '켛', '�U' => '켝', '�V' => '켞', '�W' => '켟', '�X' => '켡', '�Y' => '켢', '�Z' => '켣', '�a' => '켥', '�b' => '켦', '�c' => '켧', '�d' => '켨', '�e' => '켩', '�f' => '켪', '�g' => '켫', '�h' => '켮', '�i' => '켲', '�j' => '켳', '�k' => '켴', '�l' => '켵', '�m' => '켶', '�n' => '켷', '�o' => '켹', '�p' => '켺', '�q' => '켻', '�r' => '켼', '�s' => '켽', '�t' => '켾', '�u' => '켿', '�v' => '콀', '�w' => '콁', '�x' => '콂', '�y' => '콃', '�z' => '콄', '��' => '콅', '��' => '콆', '��' => '콇', '��' => '콈', '��' => '콉', '��' => '콊', '��' => '콋', '��' => '콌', '��' => '콍', '��' => '콎', '��' => '콏', '��' => '콐', '��' => '콑', '��' => '콒', '��' => '콓', '��' => '콖', '��' => '콗', '��' => '콙', '��' => '콚', '��' => '콛', '��' => '콝', '��' => '콞', '��' => '콟', '��' => '콠', '��' => '콡', '��' => '콢', '��' => '콣', '��' => '콦', '��' => '콨', '��' => '콪', '��' => '콫', '��' => '콬', '��' => '괌', '��' => '괍', '��' => '괏', '��' => '광', '��' => '괘', '��' => '괜', '��' => '괠', '��' => '괩', '��' => '괬', '��' => '괭', '��' => '괴', '��' => '괵', '��' => '괸', '��' => '괼', '��' => '굄', '��' => '굅', '��' => '굇', '��' => '굉', '��' => '교', '��' => '굔', '��' => '굘', '��' => '굡', '��' => '굣', '��' => '구', '��' => '국', '��' => '군', '��' => '굳', '��' => '굴', '��' => '굵', '��' => '굶', '��' => '굻', '��' => '굼', '��' => '굽', '��' => '굿', '��' => '궁', '��' => '궂', '��' => '궈', '��' => '궉', '��' => '권', '��' => '궐', '��' => '궜', '��' => '궝', '��' => '궤', '��' => '궷', '��' => '귀', '��' => '귁', '��' => '귄', '��' => '귈', '��' => '귐', '��' => '귑', '��' => '귓', '��' => '규', '��' => '균', '��' => '귤', '��' => '그', '��' => '극', '��' => '근', '��' => '귿', '��' => '글', '��' => '긁', '��' => '금', '��' => '급', '��' => '긋', '��' => '긍', '��' => '긔', '��' => '기', '��' => '긱', '��' => '긴', '��' => '긷', '��' => '길', '��' => '긺', '��' => '김', '��' => '깁', '��' => '깃', '��' => '깅', '��' => '깆', '��' => '깊', '��' => '까', '��' => '깍', '��' => '깎', '��' => '깐', '��' => '깔', '��' => '깖', '��' => '깜', '��' => '깝', '��' => '깟', '��' => '깠', '��' => '깡', '��' => '깥', '��' => '깨', '��' => '깩', '��' => '깬', '��' => '깰', '��' => '깸', '�A' => '콭', '�B' => '콮', '�C' => '콯', '�D' => '콲', '�E' => '콳', '�F' => '콵', '�G' => '콶', '�H' => '콷', '�I' => '콹', '�J' => '콺', '�K' => '콻', '�L' => '콼', '�M' => '콽', '�N' => '콾', '�O' => '콿', '�P' => '쾁', '�Q' => '쾂', '�R' => '쾃', '�S' => '쾄', '�T' => '쾆', '�U' => '쾇', '�V' => '쾈', '�W' => '쾉', '�X' => '쾊', '�Y' => '쾋', '�Z' => '쾍', '�a' => '쾎', '�b' => '쾏', '�c' => '쾐', '�d' => '쾑', '�e' => '쾒', '�f' => '쾓', '�g' => '쾔', '�h' => '쾕', '�i' => '쾖', '�j' => '쾗', '�k' => '쾘', '�l' => '쾙', '�m' => '쾚', '�n' => '쾛', '�o' => '쾜', '�p' => '쾝', '�q' => '쾞', '�r' => '쾟', '�s' => '쾠', '�t' => '쾢', '�u' => '쾣', '�v' => '쾤', '�w' => '쾥', '�x' => '쾦', '�y' => '쾧', '�z' => '쾩', '��' => '쾪', '��' => '쾫', '��' => '쾬', '��' => '쾭', '��' => '쾮', '��' => '쾯', '��' => '쾱', '��' => '쾲', '��' => '쾳', '��' => '쾴', '��' => '쾵', '��' => '쾶', '��' => '쾷', '��' => '쾸', '��' => '쾹', '��' => '쾺', '��' => '쾻', '��' => '쾼', '��' => '쾽', '��' => '쾾', '��' => '쾿', '��' => '쿀', '��' => '쿁', '��' => '쿂', '��' => '쿃', '��' => '쿅', '��' => '쿆', '��' => '쿇', '��' => '쿈', '��' => '쿉', '��' => '쿊', '��' => '쿋', '��' => '깹', '��' => '깻', '��' => '깼', '��' => '깽', '��' => '꺄', '��' => '꺅', '��' => '꺌', '��' => '꺼', '��' => '꺽', '��' => '꺾', '��' => '껀', '��' => '껄', '��' => '껌', '��' => '껍', '��' => '껏', '��' => '껐', '��' => '껑', '��' => '께', '��' => '껙', '��' => '껜', '��' => '껨', '��' => '껫', '��' => '껭', '��' => '껴', '��' => '껸', '��' => '껼', '��' => '꼇', '��' => '꼈', '��' => '꼍', '��' => '꼐', '��' => '꼬', '��' => '꼭', '��' => '꼰', '��' => '꼲', '��' => '꼴', '��' => '꼼', '��' => '꼽', '��' => '꼿', '��' => '꽁', '��' => '꽂', '��' => '꽃', '��' => '꽈', '��' => '꽉', '��' => '꽐', '��' => '꽜', '��' => '꽝', '��' => '꽤', '��' => '꽥', '��' => '꽹', '��' => '꾀', '��' => '꾄', '��' => '꾈', '��' => '꾐', '��' => '꾑', '��' => '꾕', '��' => '꾜', '��' => '꾸', '��' => '꾹', '��' => '꾼', '��' => '꿀', '��' => '꿇', '��' => '꿈', '��' => '꿉', '��' => '꿋', '��' => '꿍', '��' => '꿎', '��' => '꿔', '��' => '꿜', '��' => '꿨', '��' => '꿩', '��' => '꿰', '��' => '꿱', '��' => '꿴', '��' => '꿸', '��' => '뀀', '��' => '뀁', '��' => '뀄', '��' => '뀌', '��' => '뀐', '��' => '뀔', '��' => '뀜', '��' => '뀝', '��' => '뀨', '��' => '끄', '��' => '끅', '��' => '끈', '��' => '끊', '��' => '끌', '��' => '끎', '��' => '끓', '��' => '끔', '��' => '끕', '��' => '끗', '��' => '끙', '�A' => '쿌', '�B' => '쿍', '�C' => '쿎', '�D' => '쿏', '�E' => '쿐', '�F' => '쿑', '�G' => '쿒', '�H' => '쿓', '�I' => '쿔', '�J' => '쿕', '�K' => '쿖', '�L' => '쿗', '�M' => '쿘', '�N' => '쿙', '�O' => '쿚', '�P' => '쿛', '�Q' => '쿜', '�R' => '쿝', '�S' => '쿞', '�T' => '쿟', '�U' => '쿢', '�V' => '쿣', '�W' => '쿥', '�X' => '쿦', '�Y' => '쿧', '�Z' => '쿩', '�a' => '쿪', '�b' => '쿫', '�c' => '쿬', '�d' => '쿭', '�e' => '쿮', '�f' => '쿯', '�g' => '쿲', '�h' => '쿴', '�i' => '쿶', '�j' => '쿷', '�k' => '쿸', '�l' => '쿹', '�m' => '쿺', '�n' => '쿻', '�o' => '쿽', '�p' => '쿾', '�q' => '쿿', '�r' => '퀁', '�s' => '퀂', '�t' => '퀃', '�u' => '퀅', '�v' => '퀆', '�w' => '퀇', '�x' => '퀈', '�y' => '퀉', '�z' => '퀊', '��' => '퀋', '��' => '퀌', '��' => '퀍', '��' => '퀎', '��' => '퀏', '��' => '퀐', '��' => '퀒', '��' => '퀓', '��' => '퀔', '��' => '퀕', '��' => '퀖', '��' => '퀗', '��' => '퀙', '��' => '퀚', '��' => '퀛', '��' => '퀜', '��' => '퀝', '��' => '퀞', '��' => '퀟', '��' => '퀠', '��' => '퀡', '��' => '퀢', '��' => '퀣', '��' => '퀤', '��' => '퀥', '��' => '퀦', '��' => '퀧', '��' => '퀨', '��' => '퀩', '��' => '퀪', '��' => '퀫', '��' => '퀬', '��' => '끝', '��' => '끼', '��' => '끽', '��' => '낀', '��' => '낄', '��' => '낌', '��' => '낍', '��' => '낏', '��' => '낑', '��' => '나', '��' => '낙', '��' => '낚', '��' => '난', '��' => '낟', '��' => '날', '��' => '낡', '��' => '낢', '��' => '남', '��' => '납', '��' => '낫', '��' => '났', '��' => '낭', '��' => '낮', '��' => '낯', '��' => '낱', '��' => '낳', '��' => '내', '��' => '낵', '��' => '낸', '��' => '낼', '��' => '냄', '��' => '냅', '��' => '냇', '��' => '냈', '��' => '냉', '��' => '냐', '��' => '냑', '��' => '냔', '��' => '냘', '��' => '냠', '��' => '냥', '��' => '너', '��' => '넉', '��' => '넋', '��' => '넌', '��' => '널', '��' => '넒', '��' => '넓', '��' => '넘', '��' => '넙', '��' => '넛', '��' => '넜', '��' => '넝', '��' => '넣', '��' => '네', '��' => '넥', '��' => '넨', '��' => '넬', '��' => '넴', '��' => '넵', '��' => '넷', '��' => '넸', '��' => '넹', '��' => '녀', '��' => '녁', '��' => '년', '��' => '녈', '��' => '념', '��' => '녑', '��' => '녔', '��' => '녕', '��' => '녘', '��' => '녜', '��' => '녠', '��' => '노', '��' => '녹', '��' => '논', '��' => '놀', '��' => '놂', '��' => '놈', '��' => '놉', '��' => '놋', '��' => '농', '��' => '높', '��' => '놓', '��' => '놔', '��' => '놘', '��' => '놜', '��' => '놨', '��' => '뇌', '��' => '뇐', '��' => '뇔', '��' => '뇜', '��' => '뇝', '�A' => '퀮', '�B' => '퀯', '�C' => '퀰', '�D' => '퀱', '�E' => '퀲', '�F' => '퀳', '�G' => '퀶', '�H' => '퀷', '�I' => '퀹', '�J' => '퀺', '�K' => '퀻', '�L' => '퀽', '�M' => '퀾', '�N' => '퀿', '�O' => '큀', '�P' => '큁', '�Q' => '큂', '�R' => '큃', '�S' => '큆', '�T' => '큈', '�U' => '큊', '�V' => '큋', '�W' => '큌', '�X' => '큍', '�Y' => '큎', '�Z' => '큏', '�a' => '큑', '�b' => '큒', '�c' => '큓', '�d' => '큕', '�e' => '큖', '�f' => '큗', '�g' => '큙', '�h' => '큚', '�i' => '큛', '�j' => '큜', '�k' => '큝', '�l' => '큞', '�m' => '큟', '�n' => '큡', '�o' => '큢', '�p' => '큣', '�q' => '큤', '�r' => '큥', '�s' => '큦', '�t' => '큧', '�u' => '큨', '�v' => '큩', '�w' => '큪', '�x' => '큫', '�y' => '큮', '�z' => '큯', '��' => '큱', '��' => '큲', '��' => '큳', '��' => '큵', '��' => '큶', '��' => '큷', '��' => '큸', '��' => '큹', '��' => '큺', '��' => '큻', '��' => '큾', '��' => '큿', '��' => '킀', '��' => '킂', '��' => '킃', '��' => '킄', '��' => '킅', '��' => '킆', '��' => '킇', '��' => '킈', '��' => '킉', '��' => '킊', '��' => '킋', '��' => '킌', '��' => '킍', '��' => '킎', '��' => '킏', '��' => '킐', '��' => '킑', '��' => '킒', '��' => '킓', '��' => '킔', '��' => '뇟', '��' => '뇨', '��' => '뇩', '��' => '뇬', '��' => '뇰', '��' => '뇹', '��' => '뇻', '��' => '뇽', '��' => '누', '��' => '눅', '��' => '눈', '��' => '눋', '��' => '눌', '��' => '눔', '��' => '눕', '��' => '눗', '��' => '눙', '��' => '눠', '��' => '눴', '��' => '눼', '��' => '뉘', '��' => '뉜', '��' => '뉠', '��' => '뉨', '��' => '뉩', '��' => '뉴', '��' => '뉵', '��' => '뉼', '��' => '늄', '��' => '늅', '��' => '늉', '��' => '느', '��' => '늑', '��' => '는', '��' => '늘', '��' => '늙', '��' => '늚', '��' => '늠', '��' => '늡', '��' => '늣', '��' => '능', '��' => '늦', '��' => '늪', '��' => '늬', '��' => '늰', '��' => '늴', '��' => '니', '��' => '닉', '��' => '닌', '��' => '닐', '��' => '닒', '��' => '님', '��' => '닙', '��' => '닛', '��' => '닝', '��' => '닢', '��' => '다', '��' => '닥', '��' => '닦', '��' => '단', '��' => '닫', '��' => '달', '��' => '닭', '��' => '닮', '��' => '닯', '��' => '닳', '��' => '담', '��' => '답', '��' => '닷', '��' => '닸', '��' => '당', '��' => '닺', '��' => '닻', '��' => '닿', '��' => '대', '��' => '댁', '��' => '댄', '��' => '댈', '��' => '댐', '��' => '댑', '��' => '댓', '��' => '댔', '��' => '댕', '��' => '댜', '��' => '더', '��' => '덕', '��' => '덖', '��' => '던', '��' => '덛', '��' => '덜', '��' => '덞', '��' => '덟', '��' => '덤', '��' => '덥', '�A' => '킕', '�B' => '킖', '�C' => '킗', '�D' => '킘', '�E' => '킙', '�F' => '킚', '�G' => '킛', '�H' => '킜', '�I' => '킝', '�J' => '킞', '�K' => '킟', '�L' => '킠', '�M' => '킡', '�N' => '킢', '�O' => '킣', '�P' => '킦', '�Q' => '킧', '�R' => '킩', '�S' => '킪', '�T' => '킫', '�U' => '킭', '�V' => '킮', '�W' => '킯', '�X' => '킰', '�Y' => '킱', '�Z' => '킲', '�a' => '킳', '�b' => '킶', '�c' => '킸', '�d' => '킺', '�e' => '킻', '�f' => '킼', '�g' => '킽', '�h' => '킾', '�i' => '킿', '�j' => '탂', '�k' => '탃', '�l' => '탅', '�m' => '탆', '�n' => '탇', '�o' => '탊', '�p' => '탋', '�q' => '탌', '�r' => '탍', '�s' => '탎', '�t' => '탏', '�u' => '탒', '�v' => '탖', '�w' => '탗', '�x' => '탘', '�y' => '탙', '�z' => '탚', '��' => '탛', '��' => '탞', '��' => '탟', '��' => '탡', '��' => '탢', '��' => '탣', '��' => '탥', '��' => '탦', '��' => '탧', '��' => '탨', '��' => '탩', '��' => '탪', '��' => '탫', '��' => '탮', '��' => '탲', '��' => '탳', '��' => '탴', '��' => '탵', '��' => '탶', '��' => '탷', '��' => '탹', '��' => '탺', '��' => '탻', '��' => '탼', '��' => '탽', '��' => '탾', '��' => '탿', '��' => '턀', '��' => '턁', '��' => '턂', '��' => '턃', '��' => '턄', '��' => '덧', '��' => '덩', '��' => '덫', '��' => '덮', '��' => '데', '��' => '덱', '��' => '덴', '��' => '델', '��' => '뎀', '��' => '뎁', '��' => '뎃', '��' => '뎄', '��' => '뎅', '��' => '뎌', '��' => '뎐', '��' => '뎔', '��' => '뎠', '��' => '뎡', '��' => '뎨', '��' => '뎬', '��' => '도', '��' => '독', '��' => '돈', '��' => '돋', '��' => '돌', '��' => '돎', '��' => '돐', '��' => '돔', '��' => '돕', '��' => '돗', '��' => '동', '��' => '돛', '��' => '돝', '��' => '돠', '��' => '돤', '��' => '돨', '��' => '돼', '��' => '됐', '��' => '되', '��' => '된', '��' => '될', '��' => '됨', '��' => '됩', '��' => '됫', '��' => '됴', '��' => '두', '��' => '둑', '��' => '둔', '��' => '둘', '��' => '둠', '��' => '둡', '��' => '둣', '��' => '둥', '��' => '둬', '��' => '뒀', '��' => '뒈', '��' => '뒝', '��' => '뒤', '��' => '뒨', '��' => '뒬', '��' => '뒵', '��' => '뒷', '��' => '뒹', '��' => '듀', '��' => '듄', '��' => '듈', '��' => '듐', '��' => '듕', '��' => '드', '��' => '득', '��' => '든', '��' => '듣', '��' => '들', '��' => '듦', '��' => '듬', '��' => '듭', '��' => '듯', '��' => '등', '��' => '듸', '��' => '디', '��' => '딕', '��' => '딘', '��' => '딛', '��' => '딜', '��' => '딤', '��' => '딥', '��' => '딧', '��' => '딨', '��' => '딩', '��' => '딪', '��' => '따', '��' => '딱', '��' => '딴', '��' => '딸', '�A' => '턅', '�B' => '턆', '�C' => '턇', '�D' => '턈', '�E' => '턉', '�F' => '턊', '�G' => '턋', '�H' => '턌', '�I' => '턎', '�J' => '턏', '�K' => '턐', '�L' => '턑', '�M' => '턒', '�N' => '턓', '�O' => '턔', '�P' => '턕', '�Q' => '턖', '�R' => '턗', '�S' => '턘', '�T' => '턙', '�U' => '턚', '�V' => '턛', '�W' => '턜', '�X' => '턝', '�Y' => '턞', '�Z' => '턟', '�a' => '턠', '�b' => '턡', '�c' => '턢', '�d' => '턣', '�e' => '턤', '�f' => '턥', '�g' => '턦', '�h' => '턧', '�i' => '턨', '�j' => '턩', '�k' => '턪', '�l' => '턫', '�m' => '턬', '�n' => '턭', '�o' => '턮', '�p' => '턯', '�q' => '턲', '�r' => '턳', '�s' => '턵', '�t' => '턶', '�u' => '턷', '�v' => '턹', '�w' => '턻', '�x' => '턼', '�y' => '턽', '�z' => '턾', '��' => '턿', '��' => '텂', '��' => '텆', '��' => '텇', '��' => '텈', '��' => '텉', '��' => '텊', '��' => '텋', '��' => '텎', '��' => '텏', '��' => '텑', '��' => '텒', '��' => '텓', '��' => '텕', '��' => '텖', '��' => '텗', '��' => '텘', '��' => '텙', '��' => '텚', '��' => '텛', '��' => '텞', '��' => '텠', '��' => '텢', '��' => '텣', '��' => '텤', '��' => '텥', '��' => '텦', '��' => '텧', '��' => '텩', '��' => '텪', '��' => '텫', '��' => '텭', '��' => '땀', '��' => '땁', '��' => '땃', '��' => '땄', '��' => '땅', '��' => '땋', '��' => '때', '��' => '땍', '��' => '땐', '��' => '땔', '��' => '땜', '��' => '땝', '��' => '땟', '��' => '땠', '��' => '땡', '��' => '떠', '��' => '떡', '��' => '떤', '��' => '떨', '��' => '떪', '��' => '떫', '��' => '떰', '��' => '떱', '��' => '떳', '��' => '떴', '��' => '떵', '��' => '떻', '��' => '떼', '��' => '떽', '��' => '뗀', '��' => '뗄', '��' => '뗌', '��' => '뗍', '��' => '뗏', '��' => '뗐', '��' => '뗑', '��' => '뗘', '��' => '뗬', '��' => '또', '��' => '똑', '��' => '똔', '��' => '똘', '��' => '똥', '��' => '똬', '��' => '똴', '��' => '뙈', '��' => '뙤', '��' => '뙨', '��' => '뚜', '��' => '뚝', '��' => '뚠', '��' => '뚤', '��' => '뚫', '��' => '뚬', '��' => '뚱', '��' => '뛔', '��' => '뛰', '��' => '뛴', '��' => '뛸', '��' => '뜀', '��' => '뜁', '��' => '뜅', '��' => '뜨', '��' => '뜩', '��' => '뜬', '��' => '뜯', '��' => '뜰', '��' => '뜸', '��' => '뜹', '��' => '뜻', '��' => '띄', '��' => '띈', '��' => '띌', '��' => '띔', '��' => '띕', '��' => '띠', '��' => '띤', '��' => '띨', '��' => '띰', '��' => '띱', '��' => '띳', '��' => '띵', '��' => '라', '��' => '락', '��' => '란', '��' => '랄', '��' => '람', '��' => '랍', '��' => '랏', '��' => '랐', '��' => '랑', '��' => '랒', '��' => '랖', '��' => '랗', '�A' => '텮', '�B' => '텯', '�C' => '텰', '�D' => '텱', '�E' => '텲', '�F' => '텳', '�G' => '텴', '�H' => '텵', '�I' => '텶', '�J' => '텷', '�K' => '텸', '�L' => '텹', '�M' => '텺', '�N' => '텻', '�O' => '텽', '�P' => '텾', '�Q' => '텿', '�R' => '톀', '�S' => '톁', '�T' => '톂', '�U' => '톃', '�V' => '톅', '�W' => '톆', '�X' => '톇', '�Y' => '톉', '�Z' => '톊', '�a' => '톋', '�b' => '톌', '�c' => '톍', '�d' => '톎', '�e' => '톏', '�f' => '톐', '�g' => '톑', '�h' => '톒', '�i' => '톓', '�j' => '톔', '�k' => '톕', '�l' => '톖', '�m' => '톗', '�n' => '톘', '�o' => '톙', '�p' => '톚', '�q' => '톛', '�r' => '톜', '�s' => '톝', '�t' => '톞', '�u' => '톟', '�v' => '톢', '�w' => '톣', '�x' => '톥', '�y' => '톦', '�z' => '톧', '��' => '톩', '��' => '톪', '��' => '톫', '��' => '톬', '��' => '톭', '��' => '톮', '��' => '톯', '��' => '톲', '��' => '톴', '��' => '톶', '��' => '톷', '��' => '톸', '��' => '톹', '��' => '톻', '��' => '톽', '��' => '톾', '��' => '톿', '��' => '퇁', '��' => '퇂', '��' => '퇃', '��' => '퇄', '��' => '퇅', '��' => '퇆', '��' => '퇇', '��' => '퇈', '��' => '퇉', '��' => '퇊', '��' => '퇋', '��' => '퇌', '��' => '퇍', '��' => '퇎', '��' => '퇏', '��' => '래', '��' => '랙', '��' => '랜', '��' => '랠', '��' => '램', '��' => '랩', '��' => '랫', '��' => '랬', '��' => '랭', '��' => '랴', '��' => '략', '��' => '랸', '��' => '럇', '��' => '량', '��' => '러', '��' => '럭', '��' => '런', '��' => '럴', '��' => '럼', '��' => '럽', '��' => '럿', '��' => '렀', '��' => '렁', '��' => '렇', '��' => '레', '��' => '렉', '��' => '렌', '��' => '렐', '��' => '렘', '��' => '렙', '��' => '렛', '��' => '렝', '��' => '려', '��' => '력', '��' => '련', '��' => '렬', '��' => '렴', '��' => '렵', '��' => '렷', '��' => '렸', '��' => '령', '��' => '례', '��' => '롄', '��' => '롑', '��' => '롓', '��' => '로', '��' => '록', '��' => '론', '��' => '롤', '��' => '롬', '��' => '롭', '��' => '롯', '��' => '롱', '��' => '롸', '��' => '롼', '��' => '뢍', '��' => '뢨', '��' => '뢰', '��' => '뢴', '��' => '뢸', '��' => '룀', '��' => '룁', '��' => '룃', '��' => '룅', '��' => '료', '��' => '룐', '��' => '룔', '��' => '룝', '��' => '룟', '��' => '룡', '��' => '루', '��' => '룩', '��' => '룬', '��' => '룰', '��' => '룸', '��' => '룹', '��' => '룻', '��' => '룽', '��' => '뤄', '��' => '뤘', '��' => '뤠', '��' => '뤼', '��' => '뤽', '��' => '륀', '��' => '륄', '��' => '륌', '��' => '륏', '��' => '륑', '��' => '류', '��' => '륙', '��' => '륜', '��' => '률', '��' => '륨', '��' => '륩', '�A' => '퇐', '�B' => '퇑', '�C' => '퇒', '�D' => '퇓', '�E' => '퇔', '�F' => '퇕', '�G' => '퇖', '�H' => '퇗', '�I' => '퇙', '�J' => '퇚', '�K' => '퇛', '�L' => '퇜', '�M' => '퇝', '�N' => '퇞', '�O' => '퇟', '�P' => '퇠', '�Q' => '퇡', '�R' => '퇢', '�S' => '퇣', '�T' => '퇤', '�U' => '퇥', '�V' => '퇦', '�W' => '퇧', '�X' => '퇨', '�Y' => '퇩', '�Z' => '퇪', '�a' => '퇫', '�b' => '퇬', '�c' => '퇭', '�d' => '퇮', '�e' => '퇯', '�f' => '퇰', '�g' => '퇱', '�h' => '퇲', '�i' => '퇳', '�j' => '퇵', '�k' => '퇶', '�l' => '퇷', '�m' => '퇹', '�n' => '퇺', '�o' => '퇻', '�p' => '퇼', '�q' => '퇽', '�r' => '퇾', '�s' => '퇿', '�t' => '툀', '�u' => '툁', '�v' => '툂', '�w' => '툃', '�x' => '툄', '�y' => '툅', '�z' => '툆', '��' => '툈', '��' => '툊', '��' => '툋', '��' => '툌', '��' => '툍', '��' => '툎', '��' => '툏', '��' => '툑', '��' => '툒', '��' => '툓', '��' => '툔', '��' => '툕', '��' => '툖', '��' => '툗', '��' => '툘', '��' => '툙', '��' => '툚', '��' => '툛', '��' => '툜', '��' => '툝', '��' => '툞', '��' => '툟', '��' => '툠', '��' => '툡', '��' => '툢', '��' => '툣', '��' => '툤', '��' => '툥', '��' => '툦', '��' => '툧', '��' => '툨', '��' => '툩', '��' => '륫', '��' => '륭', '��' => '르', '��' => '륵', '��' => '른', '��' => '를', '��' => '름', '��' => '릅', '��' => '릇', '��' => '릉', '��' => '릊', '��' => '릍', '��' => '릎', '��' => '리', '��' => '릭', '��' => '린', '��' => '릴', '��' => '림', '��' => '립', '��' => '릿', '��' => '링', '��' => '마', '��' => '막', '��' => '만', '��' => '많', '��' => '맏', '��' => '말', '��' => '맑', '��' => '맒', '��' => '맘', '��' => '맙', '��' => '맛', '��' => '망', '��' => '맞', '��' => '맡', '��' => '맣', '��' => '매', '��' => '맥', '��' => '맨', '��' => '맬', '��' => '맴', '��' => '맵', '��' => '맷', '��' => '맸', '��' => '맹', '��' => '맺', '��' => '먀', '��' => '먁', '��' => '먈', '��' => '먕', '��' => '머', '��' => '먹', '��' => '먼', '��' => '멀', '��' => '멂', '��' => '멈', '��' => '멉', '��' => '멋', '��' => '멍', '��' => '멎', '��' => '멓', '��' => '메', '��' => '멕', '��' => '멘', '��' => '멜', '��' => '멤', '��' => '멥', '��' => '멧', '��' => '멨', '��' => '멩', '��' => '며', '��' => '멱', '��' => '면', '��' => '멸', '��' => '몃', '��' => '몄', '��' => '명', '��' => '몇', '��' => '몌', '��' => '모', '��' => '목', '��' => '몫', '��' => '몬', '��' => '몰', '��' => '몲', '��' => '몸', '��' => '몹', '��' => '못', '��' => '몽', '��' => '뫄', '��' => '뫈', '��' => '뫘', '��' => '뫙', '��' => '뫼', '�A' => '툪', '�B' => '툫', '�C' => '툮', '�D' => '툯', '�E' => '툱', '�F' => '툲', '�G' => '툳', '�H' => '툵', '�I' => '툶', '�J' => '툷', '�K' => '툸', '�L' => '툹', '�M' => '툺', '�N' => '툻', '�O' => '툾', '�P' => '퉀', '�Q' => '퉂', '�R' => '퉃', '�S' => '퉄', '�T' => '퉅', '�U' => '퉆', '�V' => '퉇', '�W' => '퉉', '�X' => '퉊', '�Y' => '퉋', '�Z' => '퉌', '�a' => '퉍', '�b' => '퉎', '�c' => '퉏', '�d' => '퉐', '�e' => '퉑', '�f' => '퉒', '�g' => '퉓', '�h' => '퉔', '�i' => '퉕', '�j' => '퉖', '�k' => '퉗', '�l' => '퉘', '�m' => '퉙', '�n' => '퉚', '�o' => '퉛', '�p' => '퉝', '�q' => '퉞', '�r' => '퉟', '�s' => '퉠', '�t' => '퉡', '�u' => '퉢', '�v' => '퉣', '�w' => '퉥', '�x' => '퉦', '�y' => '퉧', '�z' => '퉨', '��' => '퉩', '��' => '퉪', '��' => '퉫', '��' => '퉬', '��' => '퉭', '��' => '퉮', '��' => '퉯', '��' => '퉰', '��' => '퉱', '��' => '퉲', '��' => '퉳', '��' => '퉴', '��' => '퉵', '��' => '퉶', '��' => '퉷', '��' => '퉸', '��' => '퉹', '��' => '퉺', '��' => '퉻', '��' => '퉼', '��' => '퉽', '��' => '퉾', '��' => '퉿', '��' => '튂', '��' => '튃', '��' => '튅', '��' => '튆', '��' => '튇', '��' => '튉', '��' => '튊', '��' => '튋', '��' => '튌', '��' => '묀', '��' => '묄', '��' => '묍', '��' => '묏', '��' => '묑', '��' => '묘', '��' => '묜', '��' => '묠', '��' => '묩', '��' => '묫', '��' => '무', '��' => '묵', '��' => '묶', '��' => '문', '��' => '묻', '��' => '물', '��' => '묽', '��' => '묾', '��' => '뭄', '��' => '뭅', '��' => '뭇', '��' => '뭉', '��' => '뭍', '��' => '뭏', '��' => '뭐', '��' => '뭔', '��' => '뭘', '��' => '뭡', '��' => '뭣', '��' => '뭬', '��' => '뮈', '��' => '뮌', '��' => '뮐', '��' => '뮤', '��' => '뮨', '��' => '뮬', '��' => '뮴', '��' => '뮷', '��' => '므', '��' => '믄', '��' => '믈', '��' => '믐', '��' => '믓', '��' => '미', '��' => '믹', '��' => '민', '��' => '믿', '��' => '밀', '��' => '밂', '��' => '밈', '��' => '밉', '��' => '밋', '��' => '밌', '��' => '밍', '��' => '및', '��' => '밑', '��' => '바', '��' => '박', '��' => '밖', '��' => '밗', '��' => '반', '��' => '받', '��' => '발', '��' => '밝', '��' => '밞', '��' => '밟', '��' => '밤', '��' => '밥', '��' => '밧', '��' => '방', '��' => '밭', '��' => '배', '��' => '백', '��' => '밴', '��' => '밸', '��' => '뱀', '��' => '뱁', '��' => '뱃', '��' => '뱄', '��' => '뱅', '��' => '뱉', '��' => '뱌', '��' => '뱍', '��' => '뱐', '��' => '뱝', '��' => '버', '��' => '벅', '��' => '번', '��' => '벋', '��' => '벌', '��' => '벎', '��' => '범', '��' => '법', '��' => '벗', '�A' => '튍', '�B' => '튎', '�C' => '튏', '�D' => '튒', '�E' => '튓', '�F' => '튔', '�G' => '튖', '�H' => '튗', '�I' => '튘', '�J' => '튙', '�K' => '튚', '�L' => '튛', '�M' => '튝', '�N' => '튞', '�O' => '튟', '�P' => '튡', '�Q' => '튢', '�R' => '튣', '�S' => '튥', '�T' => '튦', '�U' => '튧', '�V' => '튨', '�W' => '튩', '�X' => '튪', '�Y' => '튫', '�Z' => '튭', '�a' => '튮', '�b' => '튯', '�c' => '튰', '�d' => '튲', '�e' => '튳', '�f' => '튴', '�g' => '튵', '�h' => '튶', '�i' => '튷', '�j' => '튺', '�k' => '튻', '�l' => '튽', '�m' => '튾', '�n' => '틁', '�o' => '틃', '�p' => '틄', '�q' => '틅', '�r' => '틆', '�s' => '틇', '�t' => '틊', '�u' => '틌', '�v' => '틍', '�w' => '틎', '�x' => '틏', '�y' => '틐', '�z' => '틑', '��' => '틒', '��' => '틓', '��' => '틕', '��' => '틖', '��' => '틗', '��' => '틙', '��' => '틚', '��' => '틛', '��' => '틝', '��' => '틞', '��' => '틟', '��' => '틠', '��' => '틡', '��' => '틢', '��' => '틣', '��' => '틦', '��' => '틧', '��' => '틨', '��' => '틩', '��' => '틪', '��' => '틫', '��' => '틬', '��' => '틭', '��' => '틮', '��' => '틯', '��' => '틲', '��' => '틳', '��' => '틵', '��' => '틶', '��' => '틷', '��' => '틹', '��' => '틺', '��' => '벙', '��' => '벚', '��' => '베', '��' => '벡', '��' => '벤', '��' => '벧', '��' => '벨', '��' => '벰', '��' => '벱', '��' => '벳', '��' => '벴', '��' => '벵', '��' => '벼', '��' => '벽', '��' => '변', '��' => '별', '��' => '볍', '��' => '볏', '��' => '볐', '��' => '병', '��' => '볕', '��' => '볘', '��' => '볜', '��' => '보', '��' => '복', '��' => '볶', '��' => '본', '��' => '볼', '��' => '봄', '��' => '봅', '��' => '봇', '��' => '봉', '��' => '봐', '��' => '봔', '��' => '봤', '��' => '봬', '��' => '뵀', '��' => '뵈', '��' => '뵉', '��' => '뵌', '��' => '뵐', '��' => '뵘', '��' => '뵙', '��' => '뵤', '��' => '뵨', '��' => '부', '��' => '북', '��' => '분', '��' => '붇', '��' => '불', '��' => '붉', '��' => '붊', '��' => '붐', '��' => '붑', '��' => '붓', '��' => '붕', '��' => '붙', '��' => '붚', '��' => '붜', '��' => '붤', '��' => '붰', '��' => '붸', '��' => '뷔', '��' => '뷕', '��' => '뷘', '��' => '뷜', '��' => '뷩', '��' => '뷰', '��' => '뷴', '��' => '뷸', '��' => '븀', '��' => '븃', '��' => '븅', '��' => '브', '��' => '븍', '��' => '븐', '��' => '블', '��' => '븜', '��' => '븝', '��' => '븟', '��' => '비', '��' => '빅', '��' => '빈', '��' => '빌', '��' => '빎', '��' => '빔', '��' => '빕', '��' => '빗', '��' => '빙', '��' => '빚', '��' => '빛', '��' => '빠', '��' => '빡', '��' => '빤', '�A' => '틻', '�B' => '틼', '�C' => '틽', '�D' => '틾', '�E' => '틿', '�F' => '팂', '�G' => '팄', '�H' => '팆', '�I' => '팇', '�J' => '팈', '�K' => '팉', '�L' => '팊', '�M' => '팋', '�N' => '팏', '�O' => '팑', '�P' => '팒', '�Q' => '팓', '�R' => '팕', '�S' => '팗', '�T' => '팘', '�U' => '팙', '�V' => '팚', '�W' => '팛', '�X' => '팞', '�Y' => '팢', '�Z' => '팣', '�a' => '팤', '�b' => '팦', '�c' => '팧', '�d' => '팪', '�e' => '팫', '�f' => '팭', '�g' => '팮', '�h' => '팯', '�i' => '팱', '�j' => '팲', '�k' => '팳', '�l' => '팴', '�m' => '팵', '�n' => '팶', '�o' => '팷', '�p' => '팺', '�q' => '팾', '�r' => '팿', '�s' => '퍀', '�t' => '퍁', '�u' => '퍂', '�v' => '퍃', '�w' => '퍆', '�x' => '퍇', '�y' => '퍈', '�z' => '퍉', '��' => '퍊', '��' => '퍋', '��' => '퍌', '��' => '퍍', '��' => '퍎', '��' => '퍏', '��' => '퍐', '��' => '퍑', '��' => '퍒', '��' => '퍓', '��' => '퍔', '��' => '퍕', '��' => '퍖', '��' => '퍗', '��' => '퍘', '��' => '퍙', '��' => '퍚', '��' => '퍛', '��' => '퍜', '��' => '퍝', '��' => '퍞', '��' => '퍟', '��' => '퍠', '��' => '퍡', '��' => '퍢', '��' => '퍣', '��' => '퍤', '��' => '퍥', '��' => '퍦', '��' => '퍧', '��' => '퍨', '��' => '퍩', '��' => '빨', '��' => '빪', '��' => '빰', '��' => '빱', '��' => '빳', '��' => '빴', '��' => '빵', '��' => '빻', '��' => '빼', '��' => '빽', '��' => '뺀', '��' => '뺄', '��' => '뺌', '��' => '뺍', '��' => '뺏', '��' => '뺐', '��' => '뺑', '��' => '뺘', '��' => '뺙', '��' => '뺨', '��' => '뻐', '��' => '뻑', '��' => '뻔', '��' => '뻗', '��' => '뻘', '��' => '뻠', '��' => '뻣', '��' => '뻤', '��' => '뻥', '��' => '뻬', '��' => '뼁', '��' => '뼈', '��' => '뼉', '��' => '뼘', '��' => '뼙', '��' => '뼛', '��' => '뼜', '��' => '뼝', '��' => '뽀', '��' => '뽁', '��' => '뽄', '��' => '뽈', '��' => '뽐', '��' => '뽑', '��' => '뽕', '��' => '뾔', '��' => '뾰', '��' => '뿅', '��' => '뿌', '��' => '뿍', '��' => '뿐', '��' => '뿔', '��' => '뿜', '��' => '뿟', '��' => '뿡', '��' => '쀼', '��' => '쁑', '��' => '쁘', '��' => '쁜', '��' => '쁠', '��' => '쁨', '��' => '쁩', '��' => '삐', '��' => '삑', '��' => '삔', '��' => '삘', '��' => '삠', '��' => '삡', '��' => '삣', '��' => '삥', '��' => '사', '��' => '삭', '��' => '삯', '��' => '산', '��' => '삳', '��' => '살', '��' => '삵', '��' => '삶', '��' => '삼', '��' => '삽', '��' => '삿', '��' => '샀', '��' => '상', '��' => '샅', '��' => '새', '��' => '색', '��' => '샌', '��' => '샐', '��' => '샘', '��' => '샙', '��' => '샛', '��' => '샜', '��' => '생', '��' => '샤', '�A' => '퍪', '�B' => '퍫', '�C' => '퍬', '�D' => '퍭', '�E' => '퍮', '�F' => '퍯', '�G' => '퍰', '�H' => '퍱', '�I' => '퍲', '�J' => '퍳', '�K' => '퍴', '�L' => '퍵', '�M' => '퍶', '�N' => '퍷', '�O' => '퍸', '�P' => '퍹', '�Q' => '퍺', '�R' => '퍻', '�S' => '퍾', '�T' => '퍿', '�U' => '펁', '�V' => '펂', '�W' => '펃', '�X' => '펅', '�Y' => '펆', '�Z' => '펇', '�a' => '펈', '�b' => '펉', '�c' => '펊', '�d' => '펋', '�e' => '펎', '�f' => '펒', '�g' => '펓', '�h' => '펔', '�i' => '펕', '�j' => '펖', '�k' => '펗', '�l' => '펚', '�m' => '펛', '�n' => '펝', '�o' => '펞', '�p' => '펟', '�q' => '펡', '�r' => '펢', '�s' => '펣', '�t' => '펤', '�u' => '펥', '�v' => '펦', '�w' => '펧', '�x' => '펪', '�y' => '펬', '�z' => '펮', '��' => '펯', '��' => '펰', '��' => '펱', '��' => '펲', '��' => '펳', '��' => '펵', '��' => '펶', '��' => '펷', '��' => '펹', '��' => '펺', '��' => '펻', '��' => '펽', '��' => '펾', '��' => '펿', '��' => '폀', '��' => '폁', '��' => '폂', '��' => '폃', '��' => '폆', '��' => '폇', '��' => '폊', '��' => '폋', '��' => '폌', '��' => '폍', '��' => '폎', '��' => '폏', '��' => '폑', '��' => '폒', '��' => '폓', '��' => '폔', '��' => '폕', '��' => '폖', '��' => '샥', '��' => '샨', '��' => '샬', '��' => '샴', '��' => '샵', '��' => '샷', '��' => '샹', '��' => '섀', '��' => '섄', '��' => '섈', '��' => '섐', '��' => '섕', '��' => '서', '��' => '석', '��' => '섞', '��' => '섟', '��' => '선', '��' => '섣', '��' => '설', '��' => '섦', '��' => '섧', '��' => '섬', '��' => '섭', '��' => '섯', '��' => '섰', '��' => '성', '��' => '섶', '��' => '세', '��' => '섹', '��' => '센', '��' => '셀', '��' => '셈', '��' => '셉', '��' => '셋', '��' => '셌', '��' => '셍', '��' => '셔', '��' => '셕', '��' => '션', '��' => '셜', '��' => '셤', '��' => '셥', '��' => '셧', '��' => '셨', '��' => '셩', '��' => '셰', '��' => '셴', '��' => '셸', '��' => '솅', '��' => '소', '��' => '속', '��' => '솎', '��' => '손', '��' => '솔', '��' => '솖', '��' => '솜', '��' => '솝', '��' => '솟', '��' => '송', '��' => '솥', '��' => '솨', '��' => '솩', '��' => '솬', '��' => '솰', '��' => '솽', '��' => '쇄', '��' => '쇈', '��' => '쇌', '��' => '쇔', '��' => '쇗', '��' => '쇘', '��' => '쇠', '��' => '쇤', '��' => '쇨', '��' => '쇰', '��' => '쇱', '��' => '쇳', '��' => '쇼', '��' => '쇽', '��' => '숀', '��' => '숄', '��' => '숌', '��' => '숍', '��' => '숏', '��' => '숑', '��' => '수', '��' => '숙', '��' => '순', '��' => '숟', '��' => '술', '��' => '숨', '��' => '숩', '��' => '숫', '��' => '숭', '�A' => '폗', '�B' => '폙', '�C' => '폚', '�D' => '폛', '�E' => '폜', '�F' => '폝', '�G' => '폞', '�H' => '폟', '�I' => '폠', '�J' => '폢', '�K' => '폤', '�L' => '폥', '�M' => '폦', '�N' => '폧', '�O' => '폨', '�P' => '폩', '�Q' => '폪', '�R' => '폫', '�S' => '폮', '�T' => '폯', '�U' => '폱', '�V' => '폲', '�W' => '폳', '�X' => '폵', '�Y' => '폶', '�Z' => '폷', '�a' => '폸', '�b' => '폹', '�c' => '폺', '�d' => '폻', '�e' => '폾', '�f' => '퐀', '�g' => '퐂', '�h' => '퐃', '�i' => '퐄', '�j' => '퐅', '�k' => '퐆', '�l' => '퐇', '�m' => '퐉', '�n' => '퐊', '�o' => '퐋', '�p' => '퐌', '�q' => '퐍', '�r' => '퐎', '�s' => '퐏', '�t' => '퐐', '�u' => '퐑', '�v' => '퐒', '�w' => '퐓', '�x' => '퐔', '�y' => '퐕', '�z' => '퐖', '��' => '퐗', '��' => '퐘', '��' => '퐙', '��' => '퐚', '��' => '퐛', '��' => '퐜', '��' => '퐞', '��' => '퐟', '��' => '퐠', '��' => '퐡', '��' => '퐢', '��' => '퐣', '��' => '퐤', '��' => '퐥', '��' => '퐦', '��' => '퐧', '��' => '퐨', '��' => '퐩', '��' => '퐪', '��' => '퐫', '��' => '퐬', '��' => '퐭', '��' => '퐮', '��' => '퐯', '��' => '퐰', '��' => '퐱', '��' => '퐲', '��' => '퐳', '��' => '퐴', '��' => '퐵', '��' => '퐶', '��' => '퐷', '��' => '숯', '��' => '숱', '��' => '숲', '��' => '숴', '��' => '쉈', '��' => '쉐', '��' => '쉑', '��' => '쉔', '��' => '쉘', '��' => '쉠', '��' => '쉥', '��' => '쉬', '��' => '쉭', '��' => '쉰', '��' => '쉴', '��' => '쉼', '��' => '쉽', '��' => '쉿', '��' => '슁', '��' => '슈', '��' => '슉', '��' => '슐', '��' => '슘', '��' => '슛', '��' => '슝', '��' => '스', '��' => '슥', '��' => '슨', '��' => '슬', '��' => '슭', '��' => '슴', '��' => '습', '��' => '슷', '��' => '승', '��' => '시', '��' => '식', '��' => '신', '��' => '싣', '��' => '실', '��' => '싫', '��' => '심', '��' => '십', '��' => '싯', '��' => '싱', '��' => '싶', '��' => '싸', '��' => '싹', '��' => '싻', '��' => '싼', '��' => '쌀', '��' => '쌈', '��' => '쌉', '��' => '쌌', '��' => '쌍', '��' => '쌓', '��' => '쌔', '��' => '쌕', '��' => '쌘', '��' => '쌜', '��' => '쌤', '��' => '쌥', '��' => '쌨', '��' => '쌩', '��' => '썅', '��' => '써', '��' => '썩', '��' => '썬', '��' => '썰', '��' => '썲', '��' => '썸', '��' => '썹', '��' => '썼', '��' => '썽', '��' => '쎄', '��' => '쎈', '��' => '쎌', '��' => '쏀', '��' => '쏘', '��' => '쏙', '��' => '쏜', '��' => '쏟', '��' => '쏠', '��' => '쏢', '��' => '쏨', '��' => '쏩', '��' => '쏭', '��' => '쏴', '��' => '쏵', '��' => '쏸', '��' => '쐈', '��' => '쐐', '��' => '쐤', '��' => '쐬', '��' => '쐰', '�A' => '퐸', '�B' => '퐹', '�C' => '퐺', '�D' => '퐻', '�E' => '퐼', '�F' => '퐽', '�G' => '퐾', '�H' => '퐿', '�I' => '푁', '�J' => '푂', '�K' => '푃', '�L' => '푅', '�M' => '푆', '�N' => '푇', '�O' => '푈', '�P' => '푉', '�Q' => '푊', '�R' => '푋', '�S' => '푌', '�T' => '푍', '�U' => '푎', '�V' => '푏', '�W' => '푐', '�X' => '푑', '�Y' => '푒', '�Z' => '푓', '�a' => '푔', '�b' => '푕', '�c' => '푖', '�d' => '푗', '�e' => '푘', '�f' => '푙', '�g' => '푚', '�h' => '푛', '�i' => '푝', '�j' => '푞', '�k' => '푟', '�l' => '푡', '�m' => '푢', '�n' => '푣', '�o' => '푥', '�p' => '푦', '�q' => '푧', '�r' => '푨', '�s' => '푩', '�t' => '푪', '�u' => '푫', '�v' => '푬', '�w' => '푮', '�x' => '푰', '�y' => '푱', '�z' => '푲', '��' => '푳', '��' => '푴', '��' => '푵', '��' => '푶', '��' => '푷', '��' => '푺', '��' => '푻', '��' => '푽', '��' => '푾', '��' => '풁', '��' => '풃', '��' => '풄', '��' => '풅', '��' => '풆', '��' => '풇', '��' => '풊', '��' => '풌', '��' => '풎', '��' => '풏', '��' => '풐', '��' => '풑', '��' => '풒', '��' => '풓', '��' => '풕', '��' => '풖', '��' => '풗', '��' => '풘', '��' => '풙', '��' => '풚', '��' => '풛', '��' => '풜', '��' => '풝', '��' => '쐴', '��' => '쐼', '��' => '쐽', '��' => '쑈', '��' => '쑤', '��' => '쑥', '��' => '쑨', '��' => '쑬', '��' => '쑴', '��' => '쑵', '��' => '쑹', '��' => '쒀', '��' => '쒔', '��' => '쒜', '��' => '쒸', '��' => '쒼', '��' => '쓩', '��' => '쓰', '��' => '쓱', '��' => '쓴', '��' => '쓸', '��' => '쓺', '��' => '쓿', '��' => '씀', '��' => '씁', '��' => '씌', '��' => '씐', '��' => '씔', '��' => '씜', '��' => '씨', '��' => '씩', '��' => '씬', '��' => '씰', '��' => '씸', '��' => '씹', '��' => '씻', '��' => '씽', '��' => '아', '��' => '악', '��' => '안', '��' => '앉', '��' => '않', '��' => '알', '��' => '앍', '��' => '앎', '��' => '앓', '��' => '암', '��' => '압', '��' => '앗', '��' => '았', '��' => '앙', '��' => '앝', '��' => '앞', '��' => '애', '��' => '액', '��' => '앤', '��' => '앨', '��' => '앰', '��' => '앱', '��' => '앳', '��' => '앴', '��' => '앵', '��' => '야', '��' => '약', '��' => '얀', '��' => '얄', '��' => '얇', '��' => '얌', '��' => '얍', '��' => '얏', '��' => '양', '��' => '얕', '��' => '얗', '��' => '얘', '��' => '얜', '��' => '얠', '��' => '얩', '��' => '어', '��' => '억', '��' => '언', '��' => '얹', '��' => '얻', '��' => '얼', '��' => '얽', '��' => '얾', '��' => '엄', '��' => '업', '��' => '없', '��' => '엇', '��' => '었', '��' => '엉', '��' => '엊', '��' => '엌', '��' => '엎', '�A' => '풞', '�B' => '풟', '�C' => '풠', '�D' => '풡', '�E' => '풢', '�F' => '풣', '�G' => '풤', '�H' => '풥', '�I' => '풦', '�J' => '풧', '�K' => '풨', '�L' => '풪', '�M' => '풫', '�N' => '풬', '�O' => '풭', '�P' => '풮', '�Q' => '풯', '�R' => '풰', '�S' => '풱', '�T' => '풲', '�U' => '풳', '�V' => '풴', '�W' => '풵', '�X' => '풶', '�Y' => '풷', '�Z' => '풸', '�a' => '풹', '�b' => '풺', '�c' => '풻', '�d' => '풼', '�e' => '풽', '�f' => '풾', '�g' => '풿', '�h' => '퓀', '�i' => '퓁', '�j' => '퓂', '�k' => '퓃', '�l' => '퓄', '�m' => '퓅', '�n' => '퓆', '�o' => '퓇', '�p' => '퓈', '�q' => '퓉', '�r' => '퓊', '�s' => '퓋', '�t' => '퓍', '�u' => '퓎', '�v' => '퓏', '�w' => '퓑', '�x' => '퓒', '�y' => '퓓', '�z' => '퓕', '��' => '퓖', '��' => '퓗', '��' => '퓘', '��' => '퓙', '��' => '퓚', '��' => '퓛', '��' => '퓝', '��' => '퓞', '��' => '퓠', '��' => '퓡', '��' => '퓢', '��' => '퓣', '��' => '퓤', '��' => '퓥', '��' => '퓦', '��' => '퓧', '��' => '퓩', '��' => '퓪', '��' => '퓫', '��' => '퓭', '��' => '퓮', '��' => '퓯', '��' => '퓱', '��' => '퓲', '��' => '퓳', '��' => '퓴', '��' => '퓵', '��' => '퓶', '��' => '퓷', '��' => '퓹', '��' => '퓺', '��' => '퓼', '��' => '에', '��' => '엑', '��' => '엔', '��' => '엘', '��' => '엠', '��' => '엡', '��' => '엣', '��' => '엥', '��' => '여', '��' => '역', '��' => '엮', '��' => '연', '��' => '열', '��' => '엶', '��' => '엷', '��' => '염', '��' => '엽', '��' => '엾', '��' => '엿', '��' => '였', '��' => '영', '��' => '옅', '��' => '옆', '��' => '옇', '��' => '예', '��' => '옌', '��' => '옐', '��' => '옘', '��' => '옙', '��' => '옛', '��' => '옜', '��' => '오', '��' => '옥', '��' => '온', '��' => '올', '��' => '옭', '��' => '옮', '��' => '옰', '��' => '옳', '��' => '옴', '��' => '옵', '��' => '옷', '��' => '옹', '��' => '옻', '��' => '와', '��' => '왁', '��' => '완', '��' => '왈', '��' => '왐', '��' => '왑', '��' => '왓', '��' => '왔', '��' => '왕', '��' => '왜', '��' => '왝', '��' => '왠', '��' => '왬', '��' => '왯', '��' => '왱', '��' => '외', '��' => '왹', '��' => '왼', '��' => '욀', '��' => '욈', '��' => '욉', '��' => '욋', '��' => '욍', '��' => '요', '��' => '욕', '��' => '욘', '��' => '욜', '��' => '욤', '��' => '욥', '��' => '욧', '��' => '용', '��' => '우', '��' => '욱', '��' => '운', '��' => '울', '��' => '욹', '��' => '욺', '��' => '움', '��' => '웁', '��' => '웃', '��' => '웅', '��' => '워', '��' => '웍', '��' => '원', '��' => '월', '��' => '웜', '��' => '웝', '��' => '웠', '��' => '웡', '��' => '웨', '�A' => '퓾', '�B' => '퓿', '�C' => '픀', '�D' => '픁', '�E' => '픂', '�F' => '픃', '�G' => '픅', '�H' => '픆', '�I' => '픇', '�J' => '픉', '�K' => '픊', '�L' => '픋', '�M' => '픍', '�N' => '픎', '�O' => '픏', '�P' => '픐', '�Q' => '픑', '�R' => '픒', '�S' => '픓', '�T' => '픖', '�U' => '픘', '�V' => '픙', '�W' => '픚', '�X' => '픛', '�Y' => '픜', '�Z' => '픝', '�a' => '픞', '�b' => '픟', '�c' => '픠', '�d' => '픡', '�e' => '픢', '�f' => '픣', '�g' => '픤', '�h' => '픥', '�i' => '픦', '�j' => '픧', '�k' => '픨', '�l' => '픩', '�m' => '픪', '�n' => '픫', '�o' => '픬', '�p' => '픭', '�q' => '픮', '�r' => '픯', '�s' => '픰', '�t' => '픱', '�u' => '픲', '�v' => '픳', '�w' => '픴', '�x' => '픵', '�y' => '픶', '�z' => '픷', '��' => '픸', '��' => '픹', '��' => '픺', '��' => '픻', '��' => '픾', '��' => '픿', '��' => '핁', '��' => '핂', '��' => '핃', '��' => '핅', '��' => '핆', '��' => '핇', '��' => '핈', '��' => '핉', '��' => '핊', '��' => '핋', '��' => '핎', '��' => '핐', '��' => '핒', '��' => '핓', '��' => '핔', '��' => '핕', '��' => '핖', '��' => '핗', '��' => '핚', '��' => '핛', '��' => '핝', '��' => '핞', '��' => '핟', '��' => '핡', '��' => '핢', '��' => '핣', '��' => '웩', '��' => '웬', '��' => '웰', '��' => '웸', '��' => '웹', '��' => '웽', '��' => '위', '��' => '윅', '��' => '윈', '��' => '윌', '��' => '윔', '��' => '윕', '��' => '윗', '��' => '윙', '��' => '유', '��' => '육', '��' => '윤', '��' => '율', '��' => '윰', '��' => '윱', '��' => '윳', '��' => '융', '��' => '윷', '��' => '으', '��' => '윽', '��' => '은', '��' => '을', '��' => '읊', '��' => '음', '��' => '읍', '��' => '읏', '��' => '응', '��' => '읒', '��' => '읓', '��' => '읔', '��' => '읕', '��' => '읖', '��' => '읗', '��' => '의', '��' => '읜', '��' => '읠', '��' => '읨', '��' => '읫', '��' => '이', '��' => '익', '��' => '인', '��' => '일', '��' => '읽', '��' => '읾', '��' => '잃', '��' => '임', '��' => '입', '��' => '잇', '��' => '있', '��' => '잉', '��' => '잊', '��' => '잎', '��' => '자', '��' => '작', '��' => '잔', '��' => '잖', '��' => '잗', '��' => '잘', '��' => '잚', '��' => '잠', '��' => '잡', '��' => '잣', '��' => '잤', '��' => '장', '��' => '잦', '��' => '재', '��' => '잭', '��' => '잰', '��' => '잴', '��' => '잼', '��' => '잽', '��' => '잿', '��' => '쟀', '��' => '쟁', '��' => '쟈', '��' => '쟉', '��' => '쟌', '��' => '쟎', '��' => '쟐', '��' => '쟘', '��' => '쟝', '��' => '쟤', '��' => '쟨', '��' => '쟬', '��' => '저', '��' => '적', '��' => '전', '��' => '절', '��' => '젊', '�A' => '핤', '�B' => '핦', '�C' => '핧', '�D' => '핪', '�E' => '핬', '�F' => '핮', '�G' => '핯', '�H' => '핰', '�I' => '핱', '�J' => '핲', '�K' => '핳', '�L' => '핶', '�M' => '핷', '�N' => '핹', '�O' => '핺', '�P' => '핻', '�Q' => '핽', '�R' => '핾', '�S' => '핿', '�T' => '햀', '�U' => '햁', '�V' => '햂', '�W' => '햃', '�X' => '햆', '�Y' => '햊', '�Z' => '햋', '�a' => '햌', '�b' => '햍', '�c' => '햎', '�d' => '햏', '�e' => '햑', '�f' => '햒', '�g' => '햓', '�h' => '햔', '�i' => '햕', '�j' => '햖', '�k' => '햗', '�l' => '햘', '�m' => '햙', '�n' => '햚', '�o' => '햛', '�p' => '햜', '�q' => '햝', '�r' => '햞', '�s' => '햟', '�t' => '햠', '�u' => '햡', '�v' => '햢', '�w' => '햣', '�x' => '햤', '�y' => '햦', '�z' => '햧', '��' => '햨', '��' => '햩', '��' => '햪', '��' => '햫', '��' => '햬', '��' => '햭', '��' => '햮', '��' => '햯', '��' => '햰', '��' => '햱', '��' => '햲', '��' => '햳', '��' => '햴', '��' => '햵', '��' => '햶', '��' => '햷', '��' => '햸', '��' => '햹', '��' => '햺', '��' => '햻', '��' => '햼', '��' => '햽', '��' => '햾', '��' => '햿', '��' => '헀', '��' => '헁', '��' => '헂', '��' => '헃', '��' => '헄', '��' => '헅', '��' => '헆', '��' => '헇', '��' => '점', '��' => '접', '��' => '젓', '��' => '정', '��' => '젖', '��' => '제', '��' => '젝', '��' => '젠', '��' => '젤', '��' => '젬', '��' => '젭', '��' => '젯', '��' => '젱', '��' => '져', '��' => '젼', '��' => '졀', '��' => '졈', '��' => '졉', '��' => '졌', '��' => '졍', '��' => '졔', '��' => '조', '��' => '족', '��' => '존', '��' => '졸', '��' => '졺', '��' => '좀', '��' => '좁', '��' => '좃', '��' => '종', '��' => '좆', '��' => '좇', '��' => '좋', '��' => '좌', '��' => '좍', '��' => '좔', '��' => '좝', '��' => '좟', '��' => '좡', '��' => '좨', '��' => '좼', '��' => '좽', '��' => '죄', '��' => '죈', '��' => '죌', '��' => '죔', '��' => '죕', '��' => '죗', '��' => '죙', '��' => '죠', '��' => '죡', '��' => '죤', '��' => '죵', '��' => '주', '��' => '죽', '��' => '준', '��' => '줄', '��' => '줅', '��' => '줆', '��' => '줌', '��' => '줍', '��' => '줏', '��' => '중', '��' => '줘', '��' => '줬', '��' => '줴', '��' => '쥐', '��' => '쥑', '��' => '쥔', '��' => '쥘', '��' => '쥠', '��' => '쥡', '��' => '쥣', '��' => '쥬', '��' => '쥰', '��' => '쥴', '��' => '쥼', '��' => '즈', '��' => '즉', '��' => '즌', '��' => '즐', '��' => '즘', '��' => '즙', '��' => '즛', '��' => '증', '��' => '지', '��' => '직', '��' => '진', '��' => '짇', '��' => '질', '��' => '짊', '��' => '짐', '��' => '집', '��' => '짓', '�A' => '헊', '�B' => '헋', '�C' => '헍', '�D' => '헎', '�E' => '헏', '�F' => '헑', '�G' => '헓', '�H' => '헔', '�I' => '헕', '�J' => '헖', '�K' => '헗', '�L' => '헚', '�M' => '헜', '�N' => '헞', '�O' => '헟', '�P' => '헠', '�Q' => '헡', '�R' => '헢', '�S' => '헣', '�T' => '헦', '�U' => '헧', '�V' => '헩', '�W' => '헪', '�X' => '헫', '�Y' => '헭', '�Z' => '헮', '�a' => '헯', '�b' => '헰', '�c' => '헱', '�d' => '헲', '�e' => '헳', '�f' => '헶', '�g' => '헸', '�h' => '헺', '�i' => '헻', '�j' => '헼', '�k' => '헽', '�l' => '헾', '�m' => '헿', '�n' => '혂', '�o' => '혃', '�p' => '혅', '�q' => '혆', '�r' => '혇', '�s' => '혉', '�t' => '혊', '�u' => '혋', '�v' => '혌', '�w' => '혍', '�x' => '혎', '�y' => '혏', '�z' => '혒', '' => '혖', '' => '혗', '' => '혘', '' => '혙', ' ' => '혚', '' => '혛', '' => '혝', '' => '혞', '' => '혟', '' => '혡', '' => '혢', '' => '혣', '' => '혥', '' => '혦', '' => '혧', '' => '혨', '' => '혩', '' => '혪', '' => '혫', '' => '혬', '' => '혮', '' => '혯', '' => '혰', '' => '혱', '' => '혲', '' => '혳', '' => '혴', '' => '혵', '' => '혶', '' => '혷', '' => '혺', ' ' => '혻', '¡' => '징', '¢' => '짖', '£' => '짙', '¤' => '짚', '¥' => '짜', '¦' => '짝', '§' => '짠', '¨' => '짢', '©' => '짤', 'ª' => '짧', '«' => '짬', '¬' => '짭', '' => '짯', '®' => '짰', '¯' => '짱', '°' => '째', '±' => '짹', '²' => '짼', '³' => '쨀', '´' => '쨈', 'µ' => '쨉', '¶' => '쨋', '·' => '쨌', '¸' => '쨍', '¹' => '쨔', 'º' => '쨘', '»' => '쨩', '¼' => '쩌', '½' => '쩍', '¾' => '쩐', '¿' => '쩔', '�' => '쩜', '�' => '쩝', '��' => '쩟', '��' => '쩠', '��' => '쩡', '��' => '쩨', '��' => '쩽', '��' => '쪄', '��' => '쪘', '��' => '쪼', '��' => '쪽', '��' => '쫀', '��' => '쫄', '��' => '쫌', '��' => '쫍', '��' => '쫏', '��' => '쫑', '��' => '쫓', '��' => '쫘', '��' => '쫙', '��' => '쫠', '��' => '쫬', '��' => '쫴', '��' => '쬈', '��' => '쬐', '��' => '쬔', '��' => '쬘', '��' => '쬠', '��' => '쬡', '��' => '쭁', '��' => '쭈', '��' => '쭉', '��' => '쭌', '��' => '쭐', '��' => '쭘', '��' => '쭙', '��' => '쭝', '��' => '쭤', '��' => '쭸', '��' => '쭹', '��' => '쮜', '��' => '쮸', '��' => '쯔', '��' => '쯤', '��' => '쯧', '��' => '쯩', '��' => '찌', '��' => '찍', '��' => '찐', '��' => '찔', '��' => '찜', '��' => '찝', '��' => '찡', '�' => '찢', '�' => '찧', '�' => '차', '�' => '착', '�' => '찬', '�' => '찮', '�' => '찰', '�' => '참', '�' => '찹', '�' => '찻', '�A' => '혽', '�B' => '혾', '�C' => '혿', '�D' => '홁', '�E' => '홂', '�F' => '홃', '�G' => '홄', '�H' => '홆', '�I' => '홇', '�J' => '홊', '�K' => '홌', '�L' => '홎', '�M' => '홏', '�N' => '홐', '�O' => '홒', '�P' => '홓', '�Q' => '홖', '�R' => '홗', '�S' => '홙', '�T' => '홚', '�U' => '홛', '�V' => '홝', '�W' => '홞', '�X' => '홟', '�Y' => '홠', '�Z' => '홡', '�a' => '홢', '�b' => '홣', '�c' => '홤', '�d' => '홥', '�e' => '홦', '�f' => '홨', '�g' => '홪', '�h' => '홫', '�i' => '홬', '�j' => '홭', '�k' => '홮', '�l' => '홯', '�m' => '홲', '�n' => '홳', '�o' => '홵', '�p' => '홶', '�q' => '홷', '�r' => '홸', '�s' => '홹', '�t' => '홺', '�u' => '홻', '�v' => '홼', '�w' => '홽', '�x' => '홾', '�y' => '홿', '�z' => '횀', 'Á' => '횁', 'Â' => '횂', 'Ã' => '횄', 'Ä' => '횆', 'Å' => '횇', 'Æ' => '횈', 'Ç' => '횉', 'È' => '횊', 'É' => '횋', 'Ê' => '횎', 'Ë' => '횏', 'Ì' => '횑', 'Í' => '횒', 'Î' => '횓', 'Ï' => '횕', 'Ð' => '횖', 'Ñ' => '횗', 'Ò' => '횘', 'Ó' => '횙', 'Ô' => '횚', 'Õ' => '횛', 'Ö' => '횜', '×' => '횞', 'Ø' => '횠', 'Ù' => '횢', 'Ú' => '횣', 'Û' => '횤', 'Ü' => '횥', 'Ý' => '횦', 'Þ' => '횧', 'ß' => '횩', 'à' => '횪', 'á' => '찼', 'â' => '창', 'ã' => '찾', 'ä' => '채', 'å' => '책', 'æ' => '챈', 'ç' => '챌', 'è' => '챔', 'é' => '챕', 'ê' => '챗', 'ë' => '챘', 'ì' => '챙', 'í' => '챠', 'î' => '챤', 'ï' => '챦', 'ð' => '챨', 'ñ' => '챰', 'ò' => '챵', 'ó' => '처', 'ô' => '척', 'õ' => '천', 'ö' => '철', '÷' => '첨', 'ø' => '첩', 'ù' => '첫', 'ú' => '첬', 'û' => '청', 'ü' => '체', 'ý' => '첵', 'þ' => '첸', 'ÿ' => '첼', '�' => '쳄', '�' => '쳅', '��' => '쳇', '��' => '쳉', '��' => '쳐', '��' => '쳔', '��' => '쳤', '��' => '쳬', '��' => '쳰', '��' => '촁', '��' => '초', '��' => '촉', '��' => '촌', '��' => '촐', '��' => '촘', '��' => '촙', '��' => '촛', '��' => '총', '��' => '촤', '��' => '촨', '��' => '촬', '��' => '촹', '��' => '최', '��' => '쵠', '��' => '쵤', '��' => '쵬', '��' => '쵭', '��' => '쵯', '��' => '쵱', '��' => '쵸', '��' => '춈', '��' => '추', '��' => '축', '��' => '춘', '��' => '출', '��' => '춤', '��' => '춥', '��' => '춧', '��' => '충', '��' => '춰', '��' => '췄', '��' => '췌', '��' => '췐', '��' => '취', '��' => '췬', '��' => '췰', '��' => '췸', '��' => '췹', '��' => '췻', '��' => '췽', '��' => '츄', '��' => '츈', '��' => '츌', '�' => '츔', '�' => '츙', '�' => '츠', '�' => '측', '�' => '츤', '�' => '츨', '�' => '츰', '�' => '츱', '�' => '츳', '�' => '층', '�A' => '횫', '�B' => '횭', '�C' => '횮', '�D' => '횯', '�E' => '횱', '�F' => '횲', '�G' => '횳', '�H' => '횴', '�I' => '횵', '�J' => '횶', '�K' => '횷', '�L' => '횸', '�M' => '횺', '�N' => '횼', '�O' => '횽', '�P' => '횾', '�Q' => '횿', '�R' => '훀', '�S' => '훁', '�T' => '훂', '�U' => '훃', '�V' => '훆', '�W' => '훇', '�X' => '훉', '�Y' => '훊', '�Z' => '훋', '�a' => '훍', '�b' => '훎', '�c' => '훏', '�d' => '훐', '�e' => '훒', '�f' => '훓', '�g' => '훕', '�h' => '훖', '�i' => '훘', '�j' => '훚', '�k' => '훛', '�l' => '훜', '�m' => '훝', '�n' => '훞', '�o' => '훟', '�p' => '훡', '�q' => '훢', '�r' => '훣', '�s' => '훥', '�t' => '훦', '�u' => '훧', '�v' => '훩', '�w' => '훪', '�x' => '훫', '�y' => '훬', '�z' => '훭', 'ā' => '훮', 'Ă' => '훯', 'ă' => '훱', 'Ą' => '훲', 'ą' => '훳', 'Ć' => '훴', 'ć' => '훶', 'Ĉ' => '훷', 'ĉ' => '훸', 'Ċ' => '훹', 'ċ' => '훺', 'Č' => '훻', 'č' => '훾', 'Ď' => '훿', 'ď' => '휁', 'Đ' => '휂', 'đ' => '휃', 'Ē' => '휅', 'ē' => '휆', 'Ĕ' => '휇', 'ĕ' => '휈', 'Ė' => '휉', 'ė' => '휊', 'Ę' => '휋', 'ę' => '휌', 'Ě' => '휍', 'ě' => '휎', 'Ĝ' => '휏', 'ĝ' => '휐', 'Ğ' => '휒', 'ğ' => '휓', 'Ġ' => '휔', 'ġ' => '치', 'Ģ' => '칙', 'ģ' => '친', 'Ĥ' => '칟', 'ĥ' => '칠', 'Ħ' => '칡', 'ħ' => '침', 'Ĩ' => '칩', 'ĩ' => '칫', 'Ī' => '칭', 'ī' => '카', 'Ĭ' => '칵', 'ĭ' => '칸', 'Į' => '칼', 'į' => '캄', 'İ' => '캅', 'ı' => '캇', 'IJ' => '캉', 'ij' => '캐', 'Ĵ' => '캑', 'ĵ' => '캔', 'Ķ' => '캘', 'ķ' => '캠', 'ĸ' => '캡', 'Ĺ' => '캣', 'ĺ' => '캤', 'Ļ' => '캥', 'ļ' => '캬', 'Ľ' => '캭', 'ľ' => '컁', 'Ŀ' => '커', '�' => '컥', '�' => '컨', '��' => '컫', '��' => '컬', '��' => '컴', '��' => '컵', '��' => '컷', '��' => '컸', '��' => '컹', '��' => '케', '��' => '켁', '��' => '켄', '��' => '켈', '��' => '켐', '��' => '켑', '��' => '켓', '��' => '켕', '��' => '켜', '��' => '켠', '��' => '켤', '��' => '켬', '��' => '켭', '��' => '켯', '��' => '켰', '��' => '켱', '��' => '켸', '��' => '코', '��' => '콕', '��' => '콘', '��' => '콜', '��' => '콤', '��' => '콥', '��' => '콧', '��' => '콩', '��' => '콰', '��' => '콱', '��' => '콴', '��' => '콸', '��' => '쾀', '��' => '쾅', '��' => '쾌', '��' => '쾡', '��' => '쾨', '��' => '쾰', '��' => '쿄', '��' => '쿠', '��' => '쿡', '��' => '쿤', '��' => '쿨', '��' => '쿰', '��' => '쿱', '��' => '쿳', '��' => '쿵', '�' => '쿼', '�' => '퀀', '�' => '퀄', '�' => '퀑', '�' => '퀘', '�' => '퀭', '�' => '퀴', '�' => '퀵', '�' => '퀸', '�' => '퀼', '�A' => '휕', '�B' => '휖', '�C' => '휗', '�D' => '휚', '�E' => '휛', '�F' => '휝', '�G' => '휞', '�H' => '휟', '�I' => '휡', '�J' => '휢', '�K' => '휣', '�L' => '휤', '�M' => '휥', '�N' => '휦', '�O' => '휧', '�P' => '휪', '�Q' => '휬', '�R' => '휮', '�S' => '휯', '�T' => '휰', '�U' => '휱', '�V' => '휲', '�W' => '휳', '�X' => '휶', '�Y' => '휷', '�Z' => '휹', '�a' => '휺', '�b' => '휻', '�c' => '휽', '�d' => '휾', '�e' => '휿', '�f' => '흀', '�g' => '흁', '�h' => '흂', '�i' => '흃', '�j' => '흅', '�k' => '흆', '�l' => '흈', '�m' => '흊', '�n' => '흋', '�o' => '흌', '�p' => '흍', '�q' => '흎', '�r' => '흏', '�s' => '흒', '�t' => '흓', '�u' => '흕', '�v' => '흚', '�w' => '흛', '�x' => '흜', '�y' => '흝', '�z' => '흞', 'Ł' => '흟', 'ł' => '흢', 'Ń' => '흤', 'ń' => '흦', 'Ņ' => '흧', 'ņ' => '흨', 'Ň' => '흪', 'ň' => '흫', 'ʼn' => '흭', 'Ŋ' => '흮', 'ŋ' => '흯', 'Ō' => '흱', 'ō' => '흲', 'Ŏ' => '흳', 'ŏ' => '흵', 'Ő' => '흶', 'ő' => '흷', 'Œ' => '흸', 'œ' => '흹', 'Ŕ' => '흺', 'ŕ' => '흻', 'Ŗ' => '흾', 'ŗ' => '흿', 'Ř' => '힀', 'ř' => '힂', 'Ś' => '힃', 'ś' => '힄', 'Ŝ' => '힅', 'ŝ' => '힆', 'Ş' => '힇', 'ş' => '힊', 'Š' => '힋', 'š' => '큄', 'Ţ' => '큅', 'ţ' => '큇', 'Ť' => '큉', 'ť' => '큐', 'Ŧ' => '큔', 'ŧ' => '큘', 'Ũ' => '큠', 'ũ' => '크', 'Ū' => '큭', 'ū' => '큰', 'Ŭ' => '클', 'ŭ' => '큼', 'Ů' => '큽', 'ů' => '킁', 'Ű' => '키', 'ű' => '킥', 'Ų' => '킨', 'ų' => '킬', 'Ŵ' => '킴', 'ŵ' => '킵', 'Ŷ' => '킷', 'ŷ' => '킹', 'Ÿ' => '타', 'Ź' => '탁', 'ź' => '탄', 'Ż' => '탈', 'ż' => '탉', 'Ž' => '탐', 'ž' => '탑', 'ſ' => '탓', '�' => '탔', '�' => '탕', '��' => '태', '��' => '택', '��' => '탠', '��' => '탤', '��' => '탬', '��' => '탭', '��' => '탯', '��' => '탰', '��' => '탱', '��' => '탸', '��' => '턍', '��' => '터', '��' => '턱', '��' => '턴', '��' => '털', '��' => '턺', '��' => '텀', '��' => '텁', '��' => '텃', '��' => '텄', '��' => '텅', '��' => '테', '��' => '텍', '��' => '텐', '��' => '텔', '��' => '템', '��' => '텝', '��' => '텟', '��' => '텡', '��' => '텨', '��' => '텬', '��' => '텼', '��' => '톄', '��' => '톈', '��' => '토', '��' => '톡', '��' => '톤', '��' => '톨', '��' => '톰', '��' => '톱', '��' => '톳', '��' => '통', '��' => '톺', '��' => '톼', '��' => '퇀', '��' => '퇘', '��' => '퇴', '��' => '퇸', '��' => '툇', '��' => '툉', '��' => '툐', '�' => '투', '�' => '툭', '�' => '툰', '�' => '툴', '�' => '툼', '�' => '툽', '�' => '툿', '�' => '퉁', '�' => '퉈', '�' => '퉜', '�A' => '힍', '�B' => '힎', '�C' => '힏', '�D' => '힑', '�E' => '힒', '�F' => '힓', '�G' => '힔', '�H' => '힕', '�I' => '힖', '�J' => '힗', '�K' => '힚', '�L' => '힜', '�M' => '힞', '�N' => '힟', '�O' => '힠', '�P' => '힡', '�Q' => '힢', '�R' => '힣', 'ơ' => '퉤', 'Ƣ' => '튀', 'ƣ' => '튁', 'Ƥ' => '튄', 'ƥ' => '튈', 'Ʀ' => '튐', 'Ƨ' => '튑', 'ƨ' => '튕', 'Ʃ' => '튜', 'ƪ' => '튠', 'ƫ' => '튤', 'Ƭ' => '튬', 'ƭ' => '튱', 'Ʈ' => '트', 'Ư' => '특', 'ư' => '튼', 'Ʊ' => '튿', 'Ʋ' => '틀', 'Ƴ' => '틂', 'ƴ' => '틈', 'Ƶ' => '틉', 'ƶ' => '틋', 'Ʒ' => '틔', 'Ƹ' => '틘', 'ƹ' => '틜', 'ƺ' => '틤', 'ƻ' => '틥', 'Ƽ' => '티', 'ƽ' => '틱', 'ƾ' => '틴', 'ƿ' => '틸', '�' => '팀', '�' => '팁', '��' => '팃', '��' => '팅', '��' => '파', '��' => '팍', '��' => '팎', '��' => '판', '��' => '팔', '��' => '팖', '��' => '팜', '��' => '팝', '��' => '팟', '��' => '팠', '��' => '팡', '��' => '팥', '��' => '패', '��' => '팩', '��' => '팬', '��' => '팰', '��' => '팸', '��' => '팹', '��' => '팻', '��' => '팼', '��' => '팽', '��' => '퍄', '��' => '퍅', '��' => '퍼', '��' => '퍽', '��' => '펀', '��' => '펄', '��' => '펌', '��' => '펍', '��' => '펏', '��' => '펐', '��' => '펑', '��' => '페', '��' => '펙', '��' => '펜', '��' => '펠', '��' => '펨', '��' => '펩', '��' => '펫', '��' => '펭', '��' => '펴', '��' => '편', '��' => '펼', '��' => '폄', '��' => '폅', '��' => '폈', '��' => '평', '��' => '폐', '��' => '폘', '�' => '폡', '�' => '폣', '�' => '포', '�' => '폭', '�' => '폰', '�' => '폴', '�' => '폼', '�' => '폽', '�' => '폿', '�' => '퐁', 'ǡ' => '퐈', 'Ǣ' => '퐝', 'ǣ' => '푀', 'Ǥ' => '푄', 'ǥ' => '표', 'Ǧ' => '푠', 'ǧ' => '푤', 'Ǩ' => '푭', 'ǩ' => '푯', 'Ǫ' => '푸', 'ǫ' => '푹', 'Ǭ' => '푼', 'ǭ' => '푿', 'Ǯ' => '풀', 'ǯ' => '풂', 'ǰ' => '품', 'DZ' => '풉', 'Dz' => '풋', 'dz' => '풍', 'Ǵ' => '풔', 'ǵ' => '풩', 'Ƕ' => '퓌', 'Ƿ' => '퓐', 'Ǹ' => '퓔', 'ǹ' => '퓜', 'Ǻ' => '퓟', 'ǻ' => '퓨', 'Ǽ' => '퓬', 'ǽ' => '퓰', 'Ǿ' => '퓸', 'ǿ' => '퓻', '�' => '퓽', '�' => '프', '��' => '픈', '��' => '플', '��' => '픔', '��' => '픕', '��' => '픗', '��' => '피', '��' => '픽', '��' => '핀', '��' => '필', '��' => '핌', '��' => '핍', '��' => '핏', '��' => '핑', '��' => '하', '��' => '학', '��' => '한', '��' => '할', '��' => '핥', '��' => '함', '��' => '합', '��' => '핫', '��' => '항', '��' => '해', '��' => '핵', '��' => '핸', '��' => '핼', '��' => '햄', '��' => '햅', '��' => '햇', '��' => '했', '��' => '행', '��' => '햐', '��' => '향', '��' => '허', '��' => '헉', '��' => '헌', '��' => '헐', '��' => '헒', '��' => '험', '��' => '헙', '��' => '헛', '��' => '헝', '��' => '헤', '��' => '헥', '��' => '헨', '��' => '헬', '��' => '헴', '��' => '헵', '��' => '헷', '��' => '헹', '��' => '혀', '�' => '혁', '�' => '현', '�' => '혈', '�' => '혐', '�' => '협', '�' => '혓', '�' => '혔', '�' => '형', '�' => '혜', '�' => '혠', 'ȡ' => '혤', 'Ȣ' => '혭', 'ȣ' => '호', 'Ȥ' => '혹', 'ȥ' => '혼', 'Ȧ' => '홀', 'ȧ' => '홅', 'Ȩ' => '홈', 'ȩ' => '홉', 'Ȫ' => '홋', 'ȫ' => '홍', 'Ȭ' => '홑', 'ȭ' => '화', 'Ȯ' => '확', 'ȯ' => '환', 'Ȱ' => '활', 'ȱ' => '홧', 'Ȳ' => '황', 'ȳ' => '홰', 'ȴ' => '홱', 'ȵ' => '홴', 'ȶ' => '횃', 'ȷ' => '횅', 'ȸ' => '회', 'ȹ' => '획', 'Ⱥ' => '횐', 'Ȼ' => '횔', 'ȼ' => '횝', 'Ƚ' => '횟', 'Ⱦ' => '횡', 'ȿ' => '효', '�' => '횬', '�' => '횰', '��' => '횹', '��' => '횻', '��' => '후', '��' => '훅', '��' => '훈', '��' => '훌', '��' => '훑', '��' => '훔', '��' => '훗', '��' => '훙', '��' => '훠', '��' => '훤', '��' => '훨', '��' => '훰', '��' => '훵', '��' => '훼', '��' => '훽', '��' => '휀', '��' => '휄', '��' => '휑', '��' => '휘', '��' => '휙', '��' => '휜', '��' => '휠', '��' => '휨', '��' => '휩', '��' => '휫', '��' => '휭', '��' => '휴', '��' => '휵', '��' => '휸', '��' => '휼', '��' => '흄', '��' => '흇', '��' => '흉', '��' => '흐', '��' => '흑', '��' => '흔', '��' => '흖', '��' => '흗', '��' => '흘', '��' => '흙', '��' => '흠', '��' => '흡', '��' => '흣', '��' => '흥', '��' => '흩', '��' => '희', '��' => '흰', '��' => '흴', '��' => '흼', '�' => '흽', '�' => '힁', '�' => '히', '�' => '힉', '�' => '힌', '�' => '힐', '�' => '힘', '�' => '힙', '�' => '힛', '�' => '힝', 'ʡ' => '伽', 'ʢ' => '佳', 'ʣ' => '假', 'ʤ' => '價', 'ʥ' => '加', 'ʦ' => '可', 'ʧ' => '呵', 'ʨ' => '哥', 'ʩ' => '嘉', 'ʪ' => '嫁', 'ʫ' => '家', 'ʬ' => '暇', 'ʭ' => '架', 'ʮ' => '枷', 'ʯ' => '柯', 'ʰ' => '歌', 'ʱ' => '珂', 'ʲ' => '痂', 'ʳ' => '稼', 'ʴ' => '苛', 'ʵ' => '茄', 'ʶ' => '街', 'ʷ' => '袈', 'ʸ' => '訶', 'ʹ' => '賈', 'ʺ' => '跏', 'ʻ' => '軻', 'ʼ' => '迦', 'ʽ' => '駕', 'ʾ' => '刻', 'ʿ' => '却', '�' => '各', '�' => '恪', '��' => '慤', '��' => '殼', '��' => '珏', '��' => '脚', '��' => '覺', '��' => '角', '��' => '閣', '��' => '侃', '��' => '刊', '��' => '墾', '��' => '奸', '��' => '姦', '��' => '干', '��' => '幹', '��' => '懇', '��' => '揀', '��' => '杆', '��' => '柬', '��' => '桿', '��' => '澗', '��' => '癎', '��' => '看', '��' => '磵', '��' => '稈', '��' => '竿', '��' => '簡', '��' => '肝', '��' => '艮', '��' => '艱', '��' => '諫', '��' => '間', '��' => '乫', '��' => '喝', '��' => '曷', '��' => '渴', '��' => '碣', '��' => '竭', '��' => '葛', '��' => '褐', '��' => '蝎', '��' => '鞨', '��' => '勘', '��' => '坎', '��' => '堪', '��' => '嵌', '��' => '感', '��' => '憾', '��' => '戡', '��' => '敢', '��' => '柑', '��' => '橄', '�' => '減', '�' => '甘', '�' => '疳', '�' => '監', '�' => '瞰', '�' => '紺', '�' => '邯', '�' => '鑑', '�' => '鑒', '�' => '龕', 'ˡ' => '匣', 'ˢ' => '岬', 'ˣ' => '甲', 'ˤ' => '胛', '˥' => '鉀', '˦' => '閘', '˧' => '剛', '˨' => '堈', '˩' => '姜', '˪' => '岡', '˫' => '崗', 'ˬ' => '康', '˭' => '强', 'ˮ' => '彊', '˯' => '慷', '˰' => '江', '˱' => '畺', '˲' => '疆', '˳' => '糠', '˴' => '絳', '˵' => '綱', '˶' => '羌', '˷' => '腔', '˸' => '舡', '˹' => '薑', '˺' => '襁', '˻' => '講', '˼' => '鋼', '˽' => '降', '˾' => '鱇', '˿' => '介', '�' => '价', '�' => '個', '��' => '凱', '��' => '塏', '��' => '愷', '��' => '愾', '��' => '慨', '��' => '改', '��' => '槪', '��' => '漑', '��' => '疥', '��' => '皆', '��' => '盖', '��' => '箇', '��' => '芥', '��' => '蓋', '��' => '豈', '��' => '鎧', '��' => '開', '��' => '喀', '��' => '客', '��' => '坑', '��' => '更', '��' => '粳', '��' => '羹', '��' => '醵', '��' => '倨', '��' => '去', '��' => '居', '��' => '巨', '��' => '拒', '��' => '据', '��' => '據', '��' => '擧', '��' => '渠', '��' => '炬', '��' => '祛', '��' => '距', '��' => '踞', '��' => '車', '��' => '遽', '��' => '鉅', '��' => '鋸', '��' => '乾', '��' => '件', '��' => '健', '��' => '巾', '��' => '建', '��' => '愆', '��' => '楗', '��' => '腱', '��' => '虔', '��' => '蹇', '�' => '鍵', '�' => '騫', '�' => '乞', '�' => '傑', '�' => '杰', '�' => '桀', '�' => '儉', '�' => '劍', '�' => '劒', '�' => '檢', '̡' => '瞼', '̢' => '鈐', '̣' => '黔', '̤' => '劫', '̥' => '怯', '̦' => '迲', '̧' => '偈', '̨' => '憩', '̩' => '揭', '̪' => '擊', '̫' => '格', '̬' => '檄', '̭' => '激', '̮' => '膈', '̯' => '覡', '̰' => '隔', '̱' => '堅', '̲' => '牽', '̳' => '犬', '̴' => '甄', '̵' => '絹', '̶' => '繭', '̷' => '肩', '̸' => '見', '̹' => '譴', '̺' => '遣', '̻' => '鵑', '̼' => '抉', '̽' => '決', '̾' => '潔', '̿' => '結', '�' => '缺', '�' => '訣', '��' => '兼', '��' => '慊', '��' => '箝', '��' => '謙', '��' => '鉗', '��' => '鎌', '��' => '京', '��' => '俓', '��' => '倞', '��' => '傾', '��' => '儆', '��' => '勁', '��' => '勍', '��' => '卿', '��' => '坰', '��' => '境', '��' => '庚', '��' => '徑', '��' => '慶', '��' => '憬', '��' => '擎', '��' => '敬', '��' => '景', '��' => '暻', '��' => '更', '��' => '梗', '��' => '涇', '��' => '炅', '��' => '烱', '��' => '璟', '��' => '璥', '��' => '瓊', '��' => '痙', '��' => '硬', '��' => '磬', '��' => '竟', '��' => '競', '��' => '絅', '��' => '經', '��' => '耕', '��' => '耿', '��' => '脛', '��' => '莖', '��' => '警', '��' => '輕', '��' => '逕', '��' => '鏡', '��' => '頃', '��' => '頸', '��' => '驚', '��' => '鯨', '�' => '係', '�' => '啓', '�' => '堺', '�' => '契', '�' => '季', '�' => '屆', '�' => '悸', '�' => '戒', '�' => '桂', '�' => '械', '͡' => '棨', '͢' => '溪', 'ͣ' => '界', 'ͤ' => '癸', 'ͥ' => '磎', 'ͦ' => '稽', 'ͧ' => '系', 'ͨ' => '繫', 'ͩ' => '繼', 'ͪ' => '計', 'ͫ' => '誡', 'ͬ' => '谿', 'ͭ' => '階', 'ͮ' => '鷄', 'ͯ' => '古', 'Ͱ' => '叩', 'ͱ' => '告', 'Ͳ' => '呱', 'ͳ' => '固', 'ʹ' => '姑', '͵' => '孤', 'Ͷ' => '尻', 'ͷ' => '庫', '' => '拷', '' => '攷', 'ͺ' => '故', 'ͻ' => '敲', 'ͼ' => '暠', 'ͽ' => '枯', ';' => '槁', 'Ϳ' => '沽', '�' => '痼', '�' => '皐', '��' => '睾', '��' => '稿', '��' => '羔', '��' => '考', '��' => '股', '��' => '膏', '��' => '苦', '��' => '苽', '��' => '菰', '��' => '藁', '��' => '蠱', '��' => '袴', '��' => '誥', '��' => '賈', '��' => '辜', '��' => '錮', '��' => '雇', '��' => '顧', '��' => '高', '��' => '鼓', '��' => '哭', '��' => '斛', '��' => '曲', '��' => '梏', '��' => '穀', '��' => '谷', '��' => '鵠', '��' => '困', '��' => '坤', '��' => '崑', '��' => '昆', '��' => '梱', '��' => '棍', '��' => '滾', '��' => '琨', '��' => '袞', '��' => '鯤', '��' => '汨', '��' => '滑', '��' => '骨', '��' => '供', '��' => '公', '��' => '共', '��' => '功', '��' => '孔', '��' => '工', '��' => '恐', '��' => '恭', '��' => '拱', '��' => '控', '��' => '攻', '�' => '珙', '�' => '空', '�' => '蚣', '�' => '貢', '�' => '鞏', '�' => '串', '�' => '寡', '�' => '戈', '�' => '果', '�' => '瓜', 'Ρ' => '科', '' => '菓', 'Σ' => '誇', 'Τ' => '課', 'Υ' => '跨', 'Φ' => '過', 'Χ' => '鍋', 'Ψ' => '顆', 'Ω' => '廓', 'Ϊ' => '槨', 'Ϋ' => '藿', 'ά' => '郭', 'έ' => '串', 'ή' => '冠', 'ί' => '官', 'ΰ' => '寬', 'α' => '慣', 'β' => '棺', 'γ' => '款', 'δ' => '灌', 'ε' => '琯', 'ζ' => '瓘', 'η' => '管', 'θ' => '罐', 'ι' => '菅', 'κ' => '觀', 'λ' => '貫', 'μ' => '關', 'ν' => '館', 'ξ' => '刮', 'ο' => '恝', '�' => '括', '�' => '适', '��' => '侊', '��' => '光', '��' => '匡', '��' => '壙', '��' => '廣', '��' => '曠', '��' => '洸', '��' => '炚', '��' => '狂', '��' => '珖', '��' => '筐', '��' => '胱', '��' => '鑛', '��' => '卦', '��' => '掛', '��' => '罫', '��' => '乖', '��' => '傀', '��' => '塊', '��' => '壞', '��' => '怪', '��' => '愧', '��' => '拐', '��' => '槐', '��' => '魁', '��' => '宏', '��' => '紘', '��' => '肱', '��' => '轟', '��' => '交', '��' => '僑', '��' => '咬', '��' => '喬', '��' => '嬌', '��' => '嶠', '��' => '巧', '��' => '攪', '��' => '敎', '��' => '校', '��' => '橋', '��' => '狡', '��' => '皎', '��' => '矯', '��' => '絞', '��' => '翹', '��' => '膠', '��' => '蕎', '��' => '蛟', '��' => '較', '��' => '轎', '��' => '郊', '�' => '餃', '�' => '驕', '�' => '鮫', '�' => '丘', '�' => '久', '�' => '九', '�' => '仇', '�' => '俱', '�' => '具', '�' => '勾', 'ϡ' => '區', 'Ϣ' => '口', 'ϣ' => '句', 'Ϥ' => '咎', 'ϥ' => '嘔', 'Ϧ' => '坵', 'ϧ' => '垢', 'Ϩ' => '寇', 'ϩ' => '嶇', 'Ϫ' => '廐', 'ϫ' => '懼', 'Ϭ' => '拘', 'ϭ' => '救', 'Ϯ' => '枸', 'ϯ' => '柩', 'ϰ' => '構', 'ϱ' => '歐', 'ϲ' => '毆', 'ϳ' => '毬', 'ϴ' => '求', 'ϵ' => '溝', '϶' => '灸', 'Ϸ' => '狗', 'ϸ' => '玖', 'Ϲ' => '球', 'Ϻ' => '瞿', 'ϻ' => '矩', 'ϼ' => '究', 'Ͻ' => '絿', 'Ͼ' => '耉', 'Ͽ' => '臼', '�' => '舅', '�' => '舊', '��' => '苟', '��' => '衢', '��' => '謳', '��' => '購', '��' => '軀', '��' => '逑', '��' => '邱', '��' => '鉤', '��' => '銶', '��' => '駒', '��' => '驅', '��' => '鳩', '��' => '鷗', '��' => '龜', '��' => '國', '��' => '局', '��' => '菊', '��' => '鞠', '��' => '鞫', '��' => '麴', '��' => '君', '��' => '窘', '��' => '群', '��' => '裙', '��' => '軍', '��' => '郡', '��' => '堀', '��' => '屈', '��' => '掘', '��' => '窟', '��' => '宮', '��' => '弓', '��' => '穹', '��' => '窮', '��' => '芎', '��' => '躬', '��' => '倦', '��' => '券', '��' => '勸', '��' => '卷', '��' => '圈', '��' => '拳', '��' => '捲', '��' => '權', '��' => '淃', '��' => '眷', '��' => '厥', '��' => '獗', '��' => '蕨', '��' => '蹶', '��' => '闕', '�' => '机', '�' => '櫃', '�' => '潰', '�' => '詭', '�' => '軌', '�' => '饋', '�' => '句', '�' => '晷', '�' => '歸', '�' => '貴', 'С' => '鬼', 'Т' => '龜', 'У' => '叫', 'Ф' => '圭', 'Х' => '奎', 'Ц' => '揆', 'Ч' => '槻', 'Ш' => '珪', 'Щ' => '硅', 'Ъ' => '窺', 'Ы' => '竅', 'Ь' => '糾', 'Э' => '葵', 'Ю' => '規', 'Я' => '赳', 'а' => '逵', 'б' => '閨', 'в' => '勻', 'г' => '均', 'д' => '畇', 'е' => '筠', 'ж' => '菌', 'з' => '鈞', 'и' => '龜', 'й' => '橘', 'к' => '克', 'л' => '剋', 'м' => '劇', 'н' => '戟', 'о' => '棘', 'п' => '極', '�' => '隙', '�' => '僅', '��' => '劤', '��' => '勤', '��' => '懃', '��' => '斤', '��' => '根', '��' => '槿', '��' => '瑾', '��' => '筋', '��' => '芹', '��' => '菫', '��' => '覲', '��' => '謹', '��' => '近', '��' => '饉', '��' => '契', '��' => '今', '��' => '妗', '��' => '擒', '��' => '昑', '��' => '檎', '��' => '琴', '��' => '禁', '��' => '禽', '��' => '芩', '��' => '衾', '��' => '衿', '��' => '襟', '��' => '金', '��' => '錦', '��' => '伋', '��' => '及', '��' => '急', '��' => '扱', '��' => '汲', '��' => '級', '��' => '給', '��' => '亘', '��' => '兢', '��' => '矜', '��' => '肯', '��' => '企', '��' => '伎', '��' => '其', '��' => '冀', '��' => '嗜', '��' => '器', '��' => '圻', '��' => '基', '��' => '埼', '��' => '夔', '��' => '奇', '�' => '妓', '�' => '寄', '�' => '岐', '�' => '崎', '�' => '己', '�' => '幾', '�' => '忌', '�' => '技', '�' => '旗', '�' => '旣', 'ѡ' => '朞', 'Ѣ' => '期', 'ѣ' => '杞', 'Ѥ' => '棋', 'ѥ' => '棄', 'Ѧ' => '機', 'ѧ' => '欺', 'Ѩ' => '氣', 'ѩ' => '汽', 'Ѫ' => '沂', 'ѫ' => '淇', 'Ѭ' => '玘', 'ѭ' => '琦', 'Ѯ' => '琪', 'ѯ' => '璂', 'Ѱ' => '璣', 'ѱ' => '畸', 'Ѳ' => '畿', 'ѳ' => '碁', 'Ѵ' => '磯', 'ѵ' => '祁', 'Ѷ' => '祇', 'ѷ' => '祈', 'Ѹ' => '祺', 'ѹ' => '箕', 'Ѻ' => '紀', 'ѻ' => '綺', 'Ѽ' => '羈', 'ѽ' => '耆', 'Ѿ' => '耭', 'ѿ' => '肌', '�' => '記', '�' => '譏', '��' => '豈', '��' => '起', '��' => '錡', '��' => '錤', '��' => '飢', '��' => '饑', '��' => '騎', '��' => '騏', '��' => '驥', '��' => '麒', '��' => '緊', '��' => '佶', '��' => '吉', '��' => '拮', '��' => '桔', '��' => '金', '��' => '喫', '��' => '儺', '��' => '喇', '��' => '奈', '��' => '娜', '��' => '懦', '��' => '懶', '��' => '拏', '��' => '拿', '��' => '癩', '��' => '羅', '��' => '蘿', '��' => '螺', '��' => '裸', '��' => '邏', '��' => '那', '��' => '樂', '��' => '洛', '��' => '烙', '��' => '珞', '��' => '落', '��' => '諾', '��' => '酪', '��' => '駱', '��' => '亂', '��' => '卵', '��' => '暖', '��' => '欄', '��' => '煖', '��' => '爛', '��' => '蘭', '��' => '難', '��' => '鸞', '��' => '捏', '��' => '捺', '�' => '南', '�' => '嵐', '�' => '枏', '�' => '楠', '�' => '湳', '�' => '濫', '�' => '男', '�' => '藍', '�' => '襤', '�' => '拉', 'ҡ' => '納', 'Ң' => '臘', 'ң' => '蠟', 'Ҥ' => '衲', 'ҥ' => '囊', 'Ҧ' => '娘', 'ҧ' => '廊', 'Ҩ' => '朗', 'ҩ' => '浪', 'Ҫ' => '狼', 'ҫ' => '郎', 'Ҭ' => '乃', 'ҭ' => '來', 'Ү' => '內', 'ү' => '奈', 'Ұ' => '柰', 'ұ' => '耐', 'Ҳ' => '冷', 'ҳ' => '女', 'Ҵ' => '年', 'ҵ' => '撚', 'Ҷ' => '秊', 'ҷ' => '念', 'Ҹ' => '恬', 'ҹ' => '拈', 'Һ' => '捻', 'һ' => '寧', 'Ҽ' => '寗', 'ҽ' => '努', 'Ҿ' => '勞', 'ҿ' => '奴', '�' => '弩', '�' => '怒', '��' => '擄', '��' => '櫓', '��' => '爐', '��' => '瑙', '��' => '盧', '��' => '老', '��' => '蘆', '��' => '虜', '��' => '路', '��' => '露', '��' => '駑', '��' => '魯', '��' => '鷺', '��' => '碌', '��' => '祿', '��' => '綠', '��' => '菉', '��' => '錄', '��' => '鹿', '��' => '論', '��' => '壟', '��' => '弄', '��' => '濃', '��' => '籠', '��' => '聾', '��' => '膿', '��' => '農', '��' => '惱', '��' => '牢', '��' => '磊', '��' => '腦', '��' => '賂', '��' => '雷', '��' => '尿', '��' => '壘', '��' => '屢', '��' => '樓', '��' => '淚', '��' => '漏', '��' => '累', '��' => '縷', '��' => '陋', '��' => '嫩', '��' => '訥', '��' => '杻', '��' => '紐', '��' => '勒', '��' => '肋', '��' => '凜', '��' => '凌', '��' => '稜', '�' => '綾', '�' => '能', '�' => '菱', '�' => '陵', '�' => '尼', '�' => '泥', '�' => '匿', '�' => '溺', '�' => '多', '�' => '茶', 'ӡ' => '丹', 'Ӣ' => '亶', 'ӣ' => '但', 'Ӥ' => '單', 'ӥ' => '團', 'Ӧ' => '壇', 'ӧ' => '彖', 'Ө' => '斷', 'ө' => '旦', 'Ӫ' => '檀', 'ӫ' => '段', 'Ӭ' => '湍', 'ӭ' => '短', 'Ӯ' => '端', 'ӯ' => '簞', 'Ӱ' => '緞', 'ӱ' => '蛋', 'Ӳ' => '袒', 'ӳ' => '鄲', 'Ӵ' => '鍛', 'ӵ' => '撻', 'Ӷ' => '澾', 'ӷ' => '獺', 'Ӹ' => '疸', 'ӹ' => '達', 'Ӻ' => '啖', 'ӻ' => '坍', 'Ӽ' => '憺', 'ӽ' => '擔', 'Ӿ' => '曇', 'ӿ' => '淡', '�' => '湛', '�' => '潭', '��' => '澹', '��' => '痰', '��' => '聃', '��' => '膽', '��' => '蕁', '��' => '覃', '��' => '談', '��' => '譚', '��' => '錟', '��' => '沓', '��' => '畓', '��' => '答', '��' => '踏', '��' => '遝', '��' => '唐', '��' => '堂', '��' => '塘', '��' => '幢', '��' => '戇', '��' => '撞', '��' => '棠', '��' => '當', '��' => '糖', '��' => '螳', '��' => '黨', '��' => '代', '��' => '垈', '��' => '坮', '��' => '大', '��' => '對', '��' => '岱', '��' => '帶', '��' => '待', '��' => '戴', '��' => '擡', '��' => '玳', '��' => '臺', '��' => '袋', '��' => '貸', '��' => '隊', '��' => '黛', '��' => '宅', '��' => '德', '��' => '悳', '��' => '倒', '��' => '刀', '��' => '到', '��' => '圖', '��' => '堵', '��' => '塗', '��' => '導', '�' => '屠', '�' => '島', '�' => '嶋', '�' => '度', '�' => '徒', '�' => '悼', '�' => '挑', '�' => '掉', '�' => '搗', '�' => '桃', 'ԡ' => '棹', 'Ԣ' => '櫂', 'ԣ' => '淘', 'Ԥ' => '渡', 'ԥ' => '滔', 'Ԧ' => '濤', 'ԧ' => '燾', 'Ԩ' => '盜', 'ԩ' => '睹', 'Ԫ' => '禱', 'ԫ' => '稻', 'Ԭ' => '萄', 'ԭ' => '覩', 'Ԯ' => '賭', 'ԯ' => '跳', '' => '蹈', 'Ա' => '逃', 'Բ' => '途', 'Գ' => '道', 'Դ' => '都', 'Ե' => '鍍', 'Զ' => '陶', 'Է' => '韜', 'Ը' => '毒', 'Թ' => '瀆', 'Ժ' => '牘', 'Ի' => '犢', 'Լ' => '獨', 'Խ' => '督', 'Ծ' => '禿', 'Կ' => '篤', '�' => '纛', '�' => '讀', '��' => '墩', '��' => '惇', '��' => '敦', '��' => '旽', '��' => '暾', '��' => '沌', '��' => '焞', '��' => '燉', '��' => '豚', '��' => '頓', '��' => '乭', '��' => '突', '��' => '仝', '��' => '冬', '��' => '凍', '��' => '動', '��' => '同', '��' => '憧', '��' => '東', '��' => '桐', '��' => '棟', '��' => '洞', '��' => '潼', '��' => '疼', '��' => '瞳', '��' => '童', '��' => '胴', '��' => '董', '��' => '銅', '��' => '兜', '��' => '斗', '��' => '杜', '��' => '枓', '��' => '痘', '��' => '竇', '��' => '荳', '��' => '讀', '��' => '豆', '��' => '逗', '��' => '頭', '��' => '屯', '��' => '臀', '��' => '芚', '��' => '遁', '��' => '遯', '��' => '鈍', '��' => '得', '��' => '嶝', '��' => '橙', '��' => '燈', '��' => '登', '�' => '等', '�' => '藤', '�' => '謄', '�' => '鄧', '�' => '騰', '�' => '喇', '�' => '懶', '�' => '拏', '�' => '癩', '�' => '羅', 'ա' => '蘿', 'բ' => '螺', 'գ' => '裸', 'դ' => '邏', 'ե' => '樂', 'զ' => '洛', 'է' => '烙', 'ը' => '珞', 'թ' => '絡', 'ժ' => '落', 'ի' => '諾', 'լ' => '酪', 'խ' => '駱', 'ծ' => '丹', 'կ' => '亂', 'հ' => '卵', 'ձ' => '欄', 'ղ' => '欒', 'ճ' => '瀾', 'մ' => '爛', 'յ' => '蘭', 'ն' => '鸞', 'շ' => '剌', 'ո' => '辣', 'չ' => '嵐', 'պ' => '擥', 'ջ' => '攬', 'ռ' => '欖', 'ս' => '濫', 'վ' => '籃', 'տ' => '纜', '�' => '藍', '�' => '襤', '��' => '覽', '��' => '拉', '��' => '臘', '��' => '蠟', '��' => '廊', '��' => '朗', '��' => '浪', '��' => '狼', '��' => '琅', '��' => '瑯', '��' => '螂', '��' => '郞', '��' => '來', '��' => '崍', '��' => '徠', '��' => '萊', '��' => '冷', '��' => '掠', '��' => '略', '��' => '亮', '��' => '倆', '��' => '兩', '��' => '凉', '��' => '梁', '��' => '樑', '��' => '粮', '��' => '粱', '��' => '糧', '��' => '良', '��' => '諒', '��' => '輛', '��' => '量', '��' => '侶', '��' => '儷', '��' => '勵', '��' => '呂', '��' => '廬', '��' => '慮', '��' => '戾', '��' => '旅', '��' => '櫚', '��' => '濾', '��' => '礪', '��' => '藜', '��' => '蠣', '��' => '閭', '��' => '驢', '��' => '驪', '��' => '麗', '��' => '黎', '��' => '力', '�' => '曆', '�' => '歷', '�' => '瀝', '�' => '礫', '�' => '轢', '�' => '靂', '�' => '憐', '�' => '戀', '�' => '攣', '�' => '漣', '֡' => '煉', '֢' => '璉', '֣' => '練', '֤' => '聯', '֥' => '蓮', '֦' => '輦', '֧' => '連', '֨' => '鍊', '֩' => '冽', '֪' => '列', '֫' => '劣', '֬' => '洌', '֭' => '烈', '֮' => '裂', '֯' => '廉', 'ְ' => '斂', 'ֱ' => '殮', 'ֲ' => '濂', 'ֳ' => '簾', 'ִ' => '獵', 'ֵ' => '令', 'ֶ' => '伶', 'ַ' => '囹', 'ָ' => '寧', 'ֹ' => '岺', 'ֺ' => '嶺', 'ֻ' => '怜', 'ּ' => '玲', 'ֽ' => '笭', '־' => '羚', 'ֿ' => '翎', '�' => '聆', '�' => '逞', '��' => '鈴', '��' => '零', '��' => '靈', '��' => '領', '��' => '齡', '��' => '例', '��' => '澧', '��' => '禮', '��' => '醴', '��' => '隷', '��' => '勞', '��' => '怒', '��' => '撈', '��' => '擄', '��' => '櫓', '��' => '潞', '��' => '瀘', '��' => '爐', '��' => '盧', '��' => '老', '��' => '蘆', '��' => '虜', '��' => '路', '��' => '輅', '��' => '露', '��' => '魯', '��' => '鷺', '��' => '鹵', '��' => '碌', '��' => '祿', '��' => '綠', '��' => '菉', '��' => '錄', '��' => '鹿', '��' => '麓', '��' => '論', '��' => '壟', '��' => '弄', '��' => '朧', '��' => '瀧', '��' => '瓏', '��' => '籠', '��' => '聾', '��' => '儡', '��' => '瀨', '��' => '牢', '��' => '磊', '��' => '賂', '��' => '賚', '��' => '賴', '��' => '雷', '�' => '了', '�' => '僚', '�' => '寮', '�' => '廖', '�' => '料', '�' => '燎', '�' => '療', '�' => '瞭', '�' => '聊', '�' => '蓼', 'ס' => '遼', 'ע' => '鬧', 'ף' => '龍', 'פ' => '壘', 'ץ' => '婁', 'צ' => '屢', 'ק' => '樓', 'ר' => '淚', 'ש' => '漏', 'ת' => '瘻', '' => '累', '' => '縷', '' => '蔞', '' => '褸', 'ׯ' => '鏤', 'װ' => '陋', 'ױ' => '劉', 'ײ' => '旒', '׳' => '柳', '״' => '榴', '' => '流', '' => '溜', '' => '瀏', '' => '琉', '' => '瑠', '' => '留', '' => '瘤', '' => '硫', '' => '謬', '' => '類', '' => '六', '�' => '戮', '�' => '陸', '��' => '侖', '��' => '倫', '��' => '崙', '��' => '淪', '��' => '綸', '��' => '輪', '��' => '律', '��' => '慄', '��' => '栗', '��' => '率', '��' => '隆', '��' => '勒', '��' => '肋', '��' => '凜', '��' => '凌', '��' => '楞', '��' => '稜', '��' => '綾', '��' => '菱', '��' => '陵', '��' => '俚', '��' => '利', '��' => '厘', '��' => '吏', '��' => '唎', '��' => '履', '��' => '悧', '��' => '李', '��' => '梨', '��' => '浬', '��' => '犁', '��' => '狸', '��' => '理', '��' => '璃', '��' => '異', '��' => '痢', '��' => '籬', '��' => '罹', '��' => '羸', '��' => '莉', '��' => '裏', '��' => '裡', '��' => '里', '��' => '釐', '��' => '離', '��' => '鯉', '��' => '吝', '��' => '潾', '��' => '燐', '��' => '璘', '��' => '藺', '�' => '躪', '�' => '隣', '�' => '鱗', '�' => '麟', '�' => '林', '�' => '淋', '�' => '琳', '�' => '臨', '�' => '霖', '�' => '砬', 'ء' => '立', 'آ' => '笠', 'أ' => '粒', 'ؤ' => '摩', 'إ' => '瑪', 'ئ' => '痲', 'ا' => '碼', 'ب' => '磨', 'ة' => '馬', 'ت' => '魔', 'ث' => '麻', 'ج' => '寞', 'ح' => '幕', 'خ' => '漠', 'د' => '膜', 'ذ' => '莫', 'ر' => '邈', 'ز' => '万', 'س' => '卍', 'ش' => '娩', 'ص' => '巒', 'ض' => '彎', 'ط' => '慢', 'ظ' => '挽', 'ع' => '晩', 'غ' => '曼', 'ػ' => '滿', 'ؼ' => '漫', 'ؽ' => '灣', 'ؾ' => '瞞', 'ؿ' => '萬', '�' => '蔓', '�' => '蠻', '��' => '輓', '��' => '饅', '��' => '鰻', '��' => '唜', '��' => '抹', '��' => '末', '��' => '沫', '��' => '茉', '��' => '襪', '��' => '靺', '��' => '亡', '��' => '妄', '��' => '忘', '��' => '忙', '��' => '望', '��' => '網', '��' => '罔', '��' => '芒', '��' => '茫', '��' => '莽', '��' => '輞', '��' => '邙', '��' => '埋', '��' => '妹', '��' => '媒', '��' => '寐', '��' => '昧', '��' => '枚', '��' => '梅', '��' => '每', '��' => '煤', '��' => '罵', '��' => '買', '��' => '賣', '��' => '邁', '��' => '魅', '��' => '脈', '��' => '貊', '��' => '陌', '��' => '驀', '��' => '麥', '��' => '孟', '��' => '氓', '��' => '猛', '��' => '盲', '��' => '盟', '��' => '萌', '��' => '冪', '��' => '覓', '��' => '免', '��' => '冕', '�' => '勉', '�' => '棉', '�' => '沔', '�' => '眄', '�' => '眠', '�' => '綿', '�' => '緬', '�' => '面', '�' => '麵', '�' => '滅', '١' => '蔑', '٢' => '冥', '٣' => '名', '٤' => '命', '٥' => '明', '٦' => '暝', '٧' => '椧', '٨' => '溟', '٩' => '皿', '٪' => '瞑', '٫' => '茗', '٬' => '蓂', '٭' => '螟', 'ٮ' => '酩', 'ٯ' => '銘', 'ٰ' => '鳴', 'ٱ' => '袂', 'ٲ' => '侮', 'ٳ' => '冒', 'ٴ' => '募', 'ٵ' => '姆', 'ٶ' => '帽', 'ٷ' => '慕', 'ٸ' => '摸', 'ٹ' => '摹', 'ٺ' => '暮', 'ٻ' => '某', 'ټ' => '模', 'ٽ' => '母', 'پ' => '毛', 'ٿ' => '牟', '�' => '牡', '�' => '瑁', '��' => '眸', '��' => '矛', '��' => '耗', '��' => '芼', '��' => '茅', '��' => '謀', '��' => '謨', '��' => '貌', '��' => '木', '��' => '沐', '��' => '牧', '��' => '目', '��' => '睦', '��' => '穆', '��' => '鶩', '��' => '歿', '��' => '沒', '��' => '夢', '��' => '朦', '��' => '蒙', '��' => '卯', '��' => '墓', '��' => '妙', '��' => '廟', '��' => '描', '��' => '昴', '��' => '杳', '��' => '渺', '��' => '猫', '��' => '竗', '��' => '苗', '��' => '錨', '��' => '務', '��' => '巫', '��' => '憮', '��' => '懋', '��' => '戊', '��' => '拇', '��' => '撫', '��' => '无', '��' => '楙', '��' => '武', '��' => '毋', '��' => '無', '��' => '珷', '��' => '畝', '��' => '繆', '��' => '舞', '��' => '茂', '��' => '蕪', '��' => '誣', '�' => '貿', '�' => '霧', '�' => '鵡', '�' => '墨', '�' => '默', '�' => '們', '�' => '刎', '�' => '吻', '�' => '問', '�' => '文', 'ڡ' => '汶', 'ڢ' => '紊', 'ڣ' => '紋', 'ڤ' => '聞', 'ڥ' => '蚊', 'ڦ' => '門', 'ڧ' => '雯', 'ڨ' => '勿', 'ک' => '沕', 'ڪ' => '物', 'ګ' => '味', 'ڬ' => '媚', 'ڭ' => '尾', 'ڮ' => '嵋', 'گ' => '彌', 'ڰ' => '微', 'ڱ' => '未', 'ڲ' => '梶', 'ڳ' => '楣', 'ڴ' => '渼', 'ڵ' => '湄', 'ڶ' => '眉', 'ڷ' => '米', 'ڸ' => '美', 'ڹ' => '薇', 'ں' => '謎', 'ڻ' => '迷', 'ڼ' => '靡', 'ڽ' => '黴', 'ھ' => '岷', 'ڿ' => '悶', '�' => '愍', '�' => '憫', '��' => '敏', '��' => '旻', '��' => '旼', '��' => '民', '��' => '泯', '��' => '玟', '��' => '珉', '��' => '緡', '��' => '閔', '��' => '密', '��' => '蜜', '��' => '謐', '��' => '剝', '��' => '博', '��' => '拍', '��' => '搏', '��' => '撲', '��' => '朴', '��' => '樸', '��' => '泊', '��' => '珀', '��' => '璞', '��' => '箔', '��' => '粕', '��' => '縛', '��' => '膊', '��' => '舶', '��' => '薄', '��' => '迫', '��' => '雹', '��' => '駁', '��' => '伴', '��' => '半', '��' => '反', '��' => '叛', '��' => '拌', '��' => '搬', '��' => '攀', '��' => '斑', '��' => '槃', '��' => '泮', '��' => '潘', '��' => '班', '��' => '畔', '��' => '瘢', '��' => '盤', '��' => '盼', '��' => '磐', '��' => '磻', '��' => '礬', '��' => '絆', '�' => '般', '�' => '蟠', '�' => '返', '�' => '頒', '�' => '飯', '�' => '勃', '�' => '拔', '�' => '撥', '�' => '渤', '�' => '潑', 'ۡ' => '發', 'ۢ' => '跋', 'ۣ' => '醱', 'ۤ' => '鉢', 'ۥ' => '髮', 'ۦ' => '魃', 'ۧ' => '倣', 'ۨ' => '傍', '۩' => '坊', '۪' => '妨', '۫' => '尨', '۬' => '幇', 'ۭ' => '彷', 'ۮ' => '房', 'ۯ' => '放', '۰' => '方', '۱' => '旁', '۲' => '昉', '۳' => '枋', '۴' => '榜', '۵' => '滂', '۶' => '磅', '۷' => '紡', '۸' => '肪', '۹' => '膀', 'ۺ' => '舫', 'ۻ' => '芳', 'ۼ' => '蒡', '۽' => '蚌', '۾' => '訪', 'ۿ' => '謗', '�' => '邦', '�' => '防', '��' => '龐', '��' => '倍', '��' => '俳', '��' => '北', '��' => '培', '��' => '徘', '��' => '拜', '��' => '排', '��' => '杯', '��' => '湃', '��' => '焙', '��' => '盃', '��' => '背', '��' => '胚', '��' => '裴', '��' => '裵', '��' => '褙', '��' => '賠', '��' => '輩', '��' => '配', '��' => '陪', '��' => '伯', '��' => '佰', '��' => '帛', '��' => '柏', '��' => '栢', '��' => '白', '��' => '百', '��' => '魄', '��' => '幡', '��' => '樊', '��' => '煩', '��' => '燔', '��' => '番', '��' => '磻', '��' => '繁', '��' => '蕃', '��' => '藩', '��' => '飜', '��' => '伐', '��' => '筏', '��' => '罰', '��' => '閥', '��' => '凡', '��' => '帆', '��' => '梵', '��' => '氾', '��' => '汎', '��' => '泛', '��' => '犯', '��' => '範', '�' => '范', '�' => '法', '�' => '琺', '�' => '僻', '�' => '劈', '�' => '壁', '�' => '擘', '�' => '檗', '�' => '璧', '�' => '癖', 'ܡ' => '碧', 'ܢ' => '蘗', 'ܣ' => '闢', 'ܤ' => '霹', 'ܥ' => '便', 'ܦ' => '卞', 'ܧ' => '弁', 'ܨ' => '變', 'ܩ' => '辨', 'ܪ' => '辯', 'ܫ' => '邊', 'ܬ' => '別', 'ܭ' => '瞥', 'ܮ' => '鱉', 'ܯ' => '鼈', 'ܰ' => '丙', 'ܱ' => '倂', 'ܲ' => '兵', 'ܳ' => '屛', 'ܴ' => '幷', 'ܵ' => '昞', 'ܶ' => '昺', 'ܷ' => '柄', 'ܸ' => '棅', 'ܹ' => '炳', 'ܺ' => '甁', 'ܻ' => '病', 'ܼ' => '秉', 'ܽ' => '竝', 'ܾ' => '輧', 'ܿ' => '餠', '�' => '騈', '�' => '保', '��' => '堡', '��' => '報', '��' => '寶', '��' => '普', '��' => '步', '��' => '洑', '��' => '湺', '��' => '潽', '��' => '珤', '��' => '甫', '��' => '菩', '��' => '補', '��' => '褓', '��' => '譜', '��' => '輔', '��' => '伏', '��' => '僕', '��' => '匐', '��' => '卜', '��' => '宓', '��' => '復', '��' => '服', '��' => '福', '��' => '腹', '��' => '茯', '��' => '蔔', '��' => '複', '��' => '覆', '��' => '輹', '��' => '輻', '��' => '馥', '��' => '鰒', '��' => '本', '��' => '乶', '��' => '俸', '��' => '奉', '��' => '封', '��' => '峯', '��' => '峰', '��' => '捧', '��' => '棒', '��' => '烽', '��' => '熢', '��' => '琫', '��' => '縫', '��' => '蓬', '��' => '蜂', '��' => '逢', '��' => '鋒', '��' => '鳳', '��' => '不', '�' => '付', '�' => '俯', '�' => '傅', '�' => '剖', '�' => '副', '�' => '否', '�' => '咐', '�' => '埠', '�' => '夫', '�' => '婦', 'ݡ' => '孚', 'ݢ' => '孵', 'ݣ' => '富', 'ݤ' => '府', 'ݥ' => '復', 'ݦ' => '扶', 'ݧ' => '敷', 'ݨ' => '斧', 'ݩ' => '浮', 'ݪ' => '溥', 'ݫ' => '父', 'ݬ' => '符', 'ݭ' => '簿', 'ݮ' => '缶', 'ݯ' => '腐', 'ݰ' => '腑', 'ݱ' => '膚', 'ݲ' => '艀', 'ݳ' => '芙', 'ݴ' => '莩', 'ݵ' => '訃', 'ݶ' => '負', 'ݷ' => '賦', 'ݸ' => '賻', 'ݹ' => '赴', 'ݺ' => '趺', 'ݻ' => '部', 'ݼ' => '釜', 'ݽ' => '阜', 'ݾ' => '附', 'ݿ' => '駙', '�' => '鳧', '�' => '北', '��' => '分', '��' => '吩', '��' => '噴', '��' => '墳', '��' => '奔', '��' => '奮', '��' => '忿', '��' => '憤', '��' => '扮', '��' => '昐', '��' => '汾', '��' => '焚', '��' => '盆', '��' => '粉', '��' => '糞', '��' => '紛', '��' => '芬', '��' => '賁', '��' => '雰', '��' => '不', '��' => '佛', '��' => '弗', '��' => '彿', '��' => '拂', '��' => '崩', '��' => '朋', '��' => '棚', '��' => '硼', '��' => '繃', '��' => '鵬', '��' => '丕', '��' => '備', '��' => '匕', '��' => '匪', '��' => '卑', '��' => '妃', '��' => '婢', '��' => '庇', '��' => '悲', '��' => '憊', '��' => '扉', '��' => '批', '��' => '斐', '��' => '枇', '��' => '榧', '��' => '比', '��' => '毖', '��' => '毗', '��' => '毘', '��' => '沸', '��' => '泌', '�' => '琵', '�' => '痺', '�' => '砒', '�' => '碑', '�' => '秕', '�' => '秘', '�' => '粃', '�' => '緋', '�' => '翡', '�' => '肥', 'ޡ' => '脾', 'ޢ' => '臂', 'ޣ' => '菲', 'ޤ' => '蜚', 'ޥ' => '裨', 'ަ' => '誹', 'ާ' => '譬', 'ި' => '費', 'ީ' => '鄙', 'ު' => '非', 'ޫ' => '飛', 'ެ' => '鼻', 'ޭ' => '嚬', 'ޮ' => '嬪', 'ޯ' => '彬', 'ް' => '斌', 'ޱ' => '檳', '' => '殯', '' => '浜', '' => '濱', '' => '瀕', '' => '牝', '' => '玭', '' => '貧', '' => '賓', '' => '頻', '' => '憑', '' => '氷', '' => '聘', '' => '騁', '' => '乍', '�' => '事', '�' => '些', '��' => '仕', '��' => '伺', '��' => '似', '��' => '使', '��' => '俟', '��' => '僿', '��' => '史', '��' => '司', '��' => '唆', '��' => '嗣', '��' => '四', '��' => '士', '��' => '奢', '��' => '娑', '��' => '寫', '��' => '寺', '��' => '射', '��' => '巳', '��' => '師', '��' => '徙', '��' => '思', '��' => '捨', '��' => '斜', '��' => '斯', '��' => '柶', '��' => '査', '��' => '梭', '��' => '死', '��' => '沙', '��' => '泗', '��' => '渣', '��' => '瀉', '��' => '獅', '��' => '砂', '��' => '社', '��' => '祀', '��' => '祠', '��' => '私', '��' => '篩', '��' => '紗', '��' => '絲', '��' => '肆', '��' => '舍', '��' => '莎', '��' => '蓑', '��' => '蛇', '��' => '裟', '��' => '詐', '��' => '詞', '��' => '謝', '��' => '賜', '�' => '赦', '�' => '辭', '�' => '邪', '�' => '飼', '�' => '駟', '�' => '麝', '�' => '削', '�' => '數', '�' => '朔', '�' => '索', 'ߡ' => '傘', 'ߢ' => '刪', 'ߣ' => '山', 'ߤ' => '散', 'ߥ' => '汕', 'ߦ' => '珊', 'ߧ' => '産', 'ߨ' => '疝', 'ߩ' => '算', 'ߪ' => '蒜', '߫' => '酸', '߬' => '霰', '߭' => '乷', '߮' => '撒', '߯' => '殺', '߰' => '煞', '߱' => '薩', '߲' => '三', '߳' => '參', 'ߴ' => '杉', 'ߵ' => '森', '߶' => '渗', '߷' => '芟', '߸' => '蔘', '߹' => '衫', 'ߺ' => '揷', '' => '澁', '' => '鈒', '߽' => '颯', '߾' => '上', '߿' => '傷', '�' => '像', '�' => '償', '��' => '商', '��' => '喪', '��' => '嘗', '��' => '孀', '��' => '尙', '��' => '峠', '��' => '常', '��' => '床', '��' => '庠', '��' => '廂', '��' => '想', '��' => '桑', '��' => '橡', '��' => '湘', '��' => '爽', '��' => '牀', '��' => '狀', '��' => '相', '��' => '祥', '��' => '箱', '��' => '翔', '��' => '裳', '��' => '觴', '��' => '詳', '��' => '象', '��' => '賞', '��' => '霜', '��' => '塞', '��' => '璽', '��' => '賽', '��' => '嗇', '��' => '塞', '��' => '穡', '��' => '索', '��' => '色', '��' => '牲', '��' => '生', '��' => '甥', '��' => '省', '��' => '笙', '��' => '墅', '��' => '壻', '��' => '嶼', '��' => '序', '��' => '庶', '��' => '徐', '��' => '恕', '��' => '抒', '��' => '捿', '��' => '敍', '��' => '暑', '�' => '曙', '�' => '書', '�' => '栖', '�' => '棲', '�' => '犀', '�' => '瑞', '�' => '筮', '�' => '絮', '�' => '緖', '�' => '署', '�' => '胥', '�' => '舒', '�' => '薯', '�' => '西', '�' => '誓', '�' => '逝', '�' => '鋤', '�' => '黍', '�' => '鼠', '�' => '夕', '�' => '奭', '�' => '席', '�' => '惜', '�' => '昔', '�' => '晳', '�' => '析', '�' => '汐', '�' => '淅', '�' => '潟', '�' => '石', '�' => '碩', '�' => '蓆', '�' => '釋', '�' => '錫', '�' => '仙', '�' => '僊', '�' => '先', '�' => '善', '�' => '嬋', '�' => '宣', '�' => '扇', '�' => '敾', '�' => '旋', '��' => '渲', '��' => '煽', '��' => '琁', '��' => '瑄', '��' => '璇', '��' => '璿', '��' => '癬', '��' => '禪', '��' => '線', '��' => '繕', '��' => '羨', '��' => '腺', '��' => '膳', '��' => '船', '��' => '蘚', '��' => '蟬', '��' => '詵', '��' => '跣', '��' => '選', '��' => '銑', '��' => '鐥', '��' => '饍', '��' => '鮮', '��' => '卨', '��' => '屑', '��' => '楔', '��' => '泄', '��' => '洩', '��' => '渫', '��' => '舌', '��' => '薛', '��' => '褻', '��' => '設', '��' => '說', '��' => '雪', '��' => '齧', '��' => '剡', '��' => '暹', '��' => '殲', '��' => '纖', '��' => '蟾', '��' => '贍', '��' => '閃', '��' => '陝', '��' => '攝', '��' => '涉', '��' => '燮', '��' => '葉', '��' => '城', '��' => '姓', '��' => '宬', '�' => '性', '�' => '惺', '�' => '成', '�' => '星', '�' => '晟', '�' => '猩', '�' => '珹', '�' => '盛', '�' => '省', '�' => '筬', '�' => '聖', '�' => '聲', '�' => '腥', '�' => '誠', '�' => '醒', '�' => '世', '�' => '勢', '�' => '歲', '�' => '洗', '�' => '稅', '�' => '笹', '�' => '細', '�' => '說', '�' => '貰', '�' => '召', '�' => '嘯', '�' => '塑', '�' => '宵', '�' => '小', '�' => '少', '�' => '巢', '�' => '所', '�' => '掃', '�' => '搔', '�' => '昭', '�' => '梳', '�' => '沼', '�' => '消', '�' => '溯', '�' => '瀟', '�' => '炤', '�' => '燒', '�' => '甦', '��' => '疏', '��' => '疎', '��' => '瘙', '��' => '笑', '��' => '篠', '��' => '簫', '��' => '素', '��' => '紹', '��' => '蔬', '��' => '蕭', '��' => '蘇', '��' => '訴', '��' => '逍', '��' => '遡', '��' => '邵', '��' => '銷', '��' => '韶', '��' => '騷', '��' => '俗', '��' => '屬', '��' => '束', '��' => '涑', '��' => '粟', '��' => '續', '��' => '謖', '��' => '贖', '��' => '速', '��' => '孫', '��' => '巽', '��' => '損', '��' => '蓀', '��' => '遜', '��' => '飡', '��' => '率', '��' => '宋', '��' => '悚', '��' => '松', '��' => '淞', '��' => '訟', '��' => '誦', '��' => '送', '��' => '頌', '��' => '刷', '��' => '殺', '��' => '灑', '��' => '碎', '��' => '鎖', '��' => '衰', '��' => '釗', '��' => '修', '��' => '受', '�' => '嗽', '�' => '囚', '�' => '垂', '�' => '壽', '�' => '嫂', '�' => '守', '�' => '岫', '�' => '峀', '�' => '帥', '�' => '愁', '�' => '戍', '�' => '手', '�' => '授', '�' => '搜', '�' => '收', '�' => '數', '�' => '樹', '�' => '殊', '�' => '水', '�' => '洙', '�' => '漱', '�' => '燧', '�' => '狩', '�' => '獸', '�' => '琇', '�' => '璲', '�' => '瘦', '�' => '睡', '�' => '秀', '�' => '穗', '�' => '竪', '�' => '粹', '�' => '綏', '�' => '綬', '�' => '繡', '�' => '羞', '�' => '脩', '�' => '茱', '�' => '蒐', '�' => '蓚', '�' => '藪', '�' => '袖', '�' => '誰', '��' => '讐', '��' => '輸', '��' => '遂', '��' => '邃', '��' => '酬', '��' => '銖', '��' => '銹', '��' => '隋', '��' => '隧', '��' => '隨', '��' => '雖', '��' => '需', '��' => '須', '��' => '首', '��' => '髓', '��' => '鬚', '��' => '叔', '��' => '塾', '��' => '夙', '��' => '孰', '��' => '宿', '��' => '淑', '��' => '潚', '��' => '熟', '��' => '琡', '��' => '璹', '��' => '肅', '��' => '菽', '��' => '巡', '��' => '徇', '��' => '循', '��' => '恂', '��' => '旬', '��' => '栒', '��' => '楯', '��' => '橓', '��' => '殉', '��' => '洵', '��' => '淳', '��' => '珣', '��' => '盾', '��' => '瞬', '��' => '筍', '��' => '純', '��' => '脣', '��' => '舜', '��' => '荀', '��' => '蓴', '��' => '蕣', '��' => '詢', '��' => '諄', '�' => '醇', '�' => '錞', '�' => '順', '�' => '馴', '�' => '戌', '�' => '術', '�' => '述', '�' => '鉥', '�' => '崇', '�' => '崧', '�' => '嵩', '�' => '瑟', '�' => '膝', '�' => '蝨', '�' => '濕', '�' => '拾', '�' => '習', '�' => '褶', '�' => '襲', '�' => '丞', '�' => '乘', '�' => '僧', '�' => '勝', '�' => '升', '�' => '承', '�' => '昇', '�' => '繩', '�' => '蠅', '�' => '陞', '�' => '侍', '�' => '匙', '�' => '嘶', '�' => '始', '�' => '媤', '�' => '尸', '�' => '屎', '�' => '屍', '�' => '市', '�' => '弑', '�' => '恃', '�' => '施', '�' => '是', '�' => '時', '��' => '枾', '��' => '柴', '��' => '猜', '��' => '矢', '��' => '示', '��' => '翅', '��' => '蒔', '��' => '蓍', '��' => '視', '��' => '試', '��' => '詩', '��' => '諡', '��' => '豕', '��' => '豺', '��' => '埴', '��' => '寔', '��' => '式', '��' => '息', '��' => '拭', '��' => '植', '��' => '殖', '��' => '湜', '��' => '熄', '��' => '篒', '��' => '蝕', '��' => '識', '��' => '軾', '��' => '食', '��' => '飾', '��' => '伸', '��' => '侁', '��' => '信', '��' => '呻', '��' => '娠', '��' => '宸', '��' => '愼', '��' => '新', '��' => '晨', '��' => '燼', '��' => '申', '��' => '神', '��' => '紳', '��' => '腎', '��' => '臣', '��' => '莘', '��' => '薪', '��' => '藎', '��' => '蜃', '��' => '訊', '��' => '身', '��' => '辛', '�' => '辰', '�' => '迅', '�' => '失', '�' => '室', '�' => '實', '�' => '悉', '�' => '審', '�' => '尋', '�' => '心', '�' => '沁', '�' => '沈', '�' => '深', '�' => '瀋', '�' => '甚', '�' => '芯', '�' => '諶', '�' => '什', '�' => '十', '�' => '拾', '�' => '雙', '�' => '氏', '�' => '亞', '�' => '俄', '�' => '兒', '�' => '啞', '�' => '娥', '�' => '峨', '�' => '我', '�' => '牙', '�' => '芽', '�' => '莪', '�' => '蛾', '�' => '衙', '�' => '訝', '�' => '阿', '�' => '雅', '�' => '餓', '�' => '鴉', '�' => '鵝', '�' => '堊', '�' => '岳', '�' => '嶽', '�' => '幄', '��' => '惡', '��' => '愕', '��' => '握', '��' => '樂', '��' => '渥', '��' => '鄂', '��' => '鍔', '��' => '顎', '��' => '鰐', '��' => '齷', '��' => '安', '��' => '岸', '��' => '按', '��' => '晏', '��' => '案', '��' => '眼', '��' => '雁', '��' => '鞍', '��' => '顔', '��' => '鮟', '��' => '斡', '��' => '謁', '��' => '軋', '��' => '閼', '��' => '唵', '��' => '岩', '��' => '巖', '��' => '庵', '��' => '暗', '��' => '癌', '��' => '菴', '��' => '闇', '��' => '壓', '��' => '押', '��' => '狎', '��' => '鴨', '��' => '仰', '��' => '央', '��' => '怏', '��' => '昻', '��' => '殃', '��' => '秧', '��' => '鴦', '��' => '厓', '��' => '哀', '��' => '埃', '��' => '崖', '��' => '愛', '��' => '曖', '��' => '涯', '��' => '碍', '�' => '艾', '�' => '隘', '�' => '靄', '�' => '厄', '�' => '扼', '�' => '掖', '�' => '液', '�' => '縊', '�' => '腋', '�' => '額', '�' => '櫻', '�' => '罌', '�' => '鶯', '�' => '鸚', '�' => '也', '�' => '倻', '�' => '冶', '�' => '夜', '�' => '惹', '�' => '揶', '�' => '椰', '�' => '爺', '�' => '耶', '�' => '若', '�' => '野', '�' => '弱', '�' => '掠', '�' => '略', '�' => '約', '�' => '若', '�' => '葯', '�' => '蒻', '�' => '藥', '�' => '躍', '�' => '亮', '�' => '佯', '�' => '兩', '�' => '凉', '�' => '壤', '�' => '孃', '�' => '恙', '�' => '揚', '�' => '攘', '��' => '敭', '��' => '暘', '��' => '梁', '��' => '楊', '��' => '樣', '��' => '洋', '��' => '瀁', '��' => '煬', '��' => '痒', '��' => '瘍', '��' => '禳', '��' => '穰', '��' => '糧', '��' => '羊', '��' => '良', '��' => '襄', '��' => '諒', '��' => '讓', '��' => '釀', '��' => '陽', '��' => '量', '��' => '養', '��' => '圄', '��' => '御', '��' => '於', '��' => '漁', '��' => '瘀', '��' => '禦', '��' => '語', '��' => '馭', '��' => '魚', '��' => '齬', '��' => '億', '��' => '憶', '��' => '抑', '��' => '檍', '��' => '臆', '��' => '偃', '��' => '堰', '��' => '彦', '��' => '焉', '��' => '言', '��' => '諺', '��' => '孼', '��' => '蘖', '��' => '俺', '��' => '儼', '��' => '嚴', '��' => '奄', '��' => '掩', '��' => '淹', '�' => '嶪', '�' => '業', '�' => '円', '�' => '予', '�' => '余', '�' => '勵', '�' => '呂', '�' => '女', '�' => '如', '�' => '廬', '�' => '旅', '�' => '歟', '�' => '汝', '�' => '濾', '�' => '璵', '�' => '礖', '�' => '礪', '�' => '與', '�' => '艅', '�' => '茹', '�' => '輿', '�' => '轝', '�' => '閭', '�' => '餘', '�' => '驪', '�' => '麗', '�' => '黎', '�' => '亦', '�' => '力', '�' => '域', '�' => '役', '�' => '易', '�' => '曆', '�' => '歷', '�' => '疫', '�' => '繹', '�' => '譯', '�' => '轢', '�' => '逆', '�' => '驛', '�' => '嚥', '�' => '堧', '�' => '姸', '��' => '娟', '��' => '宴', '��' => '年', '��' => '延', '��' => '憐', '��' => '戀', '��' => '捐', '��' => '挻', '��' => '撚', '��' => '椽', '��' => '沇', '��' => '沿', '��' => '涎', '��' => '涓', '��' => '淵', '��' => '演', '��' => '漣', '��' => '烟', '��' => '然', '��' => '煙', '��' => '煉', '��' => '燃', '��' => '燕', '��' => '璉', '��' => '硏', '��' => '硯', '��' => '秊', '��' => '筵', '��' => '緣', '��' => '練', '��' => '縯', '��' => '聯', '��' => '衍', '��' => '軟', '��' => '輦', '��' => '蓮', '��' => '連', '��' => '鉛', '��' => '鍊', '��' => '鳶', '��' => '列', '��' => '劣', '��' => '咽', '��' => '悅', '��' => '涅', '��' => '烈', '��' => '熱', '��' => '裂', '��' => '說', '��' => '閱', '��' => '厭', '�' => '廉', '�' => '念', '�' => '捻', '�' => '染', '�' => '殮', '�' => '炎', '�' => '焰', '�' => '琰', '�' => '艶', '�' => '苒', '�' => '簾', '�' => '閻', '�' => '髥', '�' => '鹽', '�' => '曄', '�' => '獵', '�' => '燁', '�' => '葉', '�' => '令', '�' => '囹', '�' => '塋', '�' => '寧', '�' => '嶺', '�' => '嶸', '�' => '影', '�' => '怜', '�' => '映', '�' => '暎', '�' => '楹', '�' => '榮', '�' => '永', '�' => '泳', '�' => '渶', '�' => '潁', '�' => '濚', '�' => '瀛', '�' => '瀯', '�' => '煐', '�' => '營', '�' => '獰', '�' => '玲', '�' => '瑛', '�' => '瑩', '��' => '瓔', '��' => '盈', '��' => '穎', '��' => '纓', '��' => '羚', '��' => '聆', '��' => '英', '��' => '詠', '��' => '迎', '��' => '鈴', '��' => '鍈', '��' => '零', '��' => '霙', '��' => '靈', '��' => '領', '��' => '乂', '��' => '倪', '��' => '例', '��' => '刈', '��' => '叡', '��' => '曳', '��' => '汭', '��' => '濊', '��' => '猊', '��' => '睿', '��' => '穢', '��' => '芮', '��' => '藝', '��' => '蘂', '��' => '禮', '��' => '裔', '��' => '詣', '��' => '譽', '��' => '豫', '��' => '醴', '��' => '銳', '��' => '隸', '��' => '霓', '��' => '預', '��' => '五', '��' => '伍', '��' => '俉', '��' => '傲', '��' => '午', '��' => '吾', '��' => '吳', '��' => '嗚', '��' => '塢', '��' => '墺', '��' => '奧', '��' => '娛', '�' => '寤', '�' => '悟', '�' => '惡', '�' => '懊', '�' => '敖', '�' => '旿', '�' => '晤', '�' => '梧', '�' => '汚', '�' => '澳', '�' => '烏', '�' => '熬', '�' => '獒', '�' => '筽', '�' => '蜈', '�' => '誤', '�' => '鰲', '�' => '鼇', '�' => '屋', '�' => '沃', '�' => '獄', '�' => '玉', '�' => '鈺', '�' => '溫', '�' => '瑥', '�' => '瘟', '�' => '穩', '�' => '縕', '�' => '蘊', '�' => '兀', '�' => '壅', '�' => '擁', '�' => '瓮', '�' => '甕', '�' => '癰', '�' => '翁', '�' => '邕', '�' => '雍', '�' => '饔', '�' => '渦', '�' => '瓦', '�' => '窩', '�' => '窪', '��' => '臥', '��' => '蛙', '��' => '蝸', '��' => '訛', '��' => '婉', '��' => '完', '��' => '宛', '��' => '梡', '��' => '椀', '��' => '浣', '��' => '玩', '��' => '琓', '��' => '琬', '��' => '碗', '��' => '緩', '��' => '翫', '��' => '脘', '��' => '腕', '��' => '莞', '��' => '豌', '��' => '阮', '��' => '頑', '��' => '曰', '��' => '往', '��' => '旺', '��' => '枉', '��' => '汪', '��' => '王', '��' => '倭', '��' => '娃', '��' => '歪', '��' => '矮', '��' => '外', '��' => '嵬', '��' => '巍', '��' => '猥', '��' => '畏', '��' => '了', '��' => '僚', '��' => '僥', '��' => '凹', '��' => '堯', '��' => '夭', '��' => '妖', '��' => '姚', '��' => '寥', '��' => '寮', '��' => '尿', '��' => '嶢', '��' => '拗', '��' => '搖', '�' => '撓', '�' => '擾', '�' => '料', '�' => '曜', '�' => '樂', '�' => '橈', '�' => '燎', '�' => '燿', '�' => '瑤', '�' => '療', '�' => '窈', '�' => '窯', '�' => '繇', '�' => '繞', '�' => '耀', '�' => '腰', '�' => '蓼', '�' => '蟯', '�' => '要', '�' => '謠', '�' => '遙', '�' => '遼', '�' => '邀', '�' => '饒', '�' => '慾', '�' => '欲', '�' => '浴', '�' => '縟', '�' => '褥', '�' => '辱', '�' => '俑', '�' => '傭', '�' => '冗', '�' => '勇', '�' => '埇', '�' => '墉', '�' => '容', '�' => '庸', '�' => '慂', '�' => '榕', '�' => '涌', '�' => '湧', '�' => '溶', '��' => '熔', '��' => '瑢', '��' => '用', '��' => '甬', '��' => '聳', '��' => '茸', '��' => '蓉', '��' => '踊', '��' => '鎔', '��' => '鏞', '��' => '龍', '��' => '于', '��' => '佑', '��' => '偶', '��' => '優', '��' => '又', '��' => '友', '��' => '右', '��' => '宇', '��' => '寓', '��' => '尤', '��' => '愚', '��' => '憂', '��' => '旴', '��' => '牛', '��' => '玗', '��' => '瑀', '��' => '盂', '��' => '祐', '��' => '禑', '��' => '禹', '��' => '紆', '��' => '羽', '��' => '芋', '��' => '藕', '��' => '虞', '��' => '迂', '��' => '遇', '��' => '郵', '��' => '釪', '��' => '隅', '��' => '雨', '��' => '雩', '��' => '勖', '��' => '彧', '��' => '旭', '��' => '昱', '��' => '栯', '��' => '煜', '��' => '稶', '��' => '郁', '�' => '頊', '�' => '云', '�' => '暈', '�' => '橒', '�' => '殞', '�' => '澐', '�' => '熉', '�' => '耘', '�' => '芸', '�' => '蕓', '�' => '運', '�' => '隕', '�' => '雲', '�' => '韻', '�' => '蔚', '�' => '鬱', '�' => '亐', '�' => '熊', '�' => '雄', '�' => '元', '�' => '原', '�' => '員', '�' => '圓', '�' => '園', '�' => '垣', '�' => '媛', '�' => '嫄', '�' => '寃', '�' => '怨', '�' => '愿', '�' => '援', '�' => '沅', '�' => '洹', '�' => '湲', '�' => '源', '�' => '爰', '�' => '猿', '�' => '瑗', '�' => '苑', '�' => '袁', '�' => '轅', '�' => '遠', '�' => '阮', '��' => '院', '��' => '願', '��' => '鴛', '��' => '月', '��' => '越', '��' => '鉞', '��' => '位', '��' => '偉', '��' => '僞', '��' => '危', '��' => '圍', '��' => '委', '��' => '威', '��' => '尉', '��' => '慰', '��' => '暐', '��' => '渭', '��' => '爲', '��' => '瑋', '��' => '緯', '��' => '胃', '��' => '萎', '��' => '葦', '��' => '蔿', '��' => '蝟', '��' => '衛', '��' => '褘', '��' => '謂', '��' => '違', '��' => '韋', '��' => '魏', '��' => '乳', '��' => '侑', '��' => '儒', '��' => '兪', '��' => '劉', '��' => '唯', '��' => '喩', '��' => '孺', '��' => '宥', '��' => '幼', '��' => '幽', '��' => '庾', '��' => '悠', '��' => '惟', '��' => '愈', '��' => '愉', '��' => '揄', '��' => '攸', '��' => '有', '��' => '杻', '�' => '柔', '�' => '柚', '�' => '柳', '�' => '楡', '�' => '楢', '�' => '油', '�' => '洧', '�' => '流', '�' => '游', '�' => '溜', '�' => '濡', '�' => '猶', '�' => '猷', '�' => '琉', '�' => '瑜', '�' => '由', '�' => '留', '�' => '癒', '�' => '硫', '�' => '紐', '�' => '維', '�' => '臾', '�' => '萸', '�' => '裕', '�' => '誘', '�' => '諛', '�' => '諭', '�' => '踰', '�' => '蹂', '�' => '遊', '�' => '逾', '�' => '遺', '�' => '酉', '�' => '釉', '�' => '鍮', '�' => '類', '�' => '六', '�' => '堉', '�' => '戮', '�' => '毓', '�' => '肉', '�' => '育', '�' => '陸', '��' => '倫', '��' => '允', '��' => '奫', '��' => '尹', '��' => '崙', '��' => '淪', '��' => '潤', '��' => '玧', '��' => '胤', '��' => '贇', '��' => '輪', '��' => '鈗', '��' => '閏', '��' => '律', '��' => '慄', '��' => '栗', '��' => '率', '��' => '聿', '��' => '戎', '��' => '瀜', '��' => '絨', '��' => '融', '��' => '隆', '��' => '垠', '��' => '恩', '��' => '慇', '��' => '殷', '��' => '誾', '��' => '銀', '��' => '隱', '��' => '乙', '��' => '吟', '��' => '淫', '��' => '蔭', '��' => '陰', '��' => '音', '��' => '飮', '��' => '揖', '��' => '泣', '��' => '邑', '��' => '凝', '��' => '應', '��' => '膺', '��' => '鷹', '��' => '依', '��' => '倚', '��' => '儀', '��' => '宜', '��' => '意', '��' => '懿', '��' => '擬', '�' => '椅', '�' => '毅', '�' => '疑', '�' => '矣', '�' => '義', '�' => '艤', '�' => '薏', '�' => '蟻', '�' => '衣', '�' => '誼', '�' => '議', '�' => '醫', '�' => '二', '�' => '以', '�' => '伊', '�' => '利', '�' => '吏', '�' => '夷', '�' => '姨', '�' => '履', '�' => '已', '�' => '弛', '�' => '彛', '�' => '怡', '�' => '易', '�' => '李', '�' => '梨', '�' => '泥', '�' => '爾', '�' => '珥', '�' => '理', '�' => '異', '�' => '痍', '�' => '痢', '�' => '移', '�' => '罹', '�' => '而', '�' => '耳', '�' => '肄', '�' => '苡', '�' => '荑', '�' => '裏', '�' => '裡', '��' => '貽', '��' => '貳', '��' => '邇', '��' => '里', '��' => '離', '��' => '飴', '��' => '餌', '��' => '匿', '��' => '溺', '��' => '瀷', '��' => '益', '��' => '翊', '��' => '翌', '��' => '翼', '��' => '謚', '��' => '人', '��' => '仁', '��' => '刃', '��' => '印', '��' => '吝', '��' => '咽', '��' => '因', '��' => '姻', '��' => '寅', '��' => '引', '��' => '忍', '��' => '湮', '��' => '燐', '��' => '璘', '��' => '絪', '��' => '茵', '��' => '藺', '��' => '蚓', '��' => '認', '��' => '隣', '��' => '靭', '��' => '靷', '��' => '鱗', '��' => '麟', '��' => '一', '��' => '佚', '��' => '佾', '��' => '壹', '��' => '日', '��' => '溢', '��' => '逸', '��' => '鎰', '��' => '馹', '��' => '任', '��' => '壬', '��' => '妊', '�' => '姙', '�' => '恁', '�' => '林', '�' => '淋', '�' => '稔', '�' => '臨', '�' => '荏', '�' => '賃', '�' => '入', '�' => '卄', '�' => '立', '�' => '笠', '�' => '粒', '�' => '仍', '�' => '剩', '�' => '孕', '�' => '芿', '�' => '仔', '�' => '刺', '�' => '咨', '�' => '姉', '�' => '姿', '�' => '子', '�' => '字', '�' => '孜', '�' => '恣', '�' => '慈', '�' => '滋', '�' => '炙', '�' => '煮', '�' => '玆', '�' => '瓷', '�' => '疵', '�' => '磁', '�' => '紫', '�' => '者', '�' => '自', '�' => '茨', '�' => '蔗', '�' => '藉', '�' => '諮', '�' => '資', '�' => '雌', '��' => '作', '��' => '勺', '��' => '嚼', '��' => '斫', '��' => '昨', '��' => '灼', '��' => '炸', '��' => '爵', '��' => '綽', '��' => '芍', '��' => '酌', '��' => '雀', '��' => '鵲', '��' => '孱', '��' => '棧', '��' => '殘', '��' => '潺', '��' => '盞', '��' => '岑', '��' => '暫', '��' => '潛', '��' => '箴', '��' => '簪', '��' => '蠶', '��' => '雜', '��' => '丈', '��' => '仗', '��' => '匠', '��' => '場', '��' => '墻', '��' => '壯', '��' => '奬', '��' => '將', '��' => '帳', '��' => '庄', '��' => '張', '��' => '掌', '��' => '暲', '��' => '杖', '��' => '樟', '��' => '檣', '��' => '欌', '��' => '漿', '��' => '牆', '��' => '狀', '��' => '獐', '��' => '璋', '��' => '章', '��' => '粧', '��' => '腸', '��' => '臟', '�' => '臧', '�' => '莊', '�' => '葬', '�' => '蔣', '�' => '薔', '�' => '藏', '�' => '裝', '�' => '贓', '�' => '醬', '�' => '長', '�' => '障', '�' => '再', '�' => '哉', '�' => '在', '�' => '宰', '�' => '才', '�' => '材', '�' => '栽', '�' => '梓', '�' => '渽', '�' => '滓', '�' => '災', '�' => '縡', '�' => '裁', '�' => '財', '�' => '載', '�' => '齋', '�' => '齎', '�' => '爭', '�' => '箏', '�' => '諍', '�' => '錚', '�' => '佇', '�' => '低', '�' => '儲', '�' => '咀', '�' => '姐', '�' => '底', '�' => '抵', '�' => '杵', '�' => '楮', '�' => '樗', '�' => '沮', '��' => '渚', '��' => '狙', '��' => '猪', '��' => '疽', '��' => '箸', '��' => '紵', '��' => '苧', '��' => '菹', '��' => '著', '��' => '藷', '��' => '詛', '��' => '貯', '��' => '躇', '��' => '這', '��' => '邸', '��' => '雎', '��' => '齟', '��' => '勣', '��' => '吊', '��' => '嫡', '��' => '寂', '��' => '摘', '��' => '敵', '��' => '滴', '��' => '狄', '��' => '炙', '��' => '的', '��' => '積', '��' => '笛', '��' => '籍', '��' => '績', '��' => '翟', '��' => '荻', '��' => '謫', '��' => '賊', '��' => '赤', '��' => '跡', '��' => '蹟', '��' => '迪', '��' => '迹', '��' => '適', '��' => '鏑', '��' => '佃', '��' => '佺', '��' => '傳', '��' => '全', '��' => '典', '��' => '前', '��' => '剪', '��' => '塡', '��' => '塼', '�' => '奠', '�' => '專', '�' => '展', '�' => '廛', '�' => '悛', '�' => '戰', '�' => '栓', '�' => '殿', '�' => '氈', '�' => '澱', '�' => '煎', '�' => '琠', '�' => '田', '�' => '甸', '�' => '畑', '�' => '癲', '�' => '筌', '�' => '箋', '�' => '箭', '�' => '篆', '�' => '纏', '�' => '詮', '�' => '輾', '�' => '轉', '�' => '鈿', '�' => '銓', '�' => '錢', '�' => '鐫', '�' => '電', '�' => '顚', '�' => '顫', '�' => '餞', '�' => '切', '�' => '截', '�' => '折', '�' => '浙', '�' => '癤', '�' => '竊', '�' => '節', '�' => '絶', '�' => '占', '�' => '岾', '�' => '店', '��' => '漸', '��' => '点', '��' => '粘', '��' => '霑', '��' => '鮎', '��' => '點', '��' => '接', '��' => '摺', '��' => '蝶', '��' => '丁', '��' => '井', '��' => '亭', '��' => '停', '��' => '偵', '��' => '呈', '��' => '姃', '��' => '定', '��' => '幀', '��' => '庭', '��' => '廷', '��' => '征', '��' => '情', '��' => '挺', '��' => '政', '��' => '整', '��' => '旌', '��' => '晶', '��' => '晸', '��' => '柾', '��' => '楨', '��' => '檉', '��' => '正', '��' => '汀', '��' => '淀', '��' => '淨', '��' => '渟', '��' => '湞', '��' => '瀞', '��' => '炡', '��' => '玎', '��' => '珽', '��' => '町', '��' => '睛', '��' => '碇', '��' => '禎', '��' => '程', '��' => '穽', '��' => '精', '��' => '綎', '��' => '艇', '��' => '訂', '�' => '諪', '�' => '貞', '�' => '鄭', '�' => '酊', '�' => '釘', '�' => '鉦', '�' => '鋌', '�' => '錠', '�' => '霆', '�' => '靖', '�' => '靜', '�' => '頂', '�' => '鼎', '�' => '制', '�' => '劑', '�' => '啼', '�' => '堤', '�' => '帝', '�' => '弟', '�' => '悌', '�' => '提', '�' => '梯', '�' => '濟', '�' => '祭', '�' => '第', '�' => '臍', '�' => '薺', '�' => '製', '�' => '諸', '�' => '蹄', '�' => '醍', '�' => '除', '�' => '際', '�' => '霽', '�' => '題', '�' => '齊', '�' => '俎', '�' => '兆', '�' => '凋', '�' => '助', '�' => '嘲', '�' => '弔', '�' => '彫', '��' => '措', '��' => '操', '��' => '早', '��' => '晁', '��' => '曺', '��' => '曹', '��' => '朝', '��' => '條', '��' => '棗', '��' => '槽', '��' => '漕', '��' => '潮', '��' => '照', '��' => '燥', '��' => '爪', '��' => '璪', '��' => '眺', '��' => '祖', '��' => '祚', '��' => '租', '��' => '稠', '��' => '窕', '��' => '粗', '��' => '糟', '��' => '組', '��' => '繰', '��' => '肇', '��' => '藻', '��' => '蚤', '��' => '詔', '��' => '調', '��' => '趙', '��' => '躁', '��' => '造', '��' => '遭', '��' => '釣', '��' => '阻', '��' => '雕', '��' => '鳥', '��' => '族', '��' => '簇', '��' => '足', '��' => '鏃', '��' => '存', '��' => '尊', '��' => '卒', '��' => '拙', '��' => '猝', '��' => '倧', '��' => '宗', '��' => '從', '�' => '悰', '�' => '慫', '�' => '棕', '�' => '淙', '�' => '琮', '�' => '種', '�' => '終', '�' => '綜', '�' => '縱', '�' => '腫', '�' => '踪', '�' => '踵', '�' => '鍾', '�' => '鐘', '�' => '佐', '�' => '坐', '�' => '左', '�' => '座', '�' => '挫', '�' => '罪', '�' => '主', '�' => '住', '�' => '侏', '�' => '做', '�' => '姝', '�' => '胄', '�' => '呪', '�' => '周', '�' => '嗾', '�' => '奏', '�' => '宙', '�' => '州', '�' => '廚', '�' => '晝', '�' => '朱', '�' => '柱', '�' => '株', '�' => '注', '�' => '洲', '�' => '湊', '�' => '澍', '�' => '炷', '�' => '珠', '��' => '疇', '��' => '籌', '��' => '紂', '��' => '紬', '��' => '綢', '��' => '舟', '��' => '蛛', '��' => '註', '��' => '誅', '��' => '走', '��' => '躊', '��' => '輳', '��' => '週', '��' => '酎', '��' => '酒', '��' => '鑄', '��' => '駐', '��' => '竹', '��' => '粥', '��' => '俊', '��' => '儁', '��' => '准', '��' => '埈', '��' => '寯', '��' => '峻', '��' => '晙', '��' => '樽', '��' => '浚', '��' => '準', '��' => '濬', '��' => '焌', '��' => '畯', '��' => '竣', '��' => '蠢', '��' => '逡', '��' => '遵', '��' => '雋', '��' => '駿', '��' => '茁', '��' => '中', '��' => '仲', '��' => '衆', '��' => '重', '��' => '卽', '��' => '櫛', '��' => '楫', '��' => '汁', '��' => '葺', '��' => '增', '��' => '憎', '��' => '曾', '�' => '拯', '�' => '烝', '�' => '甑', '�' => '症', '�' => '繒', '�' => '蒸', '�' => '證', '�' => '贈', '�' => '之', '�' => '只', '�' => '咫', '�' => '地', '�' => '址', '�' => '志', '�' => '持', '�' => '指', '�' => '摯', '�' => '支', '�' => '旨', '�' => '智', '�' => '枝', '�' => '枳', '�' => '止', '�' => '池', '�' => '沚', '�' => '漬', '�' => '知', '�' => '砥', '�' => '祉', '�' => '祗', '�' => '紙', '�' => '肢', '�' => '脂', '�' => '至', '�' => '芝', '�' => '芷', '�' => '蜘', '�' => '誌', '�' => '識', '�' => '贄', '�' => '趾', '�' => '遲', '�' => '直', '��' => '稙', '��' => '稷', '��' => '織', '��' => '職', '��' => '唇', '��' => '嗔', '��' => '塵', '��' => '振', '��' => '搢', '��' => '晉', '��' => '晋', '��' => '桭', '��' => '榛', '��' => '殄', '��' => '津', '��' => '溱', '��' => '珍', '��' => '瑨', '��' => '璡', '��' => '畛', '��' => '疹', '��' => '盡', '��' => '眞', '��' => '瞋', '��' => '秦', '��' => '縉', '��' => '縝', '��' => '臻', '��' => '蔯', '��' => '袗', '��' => '診', '��' => '賑', '��' => '軫', '��' => '辰', '��' => '進', '��' => '鎭', '��' => '陣', '��' => '陳', '��' => '震', '��' => '侄', '��' => '叱', '��' => '姪', '��' => '嫉', '��' => '帙', '��' => '桎', '��' => '瓆', '��' => '疾', '��' => '秩', '��' => '窒', '��' => '膣', '��' => '蛭', '�' => '質', '�' => '跌', '�' => '迭', '�' => '斟', '�' => '朕', '�' => '什', '�' => '執', '�' => '潗', '�' => '緝', '�' => '輯', '�' => '鏶', '�' => '集', '�' => '徵', '�' => '懲', '�' => '澄', '�' => '且', '�' => '侘', '�' => '借', '�' => '叉', '�' => '嗟', '�' => '嵯', '�' => '差', '�' => '次', '�' => '此', '�' => '磋', '�' => '箚', '�' => '茶', '�' => '蹉', '�' => '車', '�' => '遮', '�' => '捉', '�' => '搾', '�' => '着', '�' => '窄', '�' => '錯', '�' => '鑿', '�' => '齪', '�' => '撰', '�' => '澯', '�' => '燦', '�' => '璨', '�' => '瓚', '�' => '竄', '��' => '簒', '��' => '纂', '��' => '粲', '��' => '纘', '��' => '讚', '��' => '贊', '��' => '鑽', '��' => '餐', '��' => '饌', '��' => '刹', '��' => '察', '��' => '擦', '��' => '札', '��' => '紮', '��' => '僭', '��' => '參', '��' => '塹', '��' => '慘', '��' => '慙', '��' => '懺', '��' => '斬', '��' => '站', '��' => '讒', '��' => '讖', '��' => '倉', '��' => '倡', '��' => '創', '��' => '唱', '��' => '娼', '��' => '廠', '��' => '彰', '��' => '愴', '��' => '敞', '��' => '昌', '��' => '昶', '��' => '暢', '��' => '槍', '��' => '滄', '��' => '漲', '��' => '猖', '��' => '瘡', '��' => '窓', '��' => '脹', '��' => '艙', '��' => '菖', '��' => '蒼', '��' => '債', '��' => '埰', '��' => '寀', '��' => '寨', '��' => '彩', '�' => '採', '�' => '砦', '�' => '綵', '�' => '菜', '�' => '蔡', '�' => '采', '�' => '釵', '�' => '冊', '�' => '柵', '�' => '策', '�' => '責', '�' => '凄', '�' => '妻', '�' => '悽', '�' => '處', '�' => '倜', '�' => '刺', '�' => '剔', '�' => '尺', '�' => '慽', '�' => '戚', '�' => '拓', '�' => '擲', '�' => '斥', '�' => '滌', '�' => '瘠', '�' => '脊', '�' => '蹠', '�' => '陟', '�' => '隻', '�' => '仟', '�' => '千', '�' => '喘', '�' => '天', '�' => '川', '�' => '擅', '�' => '泉', '�' => '淺', '�' => '玔', '�' => '穿', '�' => '舛', '�' => '薦', '�' => '賤', '��' => '踐', '��' => '遷', '��' => '釧', '��' => '闡', '��' => '阡', '��' => '韆', '��' => '凸', '��' => '哲', '��' => '喆', '��' => '徹', '��' => '撤', '��' => '澈', '��' => '綴', '��' => '輟', '��' => '轍', '��' => '鐵', '��' => '僉', '��' => '尖', '��' => '沾', '��' => '添', '��' => '甛', '��' => '瞻', '��' => '簽', '��' => '籤', '��' => '詹', '��' => '諂', '��' => '堞', '��' => '妾', '��' => '帖', '��' => '捷', '��' => '牒', '��' => '疊', '��' => '睫', '��' => '諜', '��' => '貼', '��' => '輒', '��' => '廳', '��' => '晴', '��' => '淸', '��' => '聽', '��' => '菁', '��' => '請', '��' => '靑', '��' => '鯖', '��' => '切', '��' => '剃', '��' => '替', '��' => '涕', '��' => '滯', '��' => '締', '��' => '諦', '�' => '逮', '�' => '遞', '�' => '體', '�' => '初', '�' => '剿', '�' => '哨', '�' => '憔', '�' => '抄', '�' => '招', '�' => '梢', '��' => '椒', '��' => '楚', '��' => '樵', '��' => '炒', '��' => '焦', '��' => '硝', '��' => '礁', '��' => '礎', '��' => '秒', '��' => '稍', '��' => '肖', '��' => '艸', '��' => '苕', '��' => '草', '��' => '蕉', '��' => '貂', '��' => '超', '��' => '酢', '��' => '醋', '��' => '醮', '��' => '促', '��' => '囑', '��' => '燭', '��' => '矗', '��' => '蜀', '��' => '觸', '��' => '寸', '��' => '忖', '��' => '村', '��' => '邨', '��' => '叢', '��' => '塚', '��' => '寵', '��' => '悤', '��' => '憁', '��' => '摠', '��' => '總', '��' => '聰', '��' => '蔥', '��' => '銃', '��' => '撮', '��' => '催', '��' => '崔', '��' => '最', '��' => '墜', '��' => '抽', '��' => '推', '��' => '椎', '��' => '楸', '��' => '樞', '��' => '湫', '��' => '皺', '��' => '秋', '��' => '芻', '��' => '萩', '��' => '諏', '��' => '趨', '��' => '追', '��' => '鄒', '��' => '酋', '��' => '醜', '��' => '錐', '��' => '錘', '��' => '鎚', '��' => '雛', '��' => '騶', '��' => '鰍', '��' => '丑', '��' => '畜', '��' => '祝', '��' => '竺', '��' => '筑', '��' => '築', '��' => '縮', '��' => '蓄', '��' => '蹙', '��' => '蹴', '��' => '軸', '��' => '逐', '��' => '春', '��' => '椿', '��' => '瑃', '��' => '出', '��' => '朮', '��' => '黜', '��' => '充', '��' => '忠', '��' => '沖', '��' => '蟲', '��' => '衝', '��' => '衷', '��' => '悴', '��' => '膵', '��' => '萃', '��' => '贅', '��' => '取', '��' => '吹', '��' => '嘴', '��' => '娶', '��' => '就', '��' => '炊', '��' => '翠', '��' => '聚', '��' => '脆', '��' => '臭', '��' => '趣', '��' => '醉', '��' => '驟', '��' => '鷲', '��' => '側', '��' => '仄', '��' => '厠', '��' => '惻', '��' => '測', '��' => '層', '��' => '侈', '��' => '値', '��' => '嗤', '��' => '峙', '��' => '幟', '��' => '恥', '��' => '梔', '��' => '治', '��' => '淄', '��' => '熾', '��' => '痔', '��' => '痴', '��' => '癡', '��' => '稚', '��' => '穉', '��' => '緇', '��' => '緻', '��' => '置', '��' => '致', '��' => '蚩', '��' => '輜', '��' => '雉', '��' => '馳', '��' => '齒', '��' => '則', '��' => '勅', '��' => '飭', '��' => '親', '��' => '七', '��' => '柒', '��' => '漆', '��' => '侵', '��' => '寢', '��' => '枕', '��' => '沈', '��' => '浸', '��' => '琛', '��' => '砧', '��' => '針', '��' => '鍼', '��' => '蟄', '��' => '秤', '��' => '稱', '��' => '快', '��' => '他', '��' => '咤', '��' => '唾', '��' => '墮', '��' => '妥', '��' => '惰', '��' => '打', '��' => '拖', '��' => '朶', '��' => '楕', '��' => '舵', '��' => '陀', '��' => '馱', '��' => '駝', '��' => '倬', '��' => '卓', '��' => '啄', '��' => '坼', '��' => '度', '��' => '托', '��' => '拓', '��' => '擢', '��' => '晫', '��' => '柝', '��' => '濁', '��' => '濯', '��' => '琢', '��' => '琸', '��' => '託', '��' => '鐸', '��' => '呑', '��' => '嘆', '��' => '坦', '��' => '彈', '��' => '憚', '��' => '歎', '��' => '灘', '��' => '炭', '��' => '綻', '��' => '誕', '��' => '奪', '��' => '脫', '��' => '探', '��' => '眈', '��' => '耽', '��' => '貪', '��' => '塔', '��' => '搭', '��' => '榻', '��' => '宕', '��' => '帑', '��' => '湯', '��' => '糖', '��' => '蕩', '��' => '兌', '��' => '台', '��' => '太', '��' => '怠', '��' => '態', '��' => '殆', '��' => '汰', '��' => '泰', '��' => '笞', '��' => '胎', '��' => '苔', '��' => '跆', '��' => '邰', '��' => '颱', '��' => '宅', '��' => '擇', '��' => '澤', '��' => '撑', '��' => '攄', '��' => '兎', '��' => '吐', '��' => '土', '��' => '討', '��' => '慟', '��' => '桶', '��' => '洞', '��' => '痛', '��' => '筒', '��' => '統', '��' => '通', '��' => '堆', '��' => '槌', '��' => '腿', '��' => '褪', '��' => '退', '��' => '頹', '��' => '偸', '��' => '套', '��' => '妬', '��' => '投', '��' => '透', '��' => '鬪', '��' => '慝', '��' => '特', '��' => '闖', '��' => '坡', '��' => '婆', '��' => '巴', '��' => '把', '��' => '播', '��' => '擺', '��' => '杷', '��' => '波', '��' => '派', '��' => '爬', '��' => '琶', '��' => '破', '��' => '罷', '��' => '芭', '��' => '跛', '��' => '頗', '��' => '判', '��' => '坂', '��' => '板', '��' => '版', '��' => '瓣', '��' => '販', '��' => '辦', '��' => '鈑', '��' => '阪', '��' => '八', '��' => '叭', '��' => '捌', '��' => '佩', '��' => '唄', '��' => '悖', '��' => '敗', '��' => '沛', '��' => '浿', '��' => '牌', '��' => '狽', '��' => '稗', '��' => '覇', '��' => '貝', '��' => '彭', '��' => '澎', '��' => '烹', '��' => '膨', '��' => '愎', '��' => '便', '��' => '偏', '��' => '扁', '��' => '片', '��' => '篇', '��' => '編', '��' => '翩', '��' => '遍', '��' => '鞭', '��' => '騙', '��' => '貶', '��' => '坪', '��' => '平', '��' => '枰', '��' => '萍', '��' => '評', '��' => '吠', '��' => '嬖', '��' => '幣', '��' => '廢', '��' => '弊', '��' => '斃', '��' => '肺', '��' => '蔽', '��' => '閉', '��' => '陛', '��' => '佈', '��' => '包', '��' => '匍', '��' => '匏', '��' => '咆', '��' => '哺', '��' => '圃', '��' => '布', '��' => '怖', '��' => '抛', '��' => '抱', '��' => '捕', '��' => '暴', '��' => '泡', '��' => '浦', '��' => '疱', '��' => '砲', '��' => '胞', '��' => '脯', '��' => '苞', '��' => '葡', '��' => '蒲', '��' => '袍', '��' => '褒', '��' => '逋', '��' => '鋪', '��' => '飽', '��' => '鮑', '��' => '幅', '��' => '暴', '��' => '曝', '��' => '瀑', '��' => '爆', '��' => '輻', '��' => '俵', '��' => '剽', '��' => '彪', '��' => '慓', '��' => '杓', '��' => '標', '��' => '漂', '��' => '瓢', '��' => '票', '��' => '表', '��' => '豹', '��' => '飇', '��' => '飄', '��' => '驃', '��' => '品', '��' => '稟', '��' => '楓', '��' => '諷', '��' => '豊', '��' => '風', '��' => '馮', '��' => '彼', '��' => '披', '��' => '疲', '��' => '皮', '��' => '被', '��' => '避', '��' => '陂', '��' => '匹', '��' => '弼', '��' => '必', '��' => '泌', '��' => '珌', '��' => '畢', '��' => '疋', '��' => '筆', '��' => '苾', '��' => '馝', '��' => '乏', '��' => '逼', '��' => '下', '��' => '何', '��' => '厦', '��' => '夏', '��' => '廈', '��' => '昰', '��' => '河', '��' => '瑕', '��' => '荷', '��' => '蝦', '��' => '賀', '��' => '遐', '��' => '霞', '��' => '鰕', '��' => '壑', '��' => '學', '��' => '虐', '��' => '謔', '��' => '鶴', '��' => '寒', '��' => '恨', '��' => '悍', '��' => '旱', '��' => '汗', '��' => '漢', '��' => '澣', '��' => '瀚', '��' => '罕', '��' => '翰', '��' => '閑', '��' => '閒', '��' => '限', '��' => '韓', '��' => '割', '��' => '轄', '��' => '函', '��' => '含', '��' => '咸', '��' => '啣', '��' => '喊', '��' => '檻', '��' => '涵', '��' => '緘', '��' => '艦', '��' => '銜', '��' => '陷', '��' => '鹹', '��' => '合', '��' => '哈', '��' => '盒', '��' => '蛤', '��' => '閤', '��' => '闔', '��' => '陜', '��' => '亢', '��' => '伉', '��' => '姮', '��' => '嫦', '��' => '巷', '��' => '恒', '��' => '抗', '��' => '杭', '��' => '桁', '��' => '沆', '��' => '港', '��' => '缸', '��' => '肛', '��' => '航', '��' => '行', '��' => '降', '��' => '項', '��' => '亥', '��' => '偕', '��' => '咳', '��' => '垓', '��' => '奚', '��' => '孩', '��' => '害', '��' => '懈', '��' => '楷', '��' => '海', '��' => '瀣', '��' => '蟹', '��' => '解', '��' => '該', '��' => '諧', '��' => '邂', '��' => '駭', '��' => '骸', '��' => '劾', '��' => '核', '��' => '倖', '��' => '幸', '��' => '杏', '��' => '荇', '��' => '行', '��' => '享', '��' => '向', '��' => '嚮', '��' => '珦', '��' => '鄕', '��' => '響', '��' => '餉', '��' => '饗', '��' => '香', '��' => '噓', '��' => '墟', '��' => '虛', '��' => '許', '��' => '憲', '��' => '櫶', '��' => '獻', '��' => '軒', '��' => '歇', '��' => '險', '��' => '驗', '��' => '奕', '��' => '爀', '��' => '赫', '��' => '革', '��' => '俔', '��' => '峴', '��' => '弦', '��' => '懸', '��' => '晛', '��' => '泫', '��' => '炫', '��' => '玄', '��' => '玹', '��' => '現', '��' => '眩', '��' => '睍', '��' => '絃', '��' => '絢', '��' => '縣', '��' => '舷', '��' => '衒', '��' => '見', '��' => '賢', '��' => '鉉', '��' => '顯', '��' => '孑', '��' => '穴', '��' => '血', '��' => '頁', '��' => '嫌', '��' => '俠', '��' => '協', '��' => '夾', '��' => '峽', '��' => '挾', '��' => '浹', '��' => '狹', '��' => '脅', '��' => '脇', '��' => '莢', '��' => '鋏', '��' => '頰', '��' => '亨', '��' => '兄', '��' => '刑', '��' => '型', '��' => '形', '��' => '泂', '��' => '滎', '��' => '瀅', '��' => '灐', '��' => '炯', '��' => '熒', '��' => '珩', '��' => '瑩', '��' => '荊', '��' => '螢', '��' => '衡', '��' => '逈', '��' => '邢', '��' => '鎣', '��' => '馨', '��' => '兮', '��' => '彗', '��' => '惠', '��' => '慧', '��' => '暳', '��' => '蕙', '��' => '蹊', '��' => '醯', '��' => '鞋', '��' => '乎', '��' => '互', '��' => '呼', '��' => '壕', '��' => '壺', '��' => '好', '��' => '岵', '��' => '弧', '��' => '戶', '��' => '扈', '��' => '昊', '��' => '晧', '��' => '毫', '��' => '浩', '��' => '淏', '��' => '湖', '��' => '滸', '��' => '澔', '��' => '濠', '��' => '濩', '��' => '灝', '��' => '狐', '��' => '琥', '��' => '瑚', '��' => '瓠', '��' => '皓', '��' => '祜', '��' => '糊', '��' => '縞', '��' => '胡', '��' => '芦', '��' => '葫', '��' => '蒿', '��' => '虎', '��' => '號', '��' => '蝴', '��' => '護', '��' => '豪', '��' => '鎬', '��' => '頀', '��' => '顥', '��' => '惑', '��' => '或', '��' => '酷', '��' => '婚', '��' => '昏', '��' => '混', '��' => '渾', '��' => '琿', '��' => '魂', '��' => '忽', '��' => '惚', '��' => '笏', '��' => '哄', '��' => '弘', '��' => '汞', '��' => '泓', '��' => '洪', '��' => '烘', '��' => '紅', '��' => '虹', '��' => '訌', '��' => '鴻', '��' => '化', '��' => '和', '��' => '嬅', '��' => '樺', '��' => '火', '��' => '畵', '��' => '禍', '��' => '禾', '��' => '花', '��' => '華', '��' => '話', '��' => '譁', '��' => '貨', '��' => '靴', '��' => '廓', '��' => '擴', '��' => '攫', '��' => '確', '��' => '碻', '��' => '穫', '��' => '丸', '��' => '喚', '��' => '奐', '��' => '宦', '��' => '幻', '��' => '患', '��' => '換', '��' => '歡', '��' => '晥', '��' => '桓', '��' => '渙', '��' => '煥', '��' => '環', '��' => '紈', '��' => '還', '��' => '驩', '��' => '鰥', '��' => '活', '��' => '滑', '��' => '猾', '��' => '豁', '��' => '闊', '��' => '凰', '��' => '幌', '��' => '徨', '��' => '恍', '��' => '惶', '��' => '愰', '��' => '慌', '��' => '晃', '��' => '晄', '��' => '榥', '��' => '況', '��' => '湟', '��' => '滉', '��' => '潢', '��' => '煌', '��' => '璜', '��' => '皇', '��' => '篁', '��' => '簧', '��' => '荒', '��' => '蝗', '��' => '遑', '��' => '隍', '��' => '黃', '��' => '匯', '��' => '回', '��' => '廻', '��' => '徊', '��' => '恢', '��' => '悔', '��' => '懷', '��' => '晦', '��' => '會', '��' => '檜', '��' => '淮', '��' => '澮', '��' => '灰', '��' => '獪', '��' => '繪', '��' => '膾', '��' => '茴', '��' => '蛔', '��' => '誨', '��' => '賄', '��' => '劃', '��' => '獲', '��' => '宖', '��' => '橫', '��' => '鐄', '��' => '哮', '��' => '嚆', '��' => '孝', '��' => '效', '��' => '斅', '��' => '曉', '��' => '梟', '��' => '涍', '��' => '淆', '��' => '爻', '��' => '肴', '��' => '酵', '��' => '驍', '��' => '侯', '��' => '候', '��' => '厚', '��' => '后', '��' => '吼', '��' => '喉', '��' => '嗅', '��' => '帿', '��' => '後', '��' => '朽', '��' => '煦', '��' => '珝', '��' => '逅', '��' => '勛', '��' => '勳', '��' => '塤', '��' => '壎', '��' => '焄', '��' => '熏', '��' => '燻', '��' => '薰', '��' => '訓', '��' => '暈', '��' => '薨', '��' => '喧', '��' => '暄', '��' => '煊', '��' => '萱', '��' => '卉', '��' => '喙', '��' => '毁', '��' => '彙', '��' => '徽', '��' => '揮', '��' => '暉', '��' => '煇', '��' => '諱', '��' => '輝', '��' => '麾', '��' => '休', '��' => '携', '��' => '烋', '��' => '畦', '��' => '虧', '��' => '恤', '��' => '譎', '��' => '鷸', '��' => '兇', '��' => '凶', '��' => '匈', '��' => '洶', '��' => '胸', '��' => '黑', '��' => '昕', '��' => '欣', '��' => '炘', '��' => '痕', '��' => '吃', '��' => '屹', '��' => '紇', '��' => '訖', '��' => '欠', '��' => '欽', '��' => '歆', '��' => '吸', '��' => '恰', '��' => '洽', '��' => '翕', '��' => '興', '��' => '僖', '��' => '凞', '��' => '喜', '��' => '噫', '��' => '囍', '��' => '姬', '��' => '嬉', '��' => '希', '��' => '憙', '��' => '憘', '��' => '戱', '��' => '晞', '��' => '曦', '��' => '熙', '��' => '熹', '��' => '熺', '��' => '犧', '��' => '禧', '��' => '稀', '��' => '羲', '��' => '詰', ); $result =& $data; unset($data); return $result; PK2A#]g�CG��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp932.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '。', '�' => '「', '�' => '」', '�' => '、', '�' => '・', '�' => 'ヲ', '�' => 'ァ', '�' => 'ィ', '�' => 'ゥ', '�' => 'ェ', '�' => 'ォ', '�' => 'ャ', '�' => 'ュ', '�' => 'ョ', '�' => 'ッ', '�' => 'ー', '�' => 'ア', '�' => 'イ', '�' => 'ウ', '�' => 'エ', '�' => 'オ', '�' => 'カ', '�' => 'キ', '�' => 'ク', '�' => 'ケ', '�' => 'コ', '�' => 'サ', '�' => 'シ', '�' => 'ス', '�' => 'セ', '�' => 'ソ', '�' => 'タ', '�' => 'チ', '�' => 'ツ', '�' => 'テ', '�' => 'ト', '�' => 'ナ', '�' => 'ニ', '�' => 'ヌ', '�' => 'ネ', '�' => 'ノ', '�' => 'ハ', '�' => 'ヒ', '�' => 'フ', '�' => 'ヘ', '�' => 'ホ', '�' => 'マ', '�' => 'ミ', '�' => 'ム', '�' => 'メ', '�' => 'モ', '�' => 'ヤ', '�' => 'ユ', '�' => 'ヨ', '�' => 'ラ', '�' => 'リ', '�' => 'ル', '�' => 'レ', '�' => 'ロ', '�' => 'ワ', '�' => 'ン', '�' => '゙', '�' => '゚', '�@' => ' ', '�A' => '、', '�B' => '。', '�C' => ',', '�D' => '.', '�E' => '・', '�F' => ':', '�G' => ';', '�H' => '?', '�I' => '!', '�J' => '゛', '�K' => '゜', '�L' => '´', '�M' => '`', '�N' => '¨', '�O' => '^', '�P' => ' ̄', '�Q' => '_', '�R' => 'ヽ', '�S' => 'ヾ', '�T' => 'ゝ', '�U' => 'ゞ', '�V' => '〃', '�W' => '仝', '�X' => '々', '�Y' => '〆', '�Z' => '〇', '�[' => 'ー', '�\\' => '―', '�]' => '‐', '�^' => '/', '�_' => '\', '�`' => '~', '�a' => '∥', '�b' => '|', '�c' => '…', '�d' => '‥', '�e' => '‘', '�f' => '’', '�g' => '“', '�h' => '”', '�i' => '(', '�j' => ')', '�k' => '〔', '�l' => '〕', '�m' => '[', '�n' => ']', '�o' => '{', '�p' => '}', '�q' => '〈', '�r' => '〉', '�s' => '《', '�t' => '》', '�u' => '「', '�v' => '」', '�w' => '『', '�x' => '』', '�y' => '【', '�z' => '】', '�{' => '+', '�|' => '-', '�}' => '±', '�~' => '×', '��' => '÷', '��' => '=', '��' => '≠', '��' => '<', '��' => '>', '��' => '≦', '��' => '≧', '��' => '∞', '��' => '∴', '��' => '♂', '��' => '♀', '��' => '°', '��' => '′', '��' => '″', '��' => '℃', '��' => '¥', '��' => '$', '��' => '¢', '��' => '£', '��' => '%', '��' => '#', '��' => '&', '��' => '*', '��' => '@', '��' => '§', '��' => '☆', '��' => '★', '��' => '○', '��' => '●', '��' => '◎', '��' => '◇', '��' => '◆', '��' => '□', '��' => '■', '��' => '△', '��' => '▲', '��' => '▽', '��' => '▼', '��' => '※', '��' => '〒', '��' => '→', '��' => '←', '��' => '↑', '��' => '↓', '��' => '〓', '��' => '∈', '��' => '∋', '��' => '⊆', '��' => '⊇', '��' => '⊂', '��' => '⊃', '��' => '∪', '��' => '∩', '��' => '∧', '��' => '∨', '��' => '¬', '��' => '⇒', '��' => '⇔', '��' => '∀', '��' => '∃', '��' => '∠', '��' => '⊥', '��' => '⌒', '��' => '∂', '��' => '∇', '��' => '≡', '��' => '≒', '��' => '≪', '��' => '≫', '��' => '√', '��' => '∽', '��' => '∝', '��' => '∵', '��' => '∫', '��' => '∬', '��' => 'Å', '��' => '‰', '��' => '♯', '��' => '♭', '��' => '♪', '��' => '†', '��' => '‡', '��' => '¶', '��' => '◯', '�O' => '0', '�P' => '1', '�Q' => '2', '�R' => '3', '�S' => '4', '�T' => '5', '�U' => '6', '�V' => '7', '�W' => '8', '�X' => '9', '�`' => 'A', '�a' => 'B', '�b' => 'C', '�c' => 'D', '�d' => 'E', '�e' => 'F', '�f' => 'G', '�g' => 'H', '�h' => 'I', '�i' => 'J', '�j' => 'K', '�k' => 'L', '�l' => 'M', '�m' => 'N', '�n' => 'O', '�o' => 'P', '�p' => 'Q', '�q' => 'R', '�r' => 'S', '�s' => 'T', '�t' => 'U', '�u' => 'V', '�v' => 'W', '�w' => 'X', '�x' => 'Y', '�y' => 'Z', '��' => 'a', '��' => 'b', '��' => 'c', '��' => 'd', '��' => 'e', '��' => 'f', '��' => 'g', '��' => 'h', '��' => 'i', '��' => 'j', '��' => 'k', '��' => 'l', '��' => 'm', '��' => 'n', '��' => 'o', '��' => 'p', '��' => 'q', '��' => 'r', '��' => 's', '��' => 't', '��' => 'u', '��' => 'v', '��' => 'w', '��' => 'x', '��' => 'y', '��' => 'z', '��' => 'ぁ', '��' => 'あ', '��' => 'ぃ', '��' => 'い', '��' => 'ぅ', '��' => 'う', '��' => 'ぇ', '��' => 'え', '��' => 'ぉ', '��' => 'お', '��' => 'か', '��' => 'が', '��' => 'き', '��' => 'ぎ', '��' => 'く', '��' => 'ぐ', '��' => 'け', '��' => 'げ', '��' => 'こ', '��' => 'ご', '��' => 'さ', '��' => 'ざ', '��' => 'し', '��' => 'じ', '��' => 'す', '��' => 'ず', '��' => 'せ', '��' => 'ぜ', '��' => 'そ', '��' => 'ぞ', '��' => 'た', '��' => 'だ', '��' => 'ち', '��' => 'ぢ', '��' => 'っ', '��' => 'つ', '��' => 'づ', '��' => 'て', '��' => 'で', '��' => 'と', '��' => 'ど', '��' => 'な', '��' => 'に', '��' => 'ぬ', '��' => 'ね', '��' => 'の', '��' => 'は', '��' => 'ば', '��' => 'ぱ', '��' => 'ひ', '��' => 'び', '��' => 'ぴ', '��' => 'ふ', '��' => 'ぶ', '��' => 'ぷ', '��' => 'へ', '��' => 'べ', '��' => 'ぺ', '��' => 'ほ', '��' => 'ぼ', '��' => 'ぽ', '��' => 'ま', '��' => 'み', '��' => 'む', '��' => 'め', '��' => 'も', '��' => 'ゃ', '��' => 'や', '��' => 'ゅ', '��' => 'ゆ', '��' => 'ょ', '��' => 'よ', '��' => 'ら', '��' => 'り', '��' => 'る', '��' => 'れ', '��' => 'ろ', '��' => 'ゎ', '��' => 'わ', '��' => 'ゐ', '��' => 'ゑ', '��' => 'を', '��' => 'ん', '�@' => 'ァ', '�A' => 'ア', '�B' => 'ィ', '�C' => 'イ', '�D' => 'ゥ', '�E' => 'ウ', '�F' => 'ェ', '�G' => 'エ', '�H' => 'ォ', '�I' => 'オ', '�J' => 'カ', '�K' => 'ガ', '�L' => 'キ', '�M' => 'ギ', '�N' => 'ク', '�O' => 'グ', '�P' => 'ケ', '�Q' => 'ゲ', '�R' => 'コ', '�S' => 'ゴ', '�T' => 'サ', '�U' => 'ザ', '�V' => 'シ', '�W' => 'ジ', '�X' => 'ス', '�Y' => 'ズ', '�Z' => 'セ', '�[' => 'ゼ', '�\\' => 'ソ', '�]' => 'ゾ', '�^' => 'タ', '�_' => 'ダ', '�`' => 'チ', '�a' => 'ヂ', '�b' => 'ッ', '�c' => 'ツ', '�d' => 'ヅ', '�e' => 'テ', '�f' => 'デ', '�g' => 'ト', '�h' => 'ド', '�i' => 'ナ', '�j' => 'ニ', '�k' => 'ヌ', '�l' => 'ネ', '�m' => 'ノ', '�n' => 'ハ', '�o' => 'バ', '�p' => 'パ', '�q' => 'ヒ', '�r' => 'ビ', '�s' => 'ピ', '�t' => 'フ', '�u' => 'ブ', '�v' => 'プ', '�w' => 'ヘ', '�x' => 'ベ', '�y' => 'ペ', '�z' => 'ホ', '�{' => 'ボ', '�|' => 'ポ', '�}' => 'マ', '�~' => 'ミ', '��' => 'ム', '��' => 'メ', '��' => 'モ', '��' => 'ャ', '��' => 'ヤ', '��' => 'ュ', '��' => 'ユ', '��' => 'ョ', '��' => 'ヨ', '��' => 'ラ', '��' => 'リ', '��' => 'ル', '��' => 'レ', '��' => 'ロ', '��' => 'ヮ', '��' => 'ワ', '��' => 'ヰ', '��' => 'ヱ', '��' => 'ヲ', '��' => 'ン', '��' => 'ヴ', '��' => 'ヵ', '��' => 'ヶ', '��' => 'Α', '��' => 'Β', '��' => 'Γ', '��' => 'Δ', '��' => 'Ε', '��' => 'Ζ', '��' => 'Η', '��' => 'Θ', '��' => 'Ι', '��' => 'Κ', '��' => 'Λ', '��' => 'Μ', '��' => 'Ν', '��' => 'Ξ', '��' => 'Ο', '��' => 'Π', '��' => 'Ρ', '��' => 'Σ', '��' => 'Τ', '��' => 'Υ', '��' => 'Φ', '��' => 'Χ', '��' => 'Ψ', '��' => 'Ω', '��' => 'α', '��' => 'β', '��' => 'γ', '��' => 'δ', '��' => 'ε', '��' => 'ζ', '��' => 'η', '��' => 'θ', '��' => 'ι', '��' => 'κ', '��' => 'λ', '��' => 'μ', '��' => 'ν', '��' => 'ξ', '��' => 'ο', '��' => 'π', '��' => 'ρ', '��' => 'σ', '��' => 'τ', '��' => 'υ', '��' => 'φ', '��' => 'χ', '��' => 'ψ', '��' => 'ω', '�@' => 'А', '�A' => 'Б', '�B' => 'В', '�C' => 'Г', '�D' => 'Д', '�E' => 'Е', '�F' => 'Ё', '�G' => 'Ж', '�H' => 'З', '�I' => 'И', '�J' => 'Й', '�K' => 'К', '�L' => 'Л', '�M' => 'М', '�N' => 'Н', '�O' => 'О', '�P' => 'П', '�Q' => 'Р', '�R' => 'С', '�S' => 'Т', '�T' => 'У', '�U' => 'Ф', '�V' => 'Х', '�W' => 'Ц', '�X' => 'Ч', '�Y' => 'Ш', '�Z' => 'Щ', '�[' => 'Ъ', '�\\' => 'Ы', '�]' => 'Ь', '�^' => 'Э', '�_' => 'Ю', '�`' => 'Я', '�p' => 'а', '�q' => 'б', '�r' => 'в', '�s' => 'г', '�t' => 'д', '�u' => 'е', '�v' => 'ё', '�w' => 'ж', '�x' => 'з', '�y' => 'и', '�z' => 'й', '�{' => 'к', '�|' => 'л', '�}' => 'м', '�~' => 'н', '��' => 'о', '��' => 'п', '��' => 'р', '��' => 'с', '��' => 'т', '��' => 'у', '��' => 'ф', '��' => 'х', '��' => 'ц', '��' => 'ч', '��' => 'ш', '��' => 'щ', '��' => 'ъ', '��' => 'ы', '��' => 'ь', '��' => 'э', '��' => 'ю', '��' => 'я', '��' => '─', '��' => '│', '��' => '┌', '��' => '┐', '��' => '┘', '��' => '└', '��' => '├', '��' => '┬', '��' => '┤', '��' => '┴', '��' => '┼', '��' => '━', '��' => '┃', '��' => '┏', '��' => '┓', '��' => '┛', '��' => '┗', '��' => '┣', '��' => '┳', '��' => '┫', '��' => '┻', '��' => '╋', '��' => '┠', '��' => '┯', '��' => '┨', '��' => '┷', '��' => '┿', '��' => '┝', '��' => '┰', '��' => '┥', '��' => '┸', '��' => '╂', '�@' => '①', '�A' => '②', '�B' => '③', '�C' => '④', '�D' => '⑤', '�E' => '⑥', '�F' => '⑦', '�G' => '⑧', '�H' => '⑨', '�I' => '⑩', '�J' => '⑪', '�K' => '⑫', '�L' => '⑬', '�M' => '⑭', '�N' => '⑮', '�O' => '⑯', '�P' => '⑰', '�Q' => '⑱', '�R' => '⑲', '�S' => '⑳', '�T' => 'Ⅰ', '�U' => 'Ⅱ', '�V' => 'Ⅲ', '�W' => 'Ⅳ', '�X' => 'Ⅴ', '�Y' => 'Ⅵ', '�Z' => 'Ⅶ', '�[' => 'Ⅷ', '�\\' => 'Ⅸ', '�]' => 'Ⅹ', '�_' => '㍉', '�`' => '㌔', '�a' => '㌢', '�b' => '㍍', '�c' => '㌘', '�d' => '㌧', '�e' => '㌃', '�f' => '㌶', '�g' => '㍑', '�h' => '㍗', '�i' => '㌍', '�j' => '㌦', '�k' => '㌣', '�l' => '㌫', '�m' => '㍊', '�n' => '㌻', '�o' => '㎜', '�p' => '㎝', '�q' => '㎞', '�r' => '㎎', '�s' => '㎏', '�t' => '㏄', '�u' => '㎡', '�~' => '㍻', '��' => '〝', '��' => '〟', '��' => '№', '��' => '㏍', '��' => '℡', '��' => '㊤', '��' => '㊥', '��' => '㊦', '��' => '㊧', '��' => '㊨', '��' => '㈱', '��' => '㈲', '��' => '㈹', '��' => '㍾', '��' => '㍽', '��' => '㍼', '��' => '≒', '��' => '≡', '��' => '∫', '��' => '∮', '��' => '∑', '��' => '√', '��' => '⊥', '��' => '∠', '��' => '∟', '��' => '⊿', '��' => '∵', '��' => '∩', '��' => '∪', '��' => '亜', '��' => '唖', '��' => '娃', '��' => '阿', '��' => '哀', '��' => '愛', '��' => '挨', '��' => '姶', '��' => '逢', '��' => '葵', '��' => '茜', '��' => '穐', '��' => '悪', '��' => '握', '��' => '渥', '��' => '旭', '��' => '葦', '��' => '芦', '��' => '鯵', '��' => '梓', '��' => '圧', '��' => '斡', '��' => '扱', '��' => '宛', '��' => '姐', '��' => '虻', '��' => '飴', '��' => '絢', '��' => '綾', '��' => '鮎', '��' => '或', '��' => '粟', '��' => '袷', '��' => '安', '��' => '庵', '��' => '按', '��' => '暗', '��' => '案', '��' => '闇', '��' => '鞍', '��' => '杏', '��' => '以', '��' => '伊', '��' => '位', '��' => '依', '��' => '偉', '��' => '囲', '��' => '夷', '��' => '委', '��' => '威', '��' => '尉', '��' => '惟', '��' => '意', '��' => '慰', '��' => '易', '��' => '椅', '��' => '為', '��' => '畏', '��' => '異', '��' => '移', '��' => '維', '��' => '緯', '��' => '胃', '��' => '萎', '��' => '衣', '��' => '謂', '��' => '違', '��' => '遺', '��' => '医', '��' => '井', '��' => '亥', '��' => '域', '��' => '育', '��' => '郁', '��' => '磯', '��' => '一', '��' => '壱', '��' => '溢', '��' => '逸', '��' => '稲', '��' => '茨', '��' => '芋', '��' => '鰯', '��' => '允', '��' => '印', '��' => '咽', '��' => '員', '��' => '因', '��' => '姻', '��' => '引', '��' => '飲', '��' => '淫', '��' => '胤', '��' => '蔭', '�@' => '院', '�A' => '陰', '�B' => '隠', '�C' => '韻', '�D' => '吋', '�E' => '右', '�F' => '宇', '�G' => '烏', '�H' => '羽', '�I' => '迂', '�J' => '雨', '�K' => '卯', '�L' => '鵜', '�M' => '窺', '�N' => '丑', '�O' => '碓', '�P' => '臼', '�Q' => '渦', '�R' => '嘘', '�S' => '唄', '�T' => '欝', '�U' => '蔚', '�V' => '鰻', '�W' => '姥', '�X' => '厩', '�Y' => '浦', '�Z' => '瓜', '�[' => '閏', '�\\' => '噂', '�]' => '云', '�^' => '運', '�_' => '雲', '�`' => '荏', '�a' => '餌', '�b' => '叡', '�c' => '営', '�d' => '嬰', '�e' => '影', '�f' => '映', '�g' => '曳', '�h' => '栄', '�i' => '永', '�j' => '泳', '�k' => '洩', '�l' => '瑛', '�m' => '盈', '�n' => '穎', '�o' => '頴', '�p' => '英', '�q' => '衛', '�r' => '詠', '�s' => '鋭', '�t' => '液', '�u' => '疫', '�v' => '益', '�w' => '駅', '�x' => '悦', '�y' => '謁', '�z' => '越', '�{' => '閲', '�|' => '榎', '�}' => '厭', '�~' => '円', '��' => '園', '��' => '堰', '��' => '奄', '��' => '宴', '��' => '延', '��' => '怨', '��' => '掩', '��' => '援', '��' => '沿', '��' => '演', '��' => '炎', '��' => '焔', '��' => '煙', '��' => '燕', '��' => '猿', '��' => '縁', '��' => '艶', '��' => '苑', '��' => '薗', '��' => '遠', '��' => '鉛', '��' => '鴛', '��' => '塩', '��' => '於', '��' => '汚', '��' => '甥', '��' => '凹', '��' => '央', '��' => '奥', '��' => '往', '��' => '応', '��' => '押', '��' => '旺', '��' => '横', '��' => '欧', '��' => '殴', '��' => '王', '��' => '翁', '��' => '襖', '��' => '鴬', '��' => '鴎', '��' => '黄', '��' => '岡', '��' => '沖', '��' => '荻', '��' => '億', '��' => '屋', '��' => '憶', '��' => '臆', '��' => '桶', '��' => '牡', '��' => '乙', '��' => '俺', '��' => '卸', '��' => '恩', '��' => '温', '��' => '穏', '��' => '音', '��' => '下', '��' => '化', '��' => '仮', '��' => '何', '��' => '伽', '��' => '価', '��' => '佳', '��' => '加', '��' => '可', '��' => '嘉', '��' => '夏', '��' => '嫁', '��' => '家', '��' => '寡', '��' => '科', '��' => '暇', '��' => '果', '��' => '架', '��' => '歌', '��' => '河', '��' => '火', '��' => '珂', '��' => '禍', '��' => '禾', '��' => '稼', '��' => '箇', '��' => '花', '��' => '苛', '��' => '茄', '��' => '荷', '��' => '華', '��' => '菓', '��' => '蝦', '��' => '課', '��' => '嘩', '��' => '貨', '��' => '迦', '��' => '過', '��' => '霞', '��' => '蚊', '��' => '俄', '��' => '峨', '��' => '我', '��' => '牙', '��' => '画', '��' => '臥', '��' => '芽', '��' => '蛾', '��' => '賀', '��' => '雅', '��' => '餓', '��' => '駕', '��' => '介', '��' => '会', '��' => '解', '��' => '回', '��' => '塊', '��' => '壊', '��' => '廻', '��' => '快', '��' => '怪', '��' => '悔', '��' => '恢', '��' => '懐', '��' => '戒', '��' => '拐', '��' => '改', '�@' => '魁', '�A' => '晦', '�B' => '械', '�C' => '海', '�D' => '灰', '�E' => '界', '�F' => '皆', '�G' => '絵', '�H' => '芥', '�I' => '蟹', '�J' => '開', '�K' => '階', '�L' => '貝', '�M' => '凱', '�N' => '劾', '�O' => '外', '�P' => '咳', '�Q' => '害', '�R' => '崖', '�S' => '慨', '�T' => '概', '�U' => '涯', '�V' => '碍', '�W' => '蓋', '�X' => '街', '�Y' => '該', '�Z' => '鎧', '�[' => '骸', '�\\' => '浬', '�]' => '馨', '�^' => '蛙', '�_' => '垣', '�`' => '柿', '�a' => '蛎', '�b' => '鈎', '�c' => '劃', '�d' => '嚇', '�e' => '各', '�f' => '廓', '�g' => '拡', '�h' => '撹', '�i' => '格', '�j' => '核', '�k' => '殻', '�l' => '獲', '�m' => '確', '�n' => '穫', '�o' => '覚', '�p' => '角', '�q' => '赫', '�r' => '較', '�s' => '郭', '�t' => '閣', '�u' => '隔', '�v' => '革', '�w' => '学', '�x' => '岳', '�y' => '楽', '�z' => '額', '�{' => '顎', '�|' => '掛', '�}' => '笠', '�~' => '樫', '��' => '橿', '��' => '梶', '��' => '鰍', '��' => '潟', '��' => '割', '��' => '喝', '��' => '恰', '��' => '括', '��' => '活', '��' => '渇', '��' => '滑', '��' => '葛', '��' => '褐', '��' => '轄', '��' => '且', '��' => '鰹', '��' => '叶', '��' => '椛', '��' => '樺', '��' => '鞄', '��' => '株', '��' => '兜', '��' => '竃', '��' => '蒲', '��' => '釜', '��' => '鎌', '��' => '噛', '��' => '鴨', '��' => '栢', '��' => '茅', '��' => '萱', '��' => '粥', '��' => '刈', '��' => '苅', '��' => '瓦', '��' => '乾', '��' => '侃', '��' => '冠', '��' => '寒', '��' => '刊', '��' => '勘', '��' => '勧', '��' => '巻', '��' => '喚', '��' => '堪', '��' => '姦', '��' => '完', '��' => '官', '��' => '寛', '��' => '干', '��' => '幹', '��' => '患', '��' => '感', '��' => '慣', '��' => '憾', '��' => '換', '��' => '敢', '��' => '柑', '��' => '桓', '��' => '棺', '��' => '款', '��' => '歓', '��' => '汗', '��' => '漢', '��' => '澗', '��' => '潅', '��' => '環', '��' => '甘', '��' => '監', '��' => '看', '��' => '竿', '��' => '管', '��' => '簡', '��' => '緩', '��' => '缶', '��' => '翰', '��' => '肝', '��' => '艦', '��' => '莞', '��' => '観', '��' => '諌', '��' => '貫', '��' => '還', '��' => '鑑', '��' => '間', '��' => '閑', '��' => '関', '��' => '陥', '��' => '韓', '��' => '館', '��' => '舘', '��' => '丸', '��' => '含', '��' => '岸', '��' => '巌', '��' => '玩', '��' => '癌', '��' => '眼', '��' => '岩', '��' => '翫', '��' => '贋', '��' => '雁', '��' => '頑', '��' => '顔', '��' => '願', '��' => '企', '��' => '伎', '��' => '危', '��' => '喜', '��' => '器', '��' => '基', '��' => '奇', '��' => '嬉', '��' => '寄', '��' => '岐', '��' => '希', '��' => '幾', '��' => '忌', '��' => '揮', '��' => '机', '��' => '旗', '��' => '既', '��' => '期', '��' => '棋', '��' => '棄', '�@' => '機', '�A' => '帰', '�B' => '毅', '�C' => '気', '�D' => '汽', '�E' => '畿', '�F' => '祈', '�G' => '季', '�H' => '稀', '�I' => '紀', '�J' => '徽', '�K' => '規', '�L' => '記', '�M' => '貴', '�N' => '起', '�O' => '軌', '�P' => '輝', '�Q' => '飢', '�R' => '騎', '�S' => '鬼', '�T' => '亀', '�U' => '偽', '�V' => '儀', '�W' => '妓', '�X' => '宜', '�Y' => '戯', '�Z' => '技', '�[' => '擬', '�\\' => '欺', '�]' => '犠', '�^' => '疑', '�_' => '祇', '�`' => '義', '�a' => '蟻', '�b' => '誼', '�c' => '議', '�d' => '掬', '�e' => '菊', '�f' => '鞠', '�g' => '吉', '�h' => '吃', '�i' => '喫', '�j' => '桔', '�k' => '橘', '�l' => '詰', '�m' => '砧', '�n' => '杵', '�o' => '黍', '�p' => '却', '�q' => '客', '�r' => '脚', '�s' => '虐', '�t' => '逆', '�u' => '丘', '�v' => '久', '�w' => '仇', '�x' => '休', '�y' => '及', '�z' => '吸', '�{' => '宮', '�|' => '弓', '�}' => '急', '�~' => '救', '��' => '朽', '��' => '求', '��' => '汲', '��' => '泣', '��' => '灸', '��' => '球', '��' => '究', '��' => '窮', '��' => '笈', '��' => '級', '��' => '糾', '��' => '給', '��' => '旧', '��' => '牛', '��' => '去', '��' => '居', '��' => '巨', '��' => '拒', '��' => '拠', '��' => '挙', '��' => '渠', '��' => '虚', '��' => '許', '��' => '距', '��' => '鋸', '��' => '漁', '��' => '禦', '��' => '魚', '��' => '亨', '��' => '享', '��' => '京', '��' => '供', '��' => '侠', '��' => '僑', '��' => '兇', '��' => '競', '��' => '共', '��' => '凶', '��' => '協', '��' => '匡', '��' => '卿', '��' => '叫', '��' => '喬', '��' => '境', '��' => '峡', '��' => '強', '��' => '彊', '��' => '怯', '��' => '恐', '��' => '恭', '��' => '挟', '��' => '教', '��' => '橋', '��' => '況', '��' => '狂', '��' => '狭', '��' => '矯', '��' => '胸', '��' => '脅', '��' => '興', '��' => '蕎', '��' => '郷', '��' => '鏡', '��' => '響', '��' => '饗', '��' => '驚', '��' => '仰', '��' => '凝', '��' => '尭', '��' => '暁', '��' => '業', '��' => '局', '��' => '曲', '��' => '極', '��' => '玉', '��' => '桐', '��' => '粁', '��' => '僅', '��' => '勤', '��' => '均', '��' => '巾', '��' => '錦', '��' => '斤', '��' => '欣', '��' => '欽', '��' => '琴', '��' => '禁', '��' => '禽', '��' => '筋', '��' => '緊', '��' => '芹', '��' => '菌', '��' => '衿', '��' => '襟', '��' => '謹', '��' => '近', '��' => '金', '��' => '吟', '��' => '銀', '��' => '九', '��' => '倶', '��' => '句', '��' => '区', '��' => '狗', '��' => '玖', '��' => '矩', '��' => '苦', '��' => '躯', '��' => '駆', '��' => '駈', '��' => '駒', '��' => '具', '��' => '愚', '��' => '虞', '��' => '喰', '��' => '空', '��' => '偶', '��' => '寓', '��' => '遇', '��' => '隅', '��' => '串', '��' => '櫛', '��' => '釧', '��' => '屑', '��' => '屈', '�@' => '掘', '�A' => '窟', '�B' => '沓', '�C' => '靴', '�D' => '轡', '�E' => '窪', '�F' => '熊', '�G' => '隈', '�H' => '粂', '�I' => '栗', '�J' => '繰', '�K' => '桑', '�L' => '鍬', '�M' => '勲', '�N' => '君', '�O' => '薫', '�P' => '訓', '�Q' => '群', '�R' => '軍', '�S' => '郡', '�T' => '卦', '�U' => '袈', '�V' => '祁', '�W' => '係', '�X' => '傾', '�Y' => '刑', '�Z' => '兄', '�[' => '啓', '�\\' => '圭', '�]' => '珪', '�^' => '型', '�_' => '契', '�`' => '形', '�a' => '径', '�b' => '恵', '�c' => '慶', '�d' => '慧', '�e' => '憩', '�f' => '掲', '�g' => '携', '�h' => '敬', '�i' => '景', '�j' => '桂', '�k' => '渓', '�l' => '畦', '�m' => '稽', '�n' => '系', '�o' => '経', '�p' => '継', '�q' => '繋', '�r' => '罫', '�s' => '茎', '�t' => '荊', '�u' => '蛍', '�v' => '計', '�w' => '詣', '�x' => '警', '�y' => '軽', '�z' => '頚', '�{' => '鶏', '�|' => '芸', '�}' => '迎', '�~' => '鯨', '��' => '劇', '��' => '戟', '��' => '撃', '��' => '激', '��' => '隙', '��' => '桁', '��' => '傑', '��' => '欠', '��' => '決', '��' => '潔', '��' => '穴', '��' => '結', '��' => '血', '��' => '訣', '��' => '月', '��' => '件', '��' => '倹', '��' => '倦', '��' => '健', '��' => '兼', '��' => '券', '��' => '剣', '��' => '喧', '��' => '圏', '��' => '堅', '��' => '嫌', '��' => '建', '��' => '憲', '��' => '懸', '��' => '拳', '��' => '捲', '��' => '検', '��' => '権', '��' => '牽', '��' => '犬', '��' => '献', '��' => '研', '��' => '硯', '��' => '絹', '��' => '県', '��' => '肩', '��' => '見', '��' => '謙', '��' => '賢', '��' => '軒', '��' => '遣', '��' => '鍵', '��' => '険', '��' => '顕', '��' => '験', '��' => '鹸', '��' => '元', '��' => '原', '��' => '厳', '��' => '幻', '��' => '弦', '��' => '減', '��' => '源', '��' => '玄', '��' => '現', '��' => '絃', '��' => '舷', '��' => '言', '��' => '諺', '��' => '限', '��' => '乎', '��' => '個', '��' => '古', '��' => '呼', '��' => '固', '��' => '姑', '��' => '孤', '��' => '己', '��' => '庫', '��' => '弧', '��' => '戸', '��' => '故', '��' => '枯', '��' => '湖', '��' => '狐', '��' => '糊', '��' => '袴', '��' => '股', '��' => '胡', '��' => '菰', '��' => '虎', '��' => '誇', '��' => '跨', '��' => '鈷', '��' => '雇', '��' => '顧', '��' => '鼓', '��' => '五', '��' => '互', '��' => '伍', '��' => '午', '��' => '呉', '��' => '吾', '��' => '娯', '��' => '後', '��' => '御', '��' => '悟', '��' => '梧', '��' => '檎', '��' => '瑚', '��' => '碁', '��' => '語', '��' => '誤', '��' => '護', '��' => '醐', '��' => '乞', '��' => '鯉', '��' => '交', '��' => '佼', '��' => '侯', '��' => '候', '��' => '倖', '��' => '光', '��' => '公', '��' => '功', '��' => '効', '��' => '勾', '��' => '厚', '��' => '口', '��' => '向', '�@' => '后', '�A' => '喉', '�B' => '坑', '�C' => '垢', '�D' => '好', '�E' => '孔', '�F' => '孝', '�G' => '宏', '�H' => '工', '�I' => '巧', '�J' => '巷', '�K' => '幸', '�L' => '広', '�M' => '庚', '�N' => '康', '�O' => '弘', '�P' => '恒', '�Q' => '慌', '�R' => '抗', '�S' => '拘', '�T' => '控', '�U' => '攻', '�V' => '昂', '�W' => '晃', '�X' => '更', '�Y' => '杭', '�Z' => '校', '�[' => '梗', '�\\' => '構', '�]' => '江', '�^' => '洪', '�_' => '浩', '�`' => '港', '�a' => '溝', '�b' => '甲', '�c' => '皇', '�d' => '硬', '�e' => '稿', '�f' => '糠', '�g' => '紅', '�h' => '紘', '�i' => '絞', '�j' => '綱', '�k' => '耕', '�l' => '考', '�m' => '肯', '�n' => '肱', '�o' => '腔', '�p' => '膏', '�q' => '航', '�r' => '荒', '�s' => '行', '�t' => '衡', '�u' => '講', '�v' => '貢', '�w' => '購', '�x' => '郊', '�y' => '酵', '�z' => '鉱', '�{' => '砿', '�|' => '鋼', '�}' => '閤', '�~' => '降', '��' => '項', '��' => '香', '��' => '高', '��' => '鴻', '��' => '剛', '��' => '劫', '��' => '号', '��' => '合', '��' => '壕', '��' => '拷', '��' => '濠', '��' => '豪', '��' => '轟', '��' => '麹', '��' => '克', '��' => '刻', '��' => '告', '��' => '国', '��' => '穀', '��' => '酷', '��' => '鵠', '��' => '黒', '��' => '獄', '��' => '漉', '��' => '腰', '��' => '甑', '��' => '忽', '��' => '惚', '��' => '骨', '��' => '狛', '��' => '込', '��' => '此', '��' => '頃', '��' => '今', '��' => '困', '��' => '坤', '��' => '墾', '��' => '婚', '��' => '恨', '��' => '懇', '��' => '昏', '��' => '昆', '��' => '根', '��' => '梱', '��' => '混', '��' => '痕', '��' => '紺', '��' => '艮', '��' => '魂', '��' => '些', '��' => '佐', '��' => '叉', '��' => '唆', '��' => '嵯', '��' => '左', '��' => '差', '��' => '査', '��' => '沙', '��' => '瑳', '��' => '砂', '��' => '詐', '��' => '鎖', '��' => '裟', '��' => '坐', '��' => '座', '��' => '挫', '��' => '債', '��' => '催', '��' => '再', '��' => '最', '��' => '哉', '��' => '塞', '��' => '妻', '��' => '宰', '��' => '彩', '��' => '才', '��' => '採', '��' => '栽', '��' => '歳', '��' => '済', '��' => '災', '��' => '采', '��' => '犀', '��' => '砕', '��' => '砦', '��' => '祭', '��' => '斎', '��' => '細', '��' => '菜', '��' => '裁', '��' => '載', '��' => '際', '��' => '剤', '��' => '在', '��' => '材', '��' => '罪', '��' => '財', '��' => '冴', '��' => '坂', '��' => '阪', '��' => '堺', '��' => '榊', '��' => '肴', '��' => '咲', '��' => '崎', '��' => '埼', '��' => '碕', '��' => '鷺', '��' => '作', '��' => '削', '��' => '咋', '��' => '搾', '��' => '昨', '��' => '朔', '��' => '柵', '��' => '窄', '��' => '策', '��' => '索', '��' => '錯', '��' => '桜', '��' => '鮭', '��' => '笹', '��' => '匙', '��' => '冊', '��' => '刷', '�@' => '察', '�A' => '拶', '�B' => '撮', '�C' => '擦', '�D' => '札', '�E' => '殺', '�F' => '薩', '�G' => '雑', '�H' => '皐', '�I' => '鯖', '�J' => '捌', '�K' => '錆', '�L' => '鮫', '�M' => '皿', '�N' => '晒', '�O' => '三', '�P' => '傘', '�Q' => '参', '�R' => '山', '�S' => '惨', '�T' => '撒', '�U' => '散', '�V' => '桟', '�W' => '燦', '�X' => '珊', '�Y' => '産', '�Z' => '算', '�[' => '纂', '�\\' => '蚕', '�]' => '讃', '�^' => '賛', '�_' => '酸', '�`' => '餐', '�a' => '斬', '�b' => '暫', '�c' => '残', '�d' => '仕', '�e' => '仔', '�f' => '伺', '�g' => '使', '�h' => '刺', '�i' => '司', '�j' => '史', '�k' => '嗣', '�l' => '四', '�m' => '士', '�n' => '始', '�o' => '姉', '�p' => '姿', '�q' => '子', '�r' => '屍', '�s' => '市', '�t' => '師', '�u' => '志', '�v' => '思', '�w' => '指', '�x' => '支', '�y' => '孜', '�z' => '斯', '�{' => '施', '�|' => '旨', '�}' => '枝', '�~' => '止', '��' => '死', '��' => '氏', '��' => '獅', '��' => '祉', '��' => '私', '��' => '糸', '��' => '紙', '��' => '紫', '��' => '肢', '��' => '脂', '��' => '至', '��' => '視', '��' => '詞', '��' => '詩', '��' => '試', '��' => '誌', '��' => '諮', '��' => '資', '��' => '賜', '��' => '雌', '��' => '飼', '��' => '歯', '��' => '事', '��' => '似', '��' => '侍', '��' => '児', '��' => '字', '��' => '寺', '��' => '慈', '��' => '持', '��' => '時', '��' => '次', '��' => '滋', '��' => '治', '��' => '爾', '��' => '璽', '��' => '痔', '��' => '磁', '��' => '示', '��' => '而', '��' => '耳', '��' => '自', '��' => '蒔', '��' => '辞', '��' => '汐', '��' => '鹿', '��' => '式', '��' => '識', '��' => '鴫', '��' => '竺', '��' => '軸', '��' => '宍', '��' => '雫', '��' => '七', '��' => '叱', '��' => '執', '��' => '失', '��' => '嫉', '��' => '室', '��' => '悉', '��' => '湿', '��' => '漆', '��' => '疾', '��' => '質', '��' => '実', '��' => '蔀', '��' => '篠', '��' => '偲', '��' => '柴', '��' => '芝', '��' => '屡', '��' => '蕊', '��' => '縞', '��' => '舎', '��' => '写', '��' => '射', '��' => '捨', '��' => '赦', '��' => '斜', '��' => '煮', '��' => '社', '��' => '紗', '��' => '者', '��' => '謝', '��' => '車', '��' => '遮', '��' => '蛇', '��' => '邪', '��' => '借', '��' => '勺', '��' => '尺', '��' => '杓', '��' => '灼', '��' => '爵', '��' => '酌', '��' => '釈', '��' => '錫', '��' => '若', '��' => '寂', '��' => '弱', '��' => '惹', '��' => '主', '��' => '取', '��' => '守', '��' => '手', '��' => '朱', '��' => '殊', '��' => '狩', '��' => '珠', '��' => '種', '��' => '腫', '��' => '趣', '��' => '酒', '��' => '首', '��' => '儒', '��' => '受', '��' => '呪', '��' => '寿', '��' => '授', '��' => '樹', '��' => '綬', '��' => '需', '��' => '囚', '��' => '収', '��' => '周', '�@' => '宗', '�A' => '就', '�B' => '州', '�C' => '修', '�D' => '愁', '�E' => '拾', '�F' => '洲', '�G' => '秀', '�H' => '秋', '�I' => '終', '�J' => '繍', '�K' => '習', '�L' => '臭', '�M' => '舟', '�N' => '蒐', '�O' => '衆', '�P' => '襲', '�Q' => '讐', '�R' => '蹴', '�S' => '輯', '�T' => '週', '�U' => '酋', '�V' => '酬', '�W' => '集', '�X' => '醜', '�Y' => '什', '�Z' => '住', '�[' => '充', '�\\' => '十', '�]' => '従', '�^' => '戎', '�_' => '柔', '�`' => '汁', '�a' => '渋', '�b' => '獣', '�c' => '縦', '�d' => '重', '�e' => '銃', '�f' => '叔', '�g' => '夙', '�h' => '宿', '�i' => '淑', '�j' => '祝', '�k' => '縮', '�l' => '粛', '�m' => '塾', '�n' => '熟', '�o' => '出', '�p' => '術', '�q' => '述', '�r' => '俊', '�s' => '峻', '�t' => '春', '�u' => '瞬', '�v' => '竣', '�w' => '舜', '�x' => '駿', '�y' => '准', '�z' => '循', '�{' => '旬', '�|' => '楯', '�}' => '殉', '�~' => '淳', '��' => '準', '��' => '潤', '��' => '盾', '��' => '純', '��' => '巡', '��' => '遵', '��' => '醇', '��' => '順', '��' => '処', '��' => '初', '��' => '所', '��' => '暑', '��' => '曙', '��' => '渚', '��' => '庶', '��' => '緒', '��' => '署', '��' => '書', '��' => '薯', '��' => '藷', '��' => '諸', '��' => '助', '��' => '叙', '��' => '女', '��' => '序', '��' => '徐', '��' => '恕', '��' => '鋤', '��' => '除', '��' => '傷', '��' => '償', '��' => '勝', '��' => '匠', '��' => '升', '��' => '召', '��' => '哨', '��' => '商', '��' => '唱', '��' => '嘗', '��' => '奨', '��' => '妾', '��' => '娼', '��' => '宵', '��' => '将', '��' => '小', '��' => '少', '��' => '尚', '��' => '庄', '��' => '床', '��' => '廠', '��' => '彰', '��' => '承', '��' => '抄', '��' => '招', '��' => '掌', '��' => '捷', '��' => '昇', '��' => '昌', '��' => '昭', '��' => '晶', '��' => '松', '��' => '梢', '��' => '樟', '��' => '樵', '��' => '沼', '��' => '消', '��' => '渉', '��' => '湘', '��' => '焼', '��' => '焦', '��' => '照', '��' => '症', '��' => '省', '��' => '硝', '��' => '礁', '��' => '祥', '��' => '称', '��' => '章', '��' => '笑', '��' => '粧', '��' => '紹', '��' => '肖', '��' => '菖', '��' => '蒋', '��' => '蕉', '��' => '衝', '��' => '裳', '��' => '訟', '��' => '証', '��' => '詔', '��' => '詳', '��' => '象', '��' => '賞', '��' => '醤', '��' => '鉦', '��' => '鍾', '��' => '鐘', '��' => '障', '��' => '鞘', '��' => '上', '��' => '丈', '��' => '丞', '��' => '乗', '��' => '冗', '��' => '剰', '��' => '城', '��' => '場', '��' => '壌', '��' => '嬢', '��' => '常', '��' => '情', '��' => '擾', '��' => '条', '��' => '杖', '��' => '浄', '��' => '状', '��' => '畳', '��' => '穣', '��' => '蒸', '��' => '譲', '��' => '醸', '��' => '錠', '��' => '嘱', '��' => '埴', '��' => '飾', '�@' => '拭', '�A' => '植', '�B' => '殖', '�C' => '燭', '�D' => '織', '�E' => '職', '�F' => '色', '�G' => '触', '�H' => '食', '�I' => '蝕', '�J' => '辱', '�K' => '尻', '�L' => '伸', '�M' => '信', '�N' => '侵', '�O' => '唇', '�P' => '娠', '�Q' => '寝', '�R' => '審', '�S' => '心', '�T' => '慎', '�U' => '振', '�V' => '新', '�W' => '晋', '�X' => '森', '�Y' => '榛', '�Z' => '浸', '�[' => '深', '�\\' => '申', '�]' => '疹', '�^' => '真', '�_' => '神', '�`' => '秦', '�a' => '紳', '�b' => '臣', '�c' => '芯', '�d' => '薪', '�e' => '親', '�f' => '診', '�g' => '身', '�h' => '辛', '�i' => '進', '�j' => '針', '�k' => '震', '�l' => '人', '�m' => '仁', '�n' => '刃', '�o' => '塵', '�p' => '壬', '�q' => '尋', '�r' => '甚', '�s' => '尽', '�t' => '腎', '�u' => '訊', '�v' => '迅', '�w' => '陣', '�x' => '靭', '�y' => '笥', '�z' => '諏', '�{' => '須', '�|' => '酢', '�}' => '図', '�~' => '厨', '��' => '逗', '��' => '吹', '��' => '垂', '��' => '帥', '��' => '推', '��' => '水', '��' => '炊', '��' => '睡', '��' => '粋', '��' => '翠', '��' => '衰', '��' => '遂', '��' => '酔', '��' => '錐', '��' => '錘', '��' => '随', '��' => '瑞', '��' => '髄', '��' => '崇', '��' => '嵩', '��' => '数', '��' => '枢', '��' => '趨', '��' => '雛', '��' => '据', '��' => '杉', '��' => '椙', '��' => '菅', '��' => '頗', '��' => '雀', '��' => '裾', '��' => '澄', '��' => '摺', '��' => '寸', '��' => '世', '��' => '瀬', '��' => '畝', '��' => '是', '��' => '凄', '��' => '制', '��' => '勢', '��' => '姓', '��' => '征', '��' => '性', '��' => '成', '��' => '政', '��' => '整', '��' => '星', '��' => '晴', '��' => '棲', '��' => '栖', '��' => '正', '��' => '清', '��' => '牲', '��' => '生', '��' => '盛', '��' => '精', '��' => '聖', '��' => '声', '��' => '製', '��' => '西', '��' => '誠', '��' => '誓', '��' => '請', '��' => '逝', '��' => '醒', '��' => '青', '��' => '静', '��' => '斉', '��' => '税', '��' => '脆', '��' => '隻', '��' => '席', '��' => '惜', '��' => '戚', '��' => '斥', '��' => '昔', '��' => '析', '��' => '石', '��' => '積', '��' => '籍', '��' => '績', '��' => '脊', '��' => '責', '��' => '赤', '��' => '跡', '��' => '蹟', '��' => '碩', '��' => '切', '��' => '拙', '��' => '接', '��' => '摂', '��' => '折', '��' => '設', '��' => '窃', '��' => '節', '��' => '説', '��' => '雪', '��' => '絶', '��' => '舌', '��' => '蝉', '��' => '仙', '��' => '先', '��' => '千', '��' => '占', '��' => '宣', '��' => '専', '��' => '尖', '��' => '川', '��' => '戦', '��' => '扇', '��' => '撰', '��' => '栓', '��' => '栴', '��' => '泉', '��' => '浅', '��' => '洗', '��' => '染', '��' => '潜', '��' => '煎', '��' => '煽', '��' => '旋', '��' => '穿', '��' => '箭', '��' => '線', '�@' => '繊', '�A' => '羨', '�B' => '腺', '�C' => '舛', '�D' => '船', '�E' => '薦', '�F' => '詮', '�G' => '賎', '�H' => '践', '�I' => '選', '�J' => '遷', '�K' => '銭', '�L' => '銑', '�M' => '閃', '�N' => '鮮', '�O' => '前', '�P' => '善', '�Q' => '漸', '�R' => '然', '�S' => '全', '�T' => '禅', '�U' => '繕', '�V' => '膳', '�W' => '糎', '�X' => '噌', '�Y' => '塑', '�Z' => '岨', '�[' => '措', '�\\' => '曾', '�]' => '曽', '�^' => '楚', '�_' => '狙', '�`' => '疏', '�a' => '疎', '�b' => '礎', '�c' => '祖', '�d' => '租', '�e' => '粗', '�f' => '素', '�g' => '組', '�h' => '蘇', '�i' => '訴', '�j' => '阻', '�k' => '遡', '�l' => '鼠', '�m' => '僧', '�n' => '創', '�o' => '双', '�p' => '叢', '�q' => '倉', '�r' => '喪', '�s' => '壮', '�t' => '奏', '�u' => '爽', '�v' => '宋', '�w' => '層', '�x' => '匝', '�y' => '惣', '�z' => '想', '�{' => '捜', '�|' => '掃', '�}' => '挿', '�~' => '掻', '��' => '操', '��' => '早', '��' => '曹', '��' => '巣', '��' => '槍', '��' => '槽', '��' => '漕', '��' => '燥', '��' => '争', '��' => '痩', '��' => '相', '��' => '窓', '��' => '糟', '��' => '総', '��' => '綜', '��' => '聡', '��' => '草', '��' => '荘', '��' => '葬', '��' => '蒼', '��' => '藻', '��' => '装', '��' => '走', '��' => '送', '��' => '遭', '��' => '鎗', '��' => '霜', '��' => '騒', '��' => '像', '��' => '増', '��' => '憎', '��' => '臓', '��' => '蔵', '��' => '贈', '��' => '造', '��' => '促', '��' => '側', '��' => '則', '��' => '即', '��' => '息', '��' => '捉', '��' => '束', '��' => '測', '��' => '足', '��' => '速', '��' => '俗', '��' => '属', '��' => '賊', '��' => '族', '��' => '続', '��' => '卒', '��' => '袖', '��' => '其', '��' => '揃', '��' => '存', '��' => '孫', '��' => '尊', '��' => '損', '��' => '村', '��' => '遜', '��' => '他', '��' => '多', '��' => '太', '��' => '汰', '��' => '詑', '��' => '唾', '��' => '堕', '��' => '妥', '��' => '惰', '��' => '打', '��' => '柁', '��' => '舵', '��' => '楕', '��' => '陀', '��' => '駄', '��' => '騨', '��' => '体', '��' => '堆', '��' => '対', '��' => '耐', '��' => '岱', '��' => '帯', '��' => '待', '��' => '怠', '��' => '態', '��' => '戴', '��' => '替', '��' => '泰', '��' => '滞', '��' => '胎', '��' => '腿', '��' => '苔', '��' => '袋', '��' => '貸', '��' => '退', '��' => '逮', '��' => '隊', '��' => '黛', '��' => '鯛', '��' => '代', '��' => '台', '��' => '大', '��' => '第', '��' => '醍', '��' => '題', '��' => '鷹', '��' => '滝', '��' => '瀧', '��' => '卓', '��' => '啄', '��' => '宅', '��' => '托', '��' => '択', '��' => '拓', '��' => '沢', '��' => '濯', '��' => '琢', '��' => '託', '��' => '鐸', '��' => '濁', '��' => '諾', '��' => '茸', '��' => '凧', '��' => '蛸', '��' => '只', '�@' => '叩', '�A' => '但', '�B' => '達', '�C' => '辰', '�D' => '奪', '�E' => '脱', '�F' => '巽', '�G' => '竪', '�H' => '辿', '�I' => '棚', '�J' => '谷', '�K' => '狸', '�L' => '鱈', '�M' => '樽', '�N' => '誰', '�O' => '丹', '�P' => '単', '�Q' => '嘆', '�R' => '坦', '�S' => '担', '�T' => '探', '�U' => '旦', '�V' => '歎', '�W' => '淡', '�X' => '湛', '�Y' => '炭', '�Z' => '短', '�[' => '端', '�\\' => '箪', '�]' => '綻', '�^' => '耽', '�_' => '胆', '�`' => '蛋', '�a' => '誕', '�b' => '鍛', '�c' => '団', '�d' => '壇', '�e' => '弾', '�f' => '断', '�g' => '暖', '�h' => '檀', '�i' => '段', '�j' => '男', '�k' => '談', '�l' => '値', '�m' => '知', '�n' => '地', '�o' => '弛', '�p' => '恥', '�q' => '智', '�r' => '池', '�s' => '痴', '�t' => '稚', '�u' => '置', '�v' => '致', '�w' => '蜘', '�x' => '遅', '�y' => '馳', '�z' => '築', '�{' => '畜', '�|' => '竹', '�}' => '筑', '�~' => '蓄', '��' => '逐', '��' => '秩', '��' => '窒', '��' => '茶', '��' => '嫡', '��' => '着', '��' => '中', '��' => '仲', '��' => '宙', '��' => '忠', '��' => '抽', '��' => '昼', '��' => '柱', '��' => '注', '��' => '虫', '��' => '衷', '��' => '註', '��' => '酎', '��' => '鋳', '��' => '駐', '��' => '樗', '��' => '瀦', '��' => '猪', '��' => '苧', '��' => '著', '��' => '貯', '��' => '丁', '��' => '兆', '��' => '凋', '��' => '喋', '��' => '寵', '��' => '帖', '��' => '帳', '��' => '庁', '��' => '弔', '��' => '張', '��' => '彫', '��' => '徴', '��' => '懲', '��' => '挑', '��' => '暢', '��' => '朝', '��' => '潮', '��' => '牒', '��' => '町', '��' => '眺', '��' => '聴', '��' => '脹', '��' => '腸', '��' => '蝶', '��' => '調', '��' => '諜', '��' => '超', '��' => '跳', '��' => '銚', '��' => '長', '��' => '頂', '��' => '鳥', '��' => '勅', '��' => '捗', '��' => '直', '��' => '朕', '��' => '沈', '��' => '珍', '��' => '賃', '��' => '鎮', '��' => '陳', '��' => '津', '��' => '墜', '��' => '椎', '��' => '槌', '��' => '追', '��' => '鎚', '��' => '痛', '��' => '通', '��' => '塚', '��' => '栂', '��' => '掴', '��' => '槻', '��' => '佃', '��' => '漬', '��' => '柘', '��' => '辻', '��' => '蔦', '��' => '綴', '��' => '鍔', '��' => '椿', '��' => '潰', '��' => '坪', '��' => '壷', '��' => '嬬', '��' => '紬', '��' => '爪', '��' => '吊', '��' => '釣', '��' => '鶴', '��' => '亭', '��' => '低', '��' => '停', '��' => '偵', '��' => '剃', '��' => '貞', '��' => '呈', '��' => '堤', '��' => '定', '��' => '帝', '��' => '底', '��' => '庭', '��' => '廷', '��' => '弟', '��' => '悌', '��' => '抵', '��' => '挺', '��' => '提', '��' => '梯', '��' => '汀', '��' => '碇', '��' => '禎', '��' => '程', '��' => '締', '��' => '艇', '��' => '訂', '��' => '諦', '��' => '蹄', '��' => '逓', '�@' => '邸', '�A' => '鄭', '�B' => '釘', '�C' => '鼎', '�D' => '泥', '�E' => '摘', '�F' => '擢', '�G' => '敵', '�H' => '滴', '�I' => '的', '�J' => '笛', '�K' => '適', '�L' => '鏑', '�M' => '溺', '�N' => '哲', '�O' => '徹', '�P' => '撤', '�Q' => '轍', '�R' => '迭', '�S' => '鉄', '�T' => '典', '�U' => '填', '�V' => '天', '�W' => '展', '�X' => '店', '�Y' => '添', '�Z' => '纏', '�[' => '甜', '�\\' => '貼', '�]' => '転', '�^' => '顛', '�_' => '点', '�`' => '伝', '�a' => '殿', '�b' => '澱', '�c' => '田', '�d' => '電', '�e' => '兎', '�f' => '吐', '�g' => '堵', '�h' => '塗', '�i' => '妬', '�j' => '屠', '�k' => '徒', '�l' => '斗', '�m' => '杜', '�n' => '渡', '�o' => '登', '�p' => '菟', '�q' => '賭', '�r' => '途', '�s' => '都', '�t' => '鍍', '�u' => '砥', '�v' => '砺', '�w' => '努', '�x' => '度', '�y' => '土', '�z' => '奴', '�{' => '怒', '�|' => '倒', '�}' => '党', '�~' => '冬', '��' => '凍', '��' => '刀', '��' => '唐', '��' => '塔', '��' => '塘', '��' => '套', '��' => '宕', '��' => '島', '��' => '嶋', '��' => '悼', '��' => '投', '��' => '搭', '��' => '東', '��' => '桃', '��' => '梼', '��' => '棟', '��' => '盗', '��' => '淘', '��' => '湯', '��' => '涛', '��' => '灯', '��' => '燈', '��' => '当', '��' => '痘', '��' => '祷', '��' => '等', '��' => '答', '��' => '筒', '��' => '糖', '��' => '統', '��' => '到', '��' => '董', '��' => '蕩', '��' => '藤', '��' => '討', '��' => '謄', '��' => '豆', '��' => '踏', '��' => '逃', '��' => '透', '��' => '鐙', '��' => '陶', '��' => '頭', '��' => '騰', '��' => '闘', '��' => '働', '��' => '動', '��' => '同', '��' => '堂', '��' => '導', '��' => '憧', '��' => '撞', '��' => '洞', '��' => '瞳', '��' => '童', '��' => '胴', '��' => '萄', '��' => '道', '��' => '銅', '��' => '峠', '��' => '鴇', '��' => '匿', '��' => '得', '��' => '徳', '��' => '涜', '��' => '特', '��' => '督', '��' => '禿', '��' => '篤', '��' => '毒', '��' => '独', '��' => '読', '��' => '栃', '��' => '橡', '��' => '凸', '��' => '突', '��' => '椴', '��' => '届', '��' => '鳶', '��' => '苫', '��' => '寅', '��' => '酉', '��' => '瀞', '��' => '噸', '��' => '屯', '��' => '惇', '��' => '敦', '��' => '沌', '��' => '豚', '��' => '遁', '��' => '頓', '��' => '呑', '��' => '曇', '��' => '鈍', '��' => '奈', '��' => '那', '��' => '内', '��' => '乍', '��' => '凪', '��' => '薙', '��' => '謎', '��' => '灘', '��' => '捺', '��' => '鍋', '��' => '楢', '��' => '馴', '��' => '縄', '��' => '畷', '��' => '南', '��' => '楠', '��' => '軟', '��' => '難', '��' => '汝', '��' => '二', '��' => '尼', '��' => '弐', '��' => '迩', '��' => '匂', '��' => '賑', '��' => '肉', '��' => '虹', '��' => '廿', '��' => '日', '��' => '乳', '��' => '入', '�@' => '如', '�A' => '尿', '�B' => '韮', '�C' => '任', '�D' => '妊', '�E' => '忍', '�F' => '認', '�G' => '濡', '�H' => '禰', '�I' => '祢', '�J' => '寧', '�K' => '葱', '�L' => '猫', '�M' => '熱', '�N' => '年', '�O' => '念', '�P' => '捻', '�Q' => '撚', '�R' => '燃', '�S' => '粘', '�T' => '乃', '�U' => '廼', '�V' => '之', '�W' => '埜', '�X' => '嚢', '�Y' => '悩', '�Z' => '濃', '�[' => '納', '�\\' => '能', '�]' => '脳', '�^' => '膿', '�_' => '農', '�`' => '覗', '�a' => '蚤', '�b' => '巴', '�c' => '把', '�d' => '播', '�e' => '覇', '�f' => '杷', '�g' => '波', '�h' => '派', '�i' => '琶', '�j' => '破', '�k' => '婆', '�l' => '罵', '�m' => '芭', '�n' => '馬', '�o' => '俳', '�p' => '廃', '�q' => '拝', '�r' => '排', '�s' => '敗', '�t' => '杯', '�u' => '盃', '�v' => '牌', '�w' => '背', '�x' => '肺', '�y' => '輩', '�z' => '配', '�{' => '倍', '�|' => '培', '�}' => '媒', '�~' => '梅', '��' => '楳', '��' => '煤', '��' => '狽', '��' => '買', '��' => '売', '��' => '賠', '��' => '陪', '��' => '這', '��' => '蝿', '��' => '秤', '��' => '矧', '��' => '萩', '��' => '伯', '��' => '剥', '��' => '博', '��' => '拍', '��' => '柏', '��' => '泊', '��' => '白', '��' => '箔', '��' => '粕', '��' => '舶', '��' => '薄', '��' => '迫', '��' => '曝', '��' => '漠', '��' => '爆', '��' => '縛', '��' => '莫', '��' => '駁', '��' => '麦', '��' => '函', '��' => '箱', '��' => '硲', '��' => '箸', '��' => '肇', '��' => '筈', '��' => '櫨', '��' => '幡', '��' => '肌', '��' => '畑', '��' => '畠', '��' => '八', '��' => '鉢', '��' => '溌', '��' => '発', '��' => '醗', '��' => '髪', '��' => '伐', '��' => '罰', '��' => '抜', '��' => '筏', '��' => '閥', '��' => '鳩', '��' => '噺', '��' => '塙', '��' => '蛤', '��' => '隼', '��' => '伴', '��' => '判', '��' => '半', '��' => '反', '��' => '叛', '��' => '帆', '��' => '搬', '��' => '斑', '��' => '板', '��' => '氾', '��' => '汎', '��' => '版', '��' => '犯', '��' => '班', '��' => '畔', '��' => '繁', '��' => '般', '��' => '藩', '��' => '販', '��' => '範', '��' => '釆', '��' => '煩', '��' => '頒', '��' => '飯', '��' => '挽', '��' => '晩', '��' => '番', '��' => '盤', '��' => '磐', '��' => '蕃', '��' => '蛮', '��' => '匪', '��' => '卑', '��' => '否', '��' => '妃', '��' => '庇', '��' => '彼', '��' => '悲', '��' => '扉', '��' => '批', '��' => '披', '��' => '斐', '��' => '比', '��' => '泌', '��' => '疲', '��' => '皮', '��' => '碑', '��' => '秘', '��' => '緋', '��' => '罷', '��' => '肥', '��' => '被', '��' => '誹', '��' => '費', '��' => '避', '��' => '非', '��' => '飛', '��' => '樋', '��' => '簸', '��' => '備', '��' => '尾', '��' => '微', '��' => '枇', '��' => '毘', '��' => '琵', '��' => '眉', '��' => '美', '�@' => '鼻', '�A' => '柊', '�B' => '稗', '�C' => '匹', '�D' => '疋', '�E' => '髭', '�F' => '彦', '�G' => '膝', '�H' => '菱', '�I' => '肘', '�J' => '弼', '�K' => '必', '�L' => '畢', '�M' => '筆', '�N' => '逼', '�O' => '桧', '�P' => '姫', '�Q' => '媛', '�R' => '紐', '�S' => '百', '�T' => '謬', '�U' => '俵', '�V' => '彪', '�W' => '標', '�X' => '氷', '�Y' => '漂', '�Z' => '瓢', '�[' => '票', '�\\' => '表', '�]' => '評', '�^' => '豹', '�_' => '廟', '�`' => '描', '�a' => '病', '�b' => '秒', '�c' => '苗', '�d' => '錨', '�e' => '鋲', '�f' => '蒜', '�g' => '蛭', '�h' => '鰭', '�i' => '品', '�j' => '彬', '�k' => '斌', '�l' => '浜', '�m' => '瀕', '�n' => '貧', '�o' => '賓', '�p' => '頻', '�q' => '敏', '�r' => '瓶', '�s' => '不', '�t' => '付', '�u' => '埠', '�v' => '夫', '�w' => '婦', '�x' => '富', '�y' => '冨', '�z' => '布', '�{' => '府', '�|' => '怖', '�}' => '扶', '�~' => '敷', '��' => '斧', '��' => '普', '��' => '浮', '��' => '父', '��' => '符', '��' => '腐', '��' => '膚', '��' => '芙', '��' => '譜', '��' => '負', '��' => '賦', '��' => '赴', '��' => '阜', '��' => '附', '��' => '侮', '��' => '撫', '��' => '武', '��' => '舞', '��' => '葡', '��' => '蕪', '��' => '部', '��' => '封', '��' => '楓', '��' => '風', '��' => '葺', '��' => '蕗', '��' => '伏', '��' => '副', '��' => '復', '��' => '幅', '��' => '服', '��' => '福', '��' => '腹', '��' => '複', '��' => '覆', '��' => '淵', '��' => '弗', '��' => '払', '��' => '沸', '��' => '仏', '��' => '物', '��' => '鮒', '��' => '分', '��' => '吻', '��' => '噴', '��' => '墳', '��' => '憤', '��' => '扮', '��' => '焚', '��' => '奮', '��' => '粉', '��' => '糞', '��' => '紛', '��' => '雰', '��' => '文', '��' => '聞', '��' => '丙', '��' => '併', '��' => '兵', '��' => '塀', '��' => '幣', '��' => '平', '��' => '弊', '��' => '柄', '��' => '並', '��' => '蔽', '��' => '閉', '��' => '陛', '��' => '米', '��' => '頁', '��' => '僻', '��' => '壁', '��' => '癖', '��' => '碧', '��' => '別', '��' => '瞥', '��' => '蔑', '��' => '箆', '��' => '偏', '��' => '変', '��' => '片', '��' => '篇', '��' => '編', '��' => '辺', '��' => '返', '��' => '遍', '��' => '便', '��' => '勉', '��' => '娩', '��' => '弁', '��' => '鞭', '��' => '保', '��' => '舗', '��' => '鋪', '��' => '圃', '��' => '捕', '��' => '歩', '��' => '甫', '��' => '補', '��' => '輔', '��' => '穂', '��' => '募', '��' => '墓', '��' => '慕', '��' => '戊', '��' => '暮', '��' => '母', '��' => '簿', '��' => '菩', '��' => '倣', '��' => '俸', '��' => '包', '��' => '呆', '��' => '報', '��' => '奉', '��' => '宝', '��' => '峰', '��' => '峯', '��' => '崩', '��' => '庖', '��' => '抱', '��' => '捧', '��' => '放', '��' => '方', '��' => '朋', '�@' => '法', '�A' => '泡', '�B' => '烹', '�C' => '砲', '�D' => '縫', '�E' => '胞', '�F' => '芳', '�G' => '萌', '�H' => '蓬', '�I' => '蜂', '�J' => '褒', '�K' => '訪', '�L' => '豊', '�M' => '邦', '�N' => '鋒', '�O' => '飽', '�P' => '鳳', '�Q' => '鵬', '�R' => '乏', '�S' => '亡', '�T' => '傍', '�U' => '剖', '�V' => '坊', '�W' => '妨', '�X' => '帽', '�Y' => '忘', '�Z' => '忙', '�[' => '房', '�\\' => '暴', '�]' => '望', '�^' => '某', '�_' => '棒', '�`' => '冒', '�a' => '紡', '�b' => '肪', '�c' => '膨', '�d' => '謀', '�e' => '貌', '�f' => '貿', '�g' => '鉾', '�h' => '防', '�i' => '吠', '�j' => '頬', '�k' => '北', '�l' => '僕', '�m' => '卜', '�n' => '墨', '�o' => '撲', '�p' => '朴', '�q' => '牧', '�r' => '睦', '�s' => '穆', '�t' => '釦', '�u' => '勃', '�v' => '没', '�w' => '殆', '�x' => '堀', '�y' => '幌', '�z' => '奔', '�{' => '本', '�|' => '翻', '�}' => '凡', '�~' => '盆', '��' => '摩', '��' => '磨', '��' => '魔', '��' => '麻', '��' => '埋', '��' => '妹', '��' => '昧', '��' => '枚', '��' => '毎', '��' => '哩', '��' => '槙', '��' => '幕', '��' => '膜', '��' => '枕', '��' => '鮪', '��' => '柾', '��' => '鱒', '��' => '桝', '��' => '亦', '��' => '俣', '��' => '又', '��' => '抹', '��' => '末', '��' => '沫', '��' => '迄', '��' => '侭', '��' => '繭', '��' => '麿', '��' => '万', '��' => '慢', '��' => '満', '��' => '漫', '��' => '蔓', '��' => '味', '��' => '未', '��' => '魅', '��' => '巳', '��' => '箕', '��' => '岬', '��' => '密', '��' => '蜜', '��' => '湊', '��' => '蓑', '��' => '稔', '��' => '脈', '��' => '妙', '��' => '粍', '��' => '民', '��' => '眠', '��' => '務', '��' => '夢', '��' => '無', '��' => '牟', '��' => '矛', '��' => '霧', '��' => '鵡', '��' => '椋', '��' => '婿', '��' => '娘', '��' => '冥', '��' => '名', '��' => '命', '��' => '明', '��' => '盟', '��' => '迷', '��' => '銘', '��' => '鳴', '��' => '姪', '��' => '牝', '��' => '滅', '��' => '免', '��' => '棉', '��' => '綿', '��' => '緬', '��' => '面', '��' => '麺', '��' => '摸', '��' => '模', '��' => '茂', '��' => '妄', '��' => '孟', '��' => '毛', '��' => '猛', '��' => '盲', '��' => '網', '��' => '耗', '��' => '蒙', '��' => '儲', '��' => '木', '��' => '黙', '��' => '目', '��' => '杢', '��' => '勿', '��' => '餅', '��' => '尤', '��' => '戻', '��' => '籾', '��' => '貰', '��' => '問', '��' => '悶', '��' => '紋', '��' => '門', '��' => '匁', '��' => '也', '��' => '冶', '��' => '夜', '��' => '爺', '��' => '耶', '��' => '野', '��' => '弥', '��' => '矢', '��' => '厄', '��' => '役', '��' => '約', '��' => '薬', '��' => '訳', '��' => '躍', '��' => '靖', '��' => '柳', '��' => '薮', '��' => '鑓', '��' => '愉', '��' => '愈', '��' => '油', '��' => '癒', '�@' => '諭', '�A' => '輸', '�B' => '唯', '�C' => '佑', '�D' => '優', '�E' => '勇', '�F' => '友', '�G' => '宥', '�H' => '幽', '�I' => '悠', '�J' => '憂', '�K' => '揖', '�L' => '有', '�M' => '柚', '�N' => '湧', '�O' => '涌', '�P' => '猶', '�Q' => '猷', '�R' => '由', '�S' => '祐', '�T' => '裕', '�U' => '誘', '�V' => '遊', '�W' => '邑', '�X' => '郵', '�Y' => '雄', '�Z' => '融', '�[' => '夕', '�\\' => '予', '�]' => '余', '�^' => '与', '�_' => '誉', '�`' => '輿', '�a' => '預', '�b' => '傭', '�c' => '幼', '�d' => '妖', '�e' => '容', '�f' => '庸', '�g' => '揚', '�h' => '揺', '�i' => '擁', '�j' => '曜', '�k' => '楊', '�l' => '様', '�m' => '洋', '�n' => '溶', '�o' => '熔', '�p' => '用', '�q' => '窯', '�r' => '羊', '�s' => '耀', '�t' => '葉', '�u' => '蓉', '�v' => '要', '�w' => '謡', '�x' => '踊', '�y' => '遥', '�z' => '陽', '�{' => '養', '�|' => '慾', '�}' => '抑', '�~' => '欲', '��' => '沃', '��' => '浴', '��' => '翌', '��' => '翼', '��' => '淀', '��' => '羅', '��' => '螺', '��' => '裸', '��' => '来', '��' => '莱', '��' => '頼', '��' => '雷', '��' => '洛', '��' => '絡', '��' => '落', '��' => '酪', '��' => '乱', '��' => '卵', '��' => '嵐', '��' => '欄', '��' => '濫', '��' => '藍', '��' => '蘭', '��' => '覧', '��' => '利', '��' => '吏', '��' => '履', '��' => '李', '��' => '梨', '��' => '理', '��' => '璃', '��' => '痢', '��' => '裏', '��' => '裡', '��' => '里', '��' => '離', '��' => '陸', '��' => '律', '��' => '率', '��' => '立', '��' => '葎', '��' => '掠', '��' => '略', '��' => '劉', '��' => '流', '��' => '溜', '��' => '琉', '��' => '留', '��' => '硫', '��' => '粒', '��' => '隆', '��' => '竜', '��' => '龍', '��' => '侶', '��' => '慮', '��' => '旅', '��' => '虜', '��' => '了', '��' => '亮', '��' => '僚', '��' => '両', '��' => '凌', '��' => '寮', '��' => '料', '��' => '梁', '��' => '涼', '��' => '猟', '��' => '療', '��' => '瞭', '��' => '稜', '��' => '糧', '��' => '良', '��' => '諒', '��' => '遼', '��' => '量', '��' => '陵', '��' => '領', '��' => '力', '��' => '緑', '��' => '倫', '��' => '厘', '��' => '林', '��' => '淋', '��' => '燐', '��' => '琳', '��' => '臨', '��' => '輪', '��' => '隣', '��' => '鱗', '��' => '麟', '��' => '瑠', '��' => '塁', '��' => '涙', '��' => '累', '��' => '類', '��' => '令', '��' => '伶', '��' => '例', '��' => '冷', '��' => '励', '��' => '嶺', '��' => '怜', '��' => '玲', '��' => '礼', '��' => '苓', '��' => '鈴', '��' => '隷', '��' => '零', '��' => '霊', '��' => '麗', '��' => '齢', '��' => '暦', '��' => '歴', '��' => '列', '��' => '劣', '��' => '烈', '��' => '裂', '��' => '廉', '��' => '恋', '��' => '憐', '��' => '漣', '��' => '煉', '��' => '簾', '��' => '練', '��' => '聯', '�@' => '蓮', '�A' => '連', '�B' => '錬', '�C' => '呂', '�D' => '魯', '�E' => '櫓', '�F' => '炉', '�G' => '賂', '�H' => '路', '�I' => '露', '�J' => '労', '�K' => '婁', '�L' => '廊', '�M' => '弄', '�N' => '朗', '�O' => '楼', '�P' => '榔', '�Q' => '浪', '�R' => '漏', '�S' => '牢', '�T' => '狼', '�U' => '篭', '�V' => '老', '�W' => '聾', '�X' => '蝋', '�Y' => '郎', '�Z' => '六', '�[' => '麓', '�\\' => '禄', '�]' => '肋', '�^' => '録', '�_' => '論', '�`' => '倭', '�a' => '和', '�b' => '話', '�c' => '歪', '�d' => '賄', '�e' => '脇', '�f' => '惑', '�g' => '枠', '�h' => '鷲', '�i' => '亙', '�j' => '亘', '�k' => '鰐', '�l' => '詫', '�m' => '藁', '�n' => '蕨', '�o' => '椀', '�p' => '湾', '�q' => '碗', '�r' => '腕', '��' => '弌', '��' => '丐', '��' => '丕', '��' => '个', '��' => '丱', '��' => '丶', '��' => '丼', '��' => '丿', '��' => '乂', '��' => '乖', '��' => '乘', '��' => '亂', '��' => '亅', '��' => '豫', '��' => '亊', '��' => '舒', '��' => '弍', '��' => '于', '��' => '亞', '��' => '亟', '��' => '亠', '��' => '亢', '��' => '亰', '��' => '亳', '��' => '亶', '��' => '从', '��' => '仍', '��' => '仄', '��' => '仆', '��' => '仂', '��' => '仗', '��' => '仞', '��' => '仭', '��' => '仟', '��' => '价', '��' => '伉', '��' => '佚', '��' => '估', '��' => '佛', '��' => '佝', '��' => '佗', '��' => '佇', '��' => '佶', '��' => '侈', '��' => '侏', '��' => '侘', '��' => '佻', '��' => '佩', '��' => '佰', '��' => '侑', '��' => '佯', '��' => '來', '��' => '侖', '��' => '儘', '��' => '俔', '��' => '俟', '��' => '俎', '��' => '俘', '��' => '俛', '��' => '俑', '��' => '俚', '��' => '俐', '��' => '俤', '��' => '俥', '��' => '倚', '��' => '倨', '��' => '倔', '��' => '倪', '��' => '倥', '��' => '倅', '��' => '伜', '��' => '俶', '��' => '倡', '��' => '倩', '��' => '倬', '��' => '俾', '��' => '俯', '��' => '們', '��' => '倆', '��' => '偃', '��' => '假', '��' => '會', '��' => '偕', '��' => '偐', '��' => '偈', '��' => '做', '��' => '偖', '��' => '偬', '��' => '偸', '��' => '傀', '��' => '傚', '��' => '傅', '��' => '傴', '��' => '傲', '�@' => '僉', '�A' => '僊', '�B' => '傳', '�C' => '僂', '�D' => '僖', '�E' => '僞', '�F' => '僥', '�G' => '僭', '�H' => '僣', '�I' => '僮', '�J' => '價', '�K' => '僵', '�L' => '儉', '�M' => '儁', '�N' => '儂', '�O' => '儖', '�P' => '儕', '�Q' => '儔', '�R' => '儚', '�S' => '儡', '�T' => '儺', '�U' => '儷', '�V' => '儼', '�W' => '儻', '�X' => '儿', '�Y' => '兀', '�Z' => '兒', '�[' => '兌', '�\\' => '兔', '�]' => '兢', '�^' => '竸', '�_' => '兩', '�`' => '兪', '�a' => '兮', '�b' => '冀', '�c' => '冂', '�d' => '囘', '�e' => '册', '�f' => '冉', '�g' => '冏', '�h' => '冑', '�i' => '冓', '�j' => '冕', '�k' => '冖', '�l' => '冤', '�m' => '冦', '�n' => '冢', '�o' => '冩', '�p' => '冪', '�q' => '冫', '�r' => '决', '�s' => '冱', '�t' => '冲', '�u' => '冰', '�v' => '况', '�w' => '冽', '�x' => '凅', '�y' => '凉', '�z' => '凛', '�{' => '几', '�|' => '處', '�}' => '凩', '�~' => '凭', '��' => '凰', '��' => '凵', '��' => '凾', '��' => '刄', '��' => '刋', '��' => '刔', '��' => '刎', '��' => '刧', '��' => '刪', '��' => '刮', '��' => '刳', '��' => '刹', '��' => '剏', '��' => '剄', '��' => '剋', '��' => '剌', '��' => '剞', '��' => '剔', '��' => '剪', '��' => '剴', '��' => '剩', '��' => '剳', '��' => '剿', '��' => '剽', '��' => '劍', '��' => '劔', '��' => '劒', '��' => '剱', '��' => '劈', '��' => '劑', '��' => '辨', '��' => '辧', '��' => '劬', '��' => '劭', '��' => '劼', '��' => '劵', '��' => '勁', '��' => '勍', '��' => '勗', '��' => '勞', '��' => '勣', '��' => '勦', '��' => '飭', '��' => '勠', '��' => '勳', '��' => '勵', '��' => '勸', '��' => '勹', '��' => '匆', '��' => '匈', '��' => '甸', '��' => '匍', '��' => '匐', '��' => '匏', '��' => '匕', '��' => '匚', '��' => '匣', '��' => '匯', '��' => '匱', '��' => '匳', '��' => '匸', '��' => '區', '��' => '卆', '��' => '卅', '��' => '丗', '��' => '卉', '��' => '卍', '��' => '凖', '��' => '卞', '��' => '卩', '��' => '卮', '��' => '夘', '��' => '卻', '��' => '卷', '��' => '厂', '��' => '厖', '��' => '厠', '��' => '厦', '��' => '厥', '��' => '厮', '��' => '厰', '��' => '厶', '��' => '參', '��' => '簒', '��' => '雙', '��' => '叟', '��' => '曼', '��' => '燮', '��' => '叮', '��' => '叨', '��' => '叭', '��' => '叺', '��' => '吁', '��' => '吽', '��' => '呀', '��' => '听', '��' => '吭', '��' => '吼', '��' => '吮', '��' => '吶', '��' => '吩', '��' => '吝', '��' => '呎', '��' => '咏', '��' => '呵', '��' => '咎', '��' => '呟', '��' => '呱', '��' => '呷', '��' => '呰', '��' => '咒', '��' => '呻', '��' => '咀', '��' => '呶', '��' => '咄', '��' => '咐', '��' => '咆', '��' => '哇', '��' => '咢', '��' => '咸', '��' => '咥', '��' => '咬', '��' => '哄', '��' => '哈', '��' => '咨', '�@' => '咫', '�A' => '哂', '�B' => '咤', '�C' => '咾', '�D' => '咼', '�E' => '哘', '�F' => '哥', '�G' => '哦', '�H' => '唏', '�I' => '唔', '�J' => '哽', '�K' => '哮', '�L' => '哭', '�M' => '哺', '�N' => '哢', '�O' => '唹', '�P' => '啀', '�Q' => '啣', '�R' => '啌', '�S' => '售', '�T' => '啜', '�U' => '啅', '�V' => '啖', '�W' => '啗', '�X' => '唸', '�Y' => '唳', '�Z' => '啝', '�[' => '喙', '�\\' => '喀', '�]' => '咯', '�^' => '喊', '�_' => '喟', '�`' => '啻', '�a' => '啾', '�b' => '喘', '�c' => '喞', '�d' => '單', '�e' => '啼', '�f' => '喃', '�g' => '喩', '�h' => '喇', '�i' => '喨', '�j' => '嗚', '�k' => '嗅', '�l' => '嗟', '�m' => '嗄', '�n' => '嗜', '�o' => '嗤', '�p' => '嗔', '�q' => '嘔', '�r' => '嗷', '�s' => '嘖', '�t' => '嗾', '�u' => '嗽', '�v' => '嘛', '�w' => '嗹', '�x' => '噎', '�y' => '噐', '�z' => '營', '�{' => '嘴', '�|' => '嘶', '�}' => '嘲', '�~' => '嘸', '��' => '噫', '��' => '噤', '��' => '嘯', '��' => '噬', '��' => '噪', '��' => '嚆', '��' => '嚀', '��' => '嚊', '��' => '嚠', '��' => '嚔', '��' => '嚏', '��' => '嚥', '��' => '嚮', '��' => '嚶', '��' => '嚴', '��' => '囂', '��' => '嚼', '��' => '囁', '��' => '囃', '��' => '囀', '��' => '囈', '��' => '囎', '��' => '囑', '��' => '囓', '��' => '囗', '��' => '囮', '��' => '囹', '��' => '圀', '��' => '囿', '��' => '圄', '��' => '圉', '��' => '圈', '��' => '國', '��' => '圍', '��' => '圓', '��' => '團', '��' => '圖', '��' => '嗇', '��' => '圜', '��' => '圦', '��' => '圷', '��' => '圸', '��' => '坎', '��' => '圻', '��' => '址', '��' => '坏', '��' => '坩', '��' => '埀', '��' => '垈', '��' => '坡', '��' => '坿', '��' => '垉', '��' => '垓', '��' => '垠', '��' => '垳', '��' => '垤', '��' => '垪', '��' => '垰', '��' => '埃', '��' => '埆', '��' => '埔', '��' => '埒', '��' => '埓', '��' => '堊', '��' => '埖', '��' => '埣', '��' => '堋', '��' => '堙', '��' => '堝', '��' => '塲', '��' => '堡', '��' => '塢', '��' => '塋', '��' => '塰', '��' => '毀', '��' => '塒', '��' => '堽', '��' => '塹', '��' => '墅', '��' => '墹', '��' => '墟', '��' => '墫', '��' => '墺', '��' => '壞', '��' => '墻', '��' => '墸', '��' => '墮', '��' => '壅', '��' => '壓', '��' => '壑', '��' => '壗', '��' => '壙', '��' => '壘', '��' => '壥', '��' => '壜', '��' => '壤', '��' => '壟', '��' => '壯', '��' => '壺', '��' => '壹', '��' => '壻', '��' => '壼', '��' => '壽', '��' => '夂', '��' => '夊', '��' => '夐', '��' => '夛', '��' => '梦', '��' => '夥', '��' => '夬', '��' => '夭', '��' => '夲', '��' => '夸', '��' => '夾', '��' => '竒', '��' => '奕', '��' => '奐', '��' => '奎', '��' => '奚', '��' => '奘', '��' => '奢', '��' => '奠', '��' => '奧', '��' => '奬', '��' => '奩', '�@' => '奸', '�A' => '妁', '�B' => '妝', '�C' => '佞', '�D' => '侫', '�E' => '妣', '�F' => '妲', '�G' => '姆', '�H' => '姨', '�I' => '姜', '�J' => '妍', '�K' => '姙', '�L' => '姚', '�M' => '娥', '�N' => '娟', '�O' => '娑', '�P' => '娜', '�Q' => '娉', '�R' => '娚', '�S' => '婀', '�T' => '婬', '�U' => '婉', '�V' => '娵', '�W' => '娶', '�X' => '婢', '�Y' => '婪', '�Z' => '媚', '�[' => '媼', '�\\' => '媾', '�]' => '嫋', '�^' => '嫂', '�_' => '媽', '�`' => '嫣', '�a' => '嫗', '�b' => '嫦', '�c' => '嫩', '�d' => '嫖', '�e' => '嫺', '�f' => '嫻', '�g' => '嬌', '�h' => '嬋', '�i' => '嬖', '�j' => '嬲', '�k' => '嫐', '�l' => '嬪', '�m' => '嬶', '�n' => '嬾', '�o' => '孃', '�p' => '孅', '�q' => '孀', '�r' => '孑', '�s' => '孕', '�t' => '孚', '�u' => '孛', '�v' => '孥', '�w' => '孩', '�x' => '孰', '�y' => '孳', '�z' => '孵', '�{' => '學', '�|' => '斈', '�}' => '孺', '�~' => '宀', '��' => '它', '��' => '宦', '��' => '宸', '��' => '寃', '��' => '寇', '��' => '寉', '��' => '寔', '��' => '寐', '��' => '寤', '��' => '實', '��' => '寢', '��' => '寞', '��' => '寥', '��' => '寫', '��' => '寰', '��' => '寶', '��' => '寳', '��' => '尅', '��' => '將', '��' => '專', '��' => '對', '��' => '尓', '��' => '尠', '��' => '尢', '��' => '尨', '��' => '尸', '��' => '尹', '��' => '屁', '��' => '屆', '��' => '屎', '��' => '屓', '��' => '屐', '��' => '屏', '��' => '孱', '��' => '屬', '��' => '屮', '��' => '乢', '��' => '屶', '��' => '屹', '��' => '岌', '��' => '岑', '��' => '岔', '��' => '妛', '��' => '岫', '��' => '岻', '��' => '岶', '��' => '岼', '��' => '岷', '��' => '峅', '��' => '岾', '��' => '峇', '��' => '峙', '��' => '峩', '��' => '峽', '��' => '峺', '��' => '峭', '��' => '嶌', '��' => '峪', '��' => '崋', '��' => '崕', '��' => '崗', '��' => '嵜', '��' => '崟', '��' => '崛', '��' => '崑', '��' => '崔', '��' => '崢', '��' => '崚', '��' => '崙', '��' => '崘', '��' => '嵌', '��' => '嵒', '��' => '嵎', '��' => '嵋', '��' => '嵬', '��' => '嵳', '��' => '嵶', '��' => '嶇', '��' => '嶄', '��' => '嶂', '��' => '嶢', '��' => '嶝', '��' => '嶬', '��' => '嶮', '��' => '嶽', '��' => '嶐', '��' => '嶷', '��' => '嶼', '��' => '巉', '��' => '巍', '��' => '巓', '��' => '巒', '��' => '巖', '��' => '巛', '��' => '巫', '��' => '已', '��' => '巵', '��' => '帋', '��' => '帚', '��' => '帙', '��' => '帑', '��' => '帛', '��' => '帶', '��' => '帷', '��' => '幄', '��' => '幃', '��' => '幀', '��' => '幎', '��' => '幗', '��' => '幔', '��' => '幟', '��' => '幢', '��' => '幤', '��' => '幇', '��' => '幵', '��' => '并', '��' => '幺', '��' => '麼', '��' => '广', '��' => '庠', '��' => '廁', '��' => '廂', '��' => '廈', '��' => '廐', '��' => '廏', '�@' => '廖', '�A' => '廣', '�B' => '廝', '�C' => '廚', '�D' => '廛', '�E' => '廢', '�F' => '廡', '�G' => '廨', '�H' => '廩', '�I' => '廬', '�J' => '廱', '�K' => '廳', '�L' => '廰', '�M' => '廴', '�N' => '廸', '�O' => '廾', '�P' => '弃', '�Q' => '弉', '�R' => '彝', '�S' => '彜', '�T' => '弋', '�U' => '弑', '�V' => '弖', '�W' => '弩', '�X' => '弭', '�Y' => '弸', '�Z' => '彁', '�[' => '彈', '�\\' => '彌', '�]' => '彎', '�^' => '弯', '�_' => '彑', '�`' => '彖', '�a' => '彗', '�b' => '彙', '�c' => '彡', '�d' => '彭', '�e' => '彳', '�f' => '彷', '�g' => '徃', '�h' => '徂', '�i' => '彿', '�j' => '徊', '�k' => '很', '�l' => '徑', '�m' => '徇', '�n' => '從', '�o' => '徙', '�p' => '徘', '�q' => '徠', '�r' => '徨', '�s' => '徭', '�t' => '徼', '�u' => '忖', '�v' => '忻', '�w' => '忤', '�x' => '忸', '�y' => '忱', '�z' => '忝', '�{' => '悳', '�|' => '忿', '�}' => '怡', '�~' => '恠', '��' => '怙', '��' => '怐', '��' => '怩', '��' => '怎', '��' => '怱', '��' => '怛', '��' => '怕', '��' => '怫', '��' => '怦', '��' => '怏', '��' => '怺', '��' => '恚', '��' => '恁', '��' => '恪', '��' => '恷', '��' => '恟', '��' => '恊', '��' => '恆', '��' => '恍', '��' => '恣', '��' => '恃', '��' => '恤', '��' => '恂', '��' => '恬', '��' => '恫', '��' => '恙', '��' => '悁', '��' => '悍', '��' => '惧', '��' => '悃', '��' => '悚', '��' => '悄', '��' => '悛', '��' => '悖', '��' => '悗', '��' => '悒', '��' => '悧', '��' => '悋', '��' => '惡', '��' => '悸', '��' => '惠', '��' => '惓', '��' => '悴', '��' => '忰', '��' => '悽', '��' => '惆', '��' => '悵', '��' => '惘', '��' => '慍', '��' => '愕', '��' => '愆', '��' => '惶', '��' => '惷', '��' => '愀', '��' => '惴', '��' => '惺', '��' => '愃', '��' => '愡', '��' => '惻', '��' => '惱', '��' => '愍', '��' => '愎', '��' => '慇', '��' => '愾', '��' => '愨', '��' => '愧', '��' => '慊', '��' => '愿', '��' => '愼', '��' => '愬', '��' => '愴', '��' => '愽', '��' => '慂', '��' => '慄', '��' => '慳', '��' => '慷', '��' => '慘', '��' => '慙', '��' => '慚', '��' => '慫', '��' => '慴', '��' => '慯', '��' => '慥', '��' => '慱', '��' => '慟', '��' => '慝', '��' => '慓', '��' => '慵', '��' => '憙', '��' => '憖', '��' => '憇', '��' => '憬', '��' => '憔', '��' => '憚', '��' => '憊', '��' => '憑', '��' => '憫', '��' => '憮', '��' => '懌', '��' => '懊', '��' => '應', '��' => '懷', '��' => '懈', '��' => '懃', '��' => '懆', '��' => '憺', '��' => '懋', '��' => '罹', '��' => '懍', '��' => '懦', '��' => '懣', '��' => '懶', '��' => '懺', '��' => '懴', '��' => '懿', '��' => '懽', '��' => '懼', '��' => '懾', '��' => '戀', '��' => '戈', '��' => '戉', '��' => '戍', '��' => '戌', '��' => '戔', '��' => '戛', '�@' => '戞', '�A' => '戡', '�B' => '截', '�C' => '戮', '�D' => '戰', '�E' => '戲', '�F' => '戳', '�G' => '扁', '�H' => '扎', '�I' => '扞', '�J' => '扣', '�K' => '扛', '�L' => '扠', '�M' => '扨', '�N' => '扼', '�O' => '抂', '�P' => '抉', '�Q' => '找', '�R' => '抒', '�S' => '抓', '�T' => '抖', '�U' => '拔', '�V' => '抃', '�W' => '抔', '�X' => '拗', '�Y' => '拑', '�Z' => '抻', '�[' => '拏', '�\\' => '拿', '�]' => '拆', '�^' => '擔', '�_' => '拈', '�`' => '拜', '�a' => '拌', '�b' => '拊', '�c' => '拂', '�d' => '拇', '�e' => '抛', '�f' => '拉', '�g' => '挌', '�h' => '拮', '�i' => '拱', '�j' => '挧', '�k' => '挂', '�l' => '挈', '�m' => '拯', '�n' => '拵', '�o' => '捐', '�p' => '挾', '�q' => '捍', '�r' => '搜', '�s' => '捏', '�t' => '掖', '�u' => '掎', '�v' => '掀', '�w' => '掫', '�x' => '捶', '�y' => '掣', '�z' => '掏', '�{' => '掉', '�|' => '掟', '�}' => '掵', '�~' => '捫', '��' => '捩', '��' => '掾', '��' => '揩', '��' => '揀', '��' => '揆', '��' => '揣', '��' => '揉', '��' => '插', '��' => '揶', '��' => '揄', '��' => '搖', '��' => '搴', '��' => '搆', '��' => '搓', '��' => '搦', '��' => '搶', '��' => '攝', '��' => '搗', '��' => '搨', '��' => '搏', '��' => '摧', '��' => '摯', '��' => '摶', '��' => '摎', '��' => '攪', '��' => '撕', '��' => '撓', '��' => '撥', '��' => '撩', '��' => '撈', '��' => '撼', '��' => '據', '��' => '擒', '��' => '擅', '��' => '擇', '��' => '撻', '��' => '擘', '��' => '擂', '��' => '擱', '��' => '擧', '��' => '舉', '��' => '擠', '��' => '擡', '��' => '抬', '��' => '擣', '��' => '擯', '��' => '攬', '��' => '擶', '��' => '擴', '��' => '擲', '��' => '擺', '��' => '攀', '��' => '擽', '��' => '攘', '��' => '攜', '��' => '攅', '��' => '攤', '��' => '攣', '��' => '攫', '��' => '攴', '��' => '攵', '��' => '攷', '��' => '收', '��' => '攸', '��' => '畋', '��' => '效', '��' => '敖', '��' => '敕', '��' => '敍', '��' => '敘', '��' => '敞', '��' => '敝', '��' => '敲', '��' => '數', '��' => '斂', '��' => '斃', '��' => '變', '��' => '斛', '��' => '斟', '��' => '斫', '��' => '斷', '��' => '旃', '��' => '旆', '��' => '旁', '��' => '旄', '��' => '旌', '��' => '旒', '��' => '旛', '��' => '旙', '��' => '无', '��' => '旡', '��' => '旱', '��' => '杲', '��' => '昊', '��' => '昃', '��' => '旻', '��' => '杳', '��' => '昵', '��' => '昶', '��' => '昴', '��' => '昜', '��' => '晏', '��' => '晄', '��' => '晉', '��' => '晁', '��' => '晞', '��' => '晝', '��' => '晤', '��' => '晧', '��' => '晨', '��' => '晟', '��' => '晢', '��' => '晰', '��' => '暃', '��' => '暈', '��' => '暎', '��' => '暉', '��' => '暄', '��' => '暘', '��' => '暝', '��' => '曁', '��' => '暹', '��' => '曉', '��' => '暾', '��' => '暼', '�@' => '曄', '�A' => '暸', '�B' => '曖', '�C' => '曚', '�D' => '曠', '�E' => '昿', '�F' => '曦', '�G' => '曩', '�H' => '曰', '�I' => '曵', '�J' => '曷', '�K' => '朏', '�L' => '朖', '�M' => '朞', '�N' => '朦', '�O' => '朧', '�P' => '霸', '�Q' => '朮', '�R' => '朿', '�S' => '朶', '�T' => '杁', '�U' => '朸', '�V' => '朷', '�W' => '杆', '�X' => '杞', '�Y' => '杠', '�Z' => '杙', '�[' => '杣', '�\\' => '杤', '�]' => '枉', '�^' => '杰', '�_' => '枩', '�`' => '杼', '�a' => '杪', '�b' => '枌', '�c' => '枋', '�d' => '枦', '�e' => '枡', '�f' => '枅', '�g' => '枷', '�h' => '柯', '�i' => '枴', '�j' => '柬', '�k' => '枳', '�l' => '柩', '�m' => '枸', '�n' => '柤', '�o' => '柞', '�p' => '柝', '�q' => '柢', '�r' => '柮', '�s' => '枹', '�t' => '柎', '�u' => '柆', '�v' => '柧', '�w' => '檜', '�x' => '栞', '�y' => '框', '�z' => '栩', '�{' => '桀', '�|' => '桍', '�}' => '栲', '�~' => '桎', '��' => '梳', '��' => '栫', '��' => '桙', '��' => '档', '��' => '桷', '��' => '桿', '��' => '梟', '��' => '梏', '��' => '梭', '��' => '梔', '��' => '條', '��' => '梛', '��' => '梃', '��' => '檮', '��' => '梹', '��' => '桴', '��' => '梵', '��' => '梠', '��' => '梺', '��' => '椏', '��' => '梍', '��' => '桾', '��' => '椁', '��' => '棊', '��' => '椈', '��' => '棘', '��' => '椢', '��' => '椦', '��' => '棡', '��' => '椌', '��' => '棍', '��' => '棔', '��' => '棧', '��' => '棕', '��' => '椶', '��' => '椒', '��' => '椄', '��' => '棗', '��' => '棣', '��' => '椥', '��' => '棹', '��' => '棠', '��' => '棯', '��' => '椨', '��' => '椪', '��' => '椚', '��' => '椣', '��' => '椡', '��' => '棆', '��' => '楹', '��' => '楷', '��' => '楜', '��' => '楸', '��' => '楫', '��' => '楔', '��' => '楾', '��' => '楮', '��' => '椹', '��' => '楴', '��' => '椽', '��' => '楙', '��' => '椰', '��' => '楡', '��' => '楞', '��' => '楝', '��' => '榁', '��' => '楪', '��' => '榲', '��' => '榮', '��' => '槐', '��' => '榿', '��' => '槁', '��' => '槓', '��' => '榾', '��' => '槎', '��' => '寨', '��' => '槊', '��' => '槝', '��' => '榻', '��' => '槃', '��' => '榧', '��' => '樮', '��' => '榑', '��' => '榠', '��' => '榜', '��' => '榕', '��' => '榴', '��' => '槞', '��' => '槨', '��' => '樂', '��' => '樛', '��' => '槿', '��' => '權', '��' => '槹', '��' => '槲', '��' => '槧', '��' => '樅', '��' => '榱', '��' => '樞', '��' => '槭', '��' => '樔', '��' => '槫', '��' => '樊', '��' => '樒', '��' => '櫁', '��' => '樣', '��' => '樓', '��' => '橄', '��' => '樌', '��' => '橲', '��' => '樶', '��' => '橸', '��' => '橇', '��' => '橢', '��' => '橙', '��' => '橦', '��' => '橈', '��' => '樸', '��' => '樢', '��' => '檐', '��' => '檍', '��' => '檠', '��' => '檄', '��' => '檢', '��' => '檣', '�@' => '檗', '�A' => '蘗', '�B' => '檻', '�C' => '櫃', '�D' => '櫂', '�E' => '檸', '�F' => '檳', '�G' => '檬', '�H' => '櫞', '�I' => '櫑', '�J' => '櫟', '�K' => '檪', '�L' => '櫚', '�M' => '櫪', '�N' => '櫻', '�O' => '欅', '�P' => '蘖', '�Q' => '櫺', '�R' => '欒', '�S' => '欖', '�T' => '鬱', '�U' => '欟', '�V' => '欸', '�W' => '欷', '�X' => '盜', '�Y' => '欹', '�Z' => '飮', '�[' => '歇', '�\\' => '歃', '�]' => '歉', '�^' => '歐', '�_' => '歙', '�`' => '歔', '�a' => '歛', '�b' => '歟', '�c' => '歡', '�d' => '歸', '�e' => '歹', '�f' => '歿', '�g' => '殀', '�h' => '殄', '�i' => '殃', '�j' => '殍', '�k' => '殘', '�l' => '殕', '�m' => '殞', '�n' => '殤', '�o' => '殪', '�p' => '殫', '�q' => '殯', '�r' => '殲', '�s' => '殱', '�t' => '殳', '�u' => '殷', '�v' => '殼', '�w' => '毆', '�x' => '毋', '�y' => '毓', '�z' => '毟', '�{' => '毬', '�|' => '毫', '�}' => '毳', '�~' => '毯', '��' => '麾', '��' => '氈', '��' => '氓', '��' => '气', '��' => '氛', '��' => '氤', '��' => '氣', '��' => '汞', '��' => '汕', '��' => '汢', '��' => '汪', '��' => '沂', '��' => '沍', '��' => '沚', '��' => '沁', '��' => '沛', '��' => '汾', '��' => '汨', '��' => '汳', '��' => '沒', '��' => '沐', '��' => '泄', '��' => '泱', '��' => '泓', '��' => '沽', '��' => '泗', '��' => '泅', '��' => '泝', '��' => '沮', '��' => '沱', '��' => '沾', '��' => '沺', '��' => '泛', '��' => '泯', '��' => '泙', '��' => '泪', '��' => '洟', '��' => '衍', '��' => '洶', '��' => '洫', '��' => '洽', '��' => '洸', '��' => '洙', '��' => '洵', '��' => '洳', '��' => '洒', '��' => '洌', '��' => '浣', '��' => '涓', '��' => '浤', '��' => '浚', '��' => '浹', '��' => '浙', '��' => '涎', '��' => '涕', '��' => '濤', '��' => '涅', '��' => '淹', '��' => '渕', '��' => '渊', '��' => '涵', '��' => '淇', '��' => '淦', '��' => '涸', '��' => '淆', '��' => '淬', '��' => '淞', '��' => '淌', '��' => '淨', '��' => '淒', '��' => '淅', '��' => '淺', '��' => '淙', '��' => '淤', '��' => '淕', '��' => '淪', '��' => '淮', '��' => '渭', '��' => '湮', '��' => '渮', '��' => '渙', '��' => '湲', '��' => '湟', '��' => '渾', '��' => '渣', '��' => '湫', '��' => '渫', '��' => '湶', '��' => '湍', '��' => '渟', '��' => '湃', '��' => '渺', '��' => '湎', '��' => '渤', '��' => '滿', '��' => '渝', '��' => '游', '��' => '溂', '��' => '溪', '��' => '溘', '��' => '滉', '��' => '溷', '��' => '滓', '��' => '溽', '��' => '溯', '��' => '滄', '��' => '溲', '��' => '滔', '��' => '滕', '��' => '溏', '��' => '溥', '��' => '滂', '��' => '溟', '��' => '潁', '��' => '漑', '��' => '灌', '��' => '滬', '��' => '滸', '��' => '滾', '��' => '漿', '��' => '滲', '��' => '漱', '��' => '滯', '��' => '漲', '��' => '滌', '�@' => '漾', '�A' => '漓', '�B' => '滷', '�C' => '澆', '�D' => '潺', '�E' => '潸', '�F' => '澁', '�G' => '澀', '�H' => '潯', '�I' => '潛', '�J' => '濳', '�K' => '潭', '�L' => '澂', '�M' => '潼', '�N' => '潘', '�O' => '澎', '�P' => '澑', '�Q' => '濂', '�R' => '潦', '�S' => '澳', '�T' => '澣', '�U' => '澡', '�V' => '澤', '�W' => '澹', '�X' => '濆', '�Y' => '澪', '�Z' => '濟', '�[' => '濕', '�\\' => '濬', '�]' => '濔', '�^' => '濘', '�_' => '濱', '�`' => '濮', '�a' => '濛', '�b' => '瀉', '�c' => '瀋', '�d' => '濺', '�e' => '瀑', '�f' => '瀁', '�g' => '瀏', '�h' => '濾', '�i' => '瀛', '�j' => '瀚', '�k' => '潴', '�l' => '瀝', '�m' => '瀘', '�n' => '瀟', '�o' => '瀰', '�p' => '瀾', '�q' => '瀲', '�r' => '灑', '�s' => '灣', '�t' => '炙', '�u' => '炒', '�v' => '炯', '�w' => '烱', '�x' => '炬', '�y' => '炸', '�z' => '炳', '�{' => '炮', '�|' => '烟', '�}' => '烋', '�~' => '烝', '�' => '烙', '�' => '焉', '�' => '烽', '�' => '焜', '�' => '焙', '�' => '煥', '�' => '煕', '�' => '熈', '�' => '煦', '�' => '煢', '�' => '煌', '�' => '煖', '�' => '煬', '�' => '熏', '�' => '燻', '�' => '熄', '�' => '熕', '�' => '熨', '�' => '熬', '�' => '燗', '�' => '熹', '�' => '熾', '�' => '燒', '�' => '燉', '�' => '燔', '�' => '燎', '�' => '燠', '�' => '燬', '�' => '燧', '�' => '燵', '�' => '燼', '�' => '燹', '�' => '燿', '�' => '爍', '�' => '爐', '�' => '爛', '�' => '爨', '�' => '爭', '�' => '爬', '�' => '爰', '�' => '爲', '�' => '爻', '�' => '爼', '�' => '爿', '�' => '牀', '�' => '牆', '�' => '牋', '�' => '牘', '�' => '牴', '�' => '牾', '�' => '犂', '�' => '犁', '�' => '犇', '�' => '犒', '�' => '犖', '�' => '犢', '�' => '犧', '�' => '犹', '�' => '犲', '�' => '狃', '�' => '狆', '�' => '狄', '�' => '狎', '�' => '狒', '�' => '狢', '�' => '狠', '��' => '狡', '��' => '狹', '��' => '狷', '��' => '倏', '��' => '猗', '��' => '猊', '��' => '猜', '��' => '猖', '��' => '猝', '��' => '猴', '��' => '猯', '��' => '猩', '��' => '猥', '��' => '猾', '��' => '獎', '��' => '獏', '��' => '默', '��' => '獗', '��' => '獪', '��' => '獨', '��' => '獰', '��' => '獸', '��' => '獵', '��' => '獻', '��' => '獺', '��' => '珈', '��' => '玳', '��' => '珎', '��' => '玻', '��' => '珀', '��' => '珥', '��' => '珮', '��' => '珞', '��' => '璢', '��' => '琅', '��' => '瑯', '��' => '琥', '��' => '珸', '��' => '琲', '��' => '琺', '��' => '瑕', '��' => '琿', '��' => '瑟', '��' => '瑙', '��' => '瑁', '��' => '瑜', '��' => '瑩', '��' => '瑰', '��' => '瑣', '��' => '瑪', '��' => '瑶', '�' => '瑾', '�' => '璋', '�' => '璞', '�' => '璧', '�' => '瓊', '�' => '瓏', '�' => '瓔', '�' => '珱', '�@' => '瓠', '�A' => '瓣', '�B' => '瓧', '�C' => '瓩', '�D' => '瓮', '�E' => '瓲', '�F' => '瓰', '�G' => '瓱', '�H' => '瓸', '�I' => '瓷', '�J' => '甄', '�K' => '甃', '�L' => '甅', '�M' => '甌', '�N' => '甎', '�O' => '甍', '�P' => '甕', '�Q' => '甓', '�R' => '甞', '�S' => '甦', '�T' => '甬', '�U' => '甼', '�V' => '畄', '�W' => '畍', '�X' => '畊', '�Y' => '畉', '�Z' => '畛', '�[' => '畆', '�\\' => '畚', '�]' => '畩', '�^' => '畤', '�_' => '畧', '�`' => '畫', '�a' => '畭', '�b' => '畸', '�c' => '當', '�d' => '疆', '�e' => '疇', '�f' => '畴', '�g' => '疊', '�h' => '疉', '�i' => '疂', '�j' => '疔', '�k' => '疚', '�l' => '疝', '�m' => '疥', '�n' => '疣', '�o' => '痂', '�p' => '疳', '�q' => '痃', '�r' => '疵', '�s' => '疽', '�t' => '疸', '�u' => '疼', '�v' => '疱', '�w' => '痍', '�x' => '痊', '�y' => '痒', '�z' => '痙', '�{' => '痣', '�|' => '痞', '�}' => '痾', '�~' => '痿', '�' => '痼', '�' => '瘁', '�' => '痰', '�' => '痺', '�' => '痲', '�' => '痳', '�' => '瘋', '�' => '瘍', '�' => '瘉', '�' => '瘟', '�' => '瘧', '�' => '瘠', '�' => '瘡', '�' => '瘢', '�' => '瘤', '�' => '瘴', '�' => '瘰', '�' => '瘻', '�' => '癇', '�' => '癈', '�' => '癆', '�' => '癜', '�' => '癘', '�' => '癡', '�' => '癢', '�' => '癨', '�' => '癩', '�' => '癪', '�' => '癧', '�' => '癬', '�' => '癰', '�' => '癲', '�' => '癶', '�' => '癸', '�' => '發', '�' => '皀', '�' => '皃', '�' => '皈', '�' => '皋', '�' => '皎', '�' => '皖', '�' => '皓', '�' => '皙', '�' => '皚', '�' => '皰', '�' => '皴', '�' => '皸', '�' => '皹', '�' => '皺', '�' => '盂', '�' => '盍', '�' => '盖', '�' => '盒', '�' => '盞', '�' => '盡', '�' => '盥', '�' => '盧', '�' => '盪', '�' => '蘯', '�' => '盻', '�' => '眈', '�' => '眇', '�' => '眄', '�' => '眩', '�' => '眤', '�' => '眞', '��' => '眥', '��' => '眦', '��' => '眛', '��' => '眷', '��' => '眸', '��' => '睇', '��' => '睚', '��' => '睨', '��' => '睫', '��' => '睛', '��' => '睥', '��' => '睿', '��' => '睾', '��' => '睹', '��' => '瞎', '��' => '瞋', '��' => '瞑', '��' => '瞠', '��' => '瞞', '��' => '瞰', '��' => '瞶', '��' => '瞹', '��' => '瞿', '��' => '瞼', '��' => '瞽', '��' => '瞻', '��' => '矇', '��' => '矍', '��' => '矗', '��' => '矚', '��' => '矜', '��' => '矣', '��' => '矮', '��' => '矼', '��' => '砌', '��' => '砒', '��' => '礦', '��' => '砠', '��' => '礪', '��' => '硅', '��' => '碎', '��' => '硴', '��' => '碆', '��' => '硼', '��' => '碚', '��' => '碌', '��' => '碣', '��' => '碵', '��' => '碪', '��' => '碯', '��' => '磑', '�' => '磆', '�' => '磋', '�' => '磔', '�' => '碾', '�' => '碼', '�' => '磅', '�' => '磊', '�' => '磬', '�@' => '磧', '�A' => '磚', '�B' => '磽', '�C' => '磴', '�D' => '礇', '�E' => '礒', '�F' => '礑', '�G' => '礙', '�H' => '礬', '�I' => '礫', '�J' => '祀', '�K' => '祠', '�L' => '祗', '�M' => '祟', '�N' => '祚', '�O' => '祕', '�P' => '祓', '�Q' => '祺', '�R' => '祿', '�S' => '禊', '�T' => '禝', '�U' => '禧', '�V' => '齋', '�W' => '禪', '�X' => '禮', '�Y' => '禳', '�Z' => '禹', '�[' => '禺', '�\\' => '秉', '�]' => '秕', '�^' => '秧', '�_' => '秬', '�`' => '秡', '�a' => '秣', '�b' => '稈', '�c' => '稍', '�d' => '稘', '�e' => '稙', '�f' => '稠', '�g' => '稟', '�h' => '禀', '�i' => '稱', '�j' => '稻', '�k' => '稾', '�l' => '稷', '�m' => '穃', '�n' => '穗', '�o' => '穉', '�p' => '穡', '�q' => '穢', '�r' => '穩', '�s' => '龝', '�t' => '穰', '�u' => '穹', '�v' => '穽', '�w' => '窈', '�x' => '窗', '�y' => '窕', '�z' => '窘', '�{' => '窖', '�|' => '窩', '�}' => '竈', '�~' => '窰', '�' => '窶', '�' => '竅', '�' => '竄', '�' => '窿', '�' => '邃', '�' => '竇', '�' => '竊', '�' => '竍', '�' => '竏', '�' => '竕', '�' => '竓', '�' => '站', '�' => '竚', '�' => '竝', '�' => '竡', '�' => '竢', '�' => '竦', '�' => '竭', '�' => '竰', '�' => '笂', '�' => '笏', '�' => '笊', '�' => '笆', '�' => '笳', '�' => '笘', '�' => '笙', '�' => '笞', '�' => '笵', '�' => '笨', '�' => '笶', '�' => '筐', '�' => '筺', '�' => '笄', '�' => '筍', '�' => '笋', '�' => '筌', '�' => '筅', '�' => '筵', '�' => '筥', '�' => '筴', '�' => '筧', '�' => '筰', '�' => '筱', '�' => '筬', '�' => '筮', '�' => '箝', '�' => '箘', '�' => '箟', '�' => '箍', '�' => '箜', '�' => '箚', '�' => '箋', '�' => '箒', '�' => '箏', '�' => '筝', '�' => '箙', '�' => '篋', '�' => '篁', '�' => '篌', '�' => '篏', '�' => '箴', '�' => '篆', '�' => '篝', '�' => '篩', '�' => '簑', '�' => '簔', '��' => '篦', '��' => '篥', '��' => '籠', '��' => '簀', '��' => '簇', '��' => '簓', '��' => '篳', '��' => '篷', '��' => '簗', '��' => '簍', '��' => '篶', '��' => '簣', '��' => '簧', '��' => '簪', '��' => '簟', '��' => '簷', '��' => '簫', '��' => '簽', '��' => '籌', '��' => '籃', '��' => '籔', '��' => '籏', '��' => '籀', '��' => '籐', '��' => '籘', '��' => '籟', '��' => '籤', '��' => '籖', '��' => '籥', '��' => '籬', '��' => '籵', '��' => '粃', '��' => '粐', '��' => '粤', '��' => '粭', '��' => '粢', '��' => '粫', '��' => '粡', '��' => '粨', '��' => '粳', '��' => '粲', '��' => '粱', '��' => '粮', '��' => '粹', '��' => '粽', '��' => '糀', '��' => '糅', '��' => '糂', '��' => '糘', '��' => '糒', '��' => '糜', '�' => '糢', '�' => '鬻', '�' => '糯', '�' => '糲', '�' => '糴', '�' => '糶', '�' => '糺', '�' => '紆', '�@' => '紂', '�A' => '紜', '�B' => '紕', '�C' => '紊', '�D' => '絅', '�E' => '絋', '�F' => '紮', '�G' => '紲', '�H' => '紿', '�I' => '紵', '�J' => '絆', '�K' => '絳', '�L' => '絖', '�M' => '絎', '�N' => '絲', '�O' => '絨', '�P' => '絮', '�Q' => '絏', '�R' => '絣', '�S' => '經', '�T' => '綉', '�U' => '絛', '�V' => '綏', '�W' => '絽', '�X' => '綛', '�Y' => '綺', '�Z' => '綮', '�[' => '綣', '�\\' => '綵', '�]' => '緇', '�^' => '綽', '�_' => '綫', '�`' => '總', '�a' => '綢', '�b' => '綯', '�c' => '緜', '�d' => '綸', '�e' => '綟', '�f' => '綰', '�g' => '緘', '�h' => '緝', '�i' => '緤', '�j' => '緞', '�k' => '緻', '�l' => '緲', '�m' => '緡', '�n' => '縅', '�o' => '縊', '�p' => '縣', '�q' => '縡', '�r' => '縒', '�s' => '縱', '�t' => '縟', '�u' => '縉', '�v' => '縋', '�w' => '縢', '�x' => '繆', '�y' => '繦', '�z' => '縻', '�{' => '縵', '�|' => '縹', '�}' => '繃', '�~' => '縷', '�' => '縲', '�' => '縺', '�' => '繧', '�' => '繝', '�' => '繖', '�' => '繞', '�' => '繙', '�' => '繚', '�' => '繹', '�' => '繪', '�' => '繩', '�' => '繼', '�' => '繻', '�' => '纃', '�' => '緕', '�' => '繽', '�' => '辮', '�' => '繿', '�' => '纈', '�' => '纉', '�' => '續', '�' => '纒', '�' => '纐', '�' => '纓', '�' => '纔', '�' => '纖', '�' => '纎', '�' => '纛', '�' => '纜', '�' => '缸', '�' => '缺', '�' => '罅', '�' => '罌', '�' => '罍', '�' => '罎', '�' => '罐', '�' => '网', '�' => '罕', '�' => '罔', '�' => '罘', '�' => '罟', '�' => '罠', '�' => '罨', '�' => '罩', '�' => '罧', '�' => '罸', '�' => '羂', '�' => '羆', '�' => '羃', '�' => '羈', '�' => '羇', '�' => '羌', '�' => '羔', '�' => '羞', '�' => '羝', '�' => '羚', '�' => '羣', '�' => '羯', '�' => '羲', '�' => '羹', '�' => '羮', '�' => '羶', '�' => '羸', '�' => '譱', '�' => '翅', '�' => '翆', '��' => '翊', '��' => '翕', '��' => '翔', '��' => '翡', '��' => '翦', '��' => '翩', '��' => '翳', '��' => '翹', '��' => '飜', '��' => '耆', '��' => '耄', '��' => '耋', '��' => '耒', '��' => '耘', '��' => '耙', '��' => '耜', '��' => '耡', '��' => '耨', '��' => '耿', '��' => '耻', '��' => '聊', '��' => '聆', '��' => '聒', '��' => '聘', '��' => '聚', '��' => '聟', '��' => '聢', '��' => '聨', '��' => '聳', '��' => '聲', '��' => '聰', '��' => '聶', '��' => '聹', '��' => '聽', '��' => '聿', '��' => '肄', '��' => '肆', '��' => '肅', '��' => '肛', '��' => '肓', '��' => '肚', '��' => '肭', '��' => '冐', '��' => '肬', '��' => '胛', '��' => '胥', '��' => '胙', '��' => '胝', '��' => '胄', '��' => '胚', '��' => '胖', '�' => '脉', '�' => '胯', '�' => '胱', '�' => '脛', '�' => '脩', '�' => '脣', '�' => '脯', '�' => '腋', '�@' => '隋', '�A' => '腆', '�B' => '脾', '�C' => '腓', '�D' => '腑', '�E' => '胼', '�F' => '腱', '�G' => '腮', '�H' => '腥', '�I' => '腦', '�J' => '腴', '�K' => '膃', '�L' => '膈', '�M' => '膊', '�N' => '膀', '�O' => '膂', '�P' => '膠', '�Q' => '膕', '�R' => '膤', '�S' => '膣', '�T' => '腟', '�U' => '膓', '�V' => '膩', '�W' => '膰', '�X' => '膵', '�Y' => '膾', '�Z' => '膸', '�[' => '膽', '�\\' => '臀', '�]' => '臂', '�^' => '膺', '�_' => '臉', '�`' => '臍', '�a' => '臑', '�b' => '臙', '�c' => '臘', '�d' => '臈', '�e' => '臚', '�f' => '臟', '�g' => '臠', '�h' => '臧', '�i' => '臺', '�j' => '臻', '�k' => '臾', '�l' => '舁', '�m' => '舂', '�n' => '舅', '�o' => '與', '�p' => '舊', '�q' => '舍', '�r' => '舐', '�s' => '舖', '�t' => '舩', '�u' => '舫', '�v' => '舸', '�w' => '舳', '�x' => '艀', '�y' => '艙', '�z' => '艘', '�{' => '艝', '�|' => '艚', '�}' => '艟', '�~' => '艤', '�' => '艢', '�' => '艨', '�' => '艪', '�' => '艫', '�' => '舮', '�' => '艱', '�' => '艷', '�' => '艸', '�' => '艾', '�' => '芍', '�' => '芒', '�' => '芫', '�' => '芟', '�' => '芻', '�' => '芬', '�' => '苡', '�' => '苣', '�' => '苟', '�' => '苒', '�' => '苴', '�' => '苳', '�' => '苺', '�' => '莓', '�' => '范', '�' => '苻', '�' => '苹', '�' => '苞', '�' => '茆', '�' => '苜', '�' => '茉', '�' => '苙', '�' => '茵', '�' => '茴', '�' => '茖', '�' => '茲', '�' => '茱', '�' => '荀', '�' => '茹', '�' => '荐', '�' => '荅', '�' => '茯', '�' => '茫', '�' => '茗', '�' => '茘', '�' => '莅', '�' => '莚', '�' => '莪', '�' => '莟', '�' => '莢', '�' => '莖', '�' => '茣', '�' => '莎', '�' => '莇', '�' => '莊', '�' => '荼', '�' => '莵', '�' => '荳', '�' => '荵', '�' => '莠', '�' => '莉', '�' => '莨', '�' => '菴', '�' => '萓', '�' => '菫', '�' => '菎', '�' => '菽', '��' => '萃', '��' => '菘', '��' => '萋', '��' => '菁', '��' => '菷', '��' => '萇', '��' => '菠', '��' => '菲', '��' => '萍', '��' => '萢', '��' => '萠', '��' => '莽', '��' => '萸', '��' => '蔆', '��' => '菻', '��' => '葭', '��' => '萪', '��' => '萼', '��' => '蕚', '��' => '蒄', '��' => '葷', '��' => '葫', '��' => '蒭', '��' => '葮', '��' => '蒂', '��' => '葩', '��' => '葆', '��' => '萬', '��' => '葯', '��' => '葹', '��' => '萵', '��' => '蓊', '��' => '葢', '��' => '蒹', '��' => '蒿', '��' => '蒟', '��' => '蓙', '��' => '蓍', '��' => '蒻', '��' => '蓚', '��' => '蓐', '��' => '蓁', '��' => '蓆', '��' => '蓖', '��' => '蒡', '��' => '蔡', '��' => '蓿', '��' => '蓴', '��' => '蔗', '��' => '蔘', '��' => '蔬', '�' => '蔟', '�' => '蔕', '�' => '蔔', '�' => '蓼', '�' => '蕀', '�' => '蕣', '�' => '蕘', '�' => '蕈', '�@' => '蕁', '�A' => '蘂', '�B' => '蕋', '�C' => '蕕', '�D' => '薀', '�E' => '薤', '�F' => '薈', '�G' => '薑', '�H' => '薊', '�I' => '薨', '�J' => '蕭', '�K' => '薔', '�L' => '薛', '�M' => '藪', '�N' => '薇', '�O' => '薜', '�P' => '蕷', '�Q' => '蕾', '�R' => '薐', '�S' => '藉', '�T' => '薺', '�U' => '藏', '�V' => '薹', '�W' => '藐', '�X' => '藕', '�Y' => '藝', '�Z' => '藥', '�[' => '藜', '�\\' => '藹', '�]' => '蘊', '�^' => '蘓', '�_' => '蘋', '�`' => '藾', '�a' => '藺', '�b' => '蘆', '�c' => '蘢', '�d' => '蘚', '�e' => '蘰', '�f' => '蘿', '�g' => '虍', '�h' => '乕', '�i' => '虔', '�j' => '號', '�k' => '虧', '�l' => '虱', '�m' => '蚓', '�n' => '蚣', '�o' => '蚩', '�p' => '蚪', '�q' => '蚋', '�r' => '蚌', '�s' => '蚶', '�t' => '蚯', '�u' => '蛄', '�v' => '蛆', '�w' => '蚰', '�x' => '蛉', '�y' => '蠣', '�z' => '蚫', '�{' => '蛔', '�|' => '蛞', '�}' => '蛩', '�~' => '蛬', '�' => '蛟', '�' => '蛛', '�' => '蛯', '�' => '蜒', '�' => '蜆', '�' => '蜈', '�' => '蜀', '�' => '蜃', '�' => '蛻', '�' => '蜑', '�' => '蜉', '�' => '蜍', '�' => '蛹', '�' => '蜊', '�' => '蜴', '�' => '蜿', '�' => '蜷', '�' => '蜻', '�' => '蜥', '�' => '蜩', '�' => '蜚', '�' => '蝠', '�' => '蝟', '�' => '蝸', '�' => '蝌', '�' => '蝎', '�' => '蝴', '�' => '蝗', '�' => '蝨', '�' => '蝮', '�' => '蝙', '�' => '蝓', '�' => '蝣', '�' => '蝪', '�' => '蠅', '�' => '螢', '�' => '螟', '�' => '螂', '�' => '螯', '�' => '蟋', '�' => '螽', '�' => '蟀', '�' => '蟐', '�' => '雖', '�' => '螫', '�' => '蟄', '�' => '螳', '�' => '蟇', '�' => '蟆', '�' => '螻', '�' => '蟯', '�' => '蟲', '�' => '蟠', '�' => '蠏', '�' => '蠍', '�' => '蟾', '�' => '蟶', '�' => '蟷', '�' => '蠎', '�' => '蟒', '�' => '蠑', '�' => '蠖', '�' => '蠕', '�' => '蠢', '�' => '蠡', '�' => '蠱', '��' => '蠶', '��' => '蠹', '��' => '蠧', '��' => '蠻', '��' => '衄', '��' => '衂', '��' => '衒', '��' => '衙', '��' => '衞', '��' => '衢', '��' => '衫', '��' => '袁', '��' => '衾', '��' => '袞', '��' => '衵', '��' => '衽', '��' => '袵', '��' => '衲', '��' => '袂', '��' => '袗', '��' => '袒', '��' => '袮', '��' => '袙', '��' => '袢', '��' => '袍', '��' => '袤', '��' => '袰', '��' => '袿', '��' => '袱', '��' => '裃', '��' => '裄', '��' => '裔', '��' => '裘', '��' => '裙', '��' => '裝', '��' => '裹', '��' => '褂', '��' => '裼', '��' => '裴', '��' => '裨', '��' => '裲', '��' => '褄', '��' => '褌', '��' => '褊', '��' => '褓', '��' => '襃', '��' => '褞', '��' => '褥', '��' => '褪', '��' => '褫', '��' => '襁', '�' => '襄', '�' => '褻', '�' => '褶', '�' => '褸', '�' => '襌', '�' => '褝', '�' => '襠', '�' => '襞', '�@' => '襦', '�A' => '襤', '�B' => '襭', '�C' => '襪', '�D' => '襯', '�E' => '襴', '�F' => '襷', '�G' => '襾', '�H' => '覃', '�I' => '覈', '�J' => '覊', '�K' => '覓', '�L' => '覘', '�M' => '覡', '�N' => '覩', '�O' => '覦', '�P' => '覬', '�Q' => '覯', '�R' => '覲', '�S' => '覺', '�T' => '覽', '�U' => '覿', '�V' => '觀', '�W' => '觚', '�X' => '觜', '�Y' => '觝', '�Z' => '觧', '�[' => '觴', '�\\' => '觸', '�]' => '訃', '�^' => '訖', '�_' => '訐', '�`' => '訌', '�a' => '訛', '�b' => '訝', '�c' => '訥', '�d' => '訶', '�e' => '詁', '�f' => '詛', '�g' => '詒', '�h' => '詆', '�i' => '詈', '�j' => '詼', '�k' => '詭', '�l' => '詬', '�m' => '詢', '�n' => '誅', '�o' => '誂', '�p' => '誄', '�q' => '誨', '�r' => '誡', '�s' => '誑', '�t' => '誥', '�u' => '誦', '�v' => '誚', '�w' => '誣', '�x' => '諄', '�y' => '諍', '�z' => '諂', '�{' => '諚', '�|' => '諫', '�}' => '諳', '�~' => '諧', '�' => '諤', '�' => '諱', '�' => '謔', '�' => '諠', '�' => '諢', '�' => '諷', '�' => '諞', '�' => '諛', '�' => '謌', '�' => '謇', '�' => '謚', '�' => '諡', '�' => '謖', '�' => '謐', '�' => '謗', '�' => '謠', '�' => '謳', '�' => '鞫', '�' => '謦', '�' => '謫', '�' => '謾', '�' => '謨', '�' => '譁', '�' => '譌', '�' => '譏', '�' => '譎', '�' => '證', '�' => '譖', '�' => '譛', '�' => '譚', '�' => '譫', '�' => '譟', '�' => '譬', '�' => '譯', '�' => '譴', '�' => '譽', '�' => '讀', '�' => '讌', '�' => '讎', '�' => '讒', '�' => '讓', '�' => '讖', '�' => '讙', '�' => '讚', '�' => '谺', '�' => '豁', '�' => '谿', '�' => '豈', '�' => '豌', '�' => '豎', '�' => '豐', '�' => '豕', '�' => '豢', '�' => '豬', '�' => '豸', '�' => '豺', '�' => '貂', '�' => '貉', '�' => '貅', '�' => '貊', '�' => '貍', '�' => '貎', '�' => '貔', '�' => '豼', '�' => '貘', '�' => '戝', '��' => '貭', '��' => '貪', '��' => '貽', '��' => '貲', '��' => '貳', '��' => '貮', '��' => '貶', '��' => '賈', '��' => '賁', '��' => '賤', '��' => '賣', '��' => '賚', '��' => '賽', '��' => '賺', '��' => '賻', '��' => '贄', '��' => '贅', '��' => '贊', '��' => '贇', '��' => '贏', '��' => '贍', '��' => '贐', '��' => '齎', '��' => '贓', '��' => '賍', '��' => '贔', '��' => '贖', '��' => '赧', '��' => '赭', '��' => '赱', '��' => '赳', '��' => '趁', '��' => '趙', '��' => '跂', '��' => '趾', '��' => '趺', '��' => '跏', '��' => '跚', '��' => '跖', '��' => '跌', '��' => '跛', '��' => '跋', '��' => '跪', '��' => '跫', '��' => '跟', '��' => '跣', '��' => '跼', '��' => '踈', '��' => '踉', '��' => '跿', '��' => '踝', '�' => '踞', '�' => '踐', '�' => '踟', '�' => '蹂', '�' => '踵', '�' => '踰', '�' => '踴', '�' => '蹊', '�@' => '蹇', '�A' => '蹉', '�B' => '蹌', '�C' => '蹐', '�D' => '蹈', '�E' => '蹙', '�F' => '蹤', '�G' => '蹠', '�H' => '踪', '�I' => '蹣', '�J' => '蹕', '�K' => '蹶', '�L' => '蹲', '�M' => '蹼', '�N' => '躁', '�O' => '躇', '�P' => '躅', '�Q' => '躄', '�R' => '躋', '�S' => '躊', '�T' => '躓', '�U' => '躑', '�V' => '躔', '�W' => '躙', '�X' => '躪', '�Y' => '躡', '�Z' => '躬', '�[' => '躰', '�\\' => '軆', '�]' => '躱', '�^' => '躾', '�_' => '軅', '�`' => '軈', '�a' => '軋', '�b' => '軛', '�c' => '軣', '�d' => '軼', '�e' => '軻', '�f' => '軫', '�g' => '軾', '�h' => '輊', '�i' => '輅', '�j' => '輕', '�k' => '輒', '�l' => '輙', '�m' => '輓', '�n' => '輜', '�o' => '輟', '�p' => '輛', '�q' => '輌', '�r' => '輦', '�s' => '輳', '�t' => '輻', '�u' => '輹', '�v' => '轅', '�w' => '轂', '�x' => '輾', '�y' => '轌', '�z' => '轉', '�{' => '轆', '�|' => '轎', '�}' => '轗', '�~' => '轜', '�' => '轢', '�' => '轣', '�' => '轤', '�' => '辜', '�' => '辟', '�' => '辣', '�' => '辭', '�' => '辯', '�' => '辷', '�' => '迚', '�' => '迥', '�' => '迢', '�' => '迪', '�' => '迯', '�' => '邇', '�' => '迴', '�' => '逅', '�' => '迹', '�' => '迺', '�' => '逑', '�' => '逕', '�' => '逡', '�' => '逍', '�' => '逞', '�' => '逖', '�' => '逋', '�' => '逧', '�' => '逶', '�' => '逵', '�' => '逹', '�' => '迸', '�' => '遏', '�' => '遐', '�' => '遑', '�' => '遒', '�' => '逎', '�' => '遉', '�' => '逾', '�' => '遖', '�' => '遘', '�' => '遞', '�' => '遨', '�' => '遯', '�' => '遶', '�' => '隨', '�' => '遲', '�' => '邂', '�' => '遽', '�' => '邁', '�' => '邀', '�' => '邊', '�' => '邉', '�' => '邏', '�' => '邨', '�' => '邯', '�' => '邱', '�' => '邵', '�' => '郢', '�' => '郤', '�' => '扈', '�' => '郛', '�' => '鄂', '�' => '鄒', '�' => '鄙', '�' => '鄲', '�' => '鄰', '��' => '酊', '��' => '酖', '��' => '酘', '��' => '酣', '��' => '酥', '��' => '酩', '��' => '酳', '��' => '酲', '��' => '醋', '��' => '醉', '��' => '醂', '��' => '醢', '��' => '醫', '��' => '醯', '��' => '醪', '��' => '醵', '��' => '醴', '��' => '醺', '��' => '釀', '��' => '釁', '��' => '釉', '��' => '釋', '��' => '釐', '��' => '釖', '��' => '釟', '��' => '釡', '��' => '釛', '��' => '釼', '��' => '釵', '��' => '釶', '��' => '鈞', '��' => '釿', '��' => '鈔', '��' => '鈬', '��' => '鈕', '��' => '鈑', '��' => '鉞', '��' => '鉗', '��' => '鉅', '��' => '鉉', '��' => '鉤', '��' => '鉈', '��' => '銕', '��' => '鈿', '��' => '鉋', '��' => '鉐', '��' => '銜', '��' => '銖', '��' => '銓', '��' => '銛', '��' => '鉚', '�' => '鋏', '�' => '銹', '�' => '銷', '�' => '鋩', '�' => '錏', '�' => '鋺', '�' => '鍄', '�' => '錮', '�@' => '錙', '�A' => '錢', '�B' => '錚', '�C' => '錣', '�D' => '錺', '�E' => '錵', '�F' => '錻', '�G' => '鍜', '�H' => '鍠', '�I' => '鍼', '�J' => '鍮', '�K' => '鍖', '�L' => '鎰', '�M' => '鎬', '�N' => '鎭', '�O' => '鎔', '�P' => '鎹', '�Q' => '鏖', '�R' => '鏗', '�S' => '鏨', '�T' => '鏥', '�U' => '鏘', '�V' => '鏃', '�W' => '鏝', '�X' => '鏐', '�Y' => '鏈', '�Z' => '鏤', '�[' => '鐚', '�\\' => '鐔', '�]' => '鐓', '�^' => '鐃', '�_' => '鐇', '�`' => '鐐', '�a' => '鐶', '�b' => '鐫', '�c' => '鐵', '�d' => '鐡', '�e' => '鐺', '�f' => '鑁', '�g' => '鑒', '�h' => '鑄', '�i' => '鑛', '�j' => '鑠', '�k' => '鑢', '�l' => '鑞', '�m' => '鑪', '�n' => '鈩', '�o' => '鑰', '�p' => '鑵', '�q' => '鑷', '�r' => '鑽', '�s' => '鑚', '�t' => '鑼', '�u' => '鑾', '�v' => '钁', '�w' => '鑿', '�x' => '閂', '�y' => '閇', '�z' => '閊', '�{' => '閔', '�|' => '閖', '�}' => '閘', '�~' => '閙', '�' => '閠', '�' => '閨', '�' => '閧', '�' => '閭', '�' => '閼', '�' => '閻', '�' => '閹', '�' => '閾', '�' => '闊', '�' => '濶', '�' => '闃', '�' => '闍', '�' => '闌', '�' => '闕', '�' => '闔', '�' => '闖', '�' => '關', '�' => '闡', '�' => '闥', '�' => '闢', '�' => '阡', '�' => '阨', '�' => '阮', '�' => '阯', '�' => '陂', '�' => '陌', '�' => '陏', '�' => '陋', '�' => '陷', '�' => '陜', '�' => '陞', '�' => '陝', '�' => '陟', '�' => '陦', '�' => '陲', '�' => '陬', '�' => '隍', '�' => '隘', '�' => '隕', '�' => '隗', '�' => '險', '�' => '隧', '�' => '隱', '�' => '隲', '�' => '隰', '�' => '隴', '�' => '隶', '�' => '隸', '�' => '隹', '�' => '雎', '�' => '雋', '�' => '雉', '�' => '雍', '�' => '襍', '�' => '雜', '�' => '霍', '�' => '雕', '�' => '雹', '�' => '霄', '�' => '霆', '�' => '霈', '�' => '霓', '�' => '霎', '�' => '霑', '�' => '霏', '�' => '霖', '��' => '霙', '��' => '霤', '��' => '霪', '��' => '霰', '��' => '霹', '��' => '霽', '��' => '霾', '��' => '靄', '��' => '靆', '��' => '靈', '��' => '靂', '��' => '靉', '��' => '靜', '��' => '靠', '��' => '靤', '��' => '靦', '��' => '靨', '��' => '勒', '��' => '靫', '��' => '靱', '��' => '靹', '��' => '鞅', '��' => '靼', '��' => '鞁', '��' => '靺', '��' => '鞆', '��' => '鞋', '��' => '鞏', '��' => '鞐', '��' => '鞜', '��' => '鞨', '��' => '鞦', '��' => '鞣', '��' => '鞳', '��' => '鞴', '��' => '韃', '��' => '韆', '��' => '韈', '��' => '韋', '��' => '韜', '��' => '韭', '��' => '齏', '��' => '韲', '��' => '竟', '��' => '韶', '��' => '韵', '��' => '頏', '��' => '頌', '��' => '頸', '��' => '頤', '��' => '頡', '�' => '頷', '�' => '頽', '�' => '顆', '�' => '顏', '�' => '顋', '�' => '顫', '�' => '顯', '�' => '顰', '�@' => '顱', '�A' => '顴', '�B' => '顳', '�C' => '颪', '�D' => '颯', '�E' => '颱', '�F' => '颶', '�G' => '飄', '�H' => '飃', '�I' => '飆', '�J' => '飩', '�K' => '飫', '�L' => '餃', '�M' => '餉', '�N' => '餒', '�O' => '餔', '�P' => '餘', '�Q' => '餡', '�R' => '餝', '�S' => '餞', '�T' => '餤', '�U' => '餠', '�V' => '餬', '�W' => '餮', '�X' => '餽', '�Y' => '餾', '�Z' => '饂', '�[' => '饉', '�\\' => '饅', '�]' => '饐', '�^' => '饋', '�_' => '饑', '�`' => '饒', '�a' => '饌', '�b' => '饕', '�c' => '馗', '�d' => '馘', '�e' => '馥', '�f' => '馭', '�g' => '馮', '�h' => '馼', '�i' => '駟', '�j' => '駛', '�k' => '駝', '�l' => '駘', '�m' => '駑', '�n' => '駭', '�o' => '駮', '�p' => '駱', '�q' => '駲', '�r' => '駻', '�s' => '駸', '�t' => '騁', '�u' => '騏', '�v' => '騅', '�w' => '駢', '�x' => '騙', '�y' => '騫', '�z' => '騷', '�{' => '驅', '�|' => '驂', '�}' => '驀', '�~' => '驃', '�' => '騾', '�' => '驕', '�' => '驍', '�' => '驛', '�' => '驗', '�' => '驟', '�' => '驢', '�' => '驥', '�' => '驤', '�' => '驩', '�' => '驫', '�' => '驪', '�' => '骭', '�' => '骰', '�' => '骼', '�' => '髀', '�' => '髏', '�' => '髑', '�' => '髓', '�' => '體', '�' => '髞', '�' => '髟', '�' => '髢', '�' => '髣', '�' => '髦', '�' => '髯', '�' => '髫', '�' => '髮', '�' => '髴', '�' => '髱', '�' => '髷', '�' => '髻', '�' => '鬆', '�' => '鬘', '�' => '鬚', '�' => '鬟', '�' => '鬢', '�' => '鬣', '�' => '鬥', '�' => '鬧', '�' => '鬨', '�' => '鬩', '�' => '鬪', '�' => '鬮', '�' => '鬯', '�' => '鬲', '�' => '魄', '�' => '魃', '�' => '魏', '�' => '魍', '�' => '魎', '�' => '魑', '�' => '魘', '�' => '魴', '�' => '鮓', '�' => '鮃', '�' => '鮑', '�' => '鮖', '�' => '鮗', '�' => '鮟', '�' => '鮠', '�' => '鮨', '�' => '鮴', '�' => '鯀', '�' => '鯊', '�' => '鮹', '��' => '鯆', '��' => '鯏', '��' => '鯑', '��' => '鯒', '��' => '鯣', '��' => '鯢', '��' => '鯤', '��' => '鯔', '��' => '鯡', '��' => '鰺', '��' => '鯲', '��' => '鯱', '��' => '鯰', '��' => '鰕', '��' => '鰔', '��' => '鰉', '��' => '鰓', '��' => '鰌', '��' => '鰆', '��' => '鰈', '��' => '鰒', '��' => '鰊', '��' => '鰄', '��' => '鰮', '��' => '鰛', '��' => '鰥', '��' => '鰤', '��' => '鰡', '��' => '鰰', '��' => '鱇', '��' => '鰲', '��' => '鱆', '��' => '鰾', '��' => '鱚', '��' => '鱠', '��' => '鱧', '��' => '鱶', '��' => '鱸', '��' => '鳧', '��' => '鳬', '��' => '鳰', '��' => '鴉', '��' => '鴈', '��' => '鳫', '��' => '鴃', '��' => '鴆', '��' => '鴪', '��' => '鴦', '��' => '鶯', '��' => '鴣', '��' => '鴟', '�' => '鵄', '�' => '鴕', '�' => '鴒', '�' => '鵁', '�' => '鴿', '�' => '鴾', '�' => '鵆', '�' => '鵈', '�@' => '鵝', '�A' => '鵞', '�B' => '鵤', '�C' => '鵑', '�D' => '鵐', '�E' => '鵙', '�F' => '鵲', '�G' => '鶉', '�H' => '鶇', '�I' => '鶫', '�J' => '鵯', '�K' => '鵺', '�L' => '鶚', '�M' => '鶤', '�N' => '鶩', '�O' => '鶲', '�P' => '鷄', '�Q' => '鷁', '�R' => '鶻', '�S' => '鶸', '�T' => '鶺', '�U' => '鷆', '�V' => '鷏', '�W' => '鷂', '�X' => '鷙', '�Y' => '鷓', '�Z' => '鷸', '�[' => '鷦', '�\\' => '鷭', '�]' => '鷯', '�^' => '鷽', '�_' => '鸚', '�`' => '鸛', '�a' => '鸞', '�b' => '鹵', '�c' => '鹹', '�d' => '鹽', '�e' => '麁', '�f' => '麈', '�g' => '麋', '�h' => '麌', '�i' => '麒', '�j' => '麕', '�k' => '麑', '�l' => '麝', '�m' => '麥', '�n' => '麩', '�o' => '麸', '�p' => '麪', '�q' => '麭', '�r' => '靡', '�s' => '黌', '�t' => '黎', '�u' => '黏', '�v' => '黐', '�w' => '黔', '�x' => '黜', '�y' => '點', '�z' => '黝', '�{' => '黠', '�|' => '黥', '�}' => '黨', '�~' => '黯', '�' => '黴', '�' => '黶', '�' => '黷', '�' => '黹', '�' => '黻', '�' => '黼', '�' => '黽', '�' => '鼇', '�' => '鼈', '�' => '皷', '�' => '鼕', '�' => '鼡', '�' => '鼬', '�' => '鼾', '�' => '齊', '�' => '齒', '�' => '齔', '�' => '齣', '�' => '齟', '�' => '齠', '�' => '齡', '�' => '齦', '�' => '齧', '�' => '齬', '�' => '齪', '�' => '齷', '�' => '齲', '�' => '齶', '�' => '龕', '�' => '龜', '�' => '龠', '�' => '堯', '�' => '槇', '�' => '遙', '�' => '瑤', '�' => '凜', '�' => '熙', '�@' => '纊', '�A' => '褜', '�B' => '鍈', '�C' => '銈', '�D' => '蓜', '�E' => '俉', '�F' => '炻', '�G' => '昱', '�H' => '棈', '�I' => '鋹', '�J' => '曻', '�K' => '彅', '�L' => '丨', '�M' => '仡', '�N' => '仼', '�O' => '伀', '�P' => '伃', '�Q' => '伹', '�R' => '佖', '�S' => '侒', '�T' => '侊', '�U' => '侚', '�V' => '侔', '�W' => '俍', '�X' => '偀', '�Y' => '倢', '�Z' => '俿', '�[' => '倞', '�\\' => '偆', '�]' => '偰', '�^' => '偂', '�_' => '傔', '�`' => '僴', '�a' => '僘', '�b' => '兊', '�c' => '兤', '�d' => '冝', '�e' => '冾', '�f' => '凬', '�g' => '刕', '�h' => '劜', '�i' => '劦', '�j' => '勀', '�k' => '勛', '�l' => '匀', '�m' => '匇', '�n' => '匤', '�o' => '卲', '�p' => '厓', '�q' => '厲', '�r' => '叝', '�s' => '﨎', '�t' => '咜', '�u' => '咊', '�v' => '咩', '�w' => '哿', '�x' => '喆', '�y' => '坙', '�z' => '坥', '�{' => '垬', '�|' => '埈', '�}' => '埇', '�~' => '﨏', '�' => '塚', '�' => '增', '�' => '墲', '�' => '夋', '�' => '奓', '�' => '奛', '�' => '奝', '�' => '奣', '�' => '妤', '�' => '妺', '�' => '孖', '�' => '寀', '�' => '甯', '�' => '寘', '�' => '寬', '�' => '尞', '�' => '岦', '�' => '岺', '�' => '峵', '�' => '崧', '�' => '嵓', '�' => '﨑', '�' => '嵂', '�' => '嵭', '�' => '嶸', '�' => '嶹', '�' => '巐', '�' => '弡', '�' => '弴', '�' => '彧', '�' => '德', '�' => '忞', '�' => '恝', '�' => '悅', '�' => '悊', '�' => '惞', '�' => '惕', '�' => '愠', '�' => '惲', '�' => '愑', '�' => '愷', '�' => '愰', '�' => '憘', '�' => '戓', '�' => '抦', '�' => '揵', '�' => '摠', '�' => '撝', '�' => '擎', '�' => '敎', '�' => '昀', '�' => '昕', '�' => '昻', '�' => '昉', '�' => '昮', '�' => '昞', '�' => '昤', '�' => '晥', '�' => '晗', '�' => '晙', '�' => '晴', '�' => '晳', '�' => '暙', '�' => '暠', '�' => '暲', '�' => '暿', '��' => '曺', '��' => '朎', '��' => '朗', '��' => '杦', '��' => '枻', '��' => '桒', '��' => '柀', '��' => '栁', '��' => '桄', '��' => '棏', '��' => '﨓', '��' => '楨', '��' => '﨔', '��' => '榘', '��' => '槢', '��' => '樰', '��' => '橫', '��' => '橆', '��' => '橳', '��' => '橾', '��' => '櫢', '��' => '櫤', '��' => '毖', '��' => '氿', '��' => '汜', '��' => '沆', '��' => '汯', '��' => '泚', '��' => '洄', '��' => '涇', '��' => '浯', '��' => '涖', '��' => '涬', '��' => '淏', '��' => '淸', '��' => '淲', '��' => '淼', '��' => '渹', '��' => '湜', '��' => '渧', '��' => '渼', '��' => '溿', '��' => '澈', '��' => '澵', '��' => '濵', '��' => '瀅', '��' => '瀇', '��' => '瀨', '��' => '炅', '��' => '炫', '��' => '焏', '�' => '焄', '�' => '煜', '�' => '煆', '�' => '煇', '�' => '凞', '�' => '燁', '�' => '燾', '�' => '犱', '�@' => '犾', '�A' => '猤', '�B' => '猪', '�C' => '獷', '�D' => '玽', '�E' => '珉', '�F' => '珖', '�G' => '珣', '�H' => '珒', '�I' => '琇', '�J' => '珵', '�K' => '琦', '�L' => '琪', '�M' => '琩', '�N' => '琮', '�O' => '瑢', '�P' => '璉', '�Q' => '璟', '�R' => '甁', '�S' => '畯', '�T' => '皂', '�U' => '皜', '�V' => '皞', '�W' => '皛', '�X' => '皦', '�Y' => '益', '�Z' => '睆', '�[' => '劯', '�\\' => '砡', '�]' => '硎', '�^' => '硤', '�_' => '硺', '�`' => '礰', '�a' => '礼', '�b' => '神', '�c' => '祥', '�d' => '禔', '�e' => '福', '�f' => '禛', '�g' => '竑', '�h' => '竧', '�i' => '靖', '�j' => '竫', '�k' => '箞', '�l' => '精', '�m' => '絈', '�n' => '絜', '�o' => '綷', '�p' => '綠', '�q' => '緖', '�r' => '繒', '�s' => '罇', '�t' => '羡', '�u' => '羽', '�v' => '茁', '�w' => '荢', '�x' => '荿', '�y' => '菇', '�z' => '菶', '�{' => '葈', '�|' => '蒴', '�}' => '蕓', '�~' => '蕙', '�' => '蕫', '�' => '﨟', '�' => '薰', '�' => '蘒', '�' => '﨡', '�' => '蠇', '�' => '裵', '�' => '訒', '�' => '訷', '�' => '詹', '�' => '誧', '�' => '誾', '�' => '諟', '�' => '諸', '�' => '諶', '�' => '譓', '�' => '譿', '�' => '賰', '�' => '賴', '�' => '贒', '�' => '赶', '�' => '﨣', '�' => '軏', '�' => '﨤', '�' => '逸', '�' => '遧', '�' => '郞', '�' => '都', '�' => '鄕', '�' => '鄧', '�' => '釚', '�' => '釗', '�' => '釞', '�' => '釭', '�' => '釮', '�' => '釤', '�' => '釥', '�' => '鈆', '�' => '鈐', '�' => '鈊', '�' => '鈺', '�' => '鉀', '�' => '鈼', '�' => '鉎', '�' => '鉙', '�' => '鉑', '�' => '鈹', '�' => '鉧', '�' => '銧', '�' => '鉷', '�' => '鉸', '�' => '鋧', '�' => '鋗', '�' => '鋙', '�' => '鋐', '�' => '﨧', '�' => '鋕', '�' => '鋠', '�' => '鋓', '�' => '錥', '�' => '錡', '�' => '鋻', '�' => '﨨', '�' => '錞', '�' => '鋿', '�' => '錝', '��' => '錂', '��' => '鍰', '��' => '鍗', '��' => '鎤', '��' => '鏆', '��' => '鏞', '��' => '鏸', '��' => '鐱', '��' => '鑅', '��' => '鑈', '��' => '閒', '��' => '隆', '��' => '﨩', '��' => '隝', '��' => '隯', '��' => '霳', '��' => '霻', '��' => '靃', '��' => '靍', '��' => '靏', '��' => '靑', '��' => '靕', '��' => '顗', '��' => '顥', '��' => '飯', '��' => '飼', '��' => '餧', '��' => '館', '��' => '馞', '��' => '驎', '��' => '髙', '��' => '髜', '��' => '魵', '��' => '魲', '��' => '鮏', '��' => '鮱', '��' => '鮻', '��' => '鰀', '��' => '鵰', '��' => '鵫', '��' => '鶴', '��' => '鸙', '��' => '黑', '��' => 'ⅰ', '��' => 'ⅱ', '��' => 'ⅲ', '��' => 'ⅳ', '��' => 'ⅴ', '��' => 'ⅵ', '�' => 'ⅶ', '�' => 'ⅷ', '�' => 'ⅸ', '�' => 'ⅹ', '�' => '¬', '�' => '¦', '�' => ''', '�' => '"', '�@' => 'ⅰ', '�A' => 'ⅱ', '�B' => 'ⅲ', '�C' => 'ⅳ', '�D' => 'ⅴ', '�E' => 'ⅵ', '�F' => 'ⅶ', '�G' => 'ⅷ', '�H' => 'ⅸ', '�I' => 'ⅹ', '�J' => 'Ⅰ', '�K' => 'Ⅱ', '�L' => 'Ⅲ', '�M' => 'Ⅳ', '�N' => 'Ⅴ', '�O' => 'Ⅵ', '�P' => 'Ⅶ', '�Q' => 'Ⅷ', '�R' => 'Ⅸ', '�S' => 'Ⅹ', '�T' => '¬', '�U' => '¦', '�V' => ''', '�W' => '"', '�X' => '㈱', '�Y' => '№', '�Z' => '℡', '�[' => '∵', '�\\' => '纊', '�]' => '褜', '�^' => '鍈', '�_' => '銈', '�`' => '蓜', '�a' => '俉', '�b' => '炻', '�c' => '昱', '�d' => '棈', '�e' => '鋹', '�f' => '曻', '�g' => '彅', '�h' => '丨', '�i' => '仡', '�j' => '仼', '�k' => '伀', '�l' => '伃', '�m' => '伹', '�n' => '佖', '�o' => '侒', '�p' => '侊', '�q' => '侚', '�r' => '侔', '�s' => '俍', '�t' => '偀', '�u' => '倢', '�v' => '俿', '�w' => '倞', '�x' => '偆', '�y' => '偰', '�z' => '偂', '�{' => '傔', '�|' => '僴', '�}' => '僘', '�~' => '兊', '��' => '兤', '��' => '冝', '��' => '冾', '��' => '凬', '��' => '刕', '��' => '劜', '��' => '劦', '��' => '勀', '��' => '勛', '��' => '匀', '��' => '匇', '��' => '匤', '��' => '卲', '��' => '厓', '��' => '厲', '��' => '叝', '��' => '﨎', '��' => '咜', '��' => '咊', '��' => '咩', '��' => '哿', '��' => '喆', '��' => '坙', '��' => '坥', '��' => '垬', '��' => '埈', '��' => '埇', '��' => '﨏', '��' => '塚', '��' => '增', '��' => '墲', '��' => '夋', '��' => '奓', '��' => '奛', '��' => '奝', '��' => '奣', '��' => '妤', '��' => '妺', '��' => '孖', '��' => '寀', '��' => '甯', '��' => '寘', '��' => '寬', '��' => '尞', '��' => '岦', '��' => '岺', '��' => '峵', '��' => '崧', '��' => '嵓', '��' => '﨑', '��' => '嵂', '��' => '嵭', '��' => '嶸', '��' => '嶹', '��' => '巐', '��' => '弡', '��' => '弴', '��' => '彧', '��' => '德', '��' => '忞', '��' => '恝', '��' => '悅', '��' => '悊', '��' => '惞', '��' => '惕', '��' => '愠', '��' => '惲', '��' => '愑', '��' => '愷', '��' => '愰', '��' => '憘', '��' => '戓', '��' => '抦', '��' => '揵', '��' => '摠', '��' => '撝', '��' => '擎', '��' => '敎', '��' => '昀', '��' => '昕', '��' => '昻', '��' => '昉', '��' => '昮', '��' => '昞', '��' => '昤', '��' => '晥', '��' => '晗', '��' => '晙', '��' => '晴', '��' => '晳', '��' => '暙', '��' => '暠', '��' => '暲', '��' => '暿', '��' => '曺', '��' => '朎', '��' => '朗', '��' => '杦', '��' => '枻', '��' => '桒', '��' => '柀', '��' => '栁', '��' => '桄', '��' => '棏', '��' => '﨓', '��' => '楨', '��' => '﨔', '��' => '榘', '��' => '槢', '��' => '樰', '��' => '橫', '��' => '橆', '��' => '橳', '��' => '橾', '��' => '櫢', '��' => '櫤', '��' => '毖', '��' => '氿', '��' => '汜', '��' => '沆', '��' => '汯', '��' => '泚', '��' => '洄', '��' => '涇', '��' => '浯', '�@' => '涖', '�A' => '涬', '�B' => '淏', '�C' => '淸', '�D' => '淲', '�E' => '淼', '�F' => '渹', '�G' => '湜', '�H' => '渧', '�I' => '渼', '�J' => '溿', '�K' => '澈', '�L' => '澵', '�M' => '濵', '�N' => '瀅', '�O' => '瀇', '�P' => '瀨', '�Q' => '炅', '�R' => '炫', '�S' => '焏', '�T' => '焄', '�U' => '煜', '�V' => '煆', '�W' => '煇', '�X' => '凞', '�Y' => '燁', '�Z' => '燾', '�[' => '犱', '�\\' => '犾', '�]' => '猤', '�^' => '猪', '�_' => '獷', '�`' => '玽', '�a' => '珉', '�b' => '珖', '�c' => '珣', '�d' => '珒', '�e' => '琇', '�f' => '珵', '�g' => '琦', '�h' => '琪', '�i' => '琩', '�j' => '琮', '�k' => '瑢', '�l' => '璉', '�m' => '璟', '�n' => '甁', '�o' => '畯', '�p' => '皂', '�q' => '皜', '�r' => '皞', '�s' => '皛', '�t' => '皦', '�u' => '益', '�v' => '睆', '�w' => '劯', '�x' => '砡', '�y' => '硎', '�z' => '硤', '�{' => '硺', '�|' => '礰', '�}' => '礼', '�~' => '神', '��' => '祥', '��' => '禔', '��' => '福', '��' => '禛', '��' => '竑', '��' => '竧', '��' => '靖', '��' => '竫', '��' => '箞', '��' => '精', '��' => '絈', '��' => '絜', '��' => '綷', '��' => '綠', '��' => '緖', '��' => '繒', '��' => '罇', '��' => '羡', '��' => '羽', '��' => '茁', '��' => '荢', '��' => '荿', '��' => '菇', '��' => '菶', '��' => '葈', '��' => '蒴', '��' => '蕓', '��' => '蕙', '��' => '蕫', '��' => '﨟', '��' => '薰', '��' => '蘒', '��' => '﨡', '��' => '蠇', '��' => '裵', '��' => '訒', '��' => '訷', '��' => '詹', '��' => '誧', '��' => '誾', '��' => '諟', '��' => '諸', '��' => '諶', '��' => '譓', '��' => '譿', '��' => '賰', '��' => '賴', '��' => '贒', '��' => '赶', '��' => '﨣', '��' => '軏', '��' => '﨤', '��' => '逸', '��' => '遧', '��' => '郞', '��' => '都', '��' => '鄕', '��' => '鄧', '��' => '釚', '��' => '釗', '��' => '釞', '��' => '釭', '��' => '釮', '��' => '釤', '��' => '釥', '��' => '鈆', '��' => '鈐', '��' => '鈊', '��' => '鈺', '��' => '鉀', '��' => '鈼', '��' => '鉎', '��' => '鉙', '��' => '鉑', '��' => '鈹', '��' => '鉧', '��' => '銧', '��' => '鉷', '��' => '鉸', '��' => '鋧', '��' => '鋗', '��' => '鋙', '��' => '鋐', '��' => '﨧', '��' => '鋕', '��' => '鋠', '��' => '鋓', '��' => '錥', '��' => '錡', '��' => '鋻', '��' => '﨨', '��' => '錞', '��' => '鋿', '��' => '錝', '��' => '錂', '��' => '鍰', '��' => '鍗', '��' => '鎤', '��' => '鏆', '��' => '鏞', '��' => '鏸', '��' => '鐱', '��' => '鑅', '��' => '鑈', '��' => '閒', '��' => '隆', '��' => '﨩', '��' => '隝', '��' => '隯', '��' => '霳', '��' => '霻', '��' => '靃', '��' => '靍', '��' => '靏', '��' => '靑', '��' => '靕', '��' => '顗', '��' => '顥', '��' => '飯', '��' => '飼', '��' => '餧', '��' => '館', '��' => '馞', '��' => '驎', '��' => '髙', '�@' => '髜', '�A' => '魵', '�B' => '魲', '�C' => '鮏', '�D' => '鮱', '�E' => '鮻', '�F' => '鰀', '�G' => '鵰', '�H' => '鵫', '�I' => '鶴', '�J' => '鸙', '�K' => '黑', ); $result =& $data; unset($data); return $result; PK2A#]�꣎��=vendor/symfony/polyfill-iconv/Resources/charset/from.big5.phpnu�[���<?php static $data = array ( '�@' => ' ', '�A' => ',', '�B' => '、', '�C' => '。', '�D' => '.', '�E' => '•', '�F' => ';', '�G' => ':', '�H' => '?', '�I' => '!', '�J' => '︰', '�K' => '…', '�L' => '‥', '�M' => '﹐', '�N' => '、', '�O' => '﹒', '�P' => '·', '�Q' => '﹔', '�R' => '﹕', '�S' => '﹖', '�T' => '﹗', '�U' => '|', '�V' => '–', '�W' => '︱', '�X' => '—', '�Y' => '︳', '�Z' => '�', '�[' => '︴', '�\\' => '﹏', '�]' => '(', '�^' => ')', '�_' => '︵', '�`' => '︶', '�a' => '{', '�b' => '}', '�c' => '︷', '�d' => '︸', '�e' => '〔', '�f' => '〕', '�g' => '︹', '�h' => '︺', '�i' => '【', '�j' => '】', '�k' => '︻', '�l' => '︼', '�m' => '《', '�n' => '》', '�o' => '︽', '�p' => '︾', '�q' => '〈', '�r' => '〉', '�s' => '︿', '�t' => '﹀', '�u' => '「', '�v' => '」', '�w' => '﹁', '�x' => '﹂', '�y' => '『', '�z' => '』', '�{' => '﹃', '�|' => '﹄', '�}' => '﹙', '�~' => '﹚', '��' => '﹛', '��' => '﹜', '��' => '﹝', '��' => '﹞', '��' => '‘', '��' => '’', '��' => '“', '��' => '”', '��' => '〝', '��' => '〞', '��' => '‵', '��' => '′', '��' => '#', '��' => '&', '��' => '*', '��' => '※', '��' => '§', '��' => '〃', '��' => '○', '��' => '●', '��' => '△', '��' => '▲', '��' => '◎', '��' => '☆', '��' => '★', '��' => '◇', '��' => '◆', '��' => '□', '��' => '■', '��' => '▽', '��' => '▼', '��' => '㊣', '��' => '℅', '��' => '‾', '��' => '�', '��' => '_', '��' => '�', '��' => '﹉', '��' => '﹊', '��' => '﹍', '��' => '﹎', '��' => '﹋', '��' => '﹌', '��' => '﹟', '��' => '﹠', '��' => '﹡', '��' => '+', '��' => '-', '��' => '×', '��' => '÷', '��' => '±', '��' => '√', '��' => '<', '��' => '>', '��' => '=', '��' => '≦', '��' => '≧', '��' => '≠', '��' => '∞', '��' => '≒', '��' => '≡', '��' => '﹢', '��' => '﹣', '��' => '﹤', '��' => '﹥', '��' => '﹦', '��' => '∼', '��' => '∩', '��' => '∪', '��' => '⊥', '��' => '∠', '��' => '∟', '��' => '⊿', '��' => '㏒', '��' => '㏑', '��' => '∫', '��' => '∮', '��' => '∵', '��' => '∴', '��' => '♀', '��' => '♂', '��' => '♁', '��' => '☉', '��' => '↑', '��' => '↓', '��' => '←', '��' => '→', '��' => '↖', '��' => '↗', '��' => '↙', '��' => '↘', '��' => '∥', '��' => '∣', '��' => '�', '�@' => '�', '�A' => '/', '�B' => '\', '�C' => '$', '�D' => '¥', '�E' => '〒', '�F' => '¢', '�G' => '£', '�H' => '%', '�I' => '@', '�J' => '℃', '�K' => '℉', '�L' => '﹩', '�M' => '﹪', '�N' => '﹫', '�O' => '㏕', '�P' => '㎜', '�Q' => '㎝', '�R' => '㎞', '�S' => '㏎', '�T' => '㎡', '�U' => '㎎', '�V' => '㎏', '�W' => '㏄', '�X' => '°', '�Y' => '兙', '�Z' => '兛', '�[' => '兞', '�\\' => '兝', '�]' => '兡', '�^' => '兣', '�_' => '嗧', '�`' => '瓩', '�a' => '糎', '�b' => '▁', '�c' => '▂', '�d' => '▃', '�e' => '▄', '�f' => '▅', '�g' => '▆', '�h' => '▇', '�i' => '█', '�j' => '▏', '�k' => '▎', '�l' => '▍', '�m' => '▌', '�n' => '▋', '�o' => '▊', '�p' => '▉', '�q' => '┼', '�r' => '┴', '�s' => '┬', '�t' => '┤', '�u' => '├', '�v' => '▔', '�w' => '─', '�x' => '│', '�y' => '▕', '�z' => '┌', '�{' => '┐', '�|' => '└', '�}' => '┘', '�~' => '╭', '��' => '╮', '��' => '╰', '��' => '╯', '��' => '═', '��' => '╞', '��' => '╪', '��' => '╡', '��' => '◢', '��' => '◣', '��' => '◥', '��' => '◤', '��' => '╱', '��' => '╲', '��' => '╳', '��' => '0', '��' => '1', '��' => '2', '��' => '3', '��' => '4', '��' => '5', '��' => '6', '��' => '7', '��' => '8', '��' => '9', '��' => 'Ⅰ', '��' => 'Ⅱ', '��' => 'Ⅲ', '��' => 'Ⅳ', '��' => 'Ⅴ', '��' => 'Ⅵ', '��' => 'Ⅶ', '��' => 'Ⅷ', '��' => 'Ⅸ', '��' => 'Ⅹ', '��' => '〡', '��' => '〢', '��' => '〣', '��' => '〤', '��' => '〥', '��' => '〦', '��' => '〧', '��' => '〨', '��' => '〩', '��' => '�', '��' => '卄', '��' => '�', '��' => 'A', '��' => 'B', '��' => 'C', '��' => 'D', '��' => 'E', '��' => 'F', '��' => 'G', '��' => 'H', '��' => 'I', '��' => 'J', '��' => 'K', '��' => 'L', '��' => 'M', '��' => 'N', '��' => 'O', '��' => 'P', '��' => 'Q', '��' => 'R', '��' => 'S', '��' => 'T', '��' => 'U', '��' => 'V', '��' => 'W', '��' => 'X', '��' => 'Y', '��' => 'Z', '��' => 'a', '��' => 'b', '��' => 'c', '��' => 'd', '��' => 'e', '��' => 'f', '��' => 'g', '��' => 'h', '��' => 'i', '��' => 'j', '��' => 'k', '��' => 'l', '��' => 'm', '��' => 'n', '��' => 'o', '��' => 'p', '��' => 'q', '��' => 'r', '��' => 's', '��' => 't', '��' => 'u', '��' => 'v', '�@' => 'w', '�A' => 'x', '�B' => 'y', '�C' => 'z', '�D' => 'Α', '�E' => 'Β', '�F' => 'Γ', '�G' => 'Δ', '�H' => 'Ε', '�I' => 'Ζ', '�J' => 'Η', '�K' => 'Θ', '�L' => 'Ι', '�M' => 'Κ', '�N' => 'Λ', '�O' => 'Μ', '�P' => 'Ν', '�Q' => 'Ξ', '�R' => 'Ο', '�S' => 'Π', '�T' => 'Ρ', '�U' => 'Σ', '�V' => 'Τ', '�W' => 'Υ', '�X' => 'Φ', '�Y' => 'Χ', '�Z' => 'Ψ', '�[' => 'Ω', '�\\' => 'α', '�]' => 'β', '�^' => 'γ', '�_' => 'δ', '�`' => 'ε', '�a' => 'ζ', '�b' => 'η', '�c' => 'θ', '�d' => 'ι', '�e' => 'κ', '�f' => 'λ', '�g' => 'μ', '�h' => 'ν', '�i' => 'ξ', '�j' => 'ο', '�k' => 'π', '�l' => 'ρ', '�m' => 'σ', '�n' => 'τ', '�o' => 'υ', '�p' => 'φ', '�q' => 'χ', '�r' => 'ψ', '�s' => 'ω', '�t' => 'ㄅ', '�u' => 'ㄆ', '�v' => 'ㄇ', '�w' => 'ㄈ', '�x' => 'ㄉ', '�y' => 'ㄊ', '�z' => 'ㄋ', '�{' => 'ㄌ', '�|' => 'ㄍ', '�}' => 'ㄎ', '�~' => 'ㄏ', '��' => 'ㄐ', '��' => 'ㄑ', '��' => 'ㄒ', '��' => 'ㄓ', '��' => 'ㄔ', '��' => 'ㄕ', '��' => 'ㄖ', '��' => 'ㄗ', '��' => 'ㄘ', '��' => 'ㄙ', '��' => 'ㄚ', '��' => 'ㄛ', '��' => 'ㄜ', '��' => 'ㄝ', '��' => 'ㄞ', '��' => 'ㄟ', '��' => 'ㄠ', '��' => 'ㄡ', '��' => 'ㄢ', '��' => 'ㄣ', '��' => 'ㄤ', '��' => 'ㄥ', '��' => 'ㄦ', '��' => 'ㄧ', '��' => 'ㄨ', '��' => 'ㄩ', '��' => '˙', '��' => 'ˉ', '��' => 'ˊ', '��' => 'ˇ', '��' => 'ˋ', '�@' => '一', '�A' => '乙', '�B' => '丁', '�C' => '七', '�D' => '乃', '�E' => '九', '�F' => '了', '�G' => '二', '�H' => '人', '�I' => '儿', '�J' => '入', '�K' => '八', '�L' => '几', '�M' => '刀', '�N' => '刁', '�O' => '力', '�P' => '匕', '�Q' => '十', '�R' => '卜', '�S' => '又', '�T' => '三', '�U' => '下', '�V' => '丈', '�W' => '上', '�X' => '丫', '�Y' => '丸', '�Z' => '凡', '�[' => '久', '�\\' => '么', '�]' => '也', '�^' => '乞', '�_' => '于', '�`' => '亡', '�a' => '兀', '�b' => '刃', '�c' => '勺', '�d' => '千', '�e' => '叉', '�f' => '口', '�g' => '土', '�h' => '士', '�i' => '夕', '�j' => '大', '�k' => '女', '�l' => '子', '�m' => '孑', '�n' => '孓', '�o' => '寸', '�p' => '小', '�q' => '尢', '�r' => '尸', '�s' => '山', '�t' => '川', '�u' => '工', '�v' => '己', '�w' => '已', '�x' => '巳', '�y' => '巾', '�z' => '干', '�{' => '廾', '�|' => '弋', '�}' => '弓', '�~' => '才', '��' => '丑', '��' => '丐', '��' => '不', '��' => '中', '��' => '丰', '��' => '丹', '��' => '之', '��' => '尹', '��' => '予', '��' => '云', '��' => '井', '��' => '互', '��' => '五', '��' => '亢', '��' => '仁', '��' => '什', '��' => '仃', '��' => '仆', '��' => '仇', '��' => '仍', '��' => '今', '��' => '介', '��' => '仄', '��' => '元', '��' => '允', '��' => '內', '��' => '六', '��' => '兮', '��' => '公', '��' => '冗', '��' => '凶', '��' => '分', '��' => '切', '��' => '刈', '��' => '勻', '��' => '勾', '��' => '勿', '��' => '化', '��' => '匹', '��' => '午', '��' => '升', '��' => '卅', '��' => '卞', '��' => '厄', '��' => '友', '��' => '及', '��' => '反', '��' => '壬', '��' => '天', '��' => '夫', '��' => '太', '��' => '夭', '��' => '孔', '��' => '少', '��' => '尤', '��' => '尺', '��' => '屯', '��' => '巴', '��' => '幻', '��' => '廿', '��' => '弔', '��' => '引', '��' => '心', '��' => '戈', '��' => '戶', '��' => '手', '��' => '扎', '��' => '支', '��' => '文', '��' => '斗', '��' => '斤', '��' => '方', '��' => '日', '��' => '曰', '��' => '月', '��' => '木', '��' => '欠', '��' => '止', '��' => '歹', '��' => '毋', '��' => '比', '��' => '毛', '��' => '氏', '��' => '水', '��' => '火', '��' => '爪', '��' => '父', '��' => '爻', '��' => '片', '��' => '牙', '��' => '牛', '��' => '犬', '��' => '王', '��' => '丙', '�@' => '世', '�A' => '丕', '�B' => '且', '�C' => '丘', '�D' => '主', '�E' => '乍', '�F' => '乏', '�G' => '乎', '�H' => '以', '�I' => '付', '�J' => '仔', '�K' => '仕', '�L' => '他', '�M' => '仗', '�N' => '代', '�O' => '令', '�P' => '仙', '�Q' => '仞', '�R' => '充', '�S' => '兄', '�T' => '冉', '�U' => '冊', '�V' => '冬', '�W' => '凹', '�X' => '出', '�Y' => '凸', '�Z' => '刊', '�[' => '加', '�\\' => '功', '�]' => '包', '�^' => '匆', '�_' => '北', '�`' => '匝', '�a' => '仟', '�b' => '半', '�c' => '卉', '�d' => '卡', '�e' => '占', '�f' => '卯', '�g' => '卮', '�h' => '去', '�i' => '可', '�j' => '古', '�k' => '右', '�l' => '召', '�m' => '叮', '�n' => '叩', '�o' => '叨', '�p' => '叼', '�q' => '司', '�r' => '叵', '�s' => '叫', '�t' => '另', '�u' => '只', '�v' => '史', '�w' => '叱', '�x' => '台', '�y' => '句', '�z' => '叭', '�{' => '叻', '�|' => '四', '�}' => '囚', '�~' => '外', '��' => '央', '��' => '失', '��' => '奴', '��' => '奶', '��' => '孕', '��' => '它', '��' => '尼', '��' => '巨', '��' => '巧', '��' => '左', '��' => '市', '��' => '布', '��' => '平', '��' => '幼', '��' => '弁', '��' => '弘', '��' => '弗', '��' => '必', '��' => '戊', '��' => '打', '��' => '扔', '��' => '扒', '��' => '扑', '��' => '斥', '��' => '旦', '��' => '朮', '��' => '本', '��' => '未', '��' => '末', '��' => '札', '��' => '正', '��' => '母', '��' => '民', '��' => '氐', '��' => '永', '��' => '汁', '��' => '汀', '��' => '氾', '��' => '犯', '��' => '玄', '��' => '玉', '��' => '瓜', '��' => '瓦', '��' => '甘', '��' => '生', '��' => '用', '��' => '甩', '��' => '田', '��' => '由', '��' => '甲', '��' => '申', '��' => '疋', '��' => '白', '��' => '皮', '��' => '皿', '��' => '目', '��' => '矛', '��' => '矢', '��' => '石', '��' => '示', '��' => '禾', '��' => '穴', '��' => '立', '��' => '丞', '��' => '丟', '��' => '乒', '��' => '乓', '��' => '乩', '��' => '亙', '��' => '交', '��' => '亦', '��' => '亥', '��' => '仿', '��' => '伉', '��' => '伙', '��' => '伊', '��' => '伕', '��' => '伍', '��' => '伐', '��' => '休', '��' => '伏', '��' => '仲', '��' => '件', '��' => '任', '��' => '仰', '��' => '仳', '��' => '份', '��' => '企', '��' => '伋', '��' => '光', '��' => '兇', '��' => '兆', '��' => '先', '��' => '全', '�@' => '共', '�A' => '再', '�B' => '冰', '�C' => '列', '�D' => '刑', '�E' => '划', '�F' => '刎', '�G' => '刖', '�H' => '劣', '�I' => '匈', '�J' => '匡', '�K' => '匠', '�L' => '印', '�M' => '危', '�N' => '吉', '�O' => '吏', '�P' => '同', '�Q' => '吊', '�R' => '吐', '�S' => '吁', '�T' => '吋', '�U' => '各', '�V' => '向', '�W' => '名', '�X' => '合', '�Y' => '吃', '�Z' => '后', '�[' => '吆', '�\\' => '吒', '�]' => '因', '�^' => '回', '�_' => '囝', '�`' => '圳', '�a' => '地', '�b' => '在', '�c' => '圭', '�d' => '圬', '�e' => '圯', '�f' => '圩', '�g' => '夙', '�h' => '多', '�i' => '夷', '�j' => '夸', '�k' => '妄', '�l' => '奸', '�m' => '妃', '�n' => '好', '�o' => '她', '�p' => '如', '�q' => '妁', '�r' => '字', '�s' => '存', '�t' => '宇', '�u' => '守', '�v' => '宅', '�w' => '安', '�x' => '寺', '�y' => '尖', '�z' => '屹', '�{' => '州', '�|' => '帆', '�}' => '并', '�~' => '年', '��' => '式', '��' => '弛', '��' => '忙', '��' => '忖', '��' => '戎', '��' => '戌', '��' => '戍', '��' => '成', '��' => '扣', '��' => '扛', '��' => '托', '��' => '收', '��' => '早', '��' => '旨', '��' => '旬', '��' => '旭', '��' => '曲', '��' => '曳', '��' => '有', '��' => '朽', '��' => '朴', '��' => '朱', '��' => '朵', '��' => '次', '��' => '此', '��' => '死', '��' => '氖', '��' => '汝', '��' => '汗', '��' => '汙', '��' => '江', '��' => '池', '��' => '汐', '��' => '汕', '��' => '污', '��' => '汛', '��' => '汍', '��' => '汎', '��' => '灰', '��' => '牟', '��' => '牝', '��' => '百', '��' => '竹', '��' => '米', '��' => '糸', '��' => '缶', '��' => '羊', '��' => '羽', '��' => '老', '��' => '考', '��' => '而', '��' => '耒', '��' => '耳', '��' => '聿', '��' => '肉', '��' => '肋', '��' => '肌', '��' => '臣', '��' => '自', '��' => '至', '��' => '臼', '��' => '舌', '��' => '舛', '��' => '舟', '��' => '艮', '��' => '色', '��' => '艾', '��' => '虫', '��' => '血', '��' => '行', '��' => '衣', '��' => '西', '��' => '阡', '��' => '串', '��' => '亨', '��' => '位', '��' => '住', '��' => '佇', '��' => '佗', '��' => '佞', '��' => '伴', '��' => '佛', '��' => '何', '��' => '估', '��' => '佐', '��' => '佑', '��' => '伽', '��' => '伺', '��' => '伸', '��' => '佃', '��' => '佔', '��' => '似', '��' => '但', '��' => '佣', '�@' => '作', '�A' => '你', '�B' => '伯', '�C' => '低', '�D' => '伶', '�E' => '余', '�F' => '佝', '�G' => '佈', '�H' => '佚', '�I' => '兌', '�J' => '克', '�K' => '免', '�L' => '兵', '�M' => '冶', '�N' => '冷', '�O' => '別', '�P' => '判', '�Q' => '利', '�R' => '刪', '�S' => '刨', '�T' => '劫', '�U' => '助', '�V' => '努', '�W' => '劬', '�X' => '匣', '�Y' => '即', '�Z' => '卵', '�[' => '吝', '�\\' => '吭', '�]' => '吞', '�^' => '吾', '�_' => '否', '�`' => '呎', '�a' => '吧', '�b' => '呆', '�c' => '呃', '�d' => '吳', '�e' => '呈', '�f' => '呂', '�g' => '君', '�h' => '吩', '�i' => '告', '�j' => '吹', '�k' => '吻', '�l' => '吸', '�m' => '吮', '�n' => '吵', '�o' => '吶', '�p' => '吠', '�q' => '吼', '�r' => '呀', '�s' => '吱', '�t' => '含', '�u' => '吟', '�v' => '听', '�w' => '囪', '�x' => '困', '�y' => '囤', '�z' => '囫', '�{' => '坊', '�|' => '坑', '�}' => '址', '�~' => '坍', '��' => '均', '��' => '坎', '��' => '圾', '��' => '坐', '��' => '坏', '��' => '圻', '��' => '壯', '��' => '夾', '��' => '妝', '��' => '妒', '��' => '妨', '��' => '妞', '��' => '妣', '��' => '妙', '��' => '妖', '��' => '妍', '��' => '妤', '��' => '妓', '��' => '妊', '��' => '妥', '��' => '孝', '��' => '孜', '��' => '孚', '��' => '孛', '��' => '完', '��' => '宋', '��' => '宏', '��' => '尬', '��' => '局', '��' => '屁', '��' => '尿', '��' => '尾', '��' => '岐', '��' => '岑', '��' => '岔', '��' => '岌', '��' => '巫', '��' => '希', '��' => '序', '��' => '庇', '��' => '床', '��' => '廷', '��' => '弄', '��' => '弟', '��' => '彤', '��' => '形', '��' => '彷', '��' => '役', '��' => '忘', '��' => '忌', '��' => '志', '��' => '忍', '��' => '忱', '��' => '快', '��' => '忸', '��' => '忪', '��' => '戒', '��' => '我', '��' => '抄', '��' => '抗', '��' => '抖', '��' => '技', '��' => '扶', '��' => '抉', '��' => '扭', '��' => '把', '��' => '扼', '��' => '找', '��' => '批', '��' => '扳', '��' => '抒', '��' => '扯', '��' => '折', '��' => '扮', '��' => '投', '��' => '抓', '��' => '抑', '��' => '抆', '��' => '改', '��' => '攻', '��' => '攸', '��' => '旱', '��' => '更', '��' => '束', '��' => '李', '��' => '杏', '��' => '材', '��' => '村', '��' => '杜', '��' => '杖', '��' => '杞', '��' => '杉', '��' => '杆', '��' => '杠', '�@' => '杓', '�A' => '杗', '�B' => '步', '�C' => '每', '�D' => '求', '�E' => '汞', '�F' => '沙', '�G' => '沁', '�H' => '沈', '�I' => '沉', '�J' => '沅', '�K' => '沛', '�L' => '汪', '�M' => '決', '�N' => '沐', '�O' => '汰', '�P' => '沌', '�Q' => '汨', '�R' => '沖', '�S' => '沒', '�T' => '汽', '�U' => '沃', '�V' => '汲', '�W' => '汾', '�X' => '汴', '�Y' => '沆', '�Z' => '汶', '�[' => '沍', '�\\' => '沔', '�]' => '沘', '�^' => '沂', '�_' => '灶', '�`' => '灼', '�a' => '災', '�b' => '灸', '�c' => '牢', '�d' => '牡', '�e' => '牠', '�f' => '狄', '�g' => '狂', '�h' => '玖', '�i' => '甬', '�j' => '甫', '�k' => '男', '�l' => '甸', '�m' => '皂', '�n' => '盯', '�o' => '矣', '�p' => '私', '�q' => '秀', '�r' => '禿', '�s' => '究', '�t' => '系', '�u' => '罕', '�v' => '肖', '�w' => '肓', '�x' => '肝', '�y' => '肘', '�z' => '肛', '�{' => '肚', '�|' => '育', '�}' => '良', '�~' => '芒', '��' => '芋', '��' => '芍', '��' => '見', '��' => '角', '��' => '言', '��' => '谷', '��' => '豆', '��' => '豕', '��' => '貝', '��' => '赤', '��' => '走', '��' => '足', '��' => '身', '��' => '車', '��' => '辛', '��' => '辰', '��' => '迂', '��' => '迆', '��' => '迅', '��' => '迄', '��' => '巡', '��' => '邑', '��' => '邢', '��' => '邪', '��' => '邦', '��' => '那', '��' => '酉', '��' => '釆', '��' => '里', '��' => '防', '��' => '阮', '��' => '阱', '��' => '阪', '��' => '阬', '��' => '並', '��' => '乖', '��' => '乳', '��' => '事', '��' => '些', '��' => '亞', '��' => '享', '��' => '京', '��' => '佯', '��' => '依', '��' => '侍', '��' => '佳', '��' => '使', '��' => '佬', '��' => '供', '��' => '例', '��' => '來', '��' => '侃', '��' => '佰', '��' => '併', '��' => '侈', '��' => '佩', '��' => '佻', '��' => '侖', '��' => '佾', '��' => '侏', '��' => '侑', '��' => '佺', '��' => '兔', '��' => '兒', '��' => '兕', '��' => '兩', '��' => '具', '��' => '其', '��' => '典', '��' => '冽', '��' => '函', '��' => '刻', '��' => '券', '��' => '刷', '��' => '刺', '��' => '到', '��' => '刮', '��' => '制', '��' => '剁', '��' => '劾', '��' => '劻', '��' => '卒', '��' => '協', '��' => '卓', '��' => '卑', '��' => '卦', '��' => '卷', '��' => '卸', '��' => '卹', '��' => '取', '��' => '叔', '��' => '受', '��' => '味', '��' => '呵', '�@' => '咖', '�A' => '呸', '�B' => '咕', '�C' => '咀', '�D' => '呻', '�E' => '呷', '�F' => '咄', '�G' => '咒', '�H' => '咆', '�I' => '呼', '�J' => '咐', '�K' => '呱', '�L' => '呶', '�M' => '和', '�N' => '咚', '�O' => '呢', '�P' => '周', '�Q' => '咋', '�R' => '命', '�S' => '咎', '�T' => '固', '�U' => '垃', '�V' => '坷', '�W' => '坪', '�X' => '坩', '�Y' => '坡', '�Z' => '坦', '�[' => '坤', '�\\' => '坼', '�]' => '夜', '�^' => '奉', '�_' => '奇', '�`' => '奈', '�a' => '奄', '�b' => '奔', '�c' => '妾', '�d' => '妻', '�e' => '委', '�f' => '妹', '�g' => '妮', '�h' => '姑', '�i' => '姆', '�j' => '姐', '�k' => '姍', '�l' => '始', '�m' => '姓', '�n' => '姊', '�o' => '妯', '�p' => '妳', '�q' => '姒', '�r' => '姅', '�s' => '孟', '�t' => '孤', '�u' => '季', '�v' => '宗', '�w' => '定', '�x' => '官', '�y' => '宜', '�z' => '宙', '�{' => '宛', '�|' => '尚', '�}' => '屈', '�~' => '居', '��' => '屆', '��' => '岷', '��' => '岡', '��' => '岸', '��' => '岩', '��' => '岫', '��' => '岱', '��' => '岳', '��' => '帘', '��' => '帚', '��' => '帖', '��' => '帕', '��' => '帛', '��' => '帑', '��' => '幸', '��' => '庚', '��' => '店', '��' => '府', '��' => '底', '��' => '庖', '��' => '延', '��' => '弦', '��' => '弧', '��' => '弩', '��' => '往', '��' => '征', '��' => '彿', '��' => '彼', '��' => '忝', '��' => '忠', '��' => '忽', '��' => '念', '��' => '忿', '��' => '怏', '��' => '怔', '��' => '怯', '��' => '怵', '��' => '怖', '��' => '怪', '��' => '怕', '��' => '怡', '��' => '性', '��' => '怩', '��' => '怫', '��' => '怛', '��' => '或', '��' => '戕', '��' => '房', '��' => '戾', '��' => '所', '��' => '承', '��' => '拉', '��' => '拌', '��' => '拄', '��' => '抿', '��' => '拂', '��' => '抹', '��' => '拒', '��' => '招', '��' => '披', '��' => '拓', '��' => '拔', '��' => '拋', '��' => '拈', '��' => '抨', '��' => '抽', '��' => '押', '��' => '拐', '��' => '拙', '��' => '拇', '��' => '拍', '��' => '抵', '��' => '拚', '��' => '抱', '��' => '拘', '��' => '拖', '��' => '拗', '��' => '拆', '��' => '抬', '��' => '拎', '��' => '放', '��' => '斧', '��' => '於', '��' => '旺', '��' => '昔', '��' => '易', '��' => '昌', '��' => '昆', '��' => '昂', '��' => '明', '��' => '昀', '��' => '昏', '��' => '昕', '��' => '昊', '�@' => '昇', '�A' => '服', '�B' => '朋', '�C' => '杭', '�D' => '枋', '�E' => '枕', '�F' => '東', '�G' => '果', '�H' => '杳', '�I' => '杷', '�J' => '枇', '�K' => '枝', '�L' => '林', '�M' => '杯', '�N' => '杰', '�O' => '板', '�P' => '枉', '�Q' => '松', '�R' => '析', '�S' => '杵', '�T' => '枚', '�U' => '枓', '�V' => '杼', '�W' => '杪', '�X' => '杲', '�Y' => '欣', '�Z' => '武', '�[' => '歧', '�\\' => '歿', '�]' => '氓', '�^' => '氛', '�_' => '泣', '�`' => '注', '�a' => '泳', '�b' => '沱', '�c' => '泌', '�d' => '泥', '�e' => '河', '�f' => '沽', '�g' => '沾', '�h' => '沼', '�i' => '波', '�j' => '沫', '�k' => '法', '�l' => '泓', '�m' => '沸', '�n' => '泄', '�o' => '油', '�p' => '況', '�q' => '沮', '�r' => '泗', '�s' => '泅', '�t' => '泱', '�u' => '沿', '�v' => '治', '�w' => '泡', '�x' => '泛', '�y' => '泊', '�z' => '沬', '�{' => '泯', '�|' => '泜', '�}' => '泖', '�~' => '泠', '��' => '炕', '��' => '炎', '��' => '炒', '��' => '炊', '��' => '炙', '��' => '爬', '��' => '爭', '��' => '爸', '��' => '版', '��' => '牧', '��' => '物', '��' => '狀', '��' => '狎', '��' => '狙', '��' => '狗', '��' => '狐', '��' => '玩', '��' => '玨', '��' => '玟', '��' => '玫', '��' => '玥', '��' => '甽', '��' => '疝', '��' => '疙', '��' => '疚', '��' => '的', '��' => '盂', '��' => '盲', '��' => '直', '��' => '知', '��' => '矽', '��' => '社', '��' => '祀', '��' => '祁', '��' => '秉', '��' => '秈', '��' => '空', '��' => '穹', '��' => '竺', '��' => '糾', '��' => '罔', '��' => '羌', '��' => '羋', '��' => '者', '��' => '肺', '��' => '肥', '��' => '肢', '��' => '肱', '��' => '股', '��' => '肫', '��' => '肩', '��' => '肴', '��' => '肪', '��' => '肯', '��' => '臥', '��' => '臾', '��' => '舍', '��' => '芳', '��' => '芝', '��' => '芙', '��' => '芭', '��' => '芽', '��' => '芟', '��' => '芹', '��' => '花', '��' => '芬', '��' => '芥', '��' => '芯', '��' => '芸', '��' => '芣', '��' => '芰', '��' => '芾', '��' => '芷', '��' => '虎', '��' => '虱', '��' => '初', '��' => '表', '��' => '軋', '��' => '迎', '��' => '返', '��' => '近', '��' => '邵', '��' => '邸', '��' => '邱', '��' => '邶', '��' => '采', '��' => '金', '��' => '長', '��' => '門', '��' => '阜', '��' => '陀', '��' => '阿', '��' => '阻', '��' => '附', '�@' => '陂', '�A' => '隹', '�B' => '雨', '�C' => '青', '�D' => '非', '�E' => '亟', '�F' => '亭', '�G' => '亮', '�H' => '信', '�I' => '侵', '�J' => '侯', '�K' => '便', '�L' => '俠', '�M' => '俑', '�N' => '俏', '�O' => '保', '�P' => '促', '�Q' => '侶', '�R' => '俘', '�S' => '俟', '�T' => '俊', '�U' => '俗', '�V' => '侮', '�W' => '俐', '�X' => '俄', '�Y' => '係', '�Z' => '俚', '�[' => '俎', '�\\' => '俞', '�]' => '侷', '�^' => '兗', '�_' => '冒', '�`' => '冑', '�a' => '冠', '�b' => '剎', '�c' => '剃', '�d' => '削', '�e' => '前', '�f' => '剌', '�g' => '剋', '�h' => '則', '�i' => '勇', '�j' => '勉', '�k' => '勃', '�l' => '勁', '�m' => '匍', '�n' => '南', '�o' => '卻', '�p' => '厚', '�q' => '叛', '�r' => '咬', '�s' => '哀', '�t' => '咨', '�u' => '哎', '�v' => '哉', '�w' => '咸', '�x' => '咦', '�y' => '咳', '�z' => '哇', '�{' => '哂', '�|' => '咽', '�}' => '咪', '�~' => '品', '��' => '哄', '��' => '哈', '��' => '咯', '��' => '咫', '��' => '咱', '��' => '咻', '��' => '咩', '��' => '咧', '��' => '咿', '��' => '囿', '��' => '垂', '��' => '型', '��' => '垠', '��' => '垣', '��' => '垢', '��' => '城', '��' => '垮', '��' => '垓', '��' => '奕', '��' => '契', '��' => '奏', '��' => '奎', '��' => '奐', '��' => '姜', '��' => '姘', '��' => '姿', '��' => '姣', '��' => '姨', '��' => '娃', '��' => '姥', '��' => '姪', '��' => '姚', '��' => '姦', '��' => '威', '��' => '姻', '��' => '孩', '��' => '宣', '��' => '宦', '��' => '室', '��' => '客', '��' => '宥', '��' => '封', '��' => '屎', '��' => '屏', '��' => '屍', '��' => '屋', '��' => '峙', '��' => '峒', '��' => '巷', '��' => '帝', '��' => '帥', '��' => '帟', '��' => '幽', '��' => '庠', '��' => '度', '��' => '建', '��' => '弈', '��' => '弭', '��' => '彥', '��' => '很', '��' => '待', '��' => '徊', '��' => '律', '��' => '徇', '��' => '後', '��' => '徉', '��' => '怒', '��' => '思', '��' => '怠', '��' => '急', '��' => '怎', '��' => '怨', '��' => '恍', '��' => '恰', '��' => '恨', '��' => '恢', '��' => '恆', '��' => '恃', '��' => '恬', '��' => '恫', '��' => '恪', '��' => '恤', '��' => '扁', '��' => '拜', '��' => '挖', '��' => '按', '��' => '拼', '��' => '拭', '��' => '持', '��' => '拮', '��' => '拽', '��' => '指', '��' => '拱', '��' => '拷', '�@' => '拯', '�A' => '括', '�B' => '拾', '�C' => '拴', '�D' => '挑', '�E' => '挂', '�F' => '政', '�G' => '故', '�H' => '斫', '�I' => '施', '�J' => '既', '�K' => '春', '�L' => '昭', '�M' => '映', '�N' => '昧', '�O' => '是', '�P' => '星', '�Q' => '昨', '�R' => '昱', '�S' => '昤', '�T' => '曷', '�U' => '柿', '�V' => '染', '�W' => '柱', '�X' => '柔', '�Y' => '某', '�Z' => '柬', '�[' => '架', '�\\' => '枯', '�]' => '柵', '�^' => '柩', '�_' => '柯', '�`' => '柄', '�a' => '柑', '�b' => '枴', '�c' => '柚', '�d' => '查', '�e' => '枸', '�f' => '柏', '�g' => '柞', '�h' => '柳', '�i' => '枰', '�j' => '柙', '�k' => '柢', '�l' => '柝', '�m' => '柒', '�n' => '歪', '�o' => '殃', '�p' => '殆', '�q' => '段', '�r' => '毒', '�s' => '毗', '�t' => '氟', '�u' => '泉', '�v' => '洋', '�w' => '洲', '�x' => '洪', '�y' => '流', '�z' => '津', '�{' => '洌', '�|' => '洱', '�}' => '洞', '�~' => '洗', '��' => '活', '��' => '洽', '��' => '派', '��' => '洶', '��' => '洛', '��' => '泵', '��' => '洹', '��' => '洧', '��' => '洸', '��' => '洩', '��' => '洮', '��' => '洵', '��' => '洎', '��' => '洫', '��' => '炫', '��' => '為', '��' => '炳', '��' => '炬', '��' => '炯', '��' => '炭', '��' => '炸', '��' => '炮', '��' => '炤', '��' => '爰', '��' => '牲', '��' => '牯', '��' => '牴', '��' => '狩', '��' => '狠', '��' => '狡', '��' => '玷', '��' => '珊', '��' => '玻', '��' => '玲', '��' => '珍', '��' => '珀', '��' => '玳', '��' => '甚', '��' => '甭', '��' => '畏', '��' => '界', '��' => '畎', '��' => '畋', '��' => '疫', '��' => '疤', '��' => '疥', '��' => '疢', '��' => '疣', '��' => '癸', '��' => '皆', '��' => '皇', '��' => '皈', '��' => '盈', '��' => '盆', '��' => '盃', '��' => '盅', '��' => '省', '��' => '盹', '��' => '相', '��' => '眉', '��' => '看', '��' => '盾', '��' => '盼', '��' => '眇', '��' => '矜', '��' => '砂', '��' => '研', '��' => '砌', '��' => '砍', '��' => '祆', '��' => '祉', '��' => '祈', '��' => '祇', '��' => '禹', '��' => '禺', '��' => '科', '��' => '秒', '��' => '秋', '��' => '穿', '��' => '突', '��' => '竿', '��' => '竽', '��' => '籽', '��' => '紂', '��' => '紅', '��' => '紀', '��' => '紉', '��' => '紇', '��' => '約', '��' => '紆', '��' => '缸', '��' => '美', '��' => '羿', '��' => '耄', '�@' => '耐', '�A' => '耍', '�B' => '耑', '�C' => '耶', '�D' => '胖', '�E' => '胥', '�F' => '胚', '�G' => '胃', '�H' => '胄', '�I' => '背', '�J' => '胡', '�K' => '胛', '�L' => '胎', '�M' => '胞', '�N' => '胤', '�O' => '胝', '�P' => '致', '�Q' => '舢', '�R' => '苧', '�S' => '范', '�T' => '茅', '�U' => '苣', '�V' => '苛', '�W' => '苦', '�X' => '茄', '�Y' => '若', '�Z' => '茂', '�[' => '茉', '�\\' => '苒', '�]' => '苗', '�^' => '英', '�_' => '茁', '�`' => '苜', '�a' => '苔', '�b' => '苑', '�c' => '苞', '�d' => '苓', '�e' => '苟', '�f' => '苯', '�g' => '茆', '�h' => '虐', '�i' => '虹', '�j' => '虻', '�k' => '虺', '�l' => '衍', '�m' => '衫', '�n' => '要', '�o' => '觔', '�p' => '計', '�q' => '訂', '�r' => '訃', '�s' => '貞', '�t' => '負', '�u' => '赴', '�v' => '赳', '�w' => '趴', '�x' => '軍', '�y' => '軌', '�z' => '述', '�{' => '迦', '�|' => '迢', '�}' => '迪', '�~' => '迥', '��' => '迭', '��' => '迫', '��' => '迤', '��' => '迨', '��' => '郊', '��' => '郎', '��' => '郁', '��' => '郃', '��' => '酋', '��' => '酊', '��' => '重', '��' => '閂', '��' => '限', '��' => '陋', '��' => '陌', '��' => '降', '��' => '面', '��' => '革', '��' => '韋', '��' => '韭', '��' => '音', '��' => '頁', '��' => '風', '��' => '飛', '��' => '食', '��' => '首', '��' => '香', '��' => '乘', '��' => '亳', '��' => '倌', '��' => '倍', '��' => '倣', '��' => '俯', '��' => '倦', '��' => '倥', '��' => '俸', '��' => '倩', '��' => '倖', '��' => '倆', '��' => '值', '��' => '借', '��' => '倚', '��' => '倒', '��' => '們', '��' => '俺', '��' => '倀', '��' => '倔', '��' => '倨', '��' => '俱', '��' => '倡', '��' => '個', '��' => '候', '��' => '倘', '��' => '俳', '��' => '修', '��' => '倭', '��' => '倪', '��' => '俾', '��' => '倫', '��' => '倉', '��' => '兼', '��' => '冤', '��' => '冥', '��' => '冢', '��' => '凍', '��' => '凌', '��' => '准', '��' => '凋', '��' => '剖', '��' => '剜', '��' => '剔', '��' => '剛', '��' => '剝', '��' => '匪', '��' => '卿', '��' => '原', '��' => '厝', '��' => '叟', '��' => '哨', '��' => '唐', '��' => '唁', '��' => '唷', '��' => '哼', '��' => '哥', '��' => '哲', '��' => '唆', '��' => '哺', '��' => '唔', '��' => '哩', '��' => '哭', '��' => '員', '��' => '唉', '��' => '哮', '��' => '哪', '�@' => '哦', '�A' => '唧', '�B' => '唇', '�C' => '哽', '�D' => '唏', '�E' => '圃', '�F' => '圄', '�G' => '埂', '�H' => '埔', '�I' => '埋', '�J' => '埃', '�K' => '堉', '�L' => '夏', '�M' => '套', '�N' => '奘', '�O' => '奚', '�P' => '娑', '�Q' => '娘', '�R' => '娜', '�S' => '娟', '�T' => '娛', '�U' => '娓', '�V' => '姬', '�W' => '娠', '�X' => '娣', '�Y' => '娩', '�Z' => '娥', '�[' => '娌', '�\\' => '娉', '�]' => '孫', '�^' => '屘', '�_' => '宰', '�`' => '害', '�a' => '家', '�b' => '宴', '�c' => '宮', '�d' => '宵', '�e' => '容', '�f' => '宸', '�g' => '射', '�h' => '屑', '�i' => '展', '�j' => '屐', '�k' => '峭', '�l' => '峽', '�m' => '峻', '�n' => '峪', '�o' => '峨', '�p' => '峰', '�q' => '島', '�r' => '崁', '�s' => '峴', '�t' => '差', '�u' => '席', '�v' => '師', '�w' => '庫', '�x' => '庭', '�y' => '座', '�z' => '弱', '�{' => '徒', '�|' => '徑', '�}' => '徐', '�~' => '恙', '��' => '恣', '��' => '恥', '��' => '恐', '��' => '恕', '��' => '恭', '��' => '恩', '��' => '息', '��' => '悄', '��' => '悟', '��' => '悚', '��' => '悍', '��' => '悔', '��' => '悌', '��' => '悅', '��' => '悖', '��' => '扇', '��' => '拳', '��' => '挈', '��' => '拿', '��' => '捎', '��' => '挾', '��' => '振', '��' => '捕', '��' => '捂', '��' => '捆', '��' => '捏', '��' => '捉', '��' => '挺', '��' => '捐', '��' => '挽', '��' => '挪', '��' => '挫', '��' => '挨', '��' => '捍', '��' => '捌', '��' => '效', '��' => '敉', '��' => '料', '��' => '旁', '��' => '旅', '��' => '時', '��' => '晉', '��' => '晏', '��' => '晃', '��' => '晒', '��' => '晌', '��' => '晅', '��' => '晁', '��' => '書', '��' => '朔', '��' => '朕', '��' => '朗', '��' => '校', '��' => '核', '��' => '案', '��' => '框', '��' => '桓', '��' => '根', '��' => '桂', '��' => '桔', '��' => '栩', '��' => '梳', '��' => '栗', '��' => '桌', '��' => '桑', '��' => '栽', '��' => '柴', '��' => '桐', '��' => '桀', '��' => '格', '��' => '桃', '��' => '株', '��' => '桅', '��' => '栓', '��' => '栘', '��' => '桁', '��' => '殊', '��' => '殉', '��' => '殷', '��' => '氣', '��' => '氧', '��' => '氨', '��' => '氦', '��' => '氤', '��' => '泰', '��' => '浪', '��' => '涕', '��' => '消', '��' => '涇', '��' => '浦', '��' => '浸', '��' => '海', '��' => '浙', '��' => '涓', '�@' => '浬', '�A' => '涉', '�B' => '浮', '�C' => '浚', '�D' => '浴', '�E' => '浩', '�F' => '涌', '�G' => '涊', '�H' => '浹', '�I' => '涅', '�J' => '浥', '�K' => '涔', '�L' => '烊', '�M' => '烘', '�N' => '烤', '�O' => '烙', '�P' => '烈', '�Q' => '烏', '�R' => '爹', '�S' => '特', '�T' => '狼', '�U' => '狹', '�V' => '狽', '�W' => '狸', '�X' => '狷', '�Y' => '玆', '�Z' => '班', '�[' => '琉', '�\\' => '珮', '�]' => '珠', '�^' => '珪', '�_' => '珞', '�`' => '畔', '�a' => '畝', '�b' => '畜', '�c' => '畚', '�d' => '留', '�e' => '疾', '�f' => '病', '�g' => '症', '�h' => '疲', '�i' => '疳', '�j' => '疽', '�k' => '疼', '�l' => '疹', '�m' => '痂', '�n' => '疸', '�o' => '皋', '�p' => '皰', '�q' => '益', '�r' => '盍', '�s' => '盎', '�t' => '眩', '�u' => '真', '�v' => '眠', '�w' => '眨', '�x' => '矩', '�y' => '砰', '�z' => '砧', '�{' => '砸', '�|' => '砝', '�}' => '破', '�~' => '砷', '��' => '砥', '��' => '砭', '��' => '砠', '��' => '砟', '��' => '砲', '��' => '祕', '��' => '祐', '��' => '祠', '��' => '祟', '��' => '祖', '��' => '神', '��' => '祝', '��' => '祗', '��' => '祚', '��' => '秤', '��' => '秣', '��' => '秧', '��' => '租', '��' => '秦', '��' => '秩', '��' => '秘', '��' => '窄', '��' => '窈', '��' => '站', '��' => '笆', '��' => '笑', '��' => '粉', '��' => '紡', '��' => '紗', '��' => '紋', '��' => '紊', '��' => '素', '��' => '索', '��' => '純', '��' => '紐', '��' => '紕', '��' => '級', '��' => '紜', '��' => '納', '��' => '紙', '��' => '紛', '��' => '缺', '��' => '罟', '��' => '羔', '��' => '翅', '��' => '翁', '��' => '耆', '��' => '耘', '��' => '耕', '��' => '耙', '��' => '耗', '��' => '耽', '��' => '耿', '��' => '胱', '��' => '脂', '��' => '胰', '��' => '脅', '��' => '胭', '��' => '胴', '��' => '脆', '��' => '胸', '��' => '胳', '��' => '脈', '��' => '能', '��' => '脊', '��' => '胼', '��' => '胯', '��' => '臭', '��' => '臬', '��' => '舀', '��' => '舐', '��' => '航', '��' => '舫', '��' => '舨', '��' => '般', '��' => '芻', '��' => '茫', '��' => '荒', '��' => '荔', '��' => '荊', '��' => '茸', '��' => '荐', '��' => '草', '��' => '茵', '��' => '茴', '��' => '荏', '��' => '茲', '��' => '茹', '��' => '茶', '��' => '茗', '��' => '荀', '��' => '茱', '��' => '茨', '��' => '荃', '�@' => '虔', '�A' => '蚊', '�B' => '蚪', '�C' => '蚓', '�D' => '蚤', '�E' => '蚩', '�F' => '蚌', '�G' => '蚣', '�H' => '蚜', '�I' => '衰', '�J' => '衷', '�K' => '袁', '�L' => '袂', '�M' => '衽', '�N' => '衹', '�O' => '記', '�P' => '訐', '�Q' => '討', '�R' => '訌', '�S' => '訕', '�T' => '訊', '�U' => '託', '�V' => '訓', '�W' => '訖', '�X' => '訏', '�Y' => '訑', '�Z' => '豈', '�[' => '豺', '�\\' => '豹', '�]' => '財', '�^' => '貢', '�_' => '起', '�`' => '躬', '�a' => '軒', '�b' => '軔', '�c' => '軏', '�d' => '辱', '�e' => '送', '�f' => '逆', '�g' => '迷', '�h' => '退', '�i' => '迺', '�j' => '迴', '�k' => '逃', '�l' => '追', '�m' => '逅', '�n' => '迸', '�o' => '邕', '�p' => '郡', '�q' => '郝', '�r' => '郢', '�s' => '酒', '�t' => '配', '�u' => '酌', '�v' => '釘', '�w' => '針', '�x' => '釗', '�y' => '釜', '�z' => '釙', '�{' => '閃', '�|' => '院', '�}' => '陣', '�~' => '陡', '��' => '陛', '��' => '陝', '��' => '除', '��' => '陘', '��' => '陞', '��' => '隻', '��' => '飢', '��' => '馬', '��' => '骨', '��' => '高', '��' => '鬥', '��' => '鬲', '��' => '鬼', '��' => '乾', '��' => '偺', '��' => '偽', '��' => '停', '��' => '假', '��' => '偃', '��' => '偌', '��' => '做', '��' => '偉', '��' => '健', '��' => '偶', '��' => '偎', '��' => '偕', '��' => '偵', '��' => '側', '��' => '偷', '��' => '偏', '��' => '倏', '��' => '偯', '��' => '偭', '��' => '兜', '��' => '冕', '��' => '凰', '��' => '剪', '��' => '副', '��' => '勒', '��' => '務', '��' => '勘', '��' => '動', '��' => '匐', '��' => '匏', '��' => '匙', '��' => '匿', '��' => '區', '��' => '匾', '��' => '參', '��' => '曼', '��' => '商', '��' => '啪', '��' => '啦', '��' => '啄', '��' => '啞', '��' => '啡', '��' => '啃', '��' => '啊', '��' => '唱', '��' => '啖', '��' => '問', '��' => '啕', '��' => '唯', '��' => '啤', '��' => '唸', '��' => '售', '��' => '啜', '��' => '唬', '��' => '啣', '��' => '唳', '��' => '啁', '��' => '啗', '��' => '圈', '��' => '國', '��' => '圉', '��' => '域', '��' => '堅', '��' => '堊', '��' => '堆', '��' => '埠', '��' => '埤', '��' => '基', '��' => '堂', '��' => '堵', '��' => '執', '��' => '培', '��' => '夠', '��' => '奢', '��' => '娶', '��' => '婁', '��' => '婉', '��' => '婦', '��' => '婪', '��' => '婀', '�@' => '娼', '�A' => '婢', '�B' => '婚', '�C' => '婆', '�D' => '婊', '�E' => '孰', '�F' => '寇', '�G' => '寅', '�H' => '寄', '�I' => '寂', '�J' => '宿', '�K' => '密', '�L' => '尉', '�M' => '專', '�N' => '將', '�O' => '屠', '�P' => '屜', '�Q' => '屝', '�R' => '崇', '�S' => '崆', '�T' => '崎', '�U' => '崛', '�V' => '崖', '�W' => '崢', '�X' => '崑', '�Y' => '崩', '�Z' => '崔', '�[' => '崙', '�\\' => '崤', '�]' => '崧', '�^' => '崗', '�_' => '巢', '�`' => '常', '�a' => '帶', '�b' => '帳', '�c' => '帷', '�d' => '康', '�e' => '庸', '�f' => '庶', '�g' => '庵', '�h' => '庾', '�i' => '張', '�j' => '強', '�k' => '彗', '�l' => '彬', '�m' => '彩', '�n' => '彫', '�o' => '得', '�p' => '徙', '�q' => '從', '�r' => '徘', '�s' => '御', '�t' => '徠', '�u' => '徜', '�v' => '恿', '�w' => '患', '�x' => '悉', '�y' => '悠', '�z' => '您', '�{' => '惋', '�|' => '悴', '�}' => '惦', '�~' => '悽', '��' => '情', '��' => '悻', '��' => '悵', '��' => '惜', '��' => '悼', '��' => '惘', '��' => '惕', '��' => '惆', '��' => '惟', '��' => '悸', '��' => '惚', '��' => '惇', '��' => '戚', '��' => '戛', '��' => '扈', '��' => '掠', '��' => '控', '��' => '捲', '��' => '掖', '��' => '探', '��' => '接', '��' => '捷', '��' => '捧', '��' => '掘', '��' => '措', '��' => '捱', '��' => '掩', '��' => '掉', '��' => '掃', '��' => '掛', '��' => '捫', '��' => '推', '��' => '掄', '��' => '授', '��' => '掙', '��' => '採', '��' => '掬', '��' => '排', '��' => '掏', '��' => '掀', '��' => '捻', '��' => '捩', '��' => '捨', '��' => '捺', '��' => '敝', '��' => '敖', '��' => '救', '��' => '教', '��' => '敗', '��' => '啟', '��' => '敏', '��' => '敘', '��' => '敕', '��' => '敔', '��' => '斜', '��' => '斛', '��' => '斬', '��' => '族', '��' => '旋', '��' => '旌', '��' => '旎', '��' => '晝', '��' => '晚', '��' => '晤', '��' => '晨', '��' => '晦', '��' => '晞', '��' => '曹', '��' => '勗', '��' => '望', '��' => '梁', '��' => '梯', '��' => '梢', '��' => '梓', '��' => '梵', '��' => '桿', '��' => '桶', '��' => '梱', '��' => '梧', '��' => '梗', '��' => '械', '��' => '梃', '��' => '棄', '��' => '梭', '��' => '梆', '��' => '梅', '��' => '梔', '��' => '條', '��' => '梨', '��' => '梟', '��' => '梡', '��' => '梂', '��' => '欲', '��' => '殺', '�@' => '毫', '�A' => '毬', '�B' => '氫', '�C' => '涎', '�D' => '涼', '�E' => '淳', '�F' => '淙', '�G' => '液', '�H' => '淡', '�I' => '淌', '�J' => '淤', '�K' => '添', '�L' => '淺', '�M' => '清', '�N' => '淇', '�O' => '淋', '�P' => '涯', '�Q' => '淑', '�R' => '涮', '�S' => '淞', '�T' => '淹', '�U' => '涸', '�V' => '混', '�W' => '淵', '�X' => '淅', '�Y' => '淒', '�Z' => '渚', '�[' => '涵', '�\\' => '淚', '�]' => '淫', '�^' => '淘', '�_' => '淪', '�`' => '深', '�a' => '淮', '�b' => '淨', '�c' => '淆', '�d' => '淄', '�e' => '涪', '�f' => '淬', '�g' => '涿', '�h' => '淦', '�i' => '烹', '�j' => '焉', '�k' => '焊', '�l' => '烽', '�m' => '烯', '�n' => '爽', '�o' => '牽', '�p' => '犁', '�q' => '猜', '�r' => '猛', '�s' => '猖', '�t' => '猓', '�u' => '猙', '�v' => '率', '�w' => '琅', '�x' => '琊', '�y' => '球', '�z' => '理', '�{' => '現', '�|' => '琍', '�}' => '瓠', '�~' => '瓶', '��' => '瓷', '��' => '甜', '��' => '產', '��' => '略', '��' => '畦', '��' => '畢', '��' => '異', '��' => '疏', '��' => '痔', '��' => '痕', '��' => '疵', '��' => '痊', '��' => '痍', '��' => '皎', '��' => '盔', '��' => '盒', '��' => '盛', '��' => '眷', '��' => '眾', '��' => '眼', '��' => '眶', '��' => '眸', '��' => '眺', '��' => '硫', '��' => '硃', '��' => '硎', '��' => '祥', '��' => '票', '��' => '祭', '��' => '移', '��' => '窒', '��' => '窕', '��' => '笠', '��' => '笨', '��' => '笛', '��' => '第', '��' => '符', '��' => '笙', '��' => '笞', '��' => '笮', '��' => '粒', '��' => '粗', '��' => '粕', '��' => '絆', '��' => '絃', '��' => '統', '��' => '紮', '��' => '紹', '��' => '紼', '��' => '絀', '��' => '細', '��' => '紳', '��' => '組', '��' => '累', '��' => '終', '��' => '紲', '��' => '紱', '��' => '缽', '��' => '羞', '��' => '羚', '��' => '翌', '��' => '翎', '��' => '習', '��' => '耜', '��' => '聊', '��' => '聆', '��' => '脯', '��' => '脖', '��' => '脣', '��' => '脫', '��' => '脩', '��' => '脰', '��' => '脤', '��' => '舂', '��' => '舵', '��' => '舷', '��' => '舶', '��' => '船', '��' => '莎', '��' => '莞', '��' => '莘', '��' => '荸', '��' => '莢', '��' => '莖', '��' => '莽', '��' => '莫', '��' => '莒', '��' => '莊', '��' => '莓', '��' => '莉', '��' => '莠', '��' => '荷', '��' => '荻', '��' => '荼', '�@' => '莆', '�A' => '莧', '�B' => '處', '�C' => '彪', '�D' => '蛇', '�E' => '蛀', '�F' => '蚶', '�G' => '蛄', '�H' => '蚵', '�I' => '蛆', '�J' => '蛋', '�K' => '蚱', '�L' => '蚯', '�M' => '蛉', '�N' => '術', '�O' => '袞', '�P' => '袈', '�Q' => '被', '�R' => '袒', '�S' => '袖', '�T' => '袍', '�U' => '袋', '�V' => '覓', '�W' => '規', '�X' => '訪', '�Y' => '訝', '�Z' => '訣', '�[' => '訥', '�\\' => '許', '�]' => '設', '�^' => '訟', '�_' => '訛', '�`' => '訢', '�a' => '豉', '�b' => '豚', '�c' => '販', '�d' => '責', '�e' => '貫', '�f' => '貨', '�g' => '貪', '�h' => '貧', '�i' => '赧', '�j' => '赦', '�k' => '趾', '�l' => '趺', '�m' => '軛', '�n' => '軟', '�o' => '這', '�p' => '逍', '�q' => '通', '�r' => '逗', '�s' => '連', '�t' => '速', '�u' => '逝', '�v' => '逐', '�w' => '逕', '�x' => '逞', '�y' => '造', '�z' => '透', '�{' => '逢', '�|' => '逖', '�}' => '逛', '�~' => '途', '��' => '部', '��' => '郭', '��' => '都', '��' => '酗', '��' => '野', '��' => '釵', '��' => '釦', '��' => '釣', '��' => '釧', '��' => '釭', '��' => '釩', '��' => '閉', '��' => '陪', '��' => '陵', '��' => '陳', '��' => '陸', '��' => '陰', '��' => '陴', '��' => '陶', '��' => '陷', '��' => '陬', '��' => '雀', '��' => '雪', '��' => '雩', '��' => '章', '��' => '竟', '��' => '頂', '��' => '頃', '��' => '魚', '��' => '鳥', '��' => '鹵', '��' => '鹿', '��' => '麥', '��' => '麻', '��' => '傢', '��' => '傍', '��' => '傅', '��' => '備', '��' => '傑', '��' => '傀', '��' => '傖', '��' => '傘', '��' => '傚', '��' => '最', '��' => '凱', '��' => '割', '��' => '剴', '��' => '創', '��' => '剩', '��' => '勞', '��' => '勝', '��' => '勛', '��' => '博', '��' => '厥', '��' => '啻', '��' => '喀', '��' => '喧', '��' => '啼', '��' => '喊', '��' => '喝', '��' => '喘', '��' => '喂', '��' => '喜', '��' => '喪', '��' => '喔', '��' => '喇', '��' => '喋', '��' => '喃', '��' => '喳', '��' => '單', '��' => '喟', '��' => '唾', '��' => '喲', '��' => '喚', '��' => '喻', '��' => '喬', '��' => '喱', '��' => '啾', '��' => '喉', '��' => '喫', '��' => '喙', '��' => '圍', '��' => '堯', '��' => '堪', '��' => '場', '��' => '堤', '��' => '堰', '��' => '報', '��' => '堡', '��' => '堝', '��' => '堠', '��' => '壹', '��' => '壺', '��' => '奠', '�@' => '婷', '�A' => '媚', '�B' => '婿', '�C' => '媒', '�D' => '媛', '�E' => '媧', '�F' => '孳', '�G' => '孱', '�H' => '寒', '�I' => '富', '�J' => '寓', '�K' => '寐', '�L' => '尊', '�M' => '尋', '�N' => '就', '�O' => '嵌', '�P' => '嵐', '�Q' => '崴', '�R' => '嵇', '�S' => '巽', '�T' => '幅', '�U' => '帽', '�V' => '幀', '�W' => '幃', '�X' => '幾', '�Y' => '廊', '�Z' => '廁', '�[' => '廂', '�\\' => '廄', '�]' => '弼', '�^' => '彭', '�_' => '復', '�`' => '循', '�a' => '徨', '�b' => '惑', '�c' => '惡', '�d' => '悲', '�e' => '悶', '�f' => '惠', '�g' => '愜', '�h' => '愣', '�i' => '惺', '�j' => '愕', '�k' => '惰', '�l' => '惻', '�m' => '惴', '�n' => '慨', '�o' => '惱', '�p' => '愎', '�q' => '惶', '�r' => '愉', '�s' => '愀', '�t' => '愒', '�u' => '戟', '�v' => '扉', '�w' => '掣', '�x' => '掌', '�y' => '描', '�z' => '揀', '�{' => '揩', '�|' => '揉', '�}' => '揆', '�~' => '揍', '��' => '插', '��' => '揣', '��' => '提', '��' => '握', '��' => '揖', '��' => '揭', '��' => '揮', '��' => '捶', '��' => '援', '��' => '揪', '��' => '換', '��' => '摒', '��' => '揚', '��' => '揹', '��' => '敞', '��' => '敦', '��' => '敢', '��' => '散', '��' => '斑', '��' => '斐', '��' => '斯', '��' => '普', '��' => '晰', '��' => '晴', '��' => '晶', '��' => '景', '��' => '暑', '��' => '智', '��' => '晾', '��' => '晷', '��' => '曾', '��' => '替', '��' => '期', '��' => '朝', '��' => '棺', '��' => '棕', '��' => '棠', '��' => '棘', '��' => '棗', '��' => '椅', '��' => '棟', '��' => '棵', '��' => '森', '��' => '棧', '��' => '棹', '��' => '棒', '��' => '棲', '��' => '棣', '��' => '棋', '��' => '棍', '��' => '植', '��' => '椒', '��' => '椎', '��' => '棉', '��' => '棚', '��' => '楮', '��' => '棻', '��' => '款', '��' => '欺', '��' => '欽', '��' => '殘', '��' => '殖', '��' => '殼', '��' => '毯', '��' => '氮', '��' => '氯', '��' => '氬', '��' => '港', '��' => '游', '��' => '湔', '��' => '渡', '��' => '渲', '��' => '湧', '��' => '湊', '��' => '渠', '��' => '渥', '��' => '渣', '��' => '減', '��' => '湛', '��' => '湘', '��' => '渤', '��' => '湖', '��' => '湮', '��' => '渭', '��' => '渦', '��' => '湯', '��' => '渴', '��' => '湍', '��' => '渺', '��' => '測', '��' => '湃', '��' => '渝', '��' => '渾', '��' => '滋', '�@' => '溉', '�A' => '渙', '�B' => '湎', '�C' => '湣', '�D' => '湄', '�E' => '湲', '�F' => '湩', '�G' => '湟', '�H' => '焙', '�I' => '焚', '�J' => '焦', '�K' => '焰', '�L' => '無', '�M' => '然', '�N' => '煮', '�O' => '焜', '�P' => '牌', '�Q' => '犄', '�R' => '犀', '�S' => '猶', '�T' => '猥', '�U' => '猴', '�V' => '猩', '�W' => '琺', '�X' => '琪', '�Y' => '琳', '�Z' => '琢', '�[' => '琥', '�\\' => '琵', '�]' => '琶', '�^' => '琴', '�_' => '琯', '�`' => '琛', '�a' => '琦', '�b' => '琨', '�c' => '甥', '�d' => '甦', '�e' => '畫', '�f' => '番', '�g' => '痢', '�h' => '痛', '�i' => '痣', '�j' => '痙', '�k' => '痘', '�l' => '痞', '�m' => '痠', '�n' => '登', '�o' => '發', '�p' => '皖', '�q' => '皓', '�r' => '皴', '�s' => '盜', '�t' => '睏', '�u' => '短', '�v' => '硝', '�w' => '硬', '�x' => '硯', '�y' => '稍', '�z' => '稈', '�{' => '程', '�|' => '稅', '�}' => '稀', '�~' => '窘', '��' => '窗', '��' => '窖', '��' => '童', '��' => '竣', '��' => '等', '��' => '策', '��' => '筆', '��' => '筐', '��' => '筒', '��' => '答', '��' => '筍', '��' => '筋', '��' => '筏', '��' => '筑', '��' => '粟', '��' => '粥', '��' => '絞', '��' => '結', '��' => '絨', '��' => '絕', '��' => '紫', '��' => '絮', '��' => '絲', '��' => '絡', '��' => '給', '��' => '絢', '��' => '絰', '��' => '絳', '��' => '善', '��' => '翔', '��' => '翕', '��' => '耋', '��' => '聒', '��' => '肅', '��' => '腕', '��' => '腔', '��' => '腋', '��' => '腑', '��' => '腎', '��' => '脹', '��' => '腆', '��' => '脾', '��' => '腌', '��' => '腓', '��' => '腴', '��' => '舒', '��' => '舜', '��' => '菩', '��' => '萃', '��' => '菸', '��' => '萍', '��' => '菠', '��' => '菅', '��' => '萋', '��' => '菁', '��' => '華', '��' => '菱', '��' => '菴', '��' => '著', '��' => '萊', '��' => '菰', '��' => '萌', '��' => '菌', '��' => '菽', '��' => '菲', '��' => '菊', '��' => '萸', '��' => '萎', '��' => '萄', '��' => '菜', '��' => '萇', '��' => '菔', '��' => '菟', '��' => '虛', '��' => '蛟', '��' => '蛙', '��' => '蛭', '��' => '蛔', '��' => '蛛', '��' => '蛤', '��' => '蛐', '��' => '蛞', '��' => '街', '��' => '裁', '��' => '裂', '��' => '袱', '��' => '覃', '��' => '視', '��' => '註', '��' => '詠', '��' => '評', '��' => '詞', '��' => '証', '��' => '詁', '�@' => '詔', '�A' => '詛', '�B' => '詐', '�C' => '詆', '�D' => '訴', '�E' => '診', '�F' => '訶', '�G' => '詖', '�H' => '象', '�I' => '貂', '�J' => '貯', '�K' => '貼', '�L' => '貳', '�M' => '貽', '�N' => '賁', '�O' => '費', '�P' => '賀', '�Q' => '貴', '�R' => '買', '�S' => '貶', '�T' => '貿', '�U' => '貸', '�V' => '越', '�W' => '超', '�X' => '趁', '�Y' => '跎', '�Z' => '距', '�[' => '跋', '�\\' => '跚', '�]' => '跑', '�^' => '跌', '�_' => '跛', '�`' => '跆', '�a' => '軻', '�b' => '軸', '�c' => '軼', '�d' => '辜', '�e' => '逮', '�f' => '逵', '�g' => '週', '�h' => '逸', '�i' => '進', '�j' => '逶', '�k' => '鄂', '�l' => '郵', '�m' => '鄉', '�n' => '郾', '�o' => '酣', '�p' => '酥', '�q' => '量', '�r' => '鈔', '�s' => '鈕', '�t' => '鈣', '�u' => '鈉', '�v' => '鈞', '�w' => '鈍', '�x' => '鈐', '�y' => '鈇', '�z' => '鈑', '�{' => '閔', '�|' => '閏', '�}' => '開', '�~' => '閑', '��' => '間', '��' => '閒', '��' => '閎', '��' => '隊', '��' => '階', '��' => '隋', '��' => '陽', '��' => '隅', '��' => '隆', '��' => '隍', '��' => '陲', '��' => '隄', '��' => '雁', '��' => '雅', '��' => '雄', '��' => '集', '��' => '雇', '��' => '雯', '��' => '雲', '��' => '韌', '��' => '項', '��' => '順', '��' => '須', '��' => '飧', '��' => '飪', '��' => '飯', '��' => '飩', '��' => '飲', '��' => '飭', '��' => '馮', '��' => '馭', '��' => '黃', '��' => '黍', '��' => '黑', '��' => '亂', '��' => '傭', '��' => '債', '��' => '傲', '��' => '傳', '��' => '僅', '��' => '傾', '��' => '催', '��' => '傷', '��' => '傻', '��' => '傯', '��' => '僇', '��' => '剿', '��' => '剷', '��' => '剽', '��' => '募', '��' => '勦', '��' => '勤', '��' => '勢', '��' => '勣', '��' => '匯', '��' => '嗟', '��' => '嗨', '��' => '嗓', '��' => '嗦', '��' => '嗎', '��' => '嗜', '��' => '嗇', '��' => '嗑', '��' => '嗣', '��' => '嗤', '��' => '嗯', '��' => '嗚', '��' => '嗡', '��' => '嗅', '��' => '嗆', '��' => '嗥', '��' => '嗉', '��' => '園', '��' => '圓', '��' => '塞', '��' => '塑', '��' => '塘', '��' => '塗', '��' => '塚', '��' => '塔', '��' => '填', '��' => '塌', '��' => '塭', '��' => '塊', '��' => '塢', '��' => '塒', '��' => '塋', '��' => '奧', '��' => '嫁', '��' => '嫉', '��' => '嫌', '��' => '媾', '��' => '媽', '��' => '媼', '�@' => '媳', '�A' => '嫂', '�B' => '媲', '�C' => '嵩', '�D' => '嵯', '�E' => '幌', '�F' => '幹', '�G' => '廉', '�H' => '廈', '�I' => '弒', '�J' => '彙', '�K' => '徬', '�L' => '微', '�M' => '愚', '�N' => '意', '�O' => '慈', '�P' => '感', '�Q' => '想', '�R' => '愛', '�S' => '惹', '�T' => '愁', '�U' => '愈', '�V' => '慎', '�W' => '慌', '�X' => '慄', '�Y' => '慍', '�Z' => '愾', '�[' => '愴', '�\\' => '愧', '�]' => '愍', '�^' => '愆', '�_' => '愷', '�`' => '戡', '�a' => '戢', '�b' => '搓', '�c' => '搾', '�d' => '搞', '�e' => '搪', '�f' => '搭', '�g' => '搽', '�h' => '搬', '�i' => '搏', '�j' => '搜', '�k' => '搔', '�l' => '損', '�m' => '搶', '�n' => '搖', '�o' => '搗', '�p' => '搆', '�q' => '敬', '�r' => '斟', '�s' => '新', '�t' => '暗', '�u' => '暉', '�v' => '暇', '�w' => '暈', '�x' => '暖', '�y' => '暄', '�z' => '暘', '�{' => '暍', '�|' => '會', '�}' => '榔', '�~' => '業', '��' => '楚', '��' => '楷', '��' => '楠', '��' => '楔', '��' => '極', '��' => '椰', '��' => '概', '��' => '楊', '��' => '楨', '��' => '楫', '��' => '楞', '��' => '楓', '��' => '楹', '��' => '榆', '��' => '楝', '��' => '楣', '��' => '楛', '��' => '歇', '��' => '歲', '��' => '毀', '��' => '殿', '��' => '毓', '��' => '毽', '��' => '溢', '��' => '溯', '��' => '滓', '��' => '溶', '��' => '滂', '��' => '源', '��' => '溝', '��' => '滇', '��' => '滅', '��' => '溥', '��' => '溘', '��' => '溼', '��' => '溺', '��' => '溫', '��' => '滑', '��' => '準', '��' => '溜', '��' => '滄', '��' => '滔', '��' => '溪', '��' => '溧', '��' => '溴', '��' => '煎', '��' => '煙', '��' => '煩', '��' => '煤', '��' => '煉', '��' => '照', '��' => '煜', '��' => '煬', '��' => '煦', '��' => '煌', '��' => '煥', '��' => '煞', '��' => '煆', '��' => '煨', '��' => '煖', '��' => '爺', '��' => '牒', '��' => '猷', '��' => '獅', '��' => '猿', '��' => '猾', '��' => '瑯', '��' => '瑚', '��' => '瑕', '��' => '瑟', '��' => '瑞', '��' => '瑁', '��' => '琿', '��' => '瑙', '��' => '瑛', '��' => '瑜', '��' => '當', '��' => '畸', '��' => '瘀', '��' => '痰', '��' => '瘁', '��' => '痲', '��' => '痱', '��' => '痺', '��' => '痿', '��' => '痴', '��' => '痳', '��' => '盞', '��' => '盟', '��' => '睛', '��' => '睫', '��' => '睦', '��' => '睞', '��' => '督', '�@' => '睹', '�A' => '睪', '�B' => '睬', '�C' => '睜', '�D' => '睥', '�E' => '睨', '�F' => '睢', '�G' => '矮', '�H' => '碎', '�I' => '碰', '�J' => '碗', '�K' => '碘', '�L' => '碌', '�M' => '碉', '�N' => '硼', '�O' => '碑', '�P' => '碓', '�Q' => '硿', '�R' => '祺', '�S' => '祿', '�T' => '禁', '�U' => '萬', '�V' => '禽', '�W' => '稜', '�X' => '稚', '�Y' => '稠', '�Z' => '稔', '�[' => '稟', '�\\' => '稞', '�]' => '窟', '�^' => '窠', '�_' => '筷', '�`' => '節', '�a' => '筠', '�b' => '筮', '�c' => '筧', '�d' => '粱', '�e' => '粳', '�f' => '粵', '�g' => '經', '�h' => '絹', '�i' => '綑', '�j' => '綁', '�k' => '綏', '�l' => '絛', '�m' => '置', '�n' => '罩', '�o' => '罪', '�p' => '署', '�q' => '義', '�r' => '羨', '�s' => '群', '�t' => '聖', '�u' => '聘', '�v' => '肆', '�w' => '肄', '�x' => '腱', '�y' => '腰', '�z' => '腸', '�{' => '腥', '�|' => '腮', '�}' => '腳', '�~' => '腫', '��' => '腹', '��' => '腺', '��' => '腦', '��' => '舅', '��' => '艇', '��' => '蒂', '��' => '葷', '��' => '落', '��' => '萱', '��' => '葵', '��' => '葦', '��' => '葫', '��' => '葉', '��' => '葬', '��' => '葛', '��' => '萼', '��' => '萵', '��' => '葡', '��' => '董', '��' => '葩', '��' => '葭', '��' => '葆', '��' => '虞', '��' => '虜', '��' => '號', '��' => '蛹', '��' => '蜓', '��' => '蜈', '��' => '蜇', '��' => '蜀', '��' => '蛾', '��' => '蛻', '��' => '蜂', '��' => '蜃', '��' => '蜆', '��' => '蜊', '��' => '衙', '��' => '裟', '��' => '裔', '��' => '裙', '��' => '補', '��' => '裘', '��' => '裝', '��' => '裡', '��' => '裊', '��' => '裕', '��' => '裒', '��' => '覜', '��' => '解', '��' => '詫', '��' => '該', '��' => '詳', '��' => '試', '��' => '詩', '��' => '詰', '��' => '誇', '��' => '詼', '��' => '詣', '��' => '誠', '��' => '話', '��' => '誅', '��' => '詭', '��' => '詢', '��' => '詮', '��' => '詬', '��' => '詹', '��' => '詻', '��' => '訾', '��' => '詨', '��' => '豢', '��' => '貊', '��' => '貉', '��' => '賊', '��' => '資', '��' => '賈', '��' => '賄', '��' => '貲', '��' => '賃', '��' => '賂', '��' => '賅', '��' => '跡', '��' => '跟', '��' => '跨', '��' => '路', '��' => '跳', '��' => '跺', '��' => '跪', '��' => '跤', '��' => '跦', '��' => '躲', '��' => '較', '��' => '載', '��' => '軾', '��' => '輊', '�@' => '辟', '�A' => '農', '�B' => '運', '�C' => '遊', '�D' => '道', '�E' => '遂', '�F' => '達', '�G' => '逼', '�H' => '違', '�I' => '遐', '�J' => '遇', '�K' => '遏', '�L' => '過', '�M' => '遍', '�N' => '遑', '�O' => '逾', '�P' => '遁', '�Q' => '鄒', '�R' => '鄗', '�S' => '酬', '�T' => '酪', '�U' => '酩', '�V' => '釉', '�W' => '鈷', '�X' => '鉗', '�Y' => '鈸', '�Z' => '鈽', '�[' => '鉀', '�\\' => '鈾', '�]' => '鉛', '�^' => '鉋', '�_' => '鉤', '�`' => '鉑', '�a' => '鈴', '�b' => '鉉', '�c' => '鉍', '�d' => '鉅', '�e' => '鈹', '�f' => '鈿', '�g' => '鉚', '�h' => '閘', '�i' => '隘', '�j' => '隔', '�k' => '隕', '�l' => '雍', '�m' => '雋', '�n' => '雉', '�o' => '雊', '�p' => '雷', '�q' => '電', '�r' => '雹', '�s' => '零', '�t' => '靖', '�u' => '靴', '�v' => '靶', '�w' => '預', '�x' => '頑', '�y' => '頓', '�z' => '頊', '�{' => '頒', '�|' => '頌', '�}' => '飼', '�~' => '飴', '��' => '飽', '��' => '飾', '��' => '馳', '��' => '馱', '��' => '馴', '��' => '髡', '��' => '鳩', '��' => '麂', '��' => '鼎', '��' => '鼓', '��' => '鼠', '��' => '僧', '��' => '僮', '��' => '僥', '��' => '僖', '��' => '僭', '��' => '僚', '��' => '僕', '��' => '像', '��' => '僑', '��' => '僱', '��' => '僎', '��' => '僩', '��' => '兢', '��' => '凳', '��' => '劃', '��' => '劂', '��' => '匱', '��' => '厭', '��' => '嗾', '��' => '嘀', '��' => '嘛', '��' => '嘗', '��' => '嗽', '��' => '嘔', '��' => '嘆', '��' => '嘉', '��' => '嘍', '��' => '嘎', '��' => '嗷', '��' => '嘖', '��' => '嘟', '��' => '嘈', '��' => '嘐', '��' => '嗶', '��' => '團', '��' => '圖', '��' => '塵', '��' => '塾', '��' => '境', '��' => '墓', '��' => '墊', '��' => '塹', '��' => '墅', '��' => '塽', '��' => '壽', '��' => '夥', '��' => '夢', '��' => '夤', '��' => '奪', '��' => '奩', '��' => '嫡', '��' => '嫦', '��' => '嫩', '��' => '嫗', '��' => '嫖', '��' => '嫘', '��' => '嫣', '��' => '孵', '��' => '寞', '��' => '寧', '��' => '寡', '��' => '寥', '��' => '實', '��' => '寨', '��' => '寢', '��' => '寤', '��' => '察', '��' => '對', '��' => '屢', '��' => '嶄', '��' => '嶇', '��' => '幛', '��' => '幣', '��' => '幕', '��' => '幗', '��' => '幔', '��' => '廓', '��' => '廖', '��' => '弊', '��' => '彆', '��' => '彰', '��' => '徹', '��' => '慇', '�@' => '愿', '�A' => '態', '�B' => '慷', '�C' => '慢', '�D' => '慣', '�E' => '慟', '�F' => '慚', '�G' => '慘', '�H' => '慵', '�I' => '截', '�J' => '撇', '�K' => '摘', '�L' => '摔', '�M' => '撤', '�N' => '摸', '�O' => '摟', '�P' => '摺', '�Q' => '摑', '�R' => '摧', '�S' => '搴', '�T' => '摭', '�U' => '摻', '�V' => '敲', '�W' => '斡', '�X' => '旗', '�Y' => '旖', '�Z' => '暢', '�[' => '暨', '�\\' => '暝', '�]' => '榜', '�^' => '榨', '�_' => '榕', '�`' => '槁', '�a' => '榮', '�b' => '槓', '�c' => '構', '�d' => '榛', '�e' => '榷', '�f' => '榻', '�g' => '榫', '�h' => '榴', '�i' => '槐', '�j' => '槍', '�k' => '榭', '�l' => '槌', '�m' => '榦', '�n' => '槃', '�o' => '榣', '�p' => '歉', '�q' => '歌', '�r' => '氳', '�s' => '漳', '�t' => '演', '�u' => '滾', '�v' => '漓', '�w' => '滴', '�x' => '漩', '�y' => '漾', '�z' => '漠', '�{' => '漬', '�|' => '漏', '�}' => '漂', '�~' => '漢', '��' => '滿', '��' => '滯', '��' => '漆', '��' => '漱', '��' => '漸', '��' => '漲', '��' => '漣', '��' => '漕', '��' => '漫', '��' => '漯', '��' => '澈', '��' => '漪', '��' => '滬', '��' => '漁', '��' => '滲', '��' => '滌', '��' => '滷', '��' => '熔', '��' => '熙', '��' => '煽', '��' => '熊', '��' => '熄', '��' => '熒', '��' => '爾', '��' => '犒', '��' => '犖', '��' => '獄', '��' => '獐', '��' => '瑤', '��' => '瑣', '��' => '瑪', '��' => '瑰', '��' => '瑭', '��' => '甄', '��' => '疑', '��' => '瘧', '��' => '瘍', '��' => '瘋', '��' => '瘉', '��' => '瘓', '��' => '盡', '��' => '監', '��' => '瞄', '��' => '睽', '��' => '睿', '��' => '睡', '��' => '磁', '��' => '碟', '��' => '碧', '��' => '碳', '��' => '碩', '��' => '碣', '��' => '禎', '��' => '福', '��' => '禍', '��' => '種', '��' => '稱', '��' => '窪', '��' => '窩', '��' => '竭', '��' => '端', '��' => '管', '��' => '箕', '��' => '箋', '��' => '筵', '��' => '算', '��' => '箝', '��' => '箔', '��' => '箏', '��' => '箸', '��' => '箇', '��' => '箄', '��' => '粹', '��' => '粽', '��' => '精', '��' => '綻', '��' => '綰', '��' => '綜', '��' => '綽', '��' => '綾', '��' => '綠', '��' => '緊', '��' => '綴', '��' => '網', '��' => '綱', '��' => '綺', '��' => '綢', '��' => '綿', '��' => '綵', '��' => '綸', '��' => '維', '��' => '緒', '��' => '緇', '��' => '綬', '�@' => '罰', '�A' => '翠', '�B' => '翡', '�C' => '翟', '�D' => '聞', '�E' => '聚', '�F' => '肇', '�G' => '腐', '�H' => '膀', '�I' => '膏', '�J' => '膈', '�K' => '膊', '�L' => '腿', '�M' => '膂', '�N' => '臧', '�O' => '臺', '�P' => '與', '�Q' => '舔', '�R' => '舞', '�S' => '艋', '�T' => '蓉', '�U' => '蒿', '�V' => '蓆', '�W' => '蓄', '�X' => '蒙', '�Y' => '蒞', '�Z' => '蒲', '�[' => '蒜', '�\\' => '蓋', '�]' => '蒸', '�^' => '蓀', '�_' => '蓓', '�`' => '蒐', '�a' => '蒼', '�b' => '蓑', '�c' => '蓊', '�d' => '蜿', '�e' => '蜜', '�f' => '蜻', '�g' => '蜢', '�h' => '蜥', '�i' => '蜴', '�j' => '蜘', '�k' => '蝕', '�l' => '蜷', '�m' => '蜩', '�n' => '裳', '�o' => '褂', '�p' => '裴', '�q' => '裹', '�r' => '裸', '�s' => '製', '�t' => '裨', '�u' => '褚', '�v' => '裯', '�w' => '誦', '�x' => '誌', '�y' => '語', '�z' => '誣', '�{' => '認', '�|' => '誡', '�}' => '誓', '�~' => '誤', '��' => '說', '��' => '誥', '��' => '誨', '��' => '誘', '��' => '誑', '��' => '誚', '��' => '誧', '��' => '豪', '��' => '貍', '��' => '貌', '��' => '賓', '��' => '賑', '��' => '賒', '��' => '赫', '��' => '趙', '��' => '趕', '��' => '跼', '��' => '輔', '��' => '輒', '��' => '輕', '��' => '輓', '��' => '辣', '��' => '遠', '��' => '遘', '��' => '遜', '��' => '遣', '��' => '遙', '��' => '遞', '��' => '遢', '��' => '遝', '��' => '遛', '��' => '鄙', '��' => '鄘', '��' => '鄞', '��' => '酵', '��' => '酸', '��' => '酷', '��' => '酴', '��' => '鉸', '��' => '銀', '��' => '銅', '��' => '銘', '��' => '銖', '��' => '鉻', '��' => '銓', '��' => '銜', '��' => '銨', '��' => '鉼', '��' => '銑', '��' => '閡', '��' => '閨', '��' => '閩', '��' => '閣', '��' => '閥', '��' => '閤', '��' => '隙', '��' => '障', '��' => '際', '��' => '雌', '��' => '雒', '��' => '需', '��' => '靼', '��' => '鞅', '��' => '韶', '��' => '頗', '��' => '領', '��' => '颯', '��' => '颱', '��' => '餃', '��' => '餅', '��' => '餌', '��' => '餉', '��' => '駁', '��' => '骯', '��' => '骰', '��' => '髦', '��' => '魁', '��' => '魂', '��' => '鳴', '��' => '鳶', '��' => '鳳', '��' => '麼', '��' => '鼻', '��' => '齊', '��' => '億', '��' => '儀', '��' => '僻', '��' => '僵', '��' => '價', '��' => '儂', '��' => '儈', '��' => '儉', '��' => '儅', '��' => '凜', '�@' => '劇', '�A' => '劈', '�B' => '劉', '�C' => '劍', '�D' => '劊', '�E' => '勰', '�F' => '厲', '�G' => '嘮', '�H' => '嘻', '�I' => '嘹', '�J' => '嘲', '�K' => '嘿', '�L' => '嘴', '�M' => '嘩', '�N' => '噓', '�O' => '噎', '�P' => '噗', '�Q' => '噴', '�R' => '嘶', '�S' => '嘯', '�T' => '嘰', '�U' => '墀', '�V' => '墟', '�W' => '增', '�X' => '墳', '�Y' => '墜', '�Z' => '墮', '�[' => '墩', '�\\' => '墦', '�]' => '奭', '�^' => '嬉', '�_' => '嫻', '�`' => '嬋', '�a' => '嫵', '�b' => '嬌', '�c' => '嬈', '�d' => '寮', '�e' => '寬', '�f' => '審', '�g' => '寫', '�h' => '層', '�i' => '履', '�j' => '嶝', '�k' => '嶔', '�l' => '幢', '�m' => '幟', '�n' => '幡', '�o' => '廢', '�p' => '廚', '�q' => '廟', '�r' => '廝', '�s' => '廣', '�t' => '廠', '�u' => '彈', '�v' => '影', '�w' => '德', '�x' => '徵', '�y' => '慶', '�z' => '慧', '�{' => '慮', '�|' => '慝', '�}' => '慕', '�~' => '憂', '��' => '慼', '��' => '慰', '��' => '慫', '��' => '慾', '��' => '憧', '��' => '憐', '��' => '憫', '��' => '憎', '��' => '憬', '��' => '憚', '��' => '憤', '��' => '憔', '��' => '憮', '��' => '戮', '��' => '摩', '��' => '摯', '��' => '摹', '��' => '撞', '��' => '撲', '��' => '撈', '��' => '撐', '��' => '撰', '��' => '撥', '��' => '撓', '��' => '撕', '��' => '撩', '��' => '撒', '��' => '撮', '��' => '播', '��' => '撫', '��' => '撚', '��' => '撬', '��' => '撙', '��' => '撢', '��' => '撳', '��' => '敵', '��' => '敷', '��' => '數', '��' => '暮', '��' => '暫', '��' => '暴', '��' => '暱', '��' => '樣', '��' => '樟', '��' => '槨', '��' => '樁', '��' => '樞', '��' => '標', '��' => '槽', '��' => '模', '��' => '樓', '��' => '樊', '��' => '槳', '��' => '樂', '��' => '樅', '��' => '槭', '��' => '樑', '��' => '歐', '��' => '歎', '��' => '殤', '��' => '毅', '��' => '毆', '��' => '漿', '��' => '潼', '��' => '澄', '��' => '潑', '��' => '潦', '��' => '潔', '��' => '澆', '��' => '潭', '��' => '潛', '��' => '潸', '��' => '潮', '��' => '澎', '��' => '潺', '��' => '潰', '��' => '潤', '��' => '澗', '��' => '潘', '��' => '滕', '��' => '潯', '��' => '潠', '��' => '潟', '��' => '熟', '��' => '熬', '��' => '熱', '��' => '熨', '��' => '牖', '��' => '犛', '��' => '獎', '��' => '獗', '��' => '瑩', '��' => '璋', '��' => '璃', '�@' => '瑾', '�A' => '璀', '�B' => '畿', '�C' => '瘠', '�D' => '瘩', '�E' => '瘟', '�F' => '瘤', '�G' => '瘦', '�H' => '瘡', '�I' => '瘢', '�J' => '皚', '�K' => '皺', '�L' => '盤', '�M' => '瞎', '�N' => '瞇', '�O' => '瞌', '�P' => '瞑', '�Q' => '瞋', '�R' => '磋', '�S' => '磅', '�T' => '確', '�U' => '磊', '�V' => '碾', '�W' => '磕', '�X' => '碼', '�Y' => '磐', '�Z' => '稿', '�[' => '稼', '�\\' => '穀', '�]' => '稽', '�^' => '稷', '�_' => '稻', '�`' => '窯', '�a' => '窮', '�b' => '箭', '�c' => '箱', '�d' => '範', '�e' => '箴', '�f' => '篆', '�g' => '篇', '�h' => '篁', '�i' => '箠', '�j' => '篌', '�k' => '糊', '�l' => '締', '�m' => '練', '�n' => '緯', '�o' => '緻', '�p' => '緘', '�q' => '緬', '�r' => '緝', '�s' => '編', '�t' => '緣', '�u' => '線', '�v' => '緞', '�w' => '緩', '�x' => '綞', '�y' => '緙', '�z' => '緲', '�{' => '緹', '�|' => '罵', '�}' => '罷', '�~' => '羯', '��' => '翩', '��' => '耦', '��' => '膛', '��' => '膜', '��' => '膝', '��' => '膠', '��' => '膚', '��' => '膘', '��' => '蔗', '��' => '蔽', '��' => '蔚', '��' => '蓮', '��' => '蔬', '��' => '蔭', '��' => '蔓', '��' => '蔑', '��' => '蔣', '��' => '蔡', '��' => '蔔', '��' => '蓬', '��' => '蔥', '��' => '蓿', '��' => '蔆', '��' => '螂', '��' => '蝴', '��' => '蝶', '��' => '蝠', '��' => '蝦', '��' => '蝸', '��' => '蝨', '��' => '蝙', '��' => '蝗', '��' => '蝌', '��' => '蝓', '��' => '衛', '��' => '衝', '��' => '褐', '��' => '複', '��' => '褒', '��' => '褓', '��' => '褕', '��' => '褊', '��' => '誼', '��' => '諒', '��' => '談', '��' => '諄', '��' => '誕', '��' => '請', '��' => '諸', '��' => '課', '��' => '諉', '��' => '諂', '��' => '調', '��' => '誰', '��' => '論', '��' => '諍', '��' => '誶', '��' => '誹', '��' => '諛', '��' => '豌', '��' => '豎', '��' => '豬', '��' => '賠', '��' => '賞', '��' => '賦', '��' => '賤', '��' => '賬', '��' => '賭', '��' => '賢', '��' => '賣', '��' => '賜', '��' => '質', '��' => '賡', '��' => '赭', '��' => '趟', '��' => '趣', '��' => '踫', '��' => '踐', '��' => '踝', '��' => '踢', '��' => '踏', '��' => '踩', '��' => '踟', '��' => '踡', '��' => '踞', '��' => '躺', '��' => '輝', '��' => '輛', '��' => '輟', '��' => '輩', '��' => '輦', '��' => '輪', '��' => '輜', '��' => '輞', '�@' => '輥', '�A' => '適', '�B' => '遮', '�C' => '遨', '�D' => '遭', '�E' => '遷', '�F' => '鄰', '�G' => '鄭', '�H' => '鄧', '�I' => '鄱', '�J' => '醇', '�K' => '醉', '�L' => '醋', '�M' => '醃', '�N' => '鋅', '�O' => '銻', '�P' => '銷', '�Q' => '鋪', '�R' => '銬', '�S' => '鋤', '�T' => '鋁', '�U' => '銳', '�V' => '銼', '�W' => '鋒', '�X' => '鋇', '�Y' => '鋰', '�Z' => '銲', '�[' => '閭', '�\\' => '閱', '�]' => '霄', '�^' => '霆', '�_' => '震', '�`' => '霉', '�a' => '靠', '�b' => '鞍', '�c' => '鞋', '�d' => '鞏', '�e' => '頡', '�f' => '頫', '�g' => '頜', '�h' => '颳', '�i' => '養', '�j' => '餓', '�k' => '餒', '�l' => '餘', '�m' => '駝', '�n' => '駐', '�o' => '駟', '�p' => '駛', '�q' => '駑', '�r' => '駕', '�s' => '駒', '�t' => '駙', '�u' => '骷', '�v' => '髮', '�w' => '髯', '�x' => '鬧', '�y' => '魅', '�z' => '魄', '�{' => '魷', '�|' => '魯', '�}' => '鴆', '�~' => '鴉', '��' => '鴃', '��' => '麩', '��' => '麾', '��' => '黎', '��' => '墨', '��' => '齒', '��' => '儒', '��' => '儘', '��' => '儔', '��' => '儐', '��' => '儕', '��' => '冀', '��' => '冪', '��' => '凝', '��' => '劑', '��' => '劓', '��' => '勳', '��' => '噙', '��' => '噫', '��' => '噹', '��' => '噩', '��' => '噤', '��' => '噸', '��' => '噪', '��' => '器', '��' => '噥', '��' => '噱', '��' => '噯', '��' => '噬', '��' => '噢', '��' => '噶', '��' => '壁', '��' => '墾', '��' => '壇', '��' => '壅', '��' => '奮', '��' => '嬝', '��' => '嬴', '��' => '學', '��' => '寰', '��' => '導', '��' => '彊', '��' => '憲', '��' => '憑', '��' => '憩', '��' => '憊', '��' => '懍', '��' => '憶', '��' => '憾', '��' => '懊', '��' => '懈', '��' => '戰', '��' => '擅', '��' => '擁', '��' => '擋', '��' => '撻', '��' => '撼', '��' => '據', '��' => '擄', '��' => '擇', '��' => '擂', '��' => '操', '��' => '撿', '��' => '擒', '��' => '擔', '��' => '撾', '��' => '整', '��' => '曆', '��' => '曉', '��' => '暹', '��' => '曄', '��' => '曇', '��' => '暸', '��' => '樽', '��' => '樸', '��' => '樺', '��' => '橙', '��' => '橫', '��' => '橘', '��' => '樹', '��' => '橄', '��' => '橢', '��' => '橡', '��' => '橋', '��' => '橇', '��' => '樵', '��' => '機', '��' => '橈', '��' => '歙', '��' => '歷', '��' => '氅', '��' => '濂', '��' => '澱', '��' => '澡', '�@' => '濃', '�A' => '澤', '�B' => '濁', '�C' => '澧', '�D' => '澳', '�E' => '激', '�F' => '澹', '�G' => '澶', '�H' => '澦', '�I' => '澠', '�J' => '澴', '�K' => '熾', '�L' => '燉', '�M' => '燐', '�N' => '燒', '�O' => '燈', '�P' => '燕', '�Q' => '熹', '�R' => '燎', '�S' => '燙', '�T' => '燜', '�U' => '燃', '�V' => '燄', '�W' => '獨', '�X' => '璜', '�Y' => '璣', '�Z' => '璘', '�[' => '璟', '�\\' => '璞', '�]' => '瓢', '�^' => '甌', '�_' => '甍', '�`' => '瘴', '�a' => '瘸', '�b' => '瘺', '�c' => '盧', '�d' => '盥', '�e' => '瞠', '�f' => '瞞', '�g' => '瞟', '�h' => '瞥', '�i' => '磨', '�j' => '磚', '�k' => '磬', '�l' => '磧', '�m' => '禦', '�n' => '積', '�o' => '穎', '�p' => '穆', '�q' => '穌', '�r' => '穋', '�s' => '窺', '�t' => '篙', '�u' => '簑', '�v' => '築', '�w' => '篤', '�x' => '篛', '�y' => '篡', '�z' => '篩', '�{' => '篦', '�|' => '糕', '�}' => '糖', '�~' => '縊', '��' => '縑', '��' => '縈', '��' => '縛', '��' => '縣', '��' => '縞', '��' => '縝', '��' => '縉', '��' => '縐', '��' => '罹', '��' => '羲', '��' => '翰', '��' => '翱', '��' => '翮', '��' => '耨', '��' => '膳', '��' => '膩', '��' => '膨', '��' => '臻', '��' => '興', '��' => '艘', '��' => '艙', '��' => '蕊', '��' => '蕙', '��' => '蕈', '��' => '蕨', '��' => '蕩', '��' => '蕃', '��' => '蕉', '��' => '蕭', '��' => '蕪', '��' => '蕞', '��' => '螃', '��' => '螟', '��' => '螞', '��' => '螢', '��' => '融', '��' => '衡', '��' => '褪', '��' => '褲', '��' => '褥', '��' => '褫', '��' => '褡', '��' => '親', '��' => '覦', '��' => '諦', '��' => '諺', '��' => '諫', '��' => '諱', '��' => '謀', '��' => '諜', '��' => '諧', '��' => '諮', '��' => '諾', '��' => '謁', '��' => '謂', '��' => '諷', '��' => '諭', '��' => '諳', '��' => '諶', '��' => '諼', '��' => '豫', '��' => '豭', '��' => '貓', '��' => '賴', '��' => '蹄', '��' => '踱', '��' => '踴', '��' => '蹂', '��' => '踹', '��' => '踵', '��' => '輻', '��' => '輯', '��' => '輸', '��' => '輳', '��' => '辨', '��' => '辦', '��' => '遵', '��' => '遴', '��' => '選', '��' => '遲', '��' => '遼', '��' => '遺', '��' => '鄴', '��' => '醒', '��' => '錠', '��' => '錶', '��' => '鋸', '��' => '錳', '��' => '錯', '��' => '錢', '��' => '鋼', '��' => '錫', '��' => '錄', '��' => '錚', '�@' => '錐', '�A' => '錦', '�B' => '錡', '�C' => '錕', '�D' => '錮', '�E' => '錙', '�F' => '閻', '�G' => '隧', '�H' => '隨', '�I' => '險', '�J' => '雕', '�K' => '霎', '�L' => '霑', '�M' => '霖', '�N' => '霍', '�O' => '霓', '�P' => '霏', '�Q' => '靛', '�R' => '靜', '�S' => '靦', '�T' => '鞘', '�U' => '頰', '�V' => '頸', '�W' => '頻', '�X' => '頷', '�Y' => '頭', '�Z' => '頹', '�[' => '頤', '�\\' => '餐', '�]' => '館', '�^' => '餞', '�_' => '餛', '�`' => '餡', '�a' => '餚', '�b' => '駭', '�c' => '駢', '�d' => '駱', '�e' => '骸', '�f' => '骼', '�g' => '髻', '�h' => '髭', '�i' => '鬨', '�j' => '鮑', '�k' => '鴕', '�l' => '鴣', '�m' => '鴦', '�n' => '鴨', '�o' => '鴒', '�p' => '鴛', '�q' => '默', '�r' => '黔', '�s' => '龍', '�t' => '龜', '�u' => '優', '�v' => '償', '�w' => '儡', '�x' => '儲', '�y' => '勵', '�z' => '嚎', '�{' => '嚀', '�|' => '嚐', '�}' => '嚅', '�~' => '嚇', '��' => '嚏', '��' => '壕', '��' => '壓', '��' => '壑', '��' => '壎', '��' => '嬰', '��' => '嬪', '��' => '嬤', '��' => '孺', '��' => '尷', '��' => '屨', '��' => '嶼', '��' => '嶺', '��' => '嶽', '��' => '嶸', '��' => '幫', '��' => '彌', '��' => '徽', '��' => '應', '��' => '懂', '��' => '懇', '��' => '懦', '��' => '懋', '��' => '戲', '��' => '戴', '��' => '擎', '��' => '擊', '��' => '擘', '��' => '擠', '��' => '擰', '��' => '擦', '��' => '擬', '��' => '擱', '��' => '擢', '��' => '擭', '��' => '斂', '��' => '斃', '��' => '曙', '��' => '曖', '��' => '檀', '��' => '檔', '��' => '檄', '��' => '檢', '��' => '檜', '��' => '櫛', '��' => '檣', '��' => '橾', '��' => '檗', '��' => '檐', '��' => '檠', '��' => '歜', '��' => '殮', '��' => '毚', '��' => '氈', '��' => '濘', '��' => '濱', '��' => '濟', '��' => '濠', '��' => '濛', '��' => '濤', '��' => '濫', '��' => '濯', '��' => '澀', '��' => '濬', '��' => '濡', '��' => '濩', '��' => '濕', '��' => '濮', '��' => '濰', '��' => '燧', '��' => '營', '��' => '燮', '��' => '燦', '��' => '燥', '��' => '燭', '��' => '燬', '��' => '燴', '��' => '燠', '��' => '爵', '��' => '牆', '��' => '獰', '��' => '獲', '��' => '璩', '��' => '環', '��' => '璦', '��' => '璨', '��' => '癆', '��' => '療', '��' => '癌', '��' => '盪', '��' => '瞳', '��' => '瞪', '��' => '瞰', '��' => '瞬', '�@' => '瞧', '�A' => '瞭', '�B' => '矯', '�C' => '磷', '�D' => '磺', '�E' => '磴', '�F' => '磯', '�G' => '礁', '�H' => '禧', '�I' => '禪', '�J' => '穗', '�K' => '窿', '�L' => '簇', '�M' => '簍', '�N' => '篾', '�O' => '篷', '�P' => '簌', '�Q' => '篠', '�R' => '糠', '�S' => '糜', '�T' => '糞', '�U' => '糢', '�V' => '糟', '�W' => '糙', '�X' => '糝', '�Y' => '縮', '�Z' => '績', '�[' => '繆', '�\\' => '縷', '�]' => '縲', '�^' => '繃', '�_' => '縫', '�`' => '總', '�a' => '縱', '�b' => '繅', '�c' => '繁', '�d' => '縴', '�e' => '縹', '�f' => '繈', '�g' => '縵', '�h' => '縿', '�i' => '縯', '�j' => '罄', '�k' => '翳', '�l' => '翼', '�m' => '聱', '�n' => '聲', '�o' => '聰', '�p' => '聯', '�q' => '聳', '�r' => '臆', '�s' => '臃', '�t' => '膺', '�u' => '臂', '�v' => '臀', '�w' => '膿', '�x' => '膽', '�y' => '臉', '�z' => '膾', '�{' => '臨', '�|' => '舉', '�}' => '艱', '�~' => '薪', '��' => '薄', '��' => '蕾', '��' => '薜', '��' => '薑', '��' => '薔', '��' => '薯', '��' => '薛', '��' => '薇', '��' => '薨', '��' => '薊', '��' => '虧', '��' => '蟀', '��' => '蟑', '��' => '螳', '��' => '蟒', '��' => '蟆', '��' => '螫', '��' => '螻', '��' => '螺', '��' => '蟈', '��' => '蟋', '��' => '褻', '��' => '褶', '��' => '襄', '��' => '褸', '��' => '褽', '��' => '覬', '��' => '謎', '��' => '謗', '��' => '謙', '��' => '講', '��' => '謊', '��' => '謠', '��' => '謝', '��' => '謄', '��' => '謐', '��' => '豁', '��' => '谿', '��' => '豳', '��' => '賺', '��' => '賽', '��' => '購', '��' => '賸', '��' => '賻', '��' => '趨', '��' => '蹉', '��' => '蹋', '��' => '蹈', '��' => '蹊', '��' => '轄', '��' => '輾', '��' => '轂', '��' => '轅', '��' => '輿', '��' => '避', '��' => '遽', '��' => '還', '��' => '邁', '��' => '邂', '��' => '邀', '��' => '鄹', '��' => '醣', '��' => '醞', '��' => '醜', '��' => '鍍', '��' => '鎂', '��' => '錨', '��' => '鍵', '��' => '鍊', '��' => '鍥', '��' => '鍋', '��' => '錘', '��' => '鍾', '��' => '鍬', '��' => '鍛', '��' => '鍰', '��' => '鍚', '��' => '鍔', '��' => '闊', '��' => '闋', '��' => '闌', '��' => '闈', '��' => '闆', '��' => '隱', '��' => '隸', '��' => '雖', '��' => '霜', '��' => '霞', '��' => '鞠', '��' => '韓', '��' => '顆', '��' => '颶', '��' => '餵', '��' => '騁', '�@' => '駿', '�A' => '鮮', '�B' => '鮫', '�C' => '鮪', '�D' => '鮭', '�E' => '鴻', '�F' => '鴿', '�G' => '麋', '�H' => '黏', '�I' => '點', '�J' => '黜', '�K' => '黝', '�L' => '黛', '�M' => '鼾', '�N' => '齋', '�O' => '叢', '�P' => '嚕', '�Q' => '嚮', '�R' => '壙', '�S' => '壘', '�T' => '嬸', '�U' => '彝', '�V' => '懣', '�W' => '戳', '�X' => '擴', '�Y' => '擲', '�Z' => '擾', '�[' => '攆', '�\\' => '擺', '�]' => '擻', '�^' => '擷', '�_' => '斷', '�`' => '曜', '�a' => '朦', '�b' => '檳', '�c' => '檬', '�d' => '櫃', '�e' => '檻', '�f' => '檸', '�g' => '櫂', '�h' => '檮', '�i' => '檯', '�j' => '歟', '�k' => '歸', '�l' => '殯', '�m' => '瀉', '�n' => '瀋', '�o' => '濾', '�p' => '瀆', '�q' => '濺', '�r' => '瀑', '�s' => '瀏', '�t' => '燻', '�u' => '燼', '�v' => '燾', '�w' => '燸', '�x' => '獷', '�y' => '獵', '�z' => '璧', '�{' => '璿', '�|' => '甕', '�}' => '癖', '�~' => '癘', '¡' => '癒', '¢' => '瞽', '£' => '瞿', '¤' => '瞻', '¥' => '瞼', '¦' => '礎', '§' => '禮', '¨' => '穡', '©' => '穢', 'ª' => '穠', '«' => '竄', '¬' => '竅', '' => '簫', '®' => '簧', '¯' => '簪', '°' => '簞', '±' => '簣', '²' => '簡', '³' => '糧', '´' => '織', 'µ' => '繕', '¶' => '繞', '·' => '繚', '¸' => '繡', '¹' => '繒', 'º' => '繙', '»' => '罈', '¼' => '翹', '½' => '翻', '¾' => '職', '¿' => '聶', '�' => '臍', '�' => '臏', '��' => '舊', '��' => '藏', '��' => '薩', '��' => '藍', '��' => '藐', '��' => '藉', '��' => '薰', '��' => '薺', '��' => '薹', '��' => '薦', '��' => '蟯', '��' => '蟬', '��' => '蟲', '��' => '蟠', '��' => '覆', '��' => '覲', '��' => '觴', '��' => '謨', '��' => '謹', '��' => '謬', '��' => '謫', '��' => '豐', '��' => '贅', '��' => '蹙', '��' => '蹣', '��' => '蹦', '��' => '蹤', '��' => '蹟', '��' => '蹕', '��' => '軀', '��' => '轉', '��' => '轍', '��' => '邇', '��' => '邃', '��' => '邈', '��' => '醫', '��' => '醬', '��' => '釐', '��' => '鎔', '��' => '鎊', '��' => '鎖', '��' => '鎢', '��' => '鎳', '��' => '鎮', '��' => '鎬', '��' => '鎰', '��' => '鎘', '��' => '鎚', '��' => '鎗', '��' => '闔', '��' => '闖', '�' => '闐', '�' => '闕', '�' => '離', '�' => '雜', '�' => '雙', '�' => '雛', '�' => '雞', '�' => '霤', '�' => '鞣', '�' => '鞦', '�@' => '鞭', '�A' => '韹', '�B' => '額', '�C' => '顏', '�D' => '題', '�E' => '顎', '�F' => '顓', '�G' => '颺', '�H' => '餾', '�I' => '餿', '�J' => '餽', '�K' => '餮', '�L' => '馥', '�M' => '騎', '�N' => '髁', '�O' => '鬃', '�P' => '鬆', '�Q' => '魏', '�R' => '魎', '�S' => '魍', '�T' => '鯊', '�U' => '鯉', '�V' => '鯽', '�W' => '鯈', '�X' => '鯀', '�Y' => '鵑', '�Z' => '鵝', '�[' => '鵠', '�\\' => '黠', '�]' => '鼕', '�^' => '鼬', '�_' => '儳', '�`' => '嚥', '�a' => '壞', '�b' => '壟', '�c' => '壢', '�d' => '寵', '�e' => '龐', '�f' => '廬', '�g' => '懲', '�h' => '懷', '�i' => '懶', '�j' => '懵', '�k' => '攀', '�l' => '攏', '�m' => '曠', '�n' => '曝', '�o' => '櫥', '�p' => '櫝', '�q' => '櫚', '�r' => '櫓', '�s' => '瀛', '�t' => '瀟', '�u' => '瀨', '�v' => '瀚', '�w' => '瀝', '�x' => '瀕', '�y' => '瀘', '�z' => '爆', '�{' => '爍', '�|' => '牘', '�}' => '犢', '�~' => '獸', 'á' => '獺', 'â' => '璽', 'ã' => '瓊', 'ä' => '瓣', 'å' => '疇', 'æ' => '疆', 'ç' => '癟', 'è' => '癡', 'é' => '矇', 'ê' => '礙', 'ë' => '禱', 'ì' => '穫', 'í' => '穩', 'î' => '簾', 'ï' => '簿', 'ð' => '簸', 'ñ' => '簽', 'ò' => '簷', 'ó' => '籀', 'ô' => '繫', 'õ' => '繭', 'ö' => '繹', '÷' => '繩', 'ø' => '繪', 'ù' => '羅', 'ú' => '繳', 'û' => '羶', 'ü' => '羹', 'ý' => '羸', 'þ' => '臘', 'ÿ' => '藩', '�' => '藝', '�' => '藪', '��' => '藕', '��' => '藤', '��' => '藥', '��' => '藷', '��' => '蟻', '��' => '蠅', '��' => '蠍', '��' => '蟹', '��' => '蟾', '��' => '襠', '��' => '襟', '��' => '襖', '��' => '襞', '��' => '譁', '��' => '譜', '��' => '識', '��' => '證', '��' => '譚', '��' => '譎', '��' => '譏', '��' => '譆', '��' => '譙', '��' => '贈', '��' => '贊', '��' => '蹼', '��' => '蹲', '��' => '躇', '��' => '蹶', '��' => '蹬', '��' => '蹺', '��' => '蹴', '��' => '轔', '��' => '轎', '��' => '辭', '��' => '邊', '��' => '邋', '��' => '醱', '��' => '醮', '��' => '鏡', '��' => '鏑', '��' => '鏟', '��' => '鏃', '��' => '鏈', '��' => '鏜', '��' => '鏝', '��' => '鏖', '��' => '鏢', '��' => '鏍', '��' => '鏘', '��' => '鏤', '��' => '鏗', '�' => '鏨', '�' => '關', '�' => '隴', '�' => '難', '�' => '霪', '�' => '霧', '�' => '靡', '�' => '韜', '�' => '韻', '�' => '類', '�@' => '願', '�A' => '顛', '�B' => '颼', '�C' => '饅', '�D' => '饉', '�E' => '騖', '�F' => '騙', '�G' => '鬍', '�H' => '鯨', '�I' => '鯧', '�J' => '鯖', '�K' => '鯛', '�L' => '鶉', '�M' => '鵡', '�N' => '鵲', '�O' => '鵪', '�P' => '鵬', '�Q' => '麒', '�R' => '麗', '�S' => '麓', '�T' => '麴', '�U' => '勸', '�V' => '嚨', '�W' => '嚷', '�X' => '嚶', '�Y' => '嚴', '�Z' => '嚼', '�[' => '壤', '�\\' => '孀', '�]' => '孃', '�^' => '孽', '�_' => '寶', '�`' => '巉', '�a' => '懸', '�b' => '懺', '�c' => '攘', '�d' => '攔', '�e' => '攙', '�f' => '曦', '�g' => '朧', '�h' => '櫬', '�i' => '瀾', '�j' => '瀰', '�k' => '瀲', '�l' => '爐', '�m' => '獻', '�n' => '瓏', '�o' => '癢', '�p' => '癥', '�q' => '礦', '�r' => '礪', '�s' => '礬', '�t' => '礫', '�u' => '竇', '�v' => '競', '�w' => '籌', '�x' => '籃', '�y' => '籍', '�z' => '糯', '�{' => '糰', '�|' => '辮', '�}' => '繽', '�~' => '繼', 'ġ' => '纂', 'Ģ' => '罌', 'ģ' => '耀', 'Ĥ' => '臚', 'ĥ' => '艦', 'Ħ' => '藻', 'ħ' => '藹', 'Ĩ' => '蘑', 'ĩ' => '藺', 'Ī' => '蘆', 'ī' => '蘋', 'Ĭ' => '蘇', 'ĭ' => '蘊', 'Į' => '蠔', 'į' => '蠕', 'İ' => '襤', 'ı' => '覺', 'IJ' => '觸', 'ij' => '議', 'Ĵ' => '譬', 'ĵ' => '警', 'Ķ' => '譯', 'ķ' => '譟', 'ĸ' => '譫', 'Ĺ' => '贏', 'ĺ' => '贍', 'Ļ' => '躉', 'ļ' => '躁', 'Ľ' => '躅', 'ľ' => '躂', 'Ŀ' => '醴', '�' => '釋', '�' => '鐘', '��' => '鐃', '��' => '鏽', '��' => '闡', '��' => '霰', '��' => '飄', '��' => '饒', '��' => '饑', '��' => '馨', '��' => '騫', '��' => '騰', '��' => '騷', '��' => '騵', '��' => '鰓', '��' => '鰍', '��' => '鹹', '��' => '麵', '��' => '黨', '��' => '鼯', '��' => '齟', '��' => '齣', '��' => '齡', '��' => '儷', '��' => '儸', '��' => '囁', '��' => '囀', '��' => '囂', '��' => '夔', '��' => '屬', '��' => '巍', '��' => '懼', '��' => '懾', '��' => '攝', '��' => '攜', '��' => '斕', '��' => '曩', '��' => '櫻', '��' => '欄', '��' => '櫺', '��' => '殲', '��' => '灌', '��' => '爛', '��' => '犧', '��' => '瓖', '��' => '瓔', '��' => '癩', '��' => '矓', '��' => '籐', '��' => '纏', '��' => '續', '��' => '羼', '��' => '蘗', '�' => '蘭', '�' => '蘚', '�' => '蠣', '�' => '蠢', '�' => '蠡', '�' => '蠟', '�' => '襪', '�' => '襬', '�' => '覽', '�' => '譴', '�@' => '護', '�A' => '譽', '�B' => '贓', '�C' => '躊', '�D' => '躍', '�E' => '躋', '�F' => '轟', '�G' => '辯', '�H' => '醺', '�I' => '鐮', '�J' => '鐳', '�K' => '鐵', '�L' => '鐺', '�M' => '鐸', '�N' => '鐲', '�O' => '鐫', '�P' => '闢', '�Q' => '霸', '�R' => '霹', '�S' => '露', '�T' => '響', '�U' => '顧', '�V' => '顥', '�W' => '饗', '�X' => '驅', '�Y' => '驃', '�Z' => '驀', '�[' => '騾', '�\\' => '髏', '�]' => '魔', '�^' => '魑', '�_' => '鰭', '�`' => '鰥', '�a' => '鶯', '�b' => '鶴', '�c' => '鷂', '�d' => '鶸', '�e' => '麝', '�f' => '黯', '�g' => '鼙', '�h' => '齜', '�i' => '齦', '�j' => '齧', '�k' => '儼', '�l' => '儻', '�m' => '囈', '�n' => '囊', '�o' => '囉', '�p' => '孿', '�q' => '巔', '�r' => '巒', '�s' => '彎', '�t' => '懿', '�u' => '攤', '�v' => '權', '�w' => '歡', '�x' => '灑', '�y' => '灘', '�z' => '玀', '�{' => '瓤', '�|' => '疊', '�}' => '癮', '�~' => '癬', 'š' => '禳', 'Ţ' => '籠', 'ţ' => '籟', 'Ť' => '聾', 'ť' => '聽', 'Ŧ' => '臟', 'ŧ' => '襲', 'Ũ' => '襯', 'ũ' => '觼', 'Ū' => '讀', 'ū' => '贖', 'Ŭ' => '贗', 'ŭ' => '躑', 'Ů' => '躓', 'ů' => '轡', 'Ű' => '酈', 'ű' => '鑄', 'Ų' => '鑑', 'ų' => '鑒', 'Ŵ' => '霽', 'ŵ' => '霾', 'Ŷ' => '韃', 'ŷ' => '韁', 'Ÿ' => '顫', 'Ź' => '饕', 'ź' => '驕', 'Ż' => '驍', 'ż' => '髒', 'Ž' => '鬚', 'ž' => '鱉', 'ſ' => '鰱', '�' => '鰾', '�' => '鰻', '��' => '鷓', '��' => '鷗', '��' => '鼴', '��' => '齬', '��' => '齪', '��' => '龔', '��' => '囌', '��' => '巖', '��' => '戀', '��' => '攣', '��' => '攫', '��' => '攪', '��' => '曬', '��' => '欐', '��' => '瓚', '��' => '竊', '��' => '籤', '��' => '籣', '��' => '籥', '��' => '纓', '��' => '纖', '��' => '纔', '��' => '臢', '��' => '蘸', '��' => '蘿', '��' => '蠱', '��' => '變', '��' => '邐', '��' => '邏', '��' => '鑣', '��' => '鑠', '��' => '鑤', '��' => '靨', '��' => '顯', '��' => '饜', '��' => '驚', '��' => '驛', '��' => '驗', '��' => '髓', '��' => '體', '��' => '髑', '��' => '鱔', '��' => '鱗', '��' => '鱖', '��' => '鷥', '��' => '麟', '��' => '黴', '��' => '囑', '��' => '壩', '��' => '攬', '��' => '灞', '�' => '癱', '�' => '癲', '�' => '矗', '�' => '罐', '�' => '羈', '�' => '蠶', '�' => '蠹', '�' => '衢', '�' => '讓', '�' => '讒', '�@' => '讖', '�A' => '艷', '�B' => '贛', '�C' => '釀', '�D' => '鑪', '�E' => '靂', '�F' => '靈', '�G' => '靄', '�H' => '韆', '�I' => '顰', '�J' => '驟', '�K' => '鬢', '�L' => '魘', '�M' => '鱟', '�N' => '鷹', '�O' => '鷺', '�P' => '鹼', '�Q' => '鹽', '�R' => '鼇', '�S' => '齷', '�T' => '齲', '�U' => '廳', '�V' => '欖', '�W' => '灣', '�X' => '籬', '�Y' => '籮', '�Z' => '蠻', '�[' => '觀', '�\\' => '躡', '�]' => '釁', '�^' => '鑲', '�_' => '鑰', '�`' => '顱', '�a' => '饞', '�b' => '髖', '�c' => '鬣', '�d' => '黌', '�e' => '灤', '�f' => '矚', '�g' => '讚', '�h' => '鑷', '�i' => '韉', '�j' => '驢', '�k' => '驥', '�l' => '纜', '�m' => '讜', '�n' => '躪', '�o' => '釅', '�p' => '鑽', '�q' => '鑾', '�r' => '鑼', '�s' => '鱷', '�t' => '鱸', '�u' => '黷', '�v' => '豔', '�w' => '鑿', '�x' => '鸚', '�y' => '爨', '�z' => '驪', '�{' => '鬱', '�|' => '鸛', '�}' => '鸞', '�~' => '籲', 'ơ' => 'ヾ', 'Ƣ' => 'ゝ', 'ƣ' => 'ゞ', 'Ƥ' => '々', 'ƥ' => 'ぁ', 'Ʀ' => 'あ', 'Ƨ' => 'ぃ', 'ƨ' => 'い', 'Ʃ' => 'ぅ', 'ƪ' => 'う', 'ƫ' => 'ぇ', 'Ƭ' => 'え', 'ƭ' => 'ぉ', 'Ʈ' => 'お', 'Ư' => 'か', 'ư' => 'が', 'Ʊ' => 'き', 'Ʋ' => 'ぎ', 'Ƴ' => 'く', 'ƴ' => 'ぐ', 'Ƶ' => 'け', 'ƶ' => 'げ', 'Ʒ' => 'こ', 'Ƹ' => 'ご', 'ƹ' => 'さ', 'ƺ' => 'ざ', 'ƻ' => 'し', 'Ƽ' => 'じ', 'ƽ' => 'す', 'ƾ' => 'ず', 'ƿ' => 'せ', '�' => 'ぜ', '�' => 'そ', '��' => 'ぞ', '��' => 'た', '��' => 'だ', '��' => 'ち', '��' => 'ぢ', '��' => 'っ', '��' => 'つ', '��' => 'づ', '��' => 'て', '��' => 'で', '��' => 'と', '��' => 'ど', '��' => 'な', '��' => 'に', '��' => 'ぬ', '��' => 'ね', '��' => 'の', '��' => 'は', '��' => 'ば', '��' => 'ぱ', '��' => 'ひ', '��' => 'び', '��' => 'ぴ', '��' => 'ふ', '��' => 'ぶ', '��' => 'ぷ', '��' => 'へ', '��' => 'べ', '��' => 'ぺ', '��' => 'ほ', '��' => 'ぼ', '��' => 'ぽ', '��' => 'ま', '��' => 'み', '��' => 'む', '��' => 'め', '��' => 'も', '��' => 'ゃ', '��' => 'や', '��' => 'ゅ', '��' => 'ゆ', '��' => 'ょ', '��' => 'よ', '��' => 'ら', '��' => 'り', '��' => 'る', '��' => 'れ', '��' => 'ろ', '��' => 'ゎ', '��' => 'わ', '��' => 'ゐ', '�' => 'ゑ', '�' => 'を', '�' => 'ん', '�' => 'ァ', '�' => 'ア', '�' => 'ィ', '�' => 'イ', '�' => 'ゥ', '�' => 'ウ', '�' => 'ェ', '�@' => 'エ', '�A' => 'ォ', '�B' => 'オ', '�C' => 'カ', '�D' => 'ガ', '�E' => 'キ', '�F' => 'ギ', '�G' => 'ク', '�H' => 'グ', '�I' => 'ケ', '�J' => 'ゲ', '�K' => 'コ', '�L' => 'ゴ', '�M' => 'サ', '�N' => 'ザ', '�O' => 'シ', '�P' => 'ジ', '�Q' => 'ス', '�R' => 'ズ', '�S' => 'セ', '�T' => 'ゼ', '�U' => 'ソ', '�V' => 'ゾ', '�W' => 'タ', '�X' => 'ダ', '�Y' => 'チ', '�Z' => 'ヂ', '�[' => 'ッ', '�\\' => 'ツ', '�]' => 'ヅ', '�^' => 'テ', '�_' => 'デ', '�`' => 'ト', '�a' => 'ド', '�b' => 'ナ', '�c' => 'ニ', '�d' => 'ヌ', '�e' => 'ネ', '�f' => 'ノ', '�g' => 'ハ', '�h' => 'バ', '�i' => 'パ', '�j' => 'ヒ', '�k' => 'ビ', '�l' => 'ピ', '�m' => 'フ', '�n' => 'ブ', '�o' => 'プ', '�p' => 'ヘ', '�q' => 'ベ', '�r' => 'ペ', '�s' => 'ホ', '�t' => 'ボ', '�u' => 'ポ', '�v' => 'マ', '�w' => 'ミ', '�x' => 'ム', '�y' => 'メ', '�z' => 'モ', '�{' => 'ャ', '�|' => 'ヤ', '�}' => 'ュ', '�~' => 'ユ', 'ǡ' => 'ョ', 'Ǣ' => 'ヨ', 'ǣ' => 'ラ', 'Ǥ' => 'リ', 'ǥ' => 'ル', 'Ǧ' => 'レ', 'ǧ' => 'ロ', 'Ǩ' => 'ヮ', 'ǩ' => 'ワ', 'Ǫ' => 'ヰ', 'ǫ' => 'ヱ', 'Ǭ' => 'ヲ', 'ǭ' => 'ン', 'Ǯ' => 'ヴ', 'ǯ' => 'ヵ', 'ǰ' => 'ヶ', 'DZ' => 'Д', 'Dz' => 'Е', 'dz' => 'Ё', 'Ǵ' => 'Ж', 'ǵ' => 'З', 'Ƕ' => 'И', 'Ƿ' => 'Й', 'Ǹ' => 'К', 'ǹ' => 'Л', 'Ǻ' => 'М', 'ǻ' => 'У', 'Ǽ' => 'Ф', 'ǽ' => 'Х', 'Ǿ' => 'Ц', 'ǿ' => 'Ч', '�' => 'Ш', '�' => 'Щ', '��' => 'Ъ', '��' => 'Ы', '��' => 'Ь', '��' => 'Э', '��' => 'Ю', '��' => 'Я', '��' => 'а', '��' => 'б', '��' => 'в', '��' => 'г', '��' => 'д', '��' => 'е', '��' => 'ё', '��' => 'ж', '��' => 'з', '��' => 'и', '��' => 'й', '��' => 'к', '��' => 'л', '��' => 'м', '��' => 'н', '��' => 'о', '��' => 'п', '��' => 'р', '��' => 'с', '��' => 'т', '��' => 'у', '��' => 'ф', '��' => 'х', '��' => 'ц', '��' => 'ч', '��' => 'ш', '��' => 'щ', '��' => 'ъ', '��' => 'ы', '��' => 'ь', '��' => 'э', '��' => 'ю', '��' => 'я', '��' => '①', '��' => '②', '��' => '③', '��' => '④', '��' => '⑤', '��' => '⑥', '��' => '⑦', '��' => '⑧', '��' => '⑨', '��' => '⑩', '��' => '⑴', '��' => '⑵', '�' => '⑶', '�' => '⑷', '�' => '⑸', '�' => '⑹', '�' => '⑺', '�' => '⑻', '�' => '⑼', '�' => '⑽', '�@' => '乂', '�A' => '乜', '�B' => '凵', '�C' => '匚', '�D' => '厂', '�E' => '万', '�F' => '丌', '�G' => '乇', '�H' => '亍', '�I' => '囗', '�J' => '兀', '�K' => '屮', '�L' => '彳', '�M' => '丏', '�N' => '冇', '�O' => '与', '�P' => '丮', '�Q' => '亓', '�R' => '仂', '�S' => '仉', '�T' => '仈', '�U' => '冘', '�V' => '勼', '�W' => '卬', '�X' => '厹', '�Y' => '圠', '�Z' => '夃', '�[' => '夬', '�\\' => '尐', '�]' => '巿', '�^' => '旡', '�_' => '殳', '�`' => '毌', '�a' => '气', '�b' => '爿', '�c' => '丱', '�d' => '丼', '�e' => '仨', '�f' => '仜', '�g' => '仩', '�h' => '仡', '�i' => '仝', '�j' => '仚', '�k' => '刌', '�l' => '匜', '�m' => '卌', '�n' => '圢', '�o' => '圣', '�p' => '夗', '�q' => '夯', '�r' => '宁', '�s' => '宄', '�t' => '尒', '�u' => '尻', '�v' => '屴', '�w' => '屳', '�x' => '帄', '�y' => '庀', '�z' => '庂', '�{' => '忉', '�|' => '戉', '�}' => '扐', '�~' => '氕', 'ɡ' => '氶', 'ɢ' => '汃', 'ɣ' => '氿', 'ɤ' => '氻', 'ɥ' => '犮', 'ɦ' => '犰', 'ɧ' => '玊', 'ɨ' => '禸', 'ɩ' => '肊', 'ɪ' => '阞', 'ɫ' => '伎', 'ɬ' => '优', 'ɭ' => '伬', 'ɮ' => '仵', 'ɯ' => '伔', 'ɰ' => '仱', 'ɱ' => '伀', 'ɲ' => '价', 'ɳ' => '伈', 'ɴ' => '伝', 'ɵ' => '伂', 'ɶ' => '伅', 'ɷ' => '伢', 'ɸ' => '伓', 'ɹ' => '伄', 'ɺ' => '仴', 'ɻ' => '伒', 'ɼ' => '冱', 'ɽ' => '刓', 'ɾ' => '刉', 'ɿ' => '刐', '�' => '劦', '�' => '匢', '��' => '匟', '��' => '卍', '��' => '厊', '��' => '吇', '��' => '囡', '��' => '囟', '��' => '圮', '��' => '圪', '��' => '圴', '��' => '夼', '��' => '妀', '��' => '奼', '��' => '妅', '��' => '奻', '��' => '奾', '��' => '奷', '��' => '奿', '��' => '孖', '��' => '尕', '��' => '尥', '��' => '屼', '��' => '屺', '��' => '屻', '��' => '屾', '��' => '巟', '��' => '幵', '��' => '庄', '��' => '异', '��' => '弚', '��' => '彴', '��' => '忕', '��' => '忔', '��' => '忏', '��' => '扜', '��' => '扞', '��' => '扤', '��' => '扡', '��' => '扦', '��' => '扢', '��' => '扙', '��' => '扠', '��' => '扚', '��' => '扥', '��' => '旯', '��' => '旮', '��' => '朾', '��' => '朹', '��' => '朸', '��' => '朻', '��' => '机', '��' => '朿', '�' => '朼', '�' => '朳', '�' => '氘', '�' => '汆', '�' => '汒', '�' => '汜', '�' => '汏', '�' => '汊', '�' => '汔', '�' => '汋', '�@' => '汌', '�A' => '灱', '�B' => '牞', '�C' => '犴', '�D' => '犵', '�E' => '玎', '�F' => '甪', '�G' => '癿', '�H' => '穵', '�I' => '网', '�J' => '艸', '�K' => '艼', '�L' => '芀', '�M' => '艽', '�N' => '艿', '�O' => '虍', '�P' => '襾', '�Q' => '邙', '�R' => '邗', '�S' => '邘', '�T' => '邛', '�U' => '邔', '�V' => '阢', '�W' => '阤', '�X' => '阠', '�Y' => '阣', '�Z' => '佖', '�[' => '伻', '�\\' => '佢', '�]' => '佉', '�^' => '体', '�_' => '佤', '�`' => '伾', '�a' => '佧', '�b' => '佒', '�c' => '佟', '�d' => '佁', '�e' => '佘', '�f' => '伭', '�g' => '伳', '�h' => '伿', '�i' => '佡', '�j' => '冏', '�k' => '冹', '�l' => '刜', '�m' => '刞', '�n' => '刡', '�o' => '劭', '�p' => '劮', '�q' => '匉', '�r' => '卣', '�s' => '卲', '�t' => '厎', '�u' => '厏', '�v' => '吰', '�w' => '吷', '�x' => '吪', '�y' => '呔', '�z' => '呅', '�{' => '吙', '�|' => '吜', '�}' => '吥', '�~' => '吘', 'ʡ' => '吽', 'ʢ' => '呏', 'ʣ' => '呁', 'ʤ' => '吨', 'ʥ' => '吤', 'ʦ' => '呇', 'ʧ' => '囮', 'ʨ' => '囧', 'ʩ' => '囥', 'ʪ' => '坁', 'ʫ' => '坅', 'ʬ' => '坌', 'ʭ' => '坉', 'ʮ' => '坋', 'ʯ' => '坒', 'ʰ' => '夆', 'ʱ' => '奀', 'ʲ' => '妦', 'ʳ' => '妘', 'ʴ' => '妠', 'ʵ' => '妗', 'ʶ' => '妎', 'ʷ' => '妢', 'ʸ' => '妐', 'ʹ' => '妏', 'ʺ' => '妧', 'ʻ' => '妡', 'ʼ' => '宎', 'ʽ' => '宒', 'ʾ' => '尨', 'ʿ' => '尪', '�' => '岍', '�' => '岏', '��' => '岈', '��' => '岋', '��' => '岉', '��' => '岒', '��' => '岊', '��' => '岆', '��' => '岓', '��' => '岕', '��' => '巠', '��' => '帊', '��' => '帎', '��' => '庋', '��' => '庉', '��' => '庌', '��' => '庈', '��' => '庍', '��' => '弅', '��' => '弝', '��' => '彸', '��' => '彶', '��' => '忒', '��' => '忑', '��' => '忐', '��' => '忭', '��' => '忨', '��' => '忮', '��' => '忳', '��' => '忡', '��' => '忤', '��' => '忣', '��' => '忺', '��' => '忯', '��' => '忷', '��' => '忻', '��' => '怀', '��' => '忴', '��' => '戺', '��' => '抃', '��' => '抌', '��' => '抎', '��' => '抏', '��' => '抔', '��' => '抇', '��' => '扱', '��' => '扻', '��' => '扺', '��' => '扰', '��' => '抁', '��' => '抈', '��' => '扷', '��' => '扽', '�' => '扲', '�' => '扴', '�' => '攷', '�' => '旰', '�' => '旴', '�' => '旳', '�' => '旲', '�' => '旵', '�' => '杅', '�' => '杇', '�@' => '杙', '�A' => '杕', '�B' => '杌', '�C' => '杈', '�D' => '杝', '�E' => '杍', '�F' => '杚', '�G' => '杋', '�H' => '毐', '�I' => '氙', '�J' => '氚', '�K' => '汸', '�L' => '汧', '�M' => '汫', '�N' => '沄', '�O' => '沋', '�P' => '沏', '�Q' => '汱', '�R' => '汯', '�S' => '汩', '�T' => '沚', '�U' => '汭', '�V' => '沇', '�W' => '沕', '�X' => '沜', '�Y' => '汦', '�Z' => '汳', '�[' => '汥', '�\\' => '汻', '�]' => '沎', '�^' => '灴', '�_' => '灺', '�`' => '牣', '�a' => '犿', '�b' => '犽', '�c' => '狃', '�d' => '狆', '�e' => '狁', '�f' => '犺', '�g' => '狅', '�h' => '玕', '�i' => '玗', '�j' => '玓', '�k' => '玔', '�l' => '玒', '�m' => '町', '�n' => '甹', '�o' => '疔', '�p' => '疕', '�q' => '皁', '�r' => '礽', '�s' => '耴', '�t' => '肕', '�u' => '肙', '�v' => '肐', '�w' => '肒', '�x' => '肜', '�y' => '芐', '�z' => '芏', '�{' => '芅', '�|' => '芎', '�}' => '芑', '�~' => '芓', 'ˡ' => '芊', 'ˢ' => '芃', 'ˣ' => '芄', 'ˤ' => '豸', '˥' => '迉', '˦' => '辿', '˧' => '邟', '˨' => '邡', '˩' => '邥', '˪' => '邞', '˫' => '邧', 'ˬ' => '邠', '˭' => '阰', 'ˮ' => '阨', '˯' => '阯', '˰' => '阭', '˱' => '丳', '˲' => '侘', '˳' => '佼', '˴' => '侅', '˵' => '佽', '˶' => '侀', '˷' => '侇', '˸' => '佶', '˹' => '佴', '˺' => '侉', '˻' => '侄', '˼' => '佷', '˽' => '佌', '˾' => '侗', '˿' => '佪', '�' => '侚', '�' => '佹', '��' => '侁', '��' => '佸', '��' => '侐', '��' => '侜', '��' => '侔', '��' => '侞', '��' => '侒', '��' => '侂', '��' => '侕', '��' => '佫', '��' => '佮', '��' => '冞', '��' => '冼', '��' => '冾', '��' => '刵', '��' => '刲', '��' => '刳', '��' => '剆', '��' => '刱', '��' => '劼', '��' => '匊', '��' => '匋', '��' => '匼', '��' => '厒', '��' => '厔', '��' => '咇', '��' => '呿', '��' => '咁', '��' => '咑', '��' => '咂', '��' => '咈', '��' => '呫', '��' => '呺', '��' => '呾', '��' => '呥', '��' => '呬', '��' => '呴', '��' => '呦', '��' => '咍', '��' => '呯', '��' => '呡', '��' => '呠', '��' => '咘', '��' => '呣', '��' => '呧', '��' => '呤', '��' => '囷', '��' => '囹', '��' => '坯', '��' => '坲', '��' => '坭', '�' => '坫', '�' => '坱', '�' => '坰', '�' => '坶', '�' => '垀', '�' => '坵', '�' => '坻', '�' => '坳', '�' => '坴', '�' => '坢', '�@' => '坨', '�A' => '坽', '�B' => '夌', '�C' => '奅', '�D' => '妵', '�E' => '妺', '�F' => '姏', '�G' => '姎', '�H' => '妲', '�I' => '姌', '�J' => '姁', '�K' => '妶', '�L' => '妼', '�M' => '姃', '�N' => '姖', '�O' => '妱', '�P' => '妽', '�Q' => '姀', '�R' => '姈', '�S' => '妴', '�T' => '姇', '�U' => '孢', '�V' => '孥', '�W' => '宓', '�X' => '宕', '�Y' => '屄', '�Z' => '屇', '�[' => '岮', '�\\' => '岤', '�]' => '岠', '�^' => '岵', '�_' => '岯', '�`' => '岨', '�a' => '岬', '�b' => '岟', '�c' => '岣', '�d' => '岭', '�e' => '岢', '�f' => '岪', '�g' => '岧', '�h' => '岝', '�i' => '岥', '�j' => '岶', '�k' => '岰', '�l' => '岦', '�m' => '帗', '�n' => '帔', '�o' => '帙', '�p' => '弨', '�q' => '弢', '�r' => '弣', '�s' => '弤', '�t' => '彔', '�u' => '徂', '�v' => '彾', '�w' => '彽', '�x' => '忞', '�y' => '忥', '�z' => '怭', '�{' => '怦', '�|' => '怙', '�}' => '怲', '�~' => '怋', '̡' => '怴', '̢' => '怊', '̣' => '怗', '̤' => '怳', '̥' => '怚', '̦' => '怞', '̧' => '怬', '̨' => '怢', '̩' => '怍', '̪' => '怐', '̫' => '怮', '̬' => '怓', '̭' => '怑', '̮' => '怌', '̯' => '怉', '̰' => '怜', '̱' => '戔', '̲' => '戽', '̳' => '抭', '̴' => '抴', '̵' => '拑', '̶' => '抾', '̷' => '抪', '̸' => '抶', '̹' => '拊', '̺' => '抮', '̻' => '抳', '̼' => '抯', '̽' => '抻', '̾' => '抩', '̿' => '抰', '�' => '抸', '�' => '攽', '��' => '斨', '��' => '斻', '��' => '昉', '��' => '旼', '��' => '昄', '��' => '昒', '��' => '昈', '��' => '旻', '��' => '昃', '��' => '昋', '��' => '昍', '��' => '昅', '��' => '旽', '��' => '昑', '��' => '昐', '��' => '曶', '��' => '朊', '��' => '枅', '��' => '杬', '��' => '枎', '��' => '枒', '��' => '杶', '��' => '杻', '��' => '枘', '��' => '枆', '��' => '构', '��' => '杴', '��' => '枍', '��' => '枌', '��' => '杺', '��' => '枟', '��' => '枑', '��' => '枙', '��' => '枃', '��' => '杽', '��' => '极', '��' => '杸', '��' => '杹', '��' => '枔', '��' => '欥', '��' => '殀', '��' => '歾', '��' => '毞', '��' => '氝', '��' => '沓', '��' => '泬', '��' => '泫', '��' => '泮', '��' => '泙', '��' => '沶', '��' => '泔', '�' => '沭', '�' => '泧', '�' => '沷', '�' => '泐', '�' => '泂', '�' => '沺', '�' => '泃', '�' => '泆', '�' => '泭', '�' => '泲', '�@' => '泒', '�A' => '泝', '�B' => '沴', '�C' => '沊', '�D' => '沝', '�E' => '沀', '�F' => '泞', '�G' => '泀', '�H' => '洰', '�I' => '泍', '�J' => '泇', '�K' => '沰', '�L' => '泹', '�M' => '泏', '�N' => '泩', '�O' => '泑', '�P' => '炔', '�Q' => '炘', '�R' => '炅', '�S' => '炓', '�T' => '炆', '�U' => '炄', '�V' => '炑', '�W' => '炖', '�X' => '炂', '�Y' => '炚', '�Z' => '炃', '�[' => '牪', '�\\' => '狖', '�]' => '狋', '�^' => '狘', '�_' => '狉', '�`' => '狜', '�a' => '狒', '�b' => '狔', '�c' => '狚', '�d' => '狌', '�e' => '狑', '�f' => '玤', '�g' => '玡', '�h' => '玭', '�i' => '玦', '�j' => '玢', '�k' => '玠', '�l' => '玬', '�m' => '玝', '�n' => '瓝', '�o' => '瓨', '�p' => '甿', '�q' => '畀', '�r' => '甾', '�s' => '疌', '�t' => '疘', '�u' => '皯', '�v' => '盳', '�w' => '盱', '�x' => '盰', '�y' => '盵', '�z' => '矸', '�{' => '矼', '�|' => '矹', '�}' => '矻', '�~' => '矺', '͡' => '矷', '͢' => '祂', 'ͣ' => '礿', 'ͤ' => '秅', 'ͥ' => '穸', 'ͦ' => '穻', 'ͧ' => '竻', 'ͨ' => '籵', 'ͩ' => '糽', 'ͪ' => '耵', 'ͫ' => '肏', 'ͬ' => '肮', 'ͭ' => '肣', 'ͮ' => '肸', 'ͯ' => '肵', 'Ͱ' => '肭', 'ͱ' => '舠', 'Ͳ' => '芠', 'ͳ' => '苀', 'ʹ' => '芫', '͵' => '芚', 'Ͷ' => '芘', 'ͷ' => '芛', '' => '芵', '' => '芧', 'ͺ' => '芮', 'ͻ' => '芼', 'ͼ' => '芞', 'ͽ' => '芺', ';' => '芴', 'Ϳ' => '芨', '�' => '芡', '�' => '芩', '��' => '苂', '��' => '芤', '��' => '苃', '��' => '芶', '��' => '芢', '��' => '虰', '��' => '虯', '��' => '虭', '��' => '虮', '��' => '豖', '��' => '迒', '��' => '迋', '��' => '迓', '��' => '迍', '��' => '迖', '��' => '迕', '��' => '迗', '��' => '邲', '��' => '邴', '��' => '邯', '��' => '邳', '��' => '邰', '��' => '阹', '��' => '阽', '��' => '阼', '��' => '阺', '��' => '陃', '��' => '俍', '��' => '俅', '��' => '俓', '��' => '侲', '��' => '俉', '��' => '俋', '��' => '俁', '��' => '俔', '��' => '俜', '��' => '俙', '��' => '侻', '��' => '侳', '��' => '俛', '��' => '俇', '��' => '俖', '��' => '侺', '��' => '俀', '��' => '侹', '��' => '俬', '��' => '剄', '��' => '剉', '��' => '勀', '��' => '勂', '��' => '匽', '�' => '卼', '�' => '厗', '�' => '厖', '�' => '厙', '�' => '厘', '�' => '咺', '�' => '咡', '�' => '咭', '�' => '咥', '�' => '哏', '�@' => '哃', '�A' => '茍', '�B' => '咷', '�C' => '咮', '�D' => '哖', '�E' => '咶', '�F' => '哅', '�G' => '哆', '�H' => '咠', '�I' => '呰', '�J' => '咼', '�K' => '咢', '�L' => '咾', '�M' => '呲', '�N' => '哞', '�O' => '咰', '�P' => '垵', '�Q' => '垞', '�R' => '垟', '�S' => '垤', '�T' => '垌', '�U' => '垗', '�V' => '垝', '�W' => '垛', '�X' => '垔', '�Y' => '垘', '�Z' => '垏', '�[' => '垙', '�\\' => '垥', '�]' => '垚', '�^' => '垕', '�_' => '壴', '�`' => '复', '�a' => '奓', '�b' => '姡', '�c' => '姞', '�d' => '姮', '�e' => '娀', '�f' => '姱', '�g' => '姝', '�h' => '姺', '�i' => '姽', '�j' => '姼', '�k' => '姶', '�l' => '姤', '�m' => '姲', '�n' => '姷', '�o' => '姛', '�p' => '姩', '�q' => '姳', '�r' => '姵', '�s' => '姠', '�t' => '姾', '�u' => '姴', '�v' => '姭', '�w' => '宨', '�x' => '屌', '�y' => '峐', '�z' => '峘', '�{' => '峌', '�|' => '峗', '�}' => '峋', '�~' => '峛', 'Ρ' => '峞', '' => '峚', 'Σ' => '峉', 'Τ' => '峇', 'Υ' => '峊', 'Φ' => '峖', 'Χ' => '峓', 'Ψ' => '峔', 'Ω' => '峏', 'Ϊ' => '峈', 'Ϋ' => '峆', 'ά' => '峎', 'έ' => '峟', 'ή' => '峸', 'ί' => '巹', 'ΰ' => '帡', 'α' => '帢', 'β' => '帣', 'γ' => '帠', 'δ' => '帤', 'ε' => '庰', 'ζ' => '庤', 'η' => '庢', 'θ' => '庛', 'ι' => '庣', 'κ' => '庥', 'λ' => '弇', 'μ' => '弮', 'ν' => '彖', 'ξ' => '徆', 'ο' => '怷', '�' => '怹', '�' => '恔', '��' => '恲', '��' => '恞', '��' => '恅', '��' => '恓', '��' => '恇', '��' => '恉', '��' => '恛', '��' => '恌', '��' => '恀', '��' => '恂', '��' => '恟', '��' => '怤', '��' => '恄', '��' => '恘', '��' => '恦', '��' => '恮', '��' => '扂', '��' => '扃', '��' => '拏', '��' => '挍', '��' => '挋', '��' => '拵', '��' => '挎', '��' => '挃', '��' => '拫', '��' => '拹', '��' => '挏', '��' => '挌', '��' => '拸', '��' => '拶', '��' => '挀', '��' => '挓', '��' => '挔', '��' => '拺', '��' => '挕', '��' => '拻', '��' => '拰', '��' => '敁', '��' => '敃', '��' => '斪', '��' => '斿', '��' => '昶', '��' => '昡', '��' => '昲', '��' => '昵', '��' => '昜', '��' => '昦', '��' => '昢', '��' => '昳', '��' => '昫', '��' => '昺', '�' => '昝', '�' => '昴', '�' => '昹', '�' => '昮', '�' => '朏', '�' => '朐', '�' => '柁', '�' => '柲', '�' => '柈', '�' => '枺', '�@' => '柜', '�A' => '枻', '�B' => '柸', '�C' => '柘', '�D' => '柀', '�E' => '枷', '�F' => '柅', '�G' => '柫', '�H' => '柤', '�I' => '柟', '�J' => '枵', '�K' => '柍', '�L' => '枳', '�M' => '柷', '�N' => '柶', '�O' => '柮', '�P' => '柣', '�Q' => '柂', '�R' => '枹', '�S' => '柎', '�T' => '柧', '�U' => '柰', '�V' => '枲', '�W' => '柼', '�X' => '柆', '�Y' => '柭', '�Z' => '柌', '�[' => '枮', '�\\' => '柦', '�]' => '柛', '�^' => '柺', '�_' => '柉', '�`' => '柊', '�a' => '柃', '�b' => '柪', '�c' => '柋', '�d' => '欨', '�e' => '殂', '�f' => '殄', '�g' => '殶', '�h' => '毖', '�i' => '毘', '�j' => '毠', '�k' => '氠', '�l' => '氡', '�m' => '洨', '�n' => '洴', '�o' => '洭', '�p' => '洟', '�q' => '洼', '�r' => '洿', '�s' => '洒', '�t' => '洊', '�u' => '泚', '�v' => '洳', '�w' => '洄', '�x' => '洙', '�y' => '洺', '�z' => '洚', '�{' => '洑', '�|' => '洀', '�}' => '洝', '�~' => '浂', 'ϡ' => '洁', 'Ϣ' => '洘', 'ϣ' => '洷', 'Ϥ' => '洃', 'ϥ' => '洏', 'Ϧ' => '浀', 'ϧ' => '洇', 'Ϩ' => '洠', 'ϩ' => '洬', 'Ϫ' => '洈', 'ϫ' => '洢', 'Ϭ' => '洉', 'ϭ' => '洐', 'Ϯ' => '炷', 'ϯ' => '炟', 'ϰ' => '炾', 'ϱ' => '炱', 'ϲ' => '炰', 'ϳ' => '炡', 'ϴ' => '炴', 'ϵ' => '炵', '϶' => '炩', 'Ϸ' => '牁', 'ϸ' => '牉', 'Ϲ' => '牊', 'Ϻ' => '牬', 'ϻ' => '牰', 'ϼ' => '牳', 'Ͻ' => '牮', 'Ͼ' => '狊', 'Ͽ' => '狤', '�' => '狨', '�' => '狫', '��' => '狟', '��' => '狪', '��' => '狦', '��' => '狣', '��' => '玅', '��' => '珌', '��' => '珂', '��' => '珈', '��' => '珅', '��' => '玹', '��' => '玶', '��' => '玵', '��' => '玴', '��' => '珫', '��' => '玿', '��' => '珇', '��' => '玾', '��' => '珃', '��' => '珆', '��' => '玸', '��' => '珋', '��' => '瓬', '��' => '瓮', '��' => '甮', '��' => '畇', '��' => '畈', '��' => '疧', '��' => '疪', '��' => '癹', '��' => '盄', '��' => '眈', '��' => '眃', '��' => '眄', '��' => '眅', '��' => '眊', '��' => '盷', '��' => '盻', '��' => '盺', '��' => '矧', '��' => '矨', '��' => '砆', '��' => '砑', '��' => '砒', '��' => '砅', '��' => '砐', '��' => '砏', '��' => '砎', '��' => '砉', '��' => '砃', '��' => '砓', '��' => '祊', '�' => '祌', '�' => '祋', '�' => '祅', '�' => '祄', '�' => '秕', '�' => '种', '�' => '秏', '�' => '秖', '�' => '秎', '�' => '窀', '�@' => '穾', '�A' => '竑', '�B' => '笀', '�C' => '笁', '�D' => '籺', '�E' => '籸', '�F' => '籹', '�G' => '籿', '�H' => '粀', '�I' => '粁', '�J' => '紃', '�K' => '紈', '�L' => '紁', '�M' => '罘', '�N' => '羑', '�O' => '羍', '�P' => '羾', '�Q' => '耇', '�R' => '耎', '�S' => '耏', '�T' => '耔', '�U' => '耷', '�V' => '胘', '�W' => '胇', '�X' => '胠', '�Y' => '胑', '�Z' => '胈', '�[' => '胂', '�\\' => '胐', '�]' => '胅', '�^' => '胣', '�_' => '胙', '�`' => '胜', '�a' => '胊', '�b' => '胕', '�c' => '胉', '�d' => '胏', '�e' => '胗', '�f' => '胦', '�g' => '胍', '�h' => '臿', '�i' => '舡', '�j' => '芔', '�k' => '苙', '�l' => '苾', '�m' => '苹', '�n' => '茇', '�o' => '苨', '�p' => '茀', '�q' => '苕', '�r' => '茺', '�s' => '苫', '�t' => '苖', '�u' => '苴', '�v' => '苬', '�w' => '苡', '�x' => '苲', '�y' => '苵', '�z' => '茌', '�{' => '苻', '�|' => '苶', '�}' => '苰', '�~' => '苪', 'С' => '苤', 'Т' => '苠', 'У' => '苺', 'Ф' => '苳', 'Х' => '苭', 'Ц' => '虷', 'Ч' => '虴', 'Ш' => '虼', 'Щ' => '虳', 'Ъ' => '衁', 'Ы' => '衎', 'Ь' => '衧', 'Э' => '衪', 'Ю' => '衩', 'Я' => '觓', 'а' => '訄', 'б' => '訇', 'в' => '赲', 'г' => '迣', 'д' => '迡', 'е' => '迮', 'ж' => '迠', 'з' => '郱', 'и' => '邽', 'й' => '邿', 'к' => '郕', 'л' => '郅', 'м' => '邾', 'н' => '郇', 'о' => '郋', 'п' => '郈', '�' => '釔', '�' => '釓', '��' => '陔', '��' => '陏', '��' => '陑', '��' => '陓', '��' => '陊', '��' => '陎', '��' => '倞', '��' => '倅', '��' => '倇', '��' => '倓', '��' => '倢', '��' => '倰', '��' => '倛', '��' => '俵', '��' => '俴', '��' => '倳', '��' => '倷', '��' => '倬', '��' => '俶', '��' => '俷', '��' => '倗', '��' => '倜', '��' => '倠', '��' => '倧', '��' => '倵', '��' => '倯', '��' => '倱', '��' => '倎', '��' => '党', '��' => '冔', '��' => '冓', '��' => '凊', '��' => '凄', '��' => '凅', '��' => '凈', '��' => '凎', '��' => '剡', '��' => '剚', '��' => '剒', '��' => '剞', '��' => '剟', '��' => '剕', '��' => '剢', '��' => '勍', '��' => '匎', '��' => '厞', '��' => '唦', '��' => '哢', '��' => '唗', '��' => '唒', '��' => '哧', '�' => '哳', '�' => '哤', '�' => '唚', '�' => '哿', '�' => '唄', '�' => '唈', '�' => '哫', '�' => '唑', '�' => '唅', '�' => '哱', '�@' => '唊', '�A' => '哻', '�B' => '哷', '�C' => '哸', '�D' => '哠', '�E' => '唎', '�F' => '唃', '�G' => '唋', '�H' => '圁', '�I' => '圂', '�J' => '埌', '�K' => '堲', '�L' => '埕', '�M' => '埒', '�N' => '垺', '�O' => '埆', '�P' => '垽', '�Q' => '垼', '�R' => '垸', '�S' => '垶', '�T' => '垿', '�U' => '埇', '�V' => '埐', '�W' => '垹', '�X' => '埁', '�Y' => '夎', '�Z' => '奊', '�[' => '娙', '�\\' => '娖', '�]' => '娭', '�^' => '娮', '�_' => '娕', '�`' => '娏', '�a' => '娗', '�b' => '娊', '�c' => '娞', '�d' => '娳', '�e' => '孬', '�f' => '宧', '�g' => '宭', '�h' => '宬', '�i' => '尃', '�j' => '屖', '�k' => '屔', '�l' => '峬', '�m' => '峿', '�n' => '峮', '�o' => '峱', '�p' => '峷', '�q' => '崀', '�r' => '峹', '�s' => '帩', '�t' => '帨', '�u' => '庨', '�v' => '庮', '�w' => '庪', '�x' => '庬', '�y' => '弳', '�z' => '弰', '�{' => '彧', '�|' => '恝', '�}' => '恚', '�~' => '恧', 'ѡ' => '恁', 'Ѣ' => '悢', 'ѣ' => '悈', 'Ѥ' => '悀', 'ѥ' => '悒', 'Ѧ' => '悁', 'ѧ' => '悝', 'Ѩ' => '悃', 'ѩ' => '悕', 'Ѫ' => '悛', 'ѫ' => '悗', 'Ѭ' => '悇', 'ѭ' => '悜', 'Ѯ' => '悎', 'ѯ' => '戙', 'Ѱ' => '扆', 'ѱ' => '拲', 'Ѳ' => '挐', 'ѳ' => '捖', 'Ѵ' => '挬', 'ѵ' => '捄', 'Ѷ' => '捅', 'ѷ' => '挶', 'Ѹ' => '捃', 'ѹ' => '揤', 'Ѻ' => '挹', 'ѻ' => '捋', 'Ѽ' => '捊', 'ѽ' => '挼', 'Ѿ' => '挩', 'ѿ' => '捁', '�' => '挴', '�' => '捘', '��' => '捔', '��' => '捙', '��' => '挭', '��' => '捇', '��' => '挳', '��' => '捚', '��' => '捑', '��' => '挸', '��' => '捗', '��' => '捀', '��' => '捈', '��' => '敊', '��' => '敆', '��' => '旆', '��' => '旃', '��' => '旄', '��' => '旂', '��' => '晊', '��' => '晟', '��' => '晇', '��' => '晑', '��' => '朒', '��' => '朓', '��' => '栟', '��' => '栚', '��' => '桉', '��' => '栲', '��' => '栳', '��' => '栻', '��' => '桋', '��' => '桏', '��' => '栖', '��' => '栱', '��' => '栜', '��' => '栵', '��' => '栫', '��' => '栭', '��' => '栯', '��' => '桎', '��' => '桄', '��' => '栴', '��' => '栝', '��' => '栒', '��' => '栔', '��' => '栦', '��' => '栨', '��' => '栮', '��' => '桍', '��' => '栺', '��' => '栥', '��' => '栠', '�' => '欬', '�' => '欯', '�' => '欭', '�' => '欱', '�' => '欴', '�' => '歭', '�' => '肂', '�' => '殈', '�' => '毦', '�' => '毤', '�@' => '毨', '�A' => '毣', '�B' => '毢', '�C' => '毧', '�D' => '氥', '�E' => '浺', '�F' => '浣', '�G' => '浤', '�H' => '浶', '�I' => '洍', '�J' => '浡', '�K' => '涒', '�L' => '浘', '�M' => '浢', '�N' => '浭', '�O' => '浯', '�P' => '涑', '�Q' => '涍', '�R' => '淯', '�S' => '浿', '�T' => '涆', '�U' => '浞', '�V' => '浧', '�W' => '浠', '�X' => '涗', '�Y' => '浰', '�Z' => '浼', '�[' => '浟', '�\\' => '涂', '�]' => '涘', '�^' => '洯', '�_' => '浨', '�`' => '涋', '�a' => '浾', '�b' => '涀', '�c' => '涄', '�d' => '洖', '�e' => '涃', '�f' => '浻', '�g' => '浽', '�h' => '浵', '�i' => '涐', '�j' => '烜', '�k' => '烓', '�l' => '烑', '�m' => '烝', '�n' => '烋', '�o' => '缹', '�p' => '烢', '�q' => '烗', '�r' => '烒', '�s' => '烞', '�t' => '烠', '�u' => '烔', '�v' => '烍', '�w' => '烅', '�x' => '烆', '�y' => '烇', '�z' => '烚', '�{' => '烎', '�|' => '烡', '�}' => '牂', '�~' => '牸', 'ҡ' => '牷', 'Ң' => '牶', 'ң' => '猀', 'Ҥ' => '狺', 'ҥ' => '狴', 'Ҧ' => '狾', 'ҧ' => '狶', 'Ҩ' => '狳', 'ҩ' => '狻', 'Ҫ' => '猁', 'ҫ' => '珓', 'Ҭ' => '珙', 'ҭ' => '珥', 'Ү' => '珖', 'ү' => '玼', 'Ұ' => '珧', 'ұ' => '珣', 'Ҳ' => '珩', 'ҳ' => '珜', 'Ҵ' => '珒', 'ҵ' => '珛', 'Ҷ' => '珔', 'ҷ' => '珝', 'Ҹ' => '珚', 'ҹ' => '珗', 'Һ' => '珘', 'һ' => '珨', 'Ҽ' => '瓞', 'ҽ' => '瓟', 'Ҿ' => '瓴', 'ҿ' => '瓵', '�' => '甡', '�' => '畛', '��' => '畟', '��' => '疰', '��' => '痁', '��' => '疻', '��' => '痄', '��' => '痀', '��' => '疿', '��' => '疶', '��' => '疺', '��' => '皊', '��' => '盉', '��' => '眝', '��' => '眛', '��' => '眐', '��' => '眓', '��' => '眒', '��' => '眣', '��' => '眑', '��' => '眕', '��' => '眙', '��' => '眚', '��' => '眢', '��' => '眧', '��' => '砣', '��' => '砬', '��' => '砢', '��' => '砵', '��' => '砯', '��' => '砨', '��' => '砮', '��' => '砫', '��' => '砡', '��' => '砩', '��' => '砳', '��' => '砪', '��' => '砱', '��' => '祔', '��' => '祛', '��' => '祏', '��' => '祜', '��' => '祓', '��' => '祒', '��' => '祑', '��' => '秫', '��' => '秬', '��' => '秠', '��' => '秮', '��' => '秭', '��' => '秪', '��' => '秜', '��' => '秞', '�' => '秝', '�' => '窆', '�' => '窉', '�' => '窅', '�' => '窋', '�' => '窌', '�' => '窊', '�' => '窇', '�' => '竘', '�' => '笐', '�@' => '笄', '�A' => '笓', '�B' => '笅', '�C' => '笏', '�D' => '笈', '�E' => '笊', '�F' => '笎', '�G' => '笉', '�H' => '笒', '�I' => '粄', '�J' => '粑', '�K' => '粊', '�L' => '粌', '�M' => '粈', '�N' => '粍', '�O' => '粅', '�P' => '紞', '�Q' => '紝', '�R' => '紑', '�S' => '紎', '�T' => '紘', '�U' => '紖', '�V' => '紓', '�W' => '紟', '�X' => '紒', '�Y' => '紏', '�Z' => '紌', '�[' => '罜', '�\\' => '罡', '�]' => '罞', '�^' => '罠', '�_' => '罝', '�`' => '罛', '�a' => '羖', '�b' => '羒', '�c' => '翃', '�d' => '翂', '�e' => '翀', '�f' => '耖', '�g' => '耾', '�h' => '耹', '�i' => '胺', '�j' => '胲', '�k' => '胹', '�l' => '胵', '�m' => '脁', '�n' => '胻', '�o' => '脀', '�p' => '舁', '�q' => '舯', '�r' => '舥', '�s' => '茳', '�t' => '茭', '�u' => '荄', '�v' => '茙', '�w' => '荑', '�x' => '茥', '�y' => '荖', '�z' => '茿', '�{' => '荁', '�|' => '茦', '�}' => '茜', '�~' => '茢', 'ӡ' => '荂', 'Ӣ' => '荎', 'ӣ' => '茛', 'Ӥ' => '茪', 'ӥ' => '茈', 'Ӧ' => '茼', 'ӧ' => '荍', 'Ө' => '茖', 'ө' => '茤', 'Ӫ' => '茠', 'ӫ' => '茷', 'Ӭ' => '茯', 'ӭ' => '茩', 'Ӯ' => '荇', 'ӯ' => '荅', 'Ӱ' => '荌', 'ӱ' => '荓', 'Ӳ' => '茞', 'ӳ' => '茬', 'Ӵ' => '荋', 'ӵ' => '茧', 'Ӷ' => '荈', 'ӷ' => '虓', 'Ӹ' => '虒', 'ӹ' => '蚢', 'Ӻ' => '蚨', 'ӻ' => '蚖', 'Ӽ' => '蚍', 'ӽ' => '蚑', 'Ӿ' => '蚞', 'ӿ' => '蚇', '�' => '蚗', '�' => '蚆', '��' => '蚋', '��' => '蚚', '��' => '蚅', '��' => '蚥', '��' => '蚙', '��' => '蚡', '��' => '蚧', '��' => '蚕', '��' => '蚘', '��' => '蚎', '��' => '蚝', '��' => '蚐', '��' => '蚔', '��' => '衃', '��' => '衄', '��' => '衭', '��' => '衵', '��' => '衶', '��' => '衲', '��' => '袀', '��' => '衱', '��' => '衿', '��' => '衯', '��' => '袃', '��' => '衾', '��' => '衴', '��' => '衼', '��' => '訒', '��' => '豇', '��' => '豗', '��' => '豻', '��' => '貤', '��' => '貣', '��' => '赶', '��' => '赸', '��' => '趵', '��' => '趷', '��' => '趶', '��' => '軑', '��' => '軓', '��' => '迾', '��' => '迵', '��' => '适', '��' => '迿', '��' => '迻', '��' => '逄', '��' => '迼', '��' => '迶', '��' => '郖', '��' => '郠', '��' => '郙', '�' => '郚', '�' => '郣', '�' => '郟', '�' => '郥', '�' => '郘', '�' => '郛', '�' => '郗', '�' => '郜', '�' => '郤', '�' => '酐', '�@' => '酎', '�A' => '酏', '�B' => '釕', '�C' => '釢', '�D' => '釚', '�E' => '陜', '�F' => '陟', '�G' => '隼', '�H' => '飣', '�I' => '髟', '�J' => '鬯', '�K' => '乿', '�L' => '偰', '�M' => '偪', '�N' => '偡', '�O' => '偞', '�P' => '偠', '�Q' => '偓', '�R' => '偋', '�S' => '偝', '�T' => '偲', '�U' => '偈', '�V' => '偍', '�W' => '偁', '�X' => '偛', '�Y' => '偊', '�Z' => '偢', '�[' => '倕', '�\\' => '偅', '�]' => '偟', '�^' => '偩', '�_' => '偫', '�`' => '偣', '�a' => '偤', '�b' => '偆', '�c' => '偀', '�d' => '偮', '�e' => '偳', '�f' => '偗', '�g' => '偑', '�h' => '凐', '�i' => '剫', '�j' => '剭', '�k' => '剬', '�l' => '剮', '�m' => '勖', '�n' => '勓', '�o' => '匭', '�p' => '厜', '�q' => '啵', '�r' => '啶', '�s' => '唼', '�t' => '啍', '�u' => '啐', '�v' => '唴', '�w' => '唪', '�x' => '啑', '�y' => '啢', '�z' => '唶', '�{' => '唵', '�|' => '唰', '�}' => '啒', '�~' => '啅', 'ԡ' => '唌', 'Ԣ' => '唲', 'ԣ' => '啥', 'Ԥ' => '啎', 'ԥ' => '唹', 'Ԧ' => '啈', 'ԧ' => '唭', 'Ԩ' => '唻', 'ԩ' => '啀', 'Ԫ' => '啋', 'ԫ' => '圊', 'Ԭ' => '圇', 'ԭ' => '埻', 'Ԯ' => '堔', 'ԯ' => '埢', '' => '埶', 'Ա' => '埜', 'Բ' => '埴', 'Գ' => '堀', 'Դ' => '埭', 'Ե' => '埽', 'Զ' => '堈', 'Է' => '埸', 'Ը' => '堋', 'Թ' => '埳', 'Ժ' => '埏', 'Ի' => '堇', 'Լ' => '埮', 'Խ' => '埣', 'Ծ' => '埲', 'Կ' => '埥', '�' => '埬', '�' => '埡', '��' => '堎', '��' => '埼', '��' => '堐', '��' => '埧', '��' => '堁', '��' => '堌', '��' => '埱', '��' => '埩', '��' => '埰', '��' => '堍', '��' => '堄', '��' => '奜', '��' => '婠', '��' => '婘', '��' => '婕', '��' => '婧', '��' => '婞', '��' => '娸', '��' => '娵', '��' => '婭', '��' => '婐', '��' => '婟', '��' => '婥', '��' => '婬', '��' => '婓', '��' => '婤', '��' => '婗', '��' => '婃', '��' => '婝', '��' => '婒', '��' => '婄', '��' => '婛', '��' => '婈', '��' => '媎', '��' => '娾', '��' => '婍', '��' => '娹', '��' => '婌', '��' => '婰', '��' => '婩', '��' => '婇', '��' => '婑', '��' => '婖', '��' => '婂', '��' => '婜', '��' => '孲', '��' => '孮', '��' => '寁', '��' => '寀', '��' => '屙', '��' => '崞', '�' => '崋', '�' => '崝', '�' => '崚', '�' => '崠', '�' => '崌', '�' => '崨', '�' => '崍', '�' => '崦', '�' => '崥', '�' => '崏', '�@' => '崰', '�A' => '崒', '�B' => '崣', '�C' => '崟', '�D' => '崮', '�E' => '帾', '�F' => '帴', '�G' => '庱', '�H' => '庴', '�I' => '庹', '�J' => '庲', '�K' => '庳', '�L' => '弶', '�M' => '弸', '�N' => '徛', '�O' => '徖', '�P' => '徟', '�Q' => '悊', '�R' => '悐', '�S' => '悆', '�T' => '悾', '�U' => '悰', '�V' => '悺', '�W' => '惓', '�X' => '惔', '�Y' => '惏', '�Z' => '惤', '�[' => '惙', '�\\' => '惝', '�]' => '惈', '�^' => '悱', '�_' => '惛', '�`' => '悷', '�a' => '惊', '�b' => '悿', '�c' => '惃', '�d' => '惍', '�e' => '惀', '�f' => '挲', '�g' => '捥', '�h' => '掊', '�i' => '掂', '�j' => '捽', '�k' => '掽', '�l' => '掞', '�m' => '掭', '�n' => '掝', '�o' => '掗', '�p' => '掫', '�q' => '掎', '�r' => '捯', '�s' => '掇', '�t' => '掐', '�u' => '据', '�v' => '掯', '�w' => '捵', '�x' => '掜', '�y' => '捭', '�z' => '掮', '�{' => '捼', '�|' => '掤', '�}' => '挻', '�~' => '掟', 'ա' => '捸', 'բ' => '掅', 'գ' => '掁', 'դ' => '掑', 'ե' => '掍', 'զ' => '捰', 'է' => '敓', 'ը' => '旍', 'թ' => '晥', 'ժ' => '晡', 'ի' => '晛', 'լ' => '晙', 'խ' => '晜', 'ծ' => '晢', 'կ' => '朘', 'հ' => '桹', 'ձ' => '梇', 'ղ' => '梐', 'ճ' => '梜', 'մ' => '桭', 'յ' => '桮', 'ն' => '梮', 'շ' => '梫', 'ո' => '楖', 'չ' => '桯', 'պ' => '梣', 'ջ' => '梬', 'ռ' => '梩', 'ս' => '桵', 'վ' => '桴', 'տ' => '梲', '�' => '梏', '�' => '桷', '��' => '梒', '��' => '桼', '��' => '桫', '��' => '桲', '��' => '梪', '��' => '梀', '��' => '桱', '��' => '桾', '��' => '梛', '��' => '梖', '��' => '梋', '��' => '梠', '��' => '梉', '��' => '梤', '��' => '桸', '��' => '桻', '��' => '梑', '��' => '梌', '��' => '梊', '��' => '桽', '��' => '欶', '��' => '欳', '��' => '欷', '��' => '欸', '��' => '殑', '��' => '殏', '��' => '殍', '��' => '殎', '��' => '殌', '��' => '氪', '��' => '淀', '��' => '涫', '��' => '涴', '��' => '涳', '��' => '湴', '��' => '涬', '��' => '淩', '��' => '淢', '��' => '涷', '��' => '淶', '��' => '淔', '��' => '渀', '��' => '淈', '��' => '淠', '��' => '淟', '��' => '淖', '��' => '涾', '��' => '淥', '��' => '淜', '��' => '淝', '��' => '淛', '�' => '淴', '�' => '淊', '�' => '涽', '�' => '淭', '�' => '淰', '�' => '涺', '�' => '淕', '�' => '淂', '�' => '淏', '�' => '淉', '�@' => '淐', '�A' => '淲', '�B' => '淓', '�C' => '淽', '�D' => '淗', '�E' => '淍', '�F' => '淣', '�G' => '涻', '�H' => '烺', '�I' => '焍', '�J' => '烷', '�K' => '焗', '�L' => '烴', '�M' => '焌', '�N' => '烰', '�O' => '焄', '�P' => '烳', '�Q' => '焐', '�R' => '烼', '�S' => '烿', '�T' => '焆', '�U' => '焓', '�V' => '焀', '�W' => '烸', '�X' => '烶', '�Y' => '焋', '�Z' => '焂', '�[' => '焎', '�\\' => '牾', '�]' => '牻', '�^' => '牼', '�_' => '牿', '�`' => '猝', '�a' => '猗', '�b' => '猇', '�c' => '猑', '�d' => '猘', '�e' => '猊', '�f' => '猈', '�g' => '狿', '�h' => '猏', '�i' => '猞', '�j' => '玈', '�k' => '珶', '�l' => '珸', '�m' => '珵', '�n' => '琄', '�o' => '琁', '�p' => '珽', '�q' => '琇', '�r' => '琀', '�s' => '珺', '�t' => '珼', '�u' => '珿', '�v' => '琌', '�w' => '琋', '�x' => '珴', '�y' => '琈', '�z' => '畤', '�{' => '畣', '�|' => '痎', '�}' => '痒', '�~' => '痏', '֡' => '痋', '֢' => '痌', '֣' => '痑', '֤' => '痐', '֥' => '皏', '֦' => '皉', '֧' => '盓', '֨' => '眹', '֩' => '眯', '֪' => '眭', '֫' => '眱', '֬' => '眲', '֭' => '眴', '֮' => '眳', '֯' => '眽', 'ְ' => '眥', 'ֱ' => '眻', 'ֲ' => '眵', 'ֳ' => '硈', 'ִ' => '硒', 'ֵ' => '硉', 'ֶ' => '硍', 'ַ' => '硊', 'ָ' => '硌', 'ֹ' => '砦', 'ֺ' => '硅', 'ֻ' => '硐', 'ּ' => '祤', 'ֽ' => '祧', '־' => '祩', 'ֿ' => '祪', '�' => '祣', '�' => '祫', '��' => '祡', '��' => '离', '��' => '秺', '��' => '秸', '��' => '秶', '��' => '秷', '��' => '窏', '��' => '窔', '��' => '窐', '��' => '笵', '��' => '筇', '��' => '笴', '��' => '笥', '��' => '笰', '��' => '笢', '��' => '笤', '��' => '笳', '��' => '笘', '��' => '笪', '��' => '笝', '��' => '笱', '��' => '笫', '��' => '笭', '��' => '笯', '��' => '笲', '��' => '笸', '��' => '笚', '��' => '笣', '��' => '粔', '��' => '粘', '��' => '粖', '��' => '粣', '��' => '紵', '��' => '紽', '��' => '紸', '��' => '紶', '��' => '紺', '��' => '絅', '��' => '紬', '��' => '紩', '��' => '絁', '��' => '絇', '��' => '紾', '��' => '紿', '��' => '絊', '��' => '紻', '��' => '紨', '��' => '罣', '��' => '羕', '��' => '羜', '��' => '羝', '�' => '羛', '�' => '翊', '�' => '翋', '�' => '翍', '�' => '翐', '�' => '翑', '�' => '翇', '�' => '翏', '�' => '翉', '�' => '耟', '�@' => '耞', '�A' => '耛', '�B' => '聇', '�C' => '聃', '�D' => '聈', '�E' => '脘', '�F' => '脥', '�G' => '脙', '�H' => '脛', '�I' => '脭', '�J' => '脟', '�K' => '脬', '�L' => '脞', '�M' => '脡', '�N' => '脕', '�O' => '脧', '�P' => '脝', '�Q' => '脢', '�R' => '舑', '�S' => '舸', '�T' => '舳', '�U' => '舺', '�V' => '舴', '�W' => '舲', '�X' => '艴', '�Y' => '莐', '�Z' => '莣', '�[' => '莨', '�\\' => '莍', '�]' => '荺', '�^' => '荳', '�_' => '莤', '�`' => '荴', '�a' => '莏', '�b' => '莁', '�c' => '莕', '�d' => '莙', '�e' => '荵', '�f' => '莔', '�g' => '莩', '�h' => '荽', '�i' => '莃', '�j' => '莌', '�k' => '莝', '�l' => '莛', '�m' => '莪', '�n' => '莋', '�o' => '荾', '�p' => '莥', '�q' => '莯', '�r' => '莈', '�s' => '莗', '�t' => '莰', '�u' => '荿', '�v' => '莦', '�w' => '莇', '�x' => '莮', '�y' => '荶', '�z' => '莚', '�{' => '虙', '�|' => '虖', '�}' => '蚿', '�~' => '蚷', 'ס' => '蛂', 'ע' => '蛁', 'ף' => '蛅', 'פ' => '蚺', 'ץ' => '蚰', 'צ' => '蛈', 'ק' => '蚹', 'ר' => '蚳', 'ש' => '蚸', 'ת' => '蛌', '' => '蚴', '' => '蚻', '' => '蚼', '' => '蛃', 'ׯ' => '蚽', 'װ' => '蚾', 'ױ' => '衒', 'ײ' => '袉', '׳' => '袕', '״' => '袨', '' => '袢', '' => '袪', '' => '袚', '' => '袑', '' => '袡', '' => '袟', '' => '袘', '' => '袧', '' => '袙', '' => '袛', '' => '袗', '�' => '袤', '�' => '袬', '��' => '袌', '��' => '袓', '��' => '袎', '��' => '覂', '��' => '觖', '��' => '觙', '��' => '觕', '��' => '訰', '��' => '訧', '��' => '訬', '��' => '訞', '��' => '谹', '��' => '谻', '��' => '豜', '��' => '豝', '��' => '豽', '��' => '貥', '��' => '赽', '��' => '赻', '��' => '赹', '��' => '趼', '��' => '跂', '��' => '趹', '��' => '趿', '��' => '跁', '��' => '軘', '��' => '軞', '��' => '軝', '��' => '軜', '��' => '軗', '��' => '軠', '��' => '軡', '��' => '逤', '��' => '逋', '��' => '逑', '��' => '逜', '��' => '逌', '��' => '逡', '��' => '郯', '��' => '郪', '��' => '郰', '��' => '郴', '��' => '郲', '��' => '郳', '��' => '郔', '��' => '郫', '��' => '郬', '��' => '郩', '��' => '酖', '��' => '酘', '��' => '酚', '�' => '酓', '�' => '酕', '�' => '釬', '�' => '釴', '�' => '釱', '�' => '釳', '�' => '釸', '�' => '釤', '�' => '釹', '�' => '釪', '�@' => '釫', '�A' => '釷', '�B' => '釨', '�C' => '釮', '�D' => '镺', '�E' => '閆', '�F' => '閈', '�G' => '陼', '�H' => '陭', '�I' => '陫', '�J' => '陱', '�K' => '陯', '�L' => '隿', '�M' => '靪', '�N' => '頄', '�O' => '飥', '�P' => '馗', '�Q' => '傛', '�R' => '傕', '�S' => '傔', '�T' => '傞', '�U' => '傋', '�V' => '傣', '�W' => '傃', '�X' => '傌', '�Y' => '傎', '�Z' => '傝', '�[' => '偨', '�\\' => '傜', '�]' => '傒', '�^' => '傂', '�_' => '傇', '�`' => '兟', '�a' => '凔', '�b' => '匒', '�c' => '匑', '�d' => '厤', '�e' => '厧', '�f' => '喑', '�g' => '喨', '�h' => '喥', '�i' => '喭', '�j' => '啷', '�k' => '噅', '�l' => '喢', '�m' => '喓', '�n' => '喈', '�o' => '喏', '�p' => '喵', '�q' => '喁', '�r' => '喣', '�s' => '喒', '�t' => '喤', '�u' => '啽', '�v' => '喌', '�w' => '喦', '�x' => '啿', '�y' => '喕', '�z' => '喡', '�{' => '喎', '�|' => '圌', '�}' => '堩', '�~' => '堷', 'ء' => '堙', 'آ' => '堞', 'أ' => '堧', 'ؤ' => '堣', 'إ' => '堨', 'ئ' => '埵', 'ا' => '塈', 'ب' => '堥', 'ة' => '堜', 'ت' => '堛', 'ث' => '堳', 'ج' => '堿', 'ح' => '堶', 'خ' => '堮', 'د' => '堹', 'ذ' => '堸', 'ر' => '堭', 'ز' => '堬', 'س' => '堻', 'ش' => '奡', 'ص' => '媯', 'ض' => '媔', 'ط' => '媟', 'ظ' => '婺', 'ع' => '媢', 'غ' => '媞', 'ػ' => '婸', 'ؼ' => '媦', 'ؽ' => '婼', 'ؾ' => '媥', 'ؿ' => '媬', '�' => '媕', '�' => '媮', '��' => '娷', '��' => '媄', '��' => '媊', '��' => '媗', '��' => '媃', '��' => '媋', '��' => '媩', '��' => '婻', '��' => '婽', '��' => '媌', '��' => '媜', '��' => '媏', '��' => '媓', '��' => '媝', '��' => '寪', '��' => '寍', '��' => '寋', '��' => '寔', '��' => '寑', '��' => '寊', '��' => '寎', '��' => '尌', '��' => '尰', '��' => '崷', '��' => '嵃', '��' => '嵫', '��' => '嵁', '��' => '嵋', '��' => '崿', '��' => '崵', '��' => '嵑', '��' => '嵎', '��' => '嵕', '��' => '崳', '��' => '崺', '��' => '嵒', '��' => '崽', '��' => '崱', '��' => '嵙', '��' => '嵂', '��' => '崹', '��' => '嵉', '��' => '崸', '��' => '崼', '��' => '崲', '��' => '崶', '��' => '嵀', '��' => '嵅', '��' => '幄', '��' => '幁', '��' => '彘', '�' => '徦', '�' => '徥', '�' => '徫', '�' => '惉', '�' => '悹', '�' => '惌', '�' => '惢', '�' => '惎', '�' => '惄', '�' => '愔', '�@' => '惲', '�A' => '愊', '�B' => '愖', '�C' => '愅', '�D' => '惵', '�E' => '愓', '�F' => '惸', '�G' => '惼', '�H' => '惾', '�I' => '惁', '�J' => '愃', '�K' => '愘', '�L' => '愝', '�M' => '愐', '�N' => '惿', '�O' => '愄', '�P' => '愋', '�Q' => '扊', '�R' => '掔', '�S' => '掱', '�T' => '掰', '�U' => '揎', '�V' => '揥', '�W' => '揨', '�X' => '揯', '�Y' => '揃', '�Z' => '撝', '�[' => '揳', '�\\' => '揊', '�]' => '揠', '�^' => '揶', '�_' => '揕', '�`' => '揲', '�a' => '揵', '�b' => '摡', '�c' => '揟', '�d' => '掾', '�e' => '揝', '�f' => '揜', '�g' => '揄', '�h' => '揘', '�i' => '揓', '�j' => '揂', '�k' => '揇', '�l' => '揌', '�m' => '揋', '�n' => '揈', '�o' => '揰', '�p' => '揗', '�q' => '揙', '�r' => '攲', '�s' => '敧', '�t' => '敪', '�u' => '敤', '�v' => '敜', '�w' => '敨', '�x' => '敥', '�y' => '斌', '�z' => '斝', '�{' => '斞', '�|' => '斮', '�}' => '旐', '�~' => '旒', '١' => '晼', '٢' => '晬', '٣' => '晻', '٤' => '暀', '٥' => '晱', '٦' => '晹', '٧' => '晪', '٨' => '晲', '٩' => '朁', '٪' => '椌', '٫' => '棓', '٬' => '椄', '٭' => '棜', 'ٮ' => '椪', 'ٯ' => '棬', 'ٰ' => '棪', 'ٱ' => '棱', 'ٲ' => '椏', 'ٳ' => '棖', 'ٴ' => '棷', 'ٵ' => '棫', 'ٶ' => '棤', 'ٷ' => '棶', 'ٸ' => '椓', 'ٹ' => '椐', 'ٺ' => '棳', 'ٻ' => '棡', 'ټ' => '椇', 'ٽ' => '棌', 'پ' => '椈', 'ٿ' => '楰', '�' => '梴', '�' => '椑', '��' => '棯', '��' => '棆', '��' => '椔', '��' => '棸', '��' => '棐', '��' => '棽', '��' => '棼', '��' => '棨', '��' => '椋', '��' => '椊', '��' => '椗', '��' => '棎', '��' => '棈', '��' => '棝', '��' => '棞', '��' => '棦', '��' => '棴', '��' => '棑', '��' => '椆', '��' => '棔', '��' => '棩', '��' => '椕', '��' => '椥', '��' => '棇', '��' => '欹', '��' => '欻', '��' => '欿', '��' => '欼', '��' => '殔', '��' => '殗', '��' => '殙', '��' => '殕', '��' => '殽', '��' => '毰', '��' => '毲', '��' => '毳', '��' => '氰', '��' => '淼', '��' => '湆', '��' => '湇', '��' => '渟', '��' => '湉', '��' => '溈', '��' => '渼', '��' => '渽', '��' => '湅', '��' => '湢', '��' => '渫', '��' => '渿', '��' => '湁', '��' => '湝', '�' => '湳', '�' => '渜', '�' => '渳', '�' => '湋', '�' => '湀', '�' => '湑', '�' => '渻', '�' => '渃', '�' => '渮', '�' => '湞', '�@' => '湨', '�A' => '湜', '�B' => '湡', '�C' => '渱', '�D' => '渨', '�E' => '湠', '�F' => '湱', '�G' => '湫', '�H' => '渹', '�I' => '渢', '�J' => '渰', '�K' => '湓', '�L' => '湥', '�M' => '渧', '�N' => '湸', '�O' => '湤', '�P' => '湷', '�Q' => '湕', '�R' => '湹', '�S' => '湒', '�T' => '湦', '�U' => '渵', '�V' => '渶', '�W' => '湚', '�X' => '焠', '�Y' => '焞', '�Z' => '焯', '�[' => '烻', '�\\' => '焮', '�]' => '焱', '�^' => '焣', '�_' => '焥', '�`' => '焢', '�a' => '焲', '�b' => '焟', '�c' => '焨', '�d' => '焺', '�e' => '焛', '�f' => '牋', '�g' => '牚', '�h' => '犈', '�i' => '犉', '�j' => '犆', '�k' => '犅', '�l' => '犋', '�m' => '猒', '�n' => '猋', '�o' => '猰', '�p' => '猢', '�q' => '猱', '�r' => '猳', '�s' => '猧', '�t' => '猲', '�u' => '猭', '�v' => '猦', '�w' => '猣', '�x' => '猵', '�y' => '猌', '�z' => '琮', '�{' => '琬', '�|' => '琰', '�}' => '琫', '�~' => '琖', 'ڡ' => '琚', 'ڢ' => '琡', 'ڣ' => '琭', 'ڤ' => '琱', 'ڥ' => '琤', 'ڦ' => '琣', 'ڧ' => '琝', 'ڨ' => '琩', 'ک' => '琠', 'ڪ' => '琲', 'ګ' => '瓻', 'ڬ' => '甯', 'ڭ' => '畯', 'ڮ' => '畬', 'گ' => '痧', 'ڰ' => '痚', 'ڱ' => '痡', 'ڲ' => '痦', 'ڳ' => '痝', 'ڴ' => '痟', 'ڵ' => '痤', 'ڶ' => '痗', 'ڷ' => '皕', 'ڸ' => '皒', 'ڹ' => '盚', 'ں' => '睆', 'ڻ' => '睇', 'ڼ' => '睄', 'ڽ' => '睍', 'ھ' => '睅', 'ڿ' => '睊', '�' => '睎', '�' => '睋', '��' => '睌', '��' => '矞', '��' => '矬', '��' => '硠', '��' => '硤', '��' => '硥', '��' => '硜', '��' => '硭', '��' => '硱', '��' => '硪', '��' => '确', '��' => '硰', '��' => '硩', '��' => '硨', '��' => '硞', '��' => '硢', '��' => '祴', '��' => '祳', '��' => '祲', '��' => '祰', '��' => '稂', '��' => '稊', '��' => '稃', '��' => '稌', '��' => '稄', '��' => '窙', '��' => '竦', '��' => '竤', '��' => '筊', '��' => '笻', '��' => '筄', '��' => '筈', '��' => '筌', '��' => '筎', '��' => '筀', '��' => '筘', '��' => '筅', '��' => '粢', '��' => '粞', '��' => '粨', '��' => '粡', '��' => '絘', '��' => '絯', '��' => '絣', '��' => '絓', '��' => '絖', '��' => '絧', '��' => '絪', '��' => '絏', '��' => '絭', '��' => '絜', '�' => '絫', '�' => '絒', '�' => '絔', '�' => '絩', '�' => '絑', '�' => '絟', '�' => '絎', '�' => '缾', '�' => '缿', '�' => '罥', '�@' => '罦', '�A' => '羢', '�B' => '羠', '�C' => '羡', '�D' => '翗', '�E' => '聑', '�F' => '聏', '�G' => '聐', '�H' => '胾', '�I' => '胔', '�J' => '腃', '�K' => '腊', '�L' => '腒', '�M' => '腏', '�N' => '腇', '�O' => '脽', '�P' => '腍', '�Q' => '脺', '�R' => '臦', '�S' => '臮', '�T' => '臷', '�U' => '臸', '�V' => '臹', '�W' => '舄', '�X' => '舼', '�Y' => '舽', '�Z' => '舿', '�[' => '艵', '�\\' => '茻', '�]' => '菏', '�^' => '菹', '�_' => '萣', '�`' => '菀', '�a' => '菨', '�b' => '萒', '�c' => '菧', '�d' => '菤', '�e' => '菼', '�f' => '菶', '�g' => '萐', '�h' => '菆', '�i' => '菈', '�j' => '菫', '�k' => '菣', '�l' => '莿', '�m' => '萁', '�n' => '菝', '�o' => '菥', '�p' => '菘', '�q' => '菿', '�r' => '菡', '�s' => '菋', '�t' => '菎', '�u' => '菖', '�v' => '菵', '�w' => '菉', '�x' => '萉', '�y' => '萏', '�z' => '菞', '�{' => '萑', '�|' => '萆', '�}' => '菂', '�~' => '菳', 'ۡ' => '菕', 'ۢ' => '菺', 'ۣ' => '菇', 'ۤ' => '菑', 'ۥ' => '菪', 'ۦ' => '萓', 'ۧ' => '菃', 'ۨ' => '菬', '۩' => '菮', '۪' => '菄', '۫' => '菻', '۬' => '菗', 'ۭ' => '菢', 'ۮ' => '萛', 'ۯ' => '菛', '۰' => '菾', '۱' => '蛘', '۲' => '蛢', '۳' => '蛦', '۴' => '蛓', '۵' => '蛣', '۶' => '蛚', '۷' => '蛪', '۸' => '蛝', '۹' => '蛫', 'ۺ' => '蛜', 'ۻ' => '蛬', 'ۼ' => '蛩', '۽' => '蛗', '۾' => '蛨', 'ۿ' => '蛑', '�' => '衈', '�' => '衖', '��' => '衕', '��' => '袺', '��' => '裗', '��' => '袹', '��' => '袸', '��' => '裀', '��' => '袾', '��' => '袶', '��' => '袼', '��' => '袷', '��' => '袽', '��' => '袲', '��' => '褁', '��' => '裉', '��' => '覕', '��' => '覘', '��' => '覗', '��' => '觝', '��' => '觚', '��' => '觛', '��' => '詎', '��' => '詍', '��' => '訹', '��' => '詙', '��' => '詀', '��' => '詗', '��' => '詘', '��' => '詄', '��' => '詅', '��' => '詒', '��' => '詈', '��' => '詑', '��' => '詊', '��' => '詌', '��' => '詏', '��' => '豟', '��' => '貁', '��' => '貀', '��' => '貺', '��' => '貾', '��' => '貰', '��' => '貹', '��' => '貵', '��' => '趄', '��' => '趀', '��' => '趉', '��' => '跘', '��' => '跓', '��' => '跍', '��' => '跇', '��' => '跖', '�' => '跜', '�' => '跏', '�' => '跕', '�' => '跙', '�' => '跈', '�' => '跗', '�' => '跅', '�' => '軯', '�' => '軷', '�' => '軺', '�@' => '軹', '�A' => '軦', '�B' => '軮', '�C' => '軥', '�D' => '軵', '�E' => '軧', '�F' => '軨', '�G' => '軶', '�H' => '軫', '�I' => '軱', '�J' => '軬', '�K' => '軴', '�L' => '軩', '�M' => '逭', '�N' => '逴', '�O' => '逯', '�P' => '鄆', '�Q' => '鄬', '�R' => '鄄', '�S' => '郿', '�T' => '郼', '�U' => '鄈', '�V' => '郹', '�W' => '郻', '�X' => '鄁', '�Y' => '鄀', '�Z' => '鄇', '�[' => '鄅', '�\\' => '鄃', '�]' => '酡', '�^' => '酤', '�_' => '酟', '�`' => '酢', '�a' => '酠', '�b' => '鈁', '�c' => '鈊', '�d' => '鈥', '�e' => '鈃', '�f' => '鈚', '�g' => '鈦', '�h' => '鈏', '�i' => '鈌', '�j' => '鈀', '�k' => '鈒', '�l' => '釿', '�m' => '釽', '�n' => '鈆', '�o' => '鈄', '�p' => '鈧', '�q' => '鈂', '�r' => '鈜', '�s' => '鈤', '�t' => '鈙', '�u' => '鈗', '�v' => '鈅', '�w' => '鈖', '�x' => '镻', '�y' => '閍', '�z' => '閌', '�{' => '閐', '�|' => '隇', '�}' => '陾', '�~' => '隈', 'ܡ' => '隉', 'ܢ' => '隃', 'ܣ' => '隀', 'ܤ' => '雂', 'ܥ' => '雈', 'ܦ' => '雃', 'ܧ' => '雱', 'ܨ' => '雰', 'ܩ' => '靬', 'ܪ' => '靰', 'ܫ' => '靮', 'ܬ' => '頇', 'ܭ' => '颩', 'ܮ' => '飫', 'ܯ' => '鳦', 'ܰ' => '黹', 'ܱ' => '亃', 'ܲ' => '亄', 'ܳ' => '亶', 'ܴ' => '傽', 'ܵ' => '傿', 'ܶ' => '僆', 'ܷ' => '傮', 'ܸ' => '僄', 'ܹ' => '僊', 'ܺ' => '傴', 'ܻ' => '僈', 'ܼ' => '僂', 'ܽ' => '傰', 'ܾ' => '僁', 'ܿ' => '傺', '�' => '傱', '�' => '僋', '��' => '僉', '��' => '傶', '��' => '傸', '��' => '凗', '��' => '剺', '��' => '剸', '��' => '剻', '��' => '剼', '��' => '嗃', '��' => '嗛', '��' => '嗌', '��' => '嗐', '��' => '嗋', '��' => '嗊', '��' => '嗝', '��' => '嗀', '��' => '嗔', '��' => '嗄', '��' => '嗩', '��' => '喿', '��' => '嗒', '��' => '喍', '��' => '嗏', '��' => '嗕', '��' => '嗢', '��' => '嗖', '��' => '嗈', '��' => '嗲', '��' => '嗍', '��' => '嗙', '��' => '嗂', '��' => '圔', '��' => '塓', '��' => '塨', '��' => '塤', '��' => '塏', '��' => '塍', '��' => '塉', '��' => '塯', '��' => '塕', '��' => '塎', '��' => '塝', '��' => '塙', '��' => '塥', '��' => '塛', '��' => '堽', '��' => '塣', '��' => '塱', '��' => '壼', '��' => '嫇', '��' => '嫄', '�' => '嫋', '�' => '媺', '�' => '媸', '�' => '媱', '�' => '媵', '�' => '媰', '�' => '媿', '�' => '嫈', '�' => '媻', '�' => '嫆', '�@' => '媷', '�A' => '嫀', '�B' => '嫊', '�C' => '媴', '�D' => '媶', '�E' => '嫍', '�F' => '媹', '�G' => '媐', '�H' => '寖', '�I' => '寘', '�J' => '寙', '�K' => '尟', '�L' => '尳', '�M' => '嵱', '�N' => '嵣', '�O' => '嵊', '�P' => '嵥', '�Q' => '嵲', '�R' => '嵬', '�S' => '嵞', '�T' => '嵨', '�U' => '嵧', '�V' => '嵢', '�W' => '巰', '�X' => '幏', '�Y' => '幎', '�Z' => '幊', '�[' => '幍', '�\\' => '幋', '�]' => '廅', '�^' => '廌', '�_' => '廆', '�`' => '廋', '�a' => '廇', '�b' => '彀', '�c' => '徯', '�d' => '徭', '�e' => '惷', '�f' => '慉', '�g' => '慊', '�h' => '愫', '�i' => '慅', '�j' => '愶', '�k' => '愲', '�l' => '愮', '�m' => '慆', '�n' => '愯', '�o' => '慏', '�p' => '愩', '�q' => '慀', '�r' => '戠', '�s' => '酨', '�t' => '戣', '�u' => '戥', '�v' => '戤', '�w' => '揅', '�x' => '揱', '�y' => '揫', '�z' => '搐', '�{' => '搒', '�|' => '搉', '�}' => '搠', '�~' => '搤', 'ݡ' => '搳', 'ݢ' => '摃', 'ݣ' => '搟', 'ݤ' => '搕', 'ݥ' => '搘', 'ݦ' => '搹', 'ݧ' => '搷', 'ݨ' => '搢', 'ݩ' => '搣', 'ݪ' => '搌', 'ݫ' => '搦', 'ݬ' => '搰', 'ݭ' => '搨', 'ݮ' => '摁', 'ݯ' => '搵', 'ݰ' => '搯', 'ݱ' => '搊', 'ݲ' => '搚', 'ݳ' => '摀', 'ݴ' => '搥', 'ݵ' => '搧', 'ݶ' => '搋', 'ݷ' => '揧', 'ݸ' => '搛', 'ݹ' => '搮', 'ݺ' => '搡', 'ݻ' => '搎', 'ݼ' => '敯', 'ݽ' => '斒', 'ݾ' => '旓', 'ݿ' => '暆', '�' => '暌', '�' => '暕', '��' => '暐', '��' => '暋', '��' => '暊', '��' => '暙', '��' => '暔', '��' => '晸', '��' => '朠', '��' => '楦', '��' => '楟', '��' => '椸', '��' => '楎', '��' => '楢', '��' => '楱', '��' => '椿', '��' => '楅', '��' => '楪', '��' => '椹', '��' => '楂', '��' => '楗', '��' => '楙', '��' => '楺', '��' => '楈', '��' => '楉', '��' => '椵', '��' => '楬', '��' => '椳', '��' => '椽', '��' => '楥', '��' => '棰', '��' => '楸', '��' => '椴', '��' => '楩', '��' => '楀', '��' => '楯', '��' => '楄', '��' => '楶', '��' => '楘', '��' => '楁', '��' => '楴', '��' => '楌', '��' => '椻', '��' => '楋', '��' => '椷', '��' => '楜', '��' => '楏', '��' => '楑', '��' => '椲', '��' => '楒', '��' => '椯', '��' => '楻', '��' => '椼', '�' => '歆', '�' => '歅', '�' => '歃', '�' => '歂', '�' => '歈', '�' => '歁', '�' => '殛', '�' => '嗀', '�' => '毻', '�' => '毼', '�@' => '毹', '�A' => '毷', '�B' => '毸', '�C' => '溛', '�D' => '滖', '�E' => '滈', '�F' => '溏', '�G' => '滀', '�H' => '溟', '�I' => '溓', '�J' => '溔', '�K' => '溠', '�L' => '溱', '�M' => '溹', '�N' => '滆', '�O' => '滒', '�P' => '溽', '�Q' => '滁', '�R' => '溞', '�S' => '滉', '�T' => '溷', '�U' => '溰', '�V' => '滍', '�W' => '溦', '�X' => '滏', '�Y' => '溲', '�Z' => '溾', '�[' => '滃', '�\\' => '滜', '�]' => '滘', '�^' => '溙', '�_' => '溒', '�`' => '溎', '�a' => '溍', '�b' => '溤', '�c' => '溡', '�d' => '溿', '�e' => '溳', '�f' => '滐', '�g' => '滊', '�h' => '溗', '�i' => '溮', '�j' => '溣', '�k' => '煇', '�l' => '煔', '�m' => '煒', '�n' => '煣', '�o' => '煠', '�p' => '煁', '�q' => '煝', '�r' => '煢', '�s' => '煲', '�t' => '煸', '�u' => '煪', '�v' => '煡', '�w' => '煂', '�x' => '煘', '�y' => '煃', '�z' => '煋', '�{' => '煰', '�|' => '煟', '�}' => '煐', '�~' => '煓', 'ޡ' => '煄', 'ޢ' => '煍', 'ޣ' => '煚', 'ޤ' => '牏', 'ޥ' => '犍', 'ަ' => '犌', 'ާ' => '犑', 'ި' => '犐', 'ީ' => '犎', 'ު' => '猼', 'ޫ' => '獂', 'ެ' => '猻', 'ޭ' => '猺', 'ޮ' => '獀', 'ޯ' => '獊', 'ް' => '獉', 'ޱ' => '瑄', '' => '瑊', '' => '瑋', '' => '瑒', '' => '瑑', '' => '瑗', '' => '瑀', '' => '瑏', '' => '瑐', '' => '瑎', '' => '瑂', '' => '瑆', '' => '瑍', '' => '瑔', '' => '瓡', '�' => '瓿', '�' => '瓾', '��' => '瓽', '��' => '甝', '��' => '畹', '��' => '畷', '��' => '榃', '��' => '痯', '��' => '瘏', '��' => '瘃', '��' => '痷', '��' => '痾', '��' => '痼', '��' => '痹', '��' => '痸', '��' => '瘐', '��' => '痻', '��' => '痶', '��' => '痭', '��' => '痵', '��' => '痽', '��' => '皙', '��' => '皵', '��' => '盝', '��' => '睕', '��' => '睟', '��' => '睠', '��' => '睒', '��' => '睖', '��' => '睚', '��' => '睩', '��' => '睧', '��' => '睔', '��' => '睙', '��' => '睭', '��' => '矠', '��' => '碇', '��' => '碚', '��' => '碔', '��' => '碏', '��' => '碄', '��' => '碕', '��' => '碅', '��' => '碆', '��' => '碡', '��' => '碃', '��' => '硹', '��' => '碙', '��' => '碀', '��' => '碖', '��' => '硻', '��' => '祼', '��' => '禂', '�' => '祽', '�' => '祹', '�' => '稑', '�' => '稘', '�' => '稙', '�' => '稒', '�' => '稗', '�' => '稕', '�' => '稢', '�' => '稓', '�@' => '稛', '�A' => '稐', '�B' => '窣', '�C' => '窢', '�D' => '窞', '�E' => '竫', '�F' => '筦', '�G' => '筤', '�H' => '筭', '�I' => '筴', '�J' => '筩', '�K' => '筲', '�L' => '筥', '�M' => '筳', '�N' => '筱', '�O' => '筰', '�P' => '筡', '�Q' => '筸', '�R' => '筶', '�S' => '筣', '�T' => '粲', '�U' => '粴', '�V' => '粯', '�W' => '綈', '�X' => '綆', '�Y' => '綀', '�Z' => '綍', '�[' => '絿', '�\\' => '綅', '�]' => '絺', '�^' => '綎', '�_' => '絻', '�`' => '綃', '�a' => '絼', '�b' => '綌', '�c' => '綔', '�d' => '綄', '�e' => '絽', '�f' => '綒', '�g' => '罭', '�h' => '罫', '�i' => '罧', '�j' => '罨', '�k' => '罬', '�l' => '羦', '�m' => '羥', '�n' => '羧', '�o' => '翛', '�p' => '翜', '�q' => '耡', '�r' => '腤', '�s' => '腠', '�t' => '腷', '�u' => '腜', '�v' => '腩', '�w' => '腛', '�x' => '腢', '�y' => '腲', '�z' => '朡', '�{' => '腞', '�|' => '腶', '�}' => '腧', '�~' => '腯', 'ߡ' => '腄', 'ߢ' => '腡', 'ߣ' => '舝', 'ߤ' => '艉', 'ߥ' => '艄', 'ߦ' => '艀', 'ߧ' => '艂', 'ߨ' => '艅', 'ߩ' => '蓱', 'ߪ' => '萿', '߫' => '葖', '߬' => '葶', '߭' => '葹', '߮' => '蒏', '߯' => '蒍', '߰' => '葥', '߱' => '葑', '߲' => '葀', '߳' => '蒆', 'ߴ' => '葧', 'ߵ' => '萰', '߶' => '葍', '߷' => '葽', '߸' => '葚', '߹' => '葙', 'ߺ' => '葴', '' => '葳', '' => '葝', '߽' => '蔇', '߾' => '葞', '߿' => '萷', '�' => '萺', '�' => '萴', '��' => '葺', '��' => '葃', '��' => '葸', '��' => '萲', '��' => '葅', '��' => '萩', '��' => '菙', '��' => '葋', '��' => '萯', '��' => '葂', '��' => '萭', '��' => '葟', '��' => '葰', '��' => '萹', '��' => '葎', '��' => '葌', '��' => '葒', '��' => '葯', '��' => '蓅', '��' => '蒎', '��' => '萻', '��' => '葇', '��' => '萶', '��' => '萳', '��' => '葨', '��' => '葾', '��' => '葄', '��' => '萫', '��' => '葠', '��' => '葔', '��' => '葮', '��' => '葐', '��' => '蜋', '��' => '蜄', '��' => '蛷', '��' => '蜌', '��' => '蛺', '��' => '蛖', '��' => '蛵', '��' => '蝍', '��' => '蛸', '��' => '蜎', '��' => '蜉', '��' => '蜁', '��' => '蛶', '��' => '蜍', '��' => '蜅', '��' => '裖', '��' => '裋', '��' => '裍', '��' => '裎', '�' => '裞', '�' => '裛', '�' => '裚', '�' => '裌', '�' => '裐', '�' => '覅', '�' => '覛', '�' => '觟', '�' => '觥', '�' => '觤', '�@' => '觡', '�A' => '觠', '�B' => '觢', '�C' => '觜', '�D' => '触', '�E' => '詶', '�F' => '誆', '�G' => '詿', '�H' => '詡', '�I' => '訿', '�J' => '詷', '�K' => '誂', '�L' => '誄', '�M' => '詵', '�N' => '誃', '�O' => '誁', '�P' => '詴', '�Q' => '詺', '�R' => '谼', '�S' => '豋', '�T' => '豊', '�U' => '豥', '�V' => '豤', '�W' => '豦', '�X' => '貆', '�Y' => '貄', '�Z' => '貅', '�[' => '賌', '�\\' => '赨', '�]' => '赩', '�^' => '趑', '�_' => '趌', '�`' => '趎', '�a' => '趏', '�b' => '趍', '�c' => '趓', '�d' => '趔', '�e' => '趐', '�f' => '趒', '�g' => '跰', '�h' => '跠', '�i' => '跬', '�j' => '跱', '�k' => '跮', '�l' => '跐', '�m' => '跩', '�n' => '跣', '�o' => '跢', '�p' => '跧', '�q' => '跲', '�r' => '跫', '�s' => '跴', '�t' => '輆', '�u' => '軿', '�v' => '輁', '�w' => '輀', '�x' => '輅', '�y' => '輇', '�z' => '輈', '�{' => '輂', '�|' => '輋', '�}' => '遒', '�~' => '逿', '�' => '遄', '�' => '遉', '�' => '逽', '�' => '鄐', '�' => '鄍', '�' => '鄏', '�' => '鄑', '�' => '鄖', '�' => '鄔', '�' => '鄋', '�' => '鄎', '�' => '酮', '�' => '酯', '�' => '鉈', '�' => '鉒', '�' => '鈰', '�' => '鈺', '�' => '鉦', '�' => '鈳', '�' => '鉥', '�' => '鉞', '�' => '銃', '�' => '鈮', '�' => '鉊', '�' => '鉆', '�' => '鉭', '�' => '鉬', '�' => '鉏', '�' => '鉠', '�' => '鉧', '�' => '鉯', '�' => '鈶', '�' => '鉡', '��' => '鉰', '��' => '鈱', '��' => '鉔', '��' => '鉣', '��' => '鉐', '��' => '鉲', '��' => '鉎', '��' => '鉓', '��' => '鉌', '��' => '鉖', '��' => '鈲', '��' => '閟', '��' => '閜', '��' => '閞', '��' => '閛', '��' => '隒', '��' => '隓', '��' => '隑', '��' => '隗', '��' => '雎', '��' => '雺', '��' => '雽', '��' => '雸', '��' => '雵', '��' => '靳', '��' => '靷', '��' => '靸', '��' => '靲', '��' => '頏', '��' => '頍', '��' => '頎', '��' => '颬', '��' => '飶', '��' => '飹', '��' => '馯', '��' => '馲', '��' => '馰', '��' => '馵', '��' => '骭', '��' => '骫', '��' => '魛', '��' => '鳪', '��' => '鳭', '��' => '鳧', '��' => '麀', '��' => '黽', '��' => '僦', '��' => '僔', '��' => '僗', '��' => '僨', '��' => '僳', '�' => '僛', '�' => '僪', '�' => '僝', '�' => '僤', '�' => '僓', '�' => '僬', '�' => '僰', '�' => '僯', '�' => '僣', '�' => '僠', '�@' => '凘', '�A' => '劀', '�B' => '劁', '�C' => '勩', '�D' => '勫', '�E' => '匰', '�F' => '厬', '�G' => '嘧', '�H' => '嘕', '�I' => '嘌', '�J' => '嘒', '�K' => '嗼', '�L' => '嘏', '�M' => '嘜', '�N' => '嘁', '�O' => '嘓', '�P' => '嘂', '�Q' => '嗺', '�R' => '嘝', '�S' => '嘄', '�T' => '嗿', '�U' => '嗹', '�V' => '墉', '�W' => '塼', '�X' => '墐', '�Y' => '墘', '�Z' => '墆', '�[' => '墁', '�\\' => '塿', '�]' => '塴', '�^' => '墋', '�_' => '塺', '�`' => '墇', '�a' => '墑', '�b' => '墎', '�c' => '塶', '�d' => '墂', '�e' => '墈', '�f' => '塻', '�g' => '墔', '�h' => '墏', '�i' => '壾', '�j' => '奫', '�k' => '嫜', '�l' => '嫮', '�m' => '嫥', '�n' => '嫕', '�o' => '嫪', '�p' => '嫚', '�q' => '嫭', '�r' => '嫫', '�s' => '嫳', '�t' => '嫢', '�u' => '嫠', '�v' => '嫛', '�w' => '嫬', '�x' => '嫞', '�y' => '嫝', '�z' => '嫙', '�{' => '嫨', '�|' => '嫟', '�}' => '孷', '�~' => '寠', '�' => '寣', '�' => '屣', '�' => '嶂', '�' => '嶀', '�' => '嵽', '�' => '嶆', '�' => '嵺', '�' => '嶁', '�' => '嵷', '�' => '嶊', '�' => '嶉', '�' => '嶈', '�' => '嵾', '�' => '嵼', '�' => '嶍', '�' => '嵹', '�' => '嵿', '�' => '幘', '�' => '幙', '�' => '幓', '�' => '廘', '�' => '廑', '�' => '廗', '�' => '廎', '�' => '廜', '�' => '廕', '�' => '廙', '�' => '廒', '�' => '廔', '�' => '彄', '�' => '彃', '�' => '彯', '�' => '徶', '��' => '愬', '��' => '愨', '��' => '慁', '��' => '慞', '��' => '慱', '��' => '慳', '��' => '慒', '��' => '慓', '��' => '慲', '��' => '慬', '��' => '憀', '��' => '慴', '��' => '慔', '��' => '慺', '��' => '慛', '��' => '慥', '��' => '愻', '��' => '慪', '��' => '慡', '��' => '慖', '��' => '戩', '��' => '戧', '��' => '戫', '��' => '搫', '��' => '摍', '��' => '摛', '��' => '摝', '��' => '摴', '��' => '摶', '��' => '摲', '��' => '摳', '��' => '摽', '��' => '摵', '��' => '摦', '��' => '撦', '��' => '摎', '��' => '撂', '��' => '摞', '��' => '摜', '��' => '摋', '��' => '摓', '��' => '摠', '��' => '摐', '��' => '摿', '��' => '搿', '��' => '摬', '��' => '摫', '��' => '摙', '��' => '摥', '��' => '摷', '��' => '敳', '�' => '斠', '�' => '暡', '�' => '暠', '�' => '暟', '�' => '朅', '�' => '朄', '�' => '朢', '�' => '榱', '�' => '榶', '�' => '槉', '�@' => '榠', '�A' => '槎', '�B' => '榖', '�C' => '榰', '�D' => '榬', '�E' => '榼', '�F' => '榑', '�G' => '榙', '�H' => '榎', '�I' => '榧', '�J' => '榍', '�K' => '榩', '�L' => '榾', '�M' => '榯', '�N' => '榿', '�O' => '槄', '�P' => '榽', '�Q' => '榤', '�R' => '槔', '�S' => '榹', '�T' => '槊', '�U' => '榚', '�V' => '槏', '�W' => '榳', '�X' => '榓', '�Y' => '榪', '�Z' => '榡', '�[' => '榞', '�\\' => '槙', '�]' => '榗', '�^' => '榐', '�_' => '槂', '�`' => '榵', '�a' => '榥', '�b' => '槆', '�c' => '歊', '�d' => '歍', '�e' => '歋', '�f' => '殞', '�g' => '殟', '�h' => '殠', '�i' => '毃', '�j' => '毄', '�k' => '毾', '�l' => '滎', '�m' => '滵', '�n' => '滱', '�o' => '漃', '�p' => '漥', '�q' => '滸', '�r' => '漷', '�s' => '滻', '�t' => '漮', '�u' => '漉', '�v' => '潎', '�w' => '漙', '�x' => '漚', '�y' => '漧', '�z' => '漘', '�{' => '漻', '�|' => '漒', '�}' => '滭', '�~' => '漊', '�' => '漶', '�' => '潳', '�' => '滹', '�' => '滮', '�' => '漭', '�' => '潀', '�' => '漰', '�' => '漼', '�' => '漵', '�' => '滫', '�' => '漇', '�' => '漎', '�' => '潃', '�' => '漅', '�' => '滽', '�' => '滶', '�' => '漹', '�' => '漜', '�' => '滼', '�' => '漺', '�' => '漟', '�' => '漍', '�' => '漞', '�' => '漈', '�' => '漡', '�' => '熇', '�' => '熐', '�' => '熉', '�' => '熀', '�' => '熅', '�' => '熂', '�' => '熏', '�' => '煻', '��' => '熆', '��' => '熁', '��' => '熗', '��' => '牄', '��' => '牓', '��' => '犗', '��' => '犕', '��' => '犓', '��' => '獃', '��' => '獍', '��' => '獑', '��' => '獌', '��' => '瑢', '��' => '瑳', '��' => '瑱', '��' => '瑵', '��' => '瑲', '��' => '瑧', '��' => '瑮', '��' => '甀', '��' => '甂', '��' => '甃', '��' => '畽', '��' => '疐', '��' => '瘖', '��' => '瘈', '��' => '瘌', '��' => '瘕', '��' => '瘑', '��' => '瘊', '��' => '瘔', '��' => '皸', '��' => '瞁', '��' => '睼', '��' => '瞅', '��' => '瞂', '��' => '睮', '��' => '瞀', '��' => '睯', '��' => '睾', '��' => '瞃', '��' => '碲', '��' => '碪', '��' => '碴', '��' => '碭', '��' => '碨', '��' => '硾', '��' => '碫', '��' => '碞', '��' => '碥', '��' => '碠', '�' => '碬', '�' => '碢', '�' => '碤', '�' => '禘', '�' => '禊', '�' => '禋', '�' => '禖', '�' => '禕', '�' => '禔', '�' => '禓', '�@' => '禗', '�A' => '禈', '�B' => '禒', '�C' => '禐', '�D' => '稫', '�E' => '穊', '�F' => '稰', '�G' => '稯', '�H' => '稨', '�I' => '稦', '�J' => '窨', '�K' => '窫', '�L' => '窬', '�M' => '竮', '�N' => '箈', '�O' => '箜', '�P' => '箊', '�Q' => '箑', '�R' => '箐', '�S' => '箖', '�T' => '箍', '�U' => '箌', '�V' => '箛', '�W' => '箎', '�X' => '箅', '�Y' => '箘', '�Z' => '劄', '�[' => '箙', '�\\' => '箤', '�]' => '箂', '�^' => '粻', '�_' => '粿', '�`' => '粼', '�a' => '粺', '�b' => '綧', '�c' => '綷', '�d' => '緂', '�e' => '綣', '�f' => '綪', '�g' => '緁', '�h' => '緀', '�i' => '緅', '�j' => '綝', '�k' => '緎', '�l' => '緄', '�m' => '緆', '�n' => '緋', '�o' => '緌', '�p' => '綯', '�q' => '綹', '�r' => '綖', '�s' => '綼', '�t' => '綟', '�u' => '綦', '�v' => '綮', '�w' => '綩', '�x' => '綡', '�y' => '緉', '�z' => '罳', '�{' => '翢', '�|' => '翣', '�}' => '翥', '�~' => '翞', '�' => '耤', '�' => '聝', '�' => '聜', '�' => '膉', '�' => '膆', '�' => '膃', '�' => '膇', '�' => '膍', '�' => '膌', '�' => '膋', '�' => '舕', '�' => '蒗', '�' => '蒤', '�' => '蒡', '�' => '蒟', '�' => '蒺', '�' => '蓎', '�' => '蓂', '�' => '蒬', '�' => '蒮', '�' => '蒫', '�' => '蒹', '�' => '蒴', '�' => '蓁', '�' => '蓍', '�' => '蒪', '�' => '蒚', '�' => '蒱', '�' => '蓐', '�' => '蒝', '�' => '蒧', '�' => '蒻', '�' => '蒢', '��' => '蒔', '��' => '蓇', '��' => '蓌', '��' => '蒛', '��' => '蒩', '��' => '蒯', '��' => '蒨', '��' => '蓖', '��' => '蒘', '��' => '蒶', '��' => '蓏', '��' => '蒠', '��' => '蓗', '��' => '蓔', '��' => '蓒', '��' => '蓛', '��' => '蒰', '��' => '蒑', '��' => '虡', '��' => '蜳', '��' => '蜣', '��' => '蜨', '��' => '蝫', '��' => '蝀', '��' => '蜮', '��' => '蜞', '��' => '蜡', '��' => '蜙', '��' => '蜛', '��' => '蝃', '��' => '蜬', '��' => '蝁', '��' => '蜾', '��' => '蝆', '��' => '蜠', '��' => '蜲', '��' => '蜪', '��' => '蜭', '��' => '蜼', '��' => '蜒', '��' => '蜺', '��' => '蜱', '��' => '蜵', '��' => '蝂', '��' => '蜦', '��' => '蜧', '��' => '蜸', '��' => '蜤', '��' => '蜚', '��' => '蜰', '��' => '蜑', '�' => '裷', '�' => '裧', '�' => '裱', '�' => '裲', '�' => '裺', '�' => '裾', '�' => '裮', '�' => '裼', '�' => '裶', '�' => '裻', '�@' => '裰', '�A' => '裬', '�B' => '裫', '�C' => '覝', '�D' => '覡', '�E' => '覟', '�F' => '覞', '�G' => '觩', '�H' => '觫', '�I' => '觨', '�J' => '誫', '�K' => '誙', '�L' => '誋', '�M' => '誒', '�N' => '誏', '�O' => '誖', '�P' => '谽', '�Q' => '豨', '�R' => '豩', '�S' => '賕', '�T' => '賏', '�U' => '賗', '�V' => '趖', '�W' => '踉', '�X' => '踂', '�Y' => '跿', '�Z' => '踍', '�[' => '跽', '�\\' => '踊', '�]' => '踃', '�^' => '踇', '�_' => '踆', '�`' => '踅', '�a' => '跾', '�b' => '踀', '�c' => '踄', '�d' => '輐', '�e' => '輑', '�f' => '輎', '�g' => '輍', '�h' => '鄣', '�i' => '鄜', '�j' => '鄠', '�k' => '鄢', '�l' => '鄟', '�m' => '鄝', '�n' => '鄚', '�o' => '鄤', '�p' => '鄡', '�q' => '鄛', '�r' => '酺', '�s' => '酲', '�t' => '酹', '�u' => '酳', '�v' => '銥', '�w' => '銤', '�x' => '鉶', '�y' => '銛', '�z' => '鉺', '�{' => '銠', '�|' => '銔', '�}' => '銪', '�~' => '銍', '�' => '銦', '�' => '銚', '�' => '銫', '�' => '鉹', '�' => '銗', '�' => '鉿', '�' => '銣', '�' => '鋮', '�' => '銎', '�' => '銂', '�' => '銕', '�' => '銢', '�' => '鉽', '�' => '銈', '�' => '銡', '�' => '銊', '�' => '銆', '�' => '銌', '�' => '銙', '�' => '銧', '�' => '鉾', '�' => '銇', '�' => '銩', '�' => '銝', '�' => '銋', '�' => '鈭', '�' => '隞', '�' => '隡', '�' => '雿', '�' => '靘', '�' => '靽', '�' => '靺', '�' => '靾', '��' => '鞃', '��' => '鞀', '��' => '鞂', '��' => '靻', '��' => '鞄', '��' => '鞁', '��' => '靿', '��' => '韎', '��' => '韍', '��' => '頖', '��' => '颭', '��' => '颮', '��' => '餂', '��' => '餀', '��' => '餇', '��' => '馝', '��' => '馜', '��' => '駃', '��' => '馹', '��' => '馻', '��' => '馺', '��' => '駂', '��' => '馽', '��' => '駇', '��' => '骱', '��' => '髣', '��' => '髧', '��' => '鬾', '��' => '鬿', '��' => '魠', '��' => '魡', '��' => '魟', '��' => '鳱', '��' => '鳲', '��' => '鳵', '��' => '麧', '��' => '僿', '��' => '儃', '��' => '儰', '��' => '僸', '��' => '儆', '��' => '儇', '��' => '僶', '��' => '僾', '��' => '儋', '��' => '儌', '��' => '僽', '��' => '儊', '��' => '劋', '��' => '劌', '��' => '勱', '�' => '勯', '�' => '噈', '�' => '噂', '�' => '噌', '�' => '嘵', '�' => '噁', '�' => '噊', '�' => '噉', '�' => '噆', '�' => '噘', '�@' => '噚', '�A' => '噀', '�B' => '嘳', '�C' => '嘽', '�D' => '嘬', '�E' => '嘾', '�F' => '嘸', '�G' => '嘪', '�H' => '嘺', '�I' => '圚', '�J' => '墫', '�K' => '墝', '�L' => '墱', '�M' => '墠', '�N' => '墣', '�O' => '墯', '�P' => '墬', '�Q' => '墥', '�R' => '墡', '�S' => '壿', '�T' => '嫿', '�U' => '嫴', '�V' => '嫽', '�W' => '嫷', '�X' => '嫶', '�Y' => '嬃', '�Z' => '嫸', '�[' => '嬂', '�\\' => '嫹', '�]' => '嬁', '�^' => '嬇', '�_' => '嬅', '�`' => '嬏', '�a' => '屧', '�b' => '嶙', '�c' => '嶗', '�d' => '嶟', '�e' => '嶒', '�f' => '嶢', '�g' => '嶓', '�h' => '嶕', '�i' => '嶠', '�j' => '嶜', '�k' => '嶡', '�l' => '嶚', '�m' => '嶞', '�n' => '幩', '�o' => '幝', '�p' => '幠', '�q' => '幜', '�r' => '緳', '�s' => '廛', '�t' => '廞', '�u' => '廡', '�v' => '彉', '�w' => '徲', '�x' => '憋', '�y' => '憃', '�z' => '慹', '�{' => '憱', '�|' => '憰', '�}' => '憢', '�~' => '憉', '�' => '憛', '�' => '憓', '�' => '憯', '�' => '憭', '�' => '憟', '�' => '憒', '�' => '憪', '�' => '憡', '�' => '憍', '�' => '慦', '�' => '憳', '�' => '戭', '�' => '摮', '�' => '摰', '�' => '撖', '�' => '撠', '�' => '撅', '�' => '撗', '�' => '撜', '�' => '撏', '�' => '撋', '�' => '撊', '�' => '撌', '�' => '撣', '�' => '撟', '�' => '摨', '�' => '撱', '�' => '撘', '�' => '敶', '�' => '敺', '�' => '敹', '�' => '敻', '�' => '斲', '��' => '斳', '��' => '暵', '��' => '暰', '��' => '暩', '��' => '暲', '��' => '暷', '��' => '暪', '��' => '暯', '��' => '樀', '��' => '樆', '��' => '樗', '��' => '槥', '��' => '槸', '��' => '樕', '��' => '槱', '��' => '槤', '��' => '樠', '��' => '槿', '��' => '槬', '��' => '槢', '��' => '樛', '��' => '樝', '��' => '槾', '��' => '樧', '��' => '槲', '��' => '槮', '��' => '樔', '��' => '槷', '��' => '槧', '��' => '橀', '��' => '樈', '��' => '槦', '��' => '槻', '��' => '樍', '��' => '槼', '��' => '槫', '��' => '樉', '��' => '樄', '��' => '樘', '��' => '樥', '��' => '樏', '��' => '槶', '��' => '樦', '��' => '樇', '��' => '槴', '��' => '樖', '��' => '歑', '��' => '殥', '��' => '殣', '��' => '殢', '��' => '殦', '�' => '氁', '�' => '氀', '�' => '毿', '�' => '氂', '�' => '潁', '�' => '漦', '�' => '潾', '�' => '澇', '�' => '濆', '�' => '澒', '�@' => '澍', '�A' => '澉', '�B' => '澌', '�C' => '潢', '�D' => '潏', '�E' => '澅', '�F' => '潚', '�G' => '澖', '�H' => '潶', '�I' => '潬', '�J' => '澂', '�K' => '潕', '�L' => '潲', '�M' => '潒', '�N' => '潐', '�O' => '潗', '�P' => '澔', '�Q' => '澓', '�R' => '潝', '�S' => '漀', '�T' => '潡', '�U' => '潫', '�V' => '潽', '�W' => '潧', '�X' => '澐', '�Y' => '潓', '�Z' => '澋', '�[' => '潩', '�\\' => '潿', '�]' => '澕', '�^' => '潣', '�_' => '潷', '�`' => '潪', '�a' => '潻', '�b' => '熲', '�c' => '熯', '�d' => '熛', '�e' => '熰', '�f' => '熠', '�g' => '熚', '�h' => '熩', '�i' => '熵', '�j' => '熝', '�k' => '熥', '�l' => '熞', '�m' => '熤', '�n' => '熡', '�o' => '熪', '�p' => '熜', '�q' => '熧', '�r' => '熳', '�s' => '犘', '�t' => '犚', '�u' => '獘', '�v' => '獒', '�w' => '獞', '�x' => '獟', '�y' => '獠', '�z' => '獝', '�{' => '獛', '�|' => '獡', '�}' => '獚', '�~' => '獙', '�' => '獢', '�' => '璇', '�' => '璉', '�' => '璊', '�' => '璆', '�' => '璁', '�' => '瑽', '�' => '璅', '�' => '璈', '�' => '瑼', '�' => '瑹', '�' => '甈', '�' => '甇', '�' => '畾', '�' => '瘥', '�' => '瘞', '�' => '瘙', '�' => '瘝', '�' => '瘜', '�' => '瘣', '�' => '瘚', '�' => '瘨', '�' => '瘛', '�' => '皜', '�' => '皝', '�' => '皞', '�' => '皛', '�' => '瞍', '�' => '瞏', '�' => '瞉', '�' => '瞈', '�' => '磍', '�' => '碻', '��' => '磏', '��' => '磌', '��' => '磑', '��' => '磎', '��' => '磔', '��' => '磈', '��' => '磃', '��' => '磄', '��' => '磉', '��' => '禚', '��' => '禡', '��' => '禠', '��' => '禜', '��' => '禢', '��' => '禛', '��' => '歶', '��' => '稹', '��' => '窲', '��' => '窴', '��' => '窳', '��' => '箷', '��' => '篋', '��' => '箾', '��' => '箬', '��' => '篎', '��' => '箯', '��' => '箹', '��' => '篊', '��' => '箵', '��' => '糅', '��' => '糈', '��' => '糌', '��' => '糋', '��' => '緷', '��' => '緛', '��' => '緪', '��' => '緧', '��' => '緗', '��' => '緡', '��' => '縃', '��' => '緺', '��' => '緦', '��' => '緶', '��' => '緱', '��' => '緰', '��' => '緮', '��' => '緟', '��' => '罶', '��' => '羬', '��' => '羰', '��' => '羭', '�' => '翭', '�' => '翫', '�' => '翪', '�' => '翬', '�' => '翦', '�' => '翨', '�' => '聤', '�' => '聧', '�' => '膣', '�' => '膟', '�@' => '膞', '�A' => '膕', '�B' => '膢', '�C' => '膙', '�D' => '膗', '�E' => '舖', '�F' => '艏', '�G' => '艓', '�H' => '艒', '�I' => '艐', '�J' => '艎', '�K' => '艑', '�L' => '蔤', '�M' => '蔻', '�N' => '蔏', '�O' => '蔀', '�P' => '蔩', '�Q' => '蔎', '�R' => '蔉', '�S' => '蔍', '�T' => '蔟', '�U' => '蔊', '�V' => '蔧', '�W' => '蔜', '�X' => '蓻', '�Y' => '蔫', '�Z' => '蓺', '�[' => '蔈', '�\\' => '蔌', '�]' => '蓴', '�^' => '蔪', '�_' => '蓲', '�`' => '蔕', '�a' => '蓷', '�b' => '蓫', '�c' => '蓳', '�d' => '蓼', '�e' => '蔒', '�f' => '蓪', '�g' => '蓩', '�h' => '蔖', '�i' => '蓾', '�j' => '蔨', '�k' => '蔝', '�l' => '蔮', '�m' => '蔂', '�n' => '蓽', '�o' => '蔞', '�p' => '蓶', '�q' => '蔱', '�r' => '蔦', '�s' => '蓧', '�t' => '蓨', '�u' => '蓰', '�v' => '蓯', '�w' => '蓹', '�x' => '蔘', '�y' => '蔠', '�z' => '蔰', '�{' => '蔋', '�|' => '蔙', '�}' => '蔯', '�~' => '虢', '�' => '蝖', '�' => '蝣', '�' => '蝤', '�' => '蝷', '�' => '蟡', '�' => '蝳', '�' => '蝘', '�' => '蝔', '�' => '蝛', '�' => '蝒', '�' => '蝡', '�' => '蝚', '�' => '蝑', '�' => '蝞', '�' => '蝭', '�' => '蝪', '�' => '蝐', '�' => '蝎', '�' => '蝟', '�' => '蝝', '�' => '蝯', '�' => '蝬', '�' => '蝺', '�' => '蝮', '�' => '蝜', '�' => '蝥', '�' => '蝏', '�' => '蝻', '�' => '蝵', '�' => '蝢', '�' => '蝧', '�' => '蝩', '�' => '衚', '��' => '褅', '��' => '褌', '��' => '褔', '��' => '褋', '��' => '褗', '��' => '褘', '��' => '褙', '��' => '褆', '��' => '褖', '��' => '褑', '��' => '褎', '��' => '褉', '��' => '覢', '��' => '覤', '��' => '覣', '��' => '觭', '��' => '觰', '��' => '觬', '��' => '諏', '��' => '諆', '��' => '誸', '��' => '諓', '��' => '諑', '��' => '諔', '��' => '諕', '��' => '誻', '��' => '諗', '��' => '誾', '��' => '諀', '��' => '諅', '��' => '諘', '��' => '諃', '��' => '誺', '��' => '誽', '��' => '諙', '��' => '谾', '��' => '豍', '��' => '貏', '��' => '賥', '��' => '賟', '��' => '賙', '��' => '賨', '��' => '賚', '��' => '賝', '��' => '賧', '��' => '趠', '��' => '趜', '��' => '趡', '��' => '趛', '��' => '踠', '��' => '踣', '�' => '踥', '�' => '踤', '�' => '踮', '�' => '踕', '�' => '踛', '�' => '踖', '�' => '踑', '�' => '踙', '�' => '踦', '�' => '踧', '�@' => '踔', '�A' => '踒', '�B' => '踘', '�C' => '踓', '�D' => '踜', '�E' => '踗', '�F' => '踚', '�G' => '輬', '�H' => '輤', '�I' => '輘', '�J' => '輚', '�K' => '輠', '�L' => '輣', '�M' => '輖', '�N' => '輗', '�O' => '遳', '�P' => '遰', '�Q' => '遯', '�R' => '遧', '�S' => '遫', '�T' => '鄯', '�U' => '鄫', '�V' => '鄩', '�W' => '鄪', '�X' => '鄲', '�Y' => '鄦', '�Z' => '鄮', '�[' => '醅', '�\\' => '醆', '�]' => '醊', '�^' => '醁', '�_' => '醂', '�`' => '醄', '�a' => '醀', '�b' => '鋐', '�c' => '鋃', '�d' => '鋄', '�e' => '鋀', '�f' => '鋙', '�g' => '銶', '�h' => '鋏', '�i' => '鋱', '�j' => '鋟', '�k' => '鋘', '�l' => '鋩', '�m' => '鋗', '�n' => '鋝', '�o' => '鋌', '�p' => '鋯', '�q' => '鋂', '�r' => '鋨', '�s' => '鋊', '�t' => '鋈', '�u' => '鋎', '�v' => '鋦', '�w' => '鋍', '�x' => '鋕', '�y' => '鋉', '�z' => '鋠', '�{' => '鋞', '�|' => '鋧', '�}' => '鋑', '�~' => '鋓', '�' => '銵', '�' => '鋡', '�' => '鋆', '�' => '銴', '�' => '镼', '�' => '閬', '�' => '閫', '�' => '閮', '�' => '閰', '�' => '隤', '�' => '隢', '�' => '雓', '�' => '霅', '�' => '霈', '�' => '霂', '�' => '靚', '�' => '鞊', '�' => '鞎', '�' => '鞈', '�' => '韐', '�' => '韏', '�' => '頞', '�' => '頝', '�' => '頦', '�' => '頩', '�' => '頨', '�' => '頠', '�' => '頛', '�' => '頧', '�' => '颲', '�' => '餈', '�' => '飺', '�' => '餑', '��' => '餔', '��' => '餖', '��' => '餗', '��' => '餕', '��' => '駜', '��' => '駍', '��' => '駏', '��' => '駓', '��' => '駔', '��' => '駎', '��' => '駉', '��' => '駖', '��' => '駘', '��' => '駋', '��' => '駗', '��' => '駌', '��' => '骳', '��' => '髬', '��' => '髫', '��' => '髳', '��' => '髲', '��' => '髱', '��' => '魆', '��' => '魃', '��' => '魧', '��' => '魴', '��' => '魱', '��' => '魦', '��' => '魶', '��' => '魵', '��' => '魰', '��' => '魨', '��' => '魤', '��' => '魬', '��' => '鳼', '��' => '鳺', '��' => '鳽', '��' => '鳿', '��' => '鳷', '��' => '鴇', '��' => '鴀', '��' => '鳹', '��' => '鳻', '��' => '鴈', '��' => '鴅', '��' => '鴄', '��' => '麃', '��' => '黓', '��' => '鼏', '��' => '鼐', '��' => '儜', '�' => '儓', '�' => '儗', '�' => '儚', '�' => '儑', '�' => '凞', '�' => '匴', '�' => '叡', '�' => '噰', '�' => '噠', '�' => '噮', '�@' => '噳', '�A' => '噦', '�B' => '噣', '�C' => '噭', '�D' => '噲', '�E' => '噞', '�F' => '噷', '�G' => '圜', '�H' => '圛', '�I' => '壈', '�J' => '墽', '�K' => '壉', '�L' => '墿', '�M' => '墺', '�N' => '壂', '�O' => '墼', '�P' => '壆', '�Q' => '嬗', '�R' => '嬙', '�S' => '嬛', '�T' => '嬡', '�U' => '嬔', '�V' => '嬓', '�W' => '嬐', '�X' => '嬖', '�Y' => '嬨', '�Z' => '嬚', '�[' => '嬠', '�\\' => '嬞', '�]' => '寯', '�^' => '嶬', '�_' => '嶱', '�`' => '嶩', '�a' => '嶧', '�b' => '嶵', '�c' => '嶰', '�d' => '嶮', '�e' => '嶪', '�f' => '嶨', '�g' => '嶲', '�h' => '嶭', '�i' => '嶯', '�j' => '嶴', '�k' => '幧', '�l' => '幨', '�m' => '幦', '�n' => '幯', '�o' => '廩', '�p' => '廧', '�q' => '廦', '�r' => '廨', '�s' => '廥', '�t' => '彋', '�u' => '徼', '�v' => '憝', '�w' => '憨', '�x' => '憖', '�y' => '懅', '�z' => '憴', '�{' => '懆', '�|' => '懁', '�}' => '懌', '�~' => '憺', '�' => '憿', '�' => '憸', '�' => '憌', '�' => '擗', '�' => '擖', '�' => '擐', '�' => '擏', '�' => '擉', '�' => '撽', '�' => '撉', '�' => '擃', '�' => '擛', '�' => '擳', '�' => '擙', '�' => '攳', '�' => '敿', '�' => '敼', '�' => '斢', '�' => '曈', '�' => '暾', '�' => '曀', '�' => '曊', '�' => '曋', '�' => '曏', '�' => '暽', '�' => '暻', '�' => '暺', '�' => '曌', '�' => '朣', '�' => '樴', '�' => '橦', '�' => '橉', '�' => '橧', '��' => '樲', '��' => '橨', '��' => '樾', '��' => '橝', '��' => '橭', '��' => '橶', '��' => '橛', '��' => '橑', '��' => '樨', '��' => '橚', '��' => '樻', '��' => '樿', '��' => '橁', '��' => '橪', '��' => '橤', '��' => '橐', '��' => '橏', '��' => '橔', '��' => '橯', '��' => '橩', '��' => '橠', '��' => '樼', '��' => '橞', '��' => '橖', '��' => '橕', '��' => '橍', '��' => '橎', '��' => '橆', '��' => '歕', '��' => '歔', '��' => '歖', '��' => '殧', '��' => '殪', '��' => '殫', '��' => '毈', '��' => '毇', '��' => '氄', '��' => '氃', '��' => '氆', '��' => '澭', '��' => '濋', '��' => '澣', '��' => '濇', '��' => '澼', '��' => '濎', '��' => '濈', '��' => '潞', '��' => '濄', '��' => '澽', '��' => '澞', '��' => '濊', '�' => '澨', '�' => '瀄', '�' => '澥', '�' => '澮', '�' => '澺', '�' => '澬', '�' => '澪', '�' => '濏', '�' => '澿', '�' => '澸', '�@' => '澢', '�A' => '濉', '�B' => '澫', '�C' => '濍', '�D' => '澯', '�E' => '澲', '�F' => '澰', '�G' => '燅', '�H' => '燂', '�I' => '熿', '�J' => '熸', '�K' => '燖', '�L' => '燀', '�M' => '燁', '�N' => '燋', '�O' => '燔', '�P' => '燊', '�Q' => '燇', '�R' => '燏', '�S' => '熽', '�T' => '燘', '�U' => '熼', '�V' => '燆', '�W' => '燚', '�X' => '燛', '�Y' => '犝', '�Z' => '犞', '�[' => '獩', '�\\' => '獦', '�]' => '獧', '�^' => '獬', '�_' => '獥', '�`' => '獫', '�a' => '獪', '�b' => '瑿', '�c' => '璚', '�d' => '璠', '�e' => '璔', '�f' => '璒', '�g' => '璕', '�h' => '璡', '�i' => '甋', '�j' => '疀', '�k' => '瘯', '�l' => '瘭', '�m' => '瘱', '�n' => '瘽', '�o' => '瘳', '�p' => '瘼', '�q' => '瘵', '�r' => '瘲', '�s' => '瘰', '�t' => '皻', '�u' => '盦', '�v' => '瞚', '�w' => '瞝', '�x' => '瞡', '�y' => '瞜', '�z' => '瞛', '�{' => '瞢', '�|' => '瞣', '�}' => '瞕', '�~' => '瞙', '�' => '瞗', '�' => '磝', '�' => '磩', '�' => '磥', '�' => '磪', '�' => '磞', '�' => '磣', '�' => '磛', '�' => '磡', '�' => '磢', '�' => '磭', '�' => '磟', '�' => '磠', '�' => '禤', '�' => '穄', '�' => '穈', '�' => '穇', '�' => '窶', '�' => '窸', '�' => '窵', '�' => '窱', '�' => '窷', '�' => '篞', '�' => '篣', '�' => '篧', '�' => '篝', '�' => '篕', '�' => '篥', '�' => '篚', '�' => '篨', '�' => '篹', '�' => '篔', '�' => '篪', '��' => '篢', '��' => '篜', '��' => '篫', '��' => '篘', '��' => '篟', '��' => '糒', '��' => '糔', '��' => '糗', '��' => '糐', '��' => '糑', '��' => '縒', '��' => '縡', '��' => '縗', '��' => '縌', '��' => '縟', '��' => '縠', '��' => '縓', '��' => '縎', '��' => '縜', '��' => '縕', '��' => '縚', '��' => '縢', '��' => '縋', '��' => '縏', '��' => '縖', '��' => '縍', '��' => '縔', '��' => '縥', '��' => '縤', '��' => '罃', '��' => '罻', '��' => '罼', '��' => '罺', '��' => '羱', '��' => '翯', '��' => '耪', '��' => '耩', '��' => '聬', '��' => '膱', '��' => '膦', '��' => '膮', '��' => '膹', '��' => '膵', '��' => '膫', '��' => '膰', '��' => '膬', '��' => '膴', '��' => '膲', '��' => '膷', '��' => '膧', '��' => '臲', '�' => '艕', '�' => '艖', '�' => '艗', '�' => '蕖', '�' => '蕅', '�' => '蕫', '�' => '蕍', '�' => '蕓', '�' => '蕡', '�' => '蕘', '�@' => '蕀', '�A' => '蕆', '�B' => '蕤', '�C' => '蕁', '�D' => '蕢', '�E' => '蕄', '�F' => '蕑', '�G' => '蕇', '�H' => '蕣', '�I' => '蔾', '�J' => '蕛', '�K' => '蕱', '�L' => '蕎', '�M' => '蕮', '�N' => '蕵', '�O' => '蕕', '�P' => '蕧', '�Q' => '蕠', '�R' => '薌', '�S' => '蕦', '�T' => '蕝', '�U' => '蕔', '�V' => '蕥', '�W' => '蕬', '�X' => '虣', '�Y' => '虥', '�Z' => '虤', '�[' => '螛', '�\\' => '螏', '�]' => '螗', '�^' => '螓', '�_' => '螒', '�`' => '螈', '�a' => '螁', '�b' => '螖', '�c' => '螘', '�d' => '蝹', '�e' => '螇', '�f' => '螣', '�g' => '螅', '�h' => '螐', '�i' => '螑', '�j' => '螝', '�k' => '螄', '�l' => '螔', '�m' => '螜', '�n' => '螚', '�o' => '螉', '�p' => '褞', '�q' => '褦', '�r' => '褰', '�s' => '褭', '�t' => '褮', '�u' => '褧', '�v' => '褱', '�w' => '褢', '�x' => '褩', '�y' => '褣', '�z' => '褯', '�{' => '褬', '�|' => '褟', '�}' => '觱', '�~' => '諠', '�' => '諢', '�' => '諲', '�' => '諴', '�' => '諵', '�' => '諝', '�' => '謔', '�' => '諤', '�' => '諟', '�' => '諰', '�' => '諈', '�' => '諞', '�' => '諡', '�' => '諨', '�' => '諿', '�' => '諯', '�' => '諻', '�' => '貑', '�' => '貒', '�' => '貐', '�' => '賵', '�' => '賮', '�' => '賱', '�' => '賰', '�' => '賳', '�' => '赬', '�' => '赮', '�' => '趥', '�' => '趧', '�' => '踳', '�' => '踾', '�' => '踸', '�' => '蹀', '�' => '蹅', '��' => '踶', '��' => '踼', '��' => '踽', '��' => '蹁', '��' => '踰', '��' => '踿', '��' => '躽', '��' => '輶', '��' => '輮', '��' => '輵', '��' => '輲', '��' => '輹', '��' => '輷', '��' => '輴', '��' => '遶', '��' => '遹', '��' => '遻', '��' => '邆', '��' => '郺', '��' => '鄳', '��' => '鄵', '��' => '鄶', '��' => '醓', '��' => '醐', '��' => '醑', '��' => '醍', '��' => '醏', '��' => '錧', '��' => '錞', '��' => '錈', '��' => '錟', '��' => '錆', '��' => '錏', '��' => '鍺', '��' => '錸', '��' => '錼', '��' => '錛', '��' => '錣', '��' => '錒', '��' => '錁', '��' => '鍆', '��' => '錭', '��' => '錎', '��' => '錍', '��' => '鋋', '��' => '錝', '��' => '鋺', '��' => '錥', '��' => '錓', '��' => '鋹', '��' => '鋷', '�' => '錴', '�' => '錂', '�' => '錤', '�' => '鋿', '�' => '錩', '�' => '錹', '�' => '錵', '�' => '錪', '�' => '錔', '�' => '錌', '�@' => '錋', '�A' => '鋾', '�B' => '錉', '�C' => '錀', '�D' => '鋻', '�E' => '錖', '�F' => '閼', '�G' => '闍', '�H' => '閾', '�I' => '閹', '�J' => '閺', '�K' => '閶', '�L' => '閿', '�M' => '閵', '�N' => '閽', '�O' => '隩', '�P' => '雔', '�Q' => '霋', '�R' => '霒', '�S' => '霐', '�T' => '鞙', '�U' => '鞗', '�V' => '鞔', '�W' => '韰', '�X' => '韸', '�Y' => '頵', '�Z' => '頯', '�[' => '頲', '�\\' => '餤', '�]' => '餟', '�^' => '餧', '�_' => '餩', '�`' => '馞', '�a' => '駮', '�b' => '駬', '�c' => '駥', '�d' => '駤', '�e' => '駰', '�f' => '駣', '�g' => '駪', '�h' => '駩', '�i' => '駧', '�j' => '骹', '�k' => '骿', '�l' => '骴', '�m' => '骻', '�n' => '髶', '�o' => '髺', '�p' => '髹', '�q' => '髷', '�r' => '鬳', '�s' => '鮀', '�t' => '鮅', '�u' => '鮇', '�v' => '魼', '�w' => '魾', '�x' => '魻', '�y' => '鮂', '�z' => '鮓', '�{' => '鮒', '�|' => '鮐', '�}' => '魺', '�~' => '鮕', '�' => '魽', '�' => '鮈', '�' => '鴥', '�' => '鴗', '�' => '鴠', '�' => '鴞', '�' => '鴔', '�' => '鴩', '�' => '鴝', '�' => '鴘', '�' => '鴢', '�' => '鴐', '�' => '鴙', '�' => '鴟', '�' => '麈', '�' => '麆', '�' => '麇', '�' => '麮', '�' => '麭', '�' => '黕', '�' => '黖', '�' => '黺', '�' => '鼒', '�' => '鼽', '�' => '儦', '�' => '儥', '�' => '儢', '�' => '儤', '�' => '儠', '�' => '儩', '�' => '勴', '�' => '嚓', '�' => '嚌', '��' => '嚍', '��' => '嚆', '��' => '嚄', '��' => '嚃', '��' => '噾', '��' => '嚂', '��' => '噿', '��' => '嚁', '��' => '壖', '��' => '壔', '��' => '壏', '��' => '壒', '��' => '嬭', '��' => '嬥', '��' => '嬲', '��' => '嬣', '��' => '嬬', '��' => '嬧', '��' => '嬦', '��' => '嬯', '��' => '嬮', '��' => '孻', '��' => '寱', '��' => '寲', '��' => '嶷', '��' => '幬', '��' => '幪', '��' => '徾', '��' => '徻', '��' => '懃', '��' => '憵', '��' => '憼', '��' => '懧', '��' => '懠', '��' => '懥', '��' => '懤', '��' => '懨', '��' => '懞', '��' => '擯', '��' => '擩', '��' => '擣', '��' => '擫', '��' => '擤', '��' => '擨', '��' => '斁', '��' => '斀', '��' => '斶', '��' => '旚', '��' => '曒', '��' => '檍', '��' => '檖', '�' => '檁', '�' => '檥', '�' => '檉', '�' => '檟', '�' => '檛', '�' => '檡', '�' => '檞', '�' => '檇', '�' => '檓', '�' => '檎', '�@' => '檕', '�A' => '檃', '�B' => '檨', '�C' => '檤', '�D' => '檑', '�E' => '橿', '�F' => '檦', '�G' => '檚', '�H' => '檅', '�I' => '檌', '�J' => '檒', '�K' => '歛', '�L' => '殭', '�M' => '氉', '�N' => '濌', '�O' => '澩', '�P' => '濴', '�Q' => '濔', '�R' => '濣', '�S' => '濜', '�T' => '濭', '�U' => '濧', '�V' => '濦', '�W' => '濞', '�X' => '濲', '�Y' => '濝', '�Z' => '濢', '�[' => '濨', '�\\' => '燡', '�]' => '燱', '�^' => '燨', '�_' => '燲', '�`' => '燤', '�a' => '燰', '�b' => '燢', '�c' => '獳', '�d' => '獮', '�e' => '獯', '�f' => '璗', '�g' => '璲', '�h' => '璫', '�i' => '璐', '�j' => '璪', '�k' => '璭', '�l' => '璱', '�m' => '璥', '�n' => '璯', '�o' => '甐', '�p' => '甑', '�q' => '甒', '�r' => '甏', '�s' => '疄', '�t' => '癃', '�u' => '癈', '�v' => '癉', '�w' => '癇', '�x' => '皤', '�y' => '盩', '�z' => '瞵', '�{' => '瞫', '�|' => '瞲', '�}' => '瞷', '�~' => '瞶', '�' => '瞴', '�' => '瞱', '�' => '瞨', '�' => '矰', '�' => '磳', '�' => '磽', '�' => '礂', '�' => '磻', '�' => '磼', '�' => '磲', '�' => '礅', '�' => '磹', '�' => '磾', '�' => '礄', '�' => '禫', '�' => '禨', '�' => '穜', '�' => '穛', '�' => '穖', '�' => '穘', '�' => '穔', '�' => '穚', '�' => '窾', '�' => '竀', '�' => '竁', '�' => '簅', '�' => '簏', '�' => '篲', '�' => '簀', '�' => '篿', '�' => '篻', '�' => '簎', '�' => '篴', '��' => '簋', '��' => '篳', '��' => '簂', '��' => '簉', '��' => '簃', '��' => '簁', '��' => '篸', '��' => '篽', '��' => '簆', '��' => '篰', '��' => '篱', '��' => '簐', '��' => '簊', '��' => '糨', '��' => '縭', '��' => '縼', '��' => '繂', '��' => '縳', '��' => '顈', '��' => '縸', '��' => '縪', '��' => '繉', '��' => '繀', '��' => '繇', '��' => '縩', '��' => '繌', '��' => '縰', '��' => '縻', '��' => '縶', '��' => '繄', '��' => '縺', '��' => '罅', '��' => '罿', '��' => '罾', '��' => '罽', '��' => '翴', '��' => '翲', '��' => '耬', '��' => '膻', '��' => '臄', '��' => '臌', '��' => '臊', '��' => '臅', '��' => '臇', '��' => '膼', '��' => '臩', '��' => '艛', '��' => '艚', '��' => '艜', '��' => '薃', '��' => '薀', '�' => '薏', '�' => '薧', '�' => '薕', '�' => '薠', '�' => '薋', '�' => '薣', '�' => '蕻', '�' => '薤', '�' => '薚', '�' => '薞', '�@' => '蕷', '�A' => '蕼', '�B' => '薉', '�C' => '薡', '�D' => '蕺', '�E' => '蕸', '�F' => '蕗', '�G' => '薎', '�H' => '薖', '�I' => '薆', '�J' => '薍', '�K' => '薙', '�L' => '薝', '�M' => '薁', '�N' => '薢', '�O' => '薂', '�P' => '薈', '�Q' => '薅', '�R' => '蕹', '�S' => '蕶', '�T' => '薘', '�U' => '薐', '�V' => '薟', '�W' => '虨', '�X' => '螾', '�Y' => '螪', '�Z' => '螭', '�[' => '蟅', '�\\' => '螰', '�]' => '螬', '�^' => '螹', '�_' => '螵', '�`' => '螼', '�a' => '螮', '�b' => '蟉', '�c' => '蟃', '�d' => '蟂', '�e' => '蟌', '�f' => '螷', '�g' => '螯', '�h' => '蟄', '�i' => '蟊', '�j' => '螴', '�k' => '螶', '�l' => '螿', '�m' => '螸', '�n' => '螽', '�o' => '蟞', '�p' => '螲', '�q' => '褵', '�r' => '褳', '�s' => '褼', '�t' => '褾', '�u' => '襁', '�v' => '襒', '�w' => '褷', '�x' => '襂', '�y' => '覭', '�z' => '覯', '�{' => '覮', '�|' => '觲', '�}' => '觳', '�~' => '謞', '�' => '謘', '�' => '謖', '�' => '謑', '�' => '謅', '�' => '謋', '�' => '謢', '�' => '謏', '�' => '謒', '�' => '謕', '�' => '謇', '�' => '謍', '�' => '謈', '�' => '謆', '�' => '謜', '�' => '謓', '�' => '謚', '�' => '豏', '�' => '豰', '�' => '豲', '�' => '豱', '�' => '豯', '�' => '貕', '�' => '貔', '�' => '賹', '�' => '赯', '�' => '蹎', '�' => '蹍', '�' => '蹓', '�' => '蹐', '�' => '蹌', '�' => '蹇', '�' => '轃', '�' => '轀', '��' => '邅', '��' => '遾', '��' => '鄸', '��' => '醚', '��' => '醢', '��' => '醛', '��' => '醙', '��' => '醟', '��' => '醡', '��' => '醝', '��' => '醠', '��' => '鎡', '��' => '鎃', '��' => '鎯', '��' => '鍤', '��' => '鍖', '��' => '鍇', '��' => '鍼', '��' => '鍘', '��' => '鍜', '��' => '鍶', '��' => '鍉', '��' => '鍐', '��' => '鍑', '��' => '鍠', '��' => '鍭', '��' => '鎏', '��' => '鍌', '��' => '鍪', '��' => '鍹', '��' => '鍗', '��' => '鍕', '��' => '鍒', '��' => '鍏', '��' => '鍱', '��' => '鍷', '��' => '鍻', '��' => '鍡', '��' => '鍞', '��' => '鍣', '��' => '鍧', '��' => '鎀', '��' => '鍎', '��' => '鍙', '��' => '闇', '��' => '闀', '��' => '闉', '��' => '闃', '��' => '闅', '��' => '閷', '��' => '隮', '�' => '隰', '�' => '隬', '�' => '霠', '�' => '霟', '�' => '霘', '�' => '霝', '�' => '霙', '�' => '鞚', '�' => '鞡', '�' => '鞜', '�@' => '鞞', '�A' => '鞝', '�B' => '韕', '�C' => '韔', '�D' => '韱', '�E' => '顁', '�F' => '顄', '�G' => '顊', '�H' => '顉', '�I' => '顅', '�J' => '顃', '�K' => '餥', '�L' => '餫', '�M' => '餬', '�N' => '餪', '�O' => '餳', '�P' => '餲', '�Q' => '餯', '�R' => '餭', '�S' => '餱', '�T' => '餰', '�U' => '馘', '�V' => '馣', '�W' => '馡', '�X' => '騂', '�Y' => '駺', '�Z' => '駴', '�[' => '駷', '�\\' => '駹', '�]' => '駸', '�^' => '駶', '�_' => '駻', '�`' => '駽', '�a' => '駾', '�b' => '駼', '�c' => '騃', '�d' => '骾', '�e' => '髾', '�f' => '髽', '�g' => '鬁', '�h' => '髼', '�i' => '魈', '�j' => '鮚', '�k' => '鮨', '�l' => '鮞', '�m' => '鮛', '�n' => '鮦', '�o' => '鮡', '�p' => '鮥', '�q' => '鮤', '�r' => '鮆', '�s' => '鮢', '�t' => '鮠', '�u' => '鮯', '�v' => '鴳', '�w' => '鵁', '�x' => '鵧', '�y' => '鴶', '�z' => '鴮', '�{' => '鴯', '�|' => '鴱', '�}' => '鴸', '�~' => '鴰', '�' => '鵅', '�' => '鵂', '�' => '鵃', '�' => '鴾', '�' => '鴷', '�' => '鵀', '�' => '鴽', '�' => '翵', '�' => '鴭', '�' => '麊', '�' => '麉', '�' => '麍', '�' => '麰', '�' => '黈', '�' => '黚', '�' => '黻', '�' => '黿', '�' => '鼤', '�' => '鼣', '�' => '鼢', '�' => '齔', '�' => '龠', '�' => '儱', '�' => '儭', '�' => '儮', '�' => '嚘', '�' => '嚜', '�' => '嚗', '�' => '嚚', '�' => '嚝', '�' => '嚙', '�' => '奰', '�' => '嬼', '��' => '屩', '��' => '屪', '��' => '巀', '��' => '幭', '��' => '幮', '��' => '懘', '��' => '懟', '��' => '懭', '��' => '懮', '��' => '懱', '��' => '懪', '��' => '懰', '��' => '懫', '��' => '懖', '��' => '懩', '��' => '擿', '��' => '攄', '��' => '擽', '��' => '擸', '��' => '攁', '��' => '攃', '��' => '擼', '��' => '斔', '��' => '旛', '��' => '曚', '��' => '曛', '��' => '曘', '��' => '櫅', '��' => '檹', '��' => '檽', '��' => '櫡', '��' => '櫆', '��' => '檺', '��' => '檶', '��' => '檷', '��' => '櫇', '��' => '檴', '��' => '檭', '��' => '歞', '��' => '毉', '��' => '氋', '��' => '瀇', '��' => '瀌', '��' => '瀍', '��' => '瀁', '��' => '瀅', '��' => '瀔', '��' => '瀎', '��' => '濿', '��' => '瀀', '��' => '濻', '�' => '瀦', '�' => '濼', '�' => '濷', '�' => '瀊', '�' => '爁', '�' => '燿', '�' => '燹', '�' => '爃', '�' => '燽', '�' => '獶', '�@' => '璸', '�A' => '瓀', '�B' => '璵', '�C' => '瓁', '�D' => '璾', '�E' => '璶', '�F' => '璻', '�G' => '瓂', '�H' => '甔', '�I' => '甓', '�J' => '癜', '�K' => '癤', '�L' => '癙', '�M' => '癐', '�N' => '癓', '�O' => '癗', '�P' => '癚', '�Q' => '皦', '�R' => '皽', '�S' => '盬', '�T' => '矂', '�U' => '瞺', '�V' => '磿', '�W' => '礌', '�X' => '礓', '�Y' => '礔', '�Z' => '礉', '�[' => '礐', '�\\' => '礒', '�]' => '礑', '�^' => '禭', '�_' => '禬', '�`' => '穟', '�a' => '簜', '�b' => '簩', '�c' => '簙', '�d' => '簠', '�e' => '簟', '�f' => '簭', '�g' => '簝', '�h' => '簦', '�i' => '簨', '�j' => '簢', '�k' => '簥', '�l' => '簰', '�m' => '繜', '�n' => '繐', '�o' => '繖', '�p' => '繣', '�q' => '繘', '�r' => '繢', '�s' => '繟', '�t' => '繑', '�u' => '繠', '�v' => '繗', '�w' => '繓', '�x' => '羵', '�y' => '羳', '�z' => '翷', '�{' => '翸', '�|' => '聵', '�}' => '臑', '�~' => '臒', '�' => '臐', '�' => '艟', '�' => '艞', '�' => '薴', '�' => '藆', '�' => '藀', '�' => '藃', '�' => '藂', '�' => '薳', '�' => '薵', '�' => '薽', '�' => '藇', '�' => '藄', '�' => '薿', '�' => '藋', '�' => '藎', '�' => '藈', '�' => '藅', '�' => '薱', '�' => '薶', '�' => '藒', '�' => '蘤', '�' => '薸', '�' => '薷', '�' => '薾', '�' => '虩', '�' => '蟧', '�' => '蟦', '�' => '蟢', '�' => '蟛', '�' => '蟫', '�' => '蟪', '�' => '蟥', '��' => '蟟', '��' => '蟳', '��' => '蟤', '��' => '蟔', '��' => '蟜', '��' => '蟓', '��' => '蟭', '��' => '蟘', '��' => '蟣', '��' => '螤', '��' => '蟗', '��' => '蟙', '��' => '蠁', '��' => '蟴', '��' => '蟨', '��' => '蟝', '��' => '襓', '��' => '襋', '��' => '襏', '��' => '襌', '��' => '襆', '��' => '襐', '��' => '襑', '��' => '襉', '��' => '謪', '��' => '謧', '��' => '謣', '��' => '謳', '��' => '謰', '��' => '謵', '��' => '譇', '��' => '謯', '��' => '謼', '��' => '謾', '��' => '謱', '��' => '謥', '��' => '謷', '��' => '謦', '��' => '謶', '��' => '謮', '��' => '謤', '��' => '謻', '��' => '謽', '��' => '謺', '��' => '豂', '��' => '豵', '��' => '貙', '��' => '貘', '��' => '貗', '��' => '賾', '��' => '贄', '�' => '贂', '�' => '贀', '�' => '蹜', '�' => '蹢', '�' => '蹠', '�' => '蹗', '�' => '蹖', '�' => '蹞', '�' => '蹥', '�' => '蹧', '�@' => '蹛', '�A' => '蹚', '�B' => '蹡', '�C' => '蹝', '�D' => '蹩', '�E' => '蹔', '�F' => '轆', '�G' => '轇', '�H' => '轈', '�I' => '轋', '�J' => '鄨', '�K' => '鄺', '�L' => '鄻', '�M' => '鄾', '�N' => '醨', '�O' => '醥', '�P' => '醧', '�Q' => '醯', '�R' => '醪', '�S' => '鎵', '�T' => '鎌', '�U' => '鎒', '�V' => '鎷', '�W' => '鎛', '�X' => '鎝', '�Y' => '鎉', '�Z' => '鎧', '�[' => '鎎', '�\\' => '鎪', '�]' => '鎞', '�^' => '鎦', '�_' => '鎕', '�`' => '鎈', '�a' => '鎙', '�b' => '鎟', '�c' => '鎍', '�d' => '鎱', '�e' => '鎑', '�f' => '鎲', '�g' => '鎤', '�h' => '鎨', '�i' => '鎴', '�j' => '鎣', '�k' => '鎥', '�l' => '闒', '�m' => '闓', '�n' => '闑', '�o' => '隳', '�p' => '雗', '�q' => '雚', '�r' => '巂', '�s' => '雟', '�t' => '雘', '�u' => '雝', '�v' => '霣', '�w' => '霢', '�x' => '霥', '�y' => '鞬', '�z' => '鞮', '�{' => '鞨', '�|' => '鞫', '�}' => '鞤', '�~' => '鞪', '�' => '鞢', '�' => '鞥', '�' => '韗', '�' => '韙', '�' => '韖', '�' => '韘', '�' => '韺', '�' => '顐', '�' => '顑', '�' => '顒', '�' => '颸', '�' => '饁', '�' => '餼', '�' => '餺', '�' => '騏', '�' => '騋', '�' => '騉', '�' => '騍', '�' => '騄', '�' => '騑', '�' => '騊', '�' => '騅', '�' => '騇', '�' => '騆', '�' => '髀', '�' => '髜', '�' => '鬈', '�' => '鬄', '�' => '鬅', '�' => '鬩', '�' => '鬵', '�' => '魊', '�' => '魌', '��' => '魋', '��' => '鯇', '��' => '鯆', '��' => '鯃', '��' => '鮿', '��' => '鯁', '��' => '鮵', '��' => '鮸', '��' => '鯓', '��' => '鮶', '��' => '鯄', '��' => '鮹', '��' => '鮽', '��' => '鵜', '��' => '鵓', '��' => '鵏', '��' => '鵊', '��' => '鵛', '��' => '鵋', '��' => '鵙', '��' => '鵖', '��' => '鵌', '��' => '鵗', '��' => '鵒', '��' => '鵔', '��' => '鵟', '��' => '鵘', '��' => '鵚', '��' => '麎', '��' => '麌', '��' => '黟', '��' => '鼁', '��' => '鼀', '��' => '鼖', '��' => '鼥', '��' => '鼫', '��' => '鼪', '��' => '鼩', '��' => '鼨', '��' => '齌', '��' => '齕', '��' => '儴', '��' => '儵', '��' => '劖', '��' => '勷', '��' => '厴', '��' => '嚫', '��' => '嚭', '��' => '嚦', '��' => '嚧', '��' => '嚪', '�' => '嚬', '�' => '壚', '�' => '壝', '�' => '壛', '�' => '夒', '�' => '嬽', '�' => '嬾', '�' => '嬿', '�' => '巃', '�' => '幰', '�@' => '徿', '�A' => '懻', '�B' => '攇', '�C' => '攐', '�D' => '攍', '�E' => '攉', '�F' => '攌', '�G' => '攎', '�H' => '斄', '�I' => '旞', '�J' => '旝', '�K' => '曞', '�L' => '櫧', '�M' => '櫠', '�N' => '櫌', '�O' => '櫑', '�P' => '櫙', '�Q' => '櫋', '�R' => '櫟', '�S' => '櫜', '�T' => '櫐', '�U' => '櫫', '�V' => '櫏', '�W' => '櫍', '�X' => '櫞', '�Y' => '歠', '�Z' => '殰', '�[' => '氌', '�\\' => '瀙', '�]' => '瀧', '�^' => '瀠', '�_' => '瀖', '�`' => '瀫', '�a' => '瀡', '�b' => '瀢', '�c' => '瀣', '�d' => '瀩', '�e' => '瀗', '�f' => '瀤', '�g' => '瀜', '�h' => '瀪', '�i' => '爌', '�j' => '爊', '�k' => '爇', '�l' => '爂', '�m' => '爅', '�n' => '犥', '�o' => '犦', '�p' => '犤', '�q' => '犣', '�r' => '犡', '�s' => '瓋', '�t' => '瓅', '�u' => '璷', '�v' => '瓃', '�w' => '甖', '�x' => '癠', '�y' => '矉', '�z' => '矊', '�{' => '矄', '�|' => '矱', '�}' => '礝', '�~' => '礛', '�' => '礡', '�' => '礜', '�' => '礗', '�' => '礞', '�' => '禰', '�' => '穧', '�' => '穨', '�' => '簳', '�' => '簼', '�' => '簹', '�' => '簬', '�' => '簻', '�' => '糬', '�' => '糪', '�' => '繶', '�' => '繵', '�' => '繸', '�' => '繰', '�' => '繷', '�' => '繯', '�' => '繺', '�' => '繲', '�' => '繴', '�' => '繨', '�' => '罋', '�' => '罊', '�' => '羃', '�' => '羆', '�' => '羷', '�' => '翽', '�' => '翾', '�' => '聸', '�' => '臗', '��' => '臕', '��' => '艤', '��' => '艡', '��' => '艣', '��' => '藫', '��' => '藱', '��' => '藭', '��' => '藙', '��' => '藡', '��' => '藨', '��' => '藚', '��' => '藗', '��' => '藬', '��' => '藲', '��' => '藸', '��' => '藘', '��' => '藟', '��' => '藣', '��' => '藜', '��' => '藑', '��' => '藰', '��' => '藦', '��' => '藯', '��' => '藞', '��' => '藢', '��' => '蠀', '��' => '蟺', '��' => '蠃', '��' => '蟶', '��' => '蟷', '��' => '蠉', '��' => '蠌', '��' => '蠋', '��' => '蠆', '��' => '蟼', '��' => '蠈', '��' => '蟿', '��' => '蠊', '��' => '蠂', '��' => '襢', '��' => '襚', '��' => '襛', '��' => '襗', '��' => '襡', '��' => '襜', '��' => '襘', '��' => '襝', '��' => '襙', '��' => '覈', '��' => '覷', '��' => '覶', '�' => '觶', '�' => '譐', '�' => '譈', '�' => '譊', '�' => '譀', '�' => '譓', '�' => '譖', '�' => '譔', '�' => '譋', '�' => '譕', '�@' => '譑', '�A' => '譂', '�B' => '譒', '�C' => '譗', '�D' => '豃', '�E' => '豷', '�F' => '豶', '�G' => '貚', '�H' => '贆', '�I' => '贇', '�J' => '贉', '�K' => '趬', '�L' => '趪', '�M' => '趭', '�N' => '趫', '�O' => '蹭', '�P' => '蹸', '�Q' => '蹳', '�R' => '蹪', '�S' => '蹯', '�T' => '蹻', '�U' => '軂', '�V' => '轒', '�W' => '轑', '�X' => '轏', '�Y' => '轐', '�Z' => '轓', '�[' => '辴', '�\\' => '酀', '�]' => '鄿', '�^' => '醰', '�_' => '醭', '�`' => '鏞', '�a' => '鏇', '�b' => '鏏', '�c' => '鏂', '�d' => '鏚', '�e' => '鏐', '�f' => '鏹', '�g' => '鏬', '�h' => '鏌', '�i' => '鏙', '�j' => '鎩', '�k' => '鏦', '�l' => '鏊', '�m' => '鏔', '�n' => '鏮', '�o' => '鏣', '�p' => '鏕', '�q' => '鏄', '�r' => '鏎', '�s' => '鏀', '�t' => '鏒', '�u' => '鏧', '�v' => '镽', '�w' => '闚', '�x' => '闛', '�y' => '雡', '�z' => '霩', '�{' => '霫', '�|' => '霬', '�}' => '霨', '�~' => '霦', '�' => '鞳', '�' => '鞷', '�' => '鞶', '�' => '韝', '�' => '韞', '�' => '韟', '�' => '顜', '�' => '顙', '�' => '顝', '�' => '顗', '�' => '颿', '�' => '颽', '�' => '颻', '�' => '颾', '�' => '饈', '�' => '饇', '�' => '饃', '�' => '馦', '�' => '馧', '�' => '騚', '�' => '騕', '�' => '騥', '�' => '騝', '�' => '騤', '�' => '騛', '�' => '騢', '�' => '騠', '�' => '騧', '�' => '騣', '�' => '騞', '�' => '騜', '�' => '騔', '�' => '髂', '��' => '鬋', '��' => '鬊', '��' => '鬎', '��' => '鬌', '��' => '鬷', '��' => '鯪', '��' => '鯫', '��' => '鯠', '��' => '鯞', '��' => '鯤', '��' => '鯦', '��' => '鯢', '��' => '鯰', '��' => '鯔', '��' => '鯗', '��' => '鯬', '��' => '鯜', '��' => '鯙', '��' => '鯥', '��' => '鯕', '��' => '鯡', '��' => '鯚', '��' => '鵷', '��' => '鶁', '��' => '鶊', '��' => '鶄', '��' => '鶈', '��' => '鵱', '��' => '鶀', '��' => '鵸', '��' => '鶆', '��' => '鶋', '��' => '鶌', '��' => '鵽', '��' => '鵫', '��' => '鵴', '��' => '鵵', '��' => '鵰', '��' => '鵩', '��' => '鶅', '��' => '鵳', '��' => '鵻', '��' => '鶂', '��' => '鵯', '��' => '鵹', '��' => '鵿', '��' => '鶇', '��' => '鵨', '��' => '麔', '��' => '麑', '��' => '黀', '�' => '黼', '�' => '鼭', '�' => '齀', '�' => '齁', '�' => '齍', '�' => '齖', '�' => '齗', '�' => '齘', '�' => '匷', '�' => '嚲', '�@' => '嚵', '�A' => '嚳', '�B' => '壣', '�C' => '孅', '�D' => '巆', '�E' => '巇', '�F' => '廮', '�G' => '廯', '�H' => '忀', '�I' => '忁', '�J' => '懹', '�K' => '攗', '�L' => '攖', '�M' => '攕', '�N' => '攓', '�O' => '旟', '�P' => '曨', '�Q' => '曣', '�R' => '曤', '�S' => '櫳', '�T' => '櫰', '�U' => '櫪', '�V' => '櫨', '�W' => '櫹', '�X' => '櫱', '�Y' => '櫮', '�Z' => '櫯', '�[' => '瀼', '�\\' => '瀵', '�]' => '瀯', '�^' => '瀷', '�_' => '瀴', '�`' => '瀱', '�a' => '灂', '�b' => '瀸', '�c' => '瀿', '�d' => '瀺', '�e' => '瀹', '�f' => '灀', '�g' => '瀻', '�h' => '瀳', '�i' => '灁', '�j' => '爓', '�k' => '爔', '�l' => '犨', '�m' => '獽', '�n' => '獼', '�o' => '璺', '�p' => '皫', '�q' => '皪', '�r' => '皾', '�s' => '盭', '�t' => '矌', '�u' => '矎', '�v' => '矏', '�w' => '矍', '�x' => '矲', '�y' => '礥', '�z' => '礣', '�{' => '礧', '�|' => '礨', '�}' => '礤', '�~' => '礩', '�' => '禲', '�' => '穮', '�' => '穬', '�' => '穭', '�' => '竷', '�' => '籉', '�' => '籈', '�' => '籊', '�' => '籇', '�' => '籅', '�' => '糮', '�' => '繻', '�' => '繾', '�' => '纁', '�' => '纀', '�' => '羺', '�' => '翿', '�' => '聹', '�' => '臛', '�' => '臙', '�' => '舋', '�' => '艨', '�' => '艩', '�' => '蘢', '�' => '藿', '�' => '蘁', '�' => '藾', '�' => '蘛', '�' => '蘀', '�' => '藶', '�' => '蘄', '�' => '蘉', '�' => '蘅', '��' => '蘌', '��' => '藽', '��' => '蠙', '��' => '蠐', '��' => '蠑', '��' => '蠗', '��' => '蠓', '��' => '蠖', '��' => '襣', '��' => '襦', '��' => '覹', '��' => '觷', '��' => '譠', '��' => '譪', '��' => '譝', '��' => '譨', '��' => '譣', '��' => '譥', '��' => '譧', '��' => '譭', '��' => '趮', '��' => '躆', '��' => '躈', '��' => '躄', '��' => '轙', '��' => '轖', '��' => '轗', '��' => '轕', '��' => '轘', '��' => '轚', '��' => '邍', '��' => '酃', '��' => '酁', '��' => '醷', '��' => '醵', '��' => '醲', '��' => '醳', '��' => '鐋', '��' => '鐓', '��' => '鏻', '��' => '鐠', '��' => '鐏', '��' => '鐔', '��' => '鏾', '��' => '鐕', '��' => '鐐', '��' => '鐨', '��' => '鐙', '��' => '鐍', '��' => '鏵', '��' => '鐀', '�' => '鏷', '�' => '鐇', '�' => '鐎', '�' => '鐖', '�' => '鐒', '�' => '鏺', '�' => '鐉', '�' => '鏸', '�' => '鐊', '�' => '鏿', '�@' => '鏼', '�A' => '鐌', '�B' => '鏶', '�C' => '鐑', '�D' => '鐆', '�E' => '闞', '�F' => '闠', '�G' => '闟', '�H' => '霮', '�I' => '霯', '�J' => '鞹', '�K' => '鞻', '�L' => '韽', '�M' => '韾', '�N' => '顠', '�O' => '顢', '�P' => '顣', '�Q' => '顟', '�R' => '飁', '�S' => '飂', '�T' => '饐', '�U' => '饎', '�V' => '饙', '�W' => '饌', '�X' => '饋', '�Y' => '饓', '�Z' => '騲', '�[' => '騴', '�\\' => '騱', '�]' => '騬', '�^' => '騪', '�_' => '騶', '�`' => '騩', '�a' => '騮', '�b' => '騸', '�c' => '騭', '�d' => '髇', '�e' => '髊', '�f' => '髆', '�g' => '鬐', '�h' => '鬒', '�i' => '鬑', '�j' => '鰋', '�k' => '鰈', '�l' => '鯷', '�m' => '鰅', '�n' => '鰒', '�o' => '鯸', '�p' => '鱀', '�q' => '鰇', '�r' => '鰎', '�s' => '鰆', '�t' => '鰗', '�u' => '鰔', '�v' => '鰉', '�w' => '鶟', '�x' => '鶙', '�y' => '鶤', '�z' => '鶝', '�{' => '鶒', '�|' => '鶘', '�}' => '鶐', '�~' => '鶛', '��' => '鶠', '��' => '鶔', '��' => '鶜', '��' => '鶪', '��' => '鶗', '��' => '鶡', '��' => '鶚', '��' => '鶢', '��' => '鶨', '��' => '鶞', '��' => '鶣', '��' => '鶿', '��' => '鶩', '��' => '鶖', '��' => '鶦', '��' => '鶧', '��' => '麙', '��' => '麛', '��' => '麚', '��' => '黥', '��' => '黤', '��' => '黧', '��' => '黦', '��' => '鼰', '��' => '鼮', '��' => '齛', '��' => '齠', '��' => '齞', '��' => '齝', '��' => '齙', '��' => '龑', '��' => '儺', '��' => '儹', '��' => '劘', '��' => '劗', '��' => '囃', '��' => '嚽', '��' => '嚾', '��' => '孈', '��' => '孇', '��' => '巋', '��' => '巏', '��' => '廱', '��' => '懽', '��' => '攛', '��' => '欂', '��' => '櫼', '��' => '欃', '��' => '櫸', '��' => '欀', '��' => '灃', '��' => '灄', '��' => '灊', '��' => '灈', '��' => '灉', '��' => '灅', '��' => '灆', '��' => '爝', '��' => '爚', '��' => '爙', '��' => '獾', '��' => '甗', '��' => '癪', '��' => '矐', '��' => '礭', '��' => '礱', '��' => '礯', '��' => '籔', '��' => '籓', '��' => '糲', '��' => '纊', '��' => '纇', '��' => '纈', '��' => '纋', '��' => '纆', '��' => '纍', '��' => '罍', '��' => '羻', '��' => '耰', '��' => '臝', '��' => '蘘', '��' => '蘪', '��' => '蘦', '��' => '蘟', '��' => '蘣', '��' => '蘜', '��' => '蘙', '��' => '蘧', '��' => '蘮', '��' => '蘡', '��' => '蘠', '��' => '蘩', '��' => '蘞', '��' => '蘥', '�@' => '蠩', '�A' => '蠝', '�B' => '蠛', '�C' => '蠠', '�D' => '蠤', '�E' => '蠜', '�F' => '蠫', '�G' => '衊', '�H' => '襭', '�I' => '襩', '�J' => '襮', '�K' => '襫', '�L' => '觺', '�M' => '譹', '�N' => '譸', '�O' => '譅', '�P' => '譺', '�Q' => '譻', '�R' => '贐', '�S' => '贔', '�T' => '趯', '�U' => '躎', '�V' => '躌', '�W' => '轞', '�X' => '轛', '�Y' => '轝', '�Z' => '酆', '�[' => '酄', '�\\' => '酅', '�]' => '醹', '�^' => '鐿', '�_' => '鐻', '�`' => '鐶', '�a' => '鐩', '�b' => '鐽', '�c' => '鐼', '�d' => '鐰', '�e' => '鐹', '�f' => '鐪', '�g' => '鐷', '�h' => '鐬', '�i' => '鑀', '�j' => '鐱', '�k' => '闥', '�l' => '闤', '�m' => '闣', '�n' => '霵', '�o' => '霺', '�p' => '鞿', '�q' => '韡', '�r' => '顤', '�s' => '飉', '�t' => '飆', '�u' => '飀', '�v' => '饘', '�w' => '饖', '�x' => '騹', '�y' => '騽', '�z' => '驆', '�{' => '驄', '�|' => '驂', '�}' => '驁', '�~' => '騺', '��' => '騿', '��' => '髍', '��' => '鬕', '��' => '鬗', '��' => '鬘', '��' => '鬖', '��' => '鬺', '��' => '魒', '��' => '鰫', '��' => '鰝', '��' => '鰜', '��' => '鰬', '��' => '鰣', '��' => '鰨', '��' => '鰩', '��' => '鰤', '��' => '鰡', '��' => '鶷', '��' => '鶶', '��' => '鶼', '��' => '鷁', '��' => '鷇', '��' => '鷊', '��' => '鷏', '��' => '鶾', '��' => '鷅', '��' => '鷃', '��' => '鶻', '��' => '鶵', '��' => '鷎', '��' => '鶹', '��' => '鶺', '��' => '鶬', '��' => '鷈', '��' => '鶱', '��' => '鶭', '��' => '鷌', '��' => '鶳', '��' => '鷍', '��' => '鶲', '��' => '鹺', '��' => '麜', '��' => '黫', '��' => '黮', '��' => '黭', '��' => '鼛', '��' => '鼘', '��' => '鼚', '��' => '鼱', '��' => '齎', '��' => '齥', '��' => '齤', '��' => '龒', '��' => '亹', '��' => '囆', '��' => '囅', '��' => '囋', '��' => '奱', '��' => '孋', '��' => '孌', '��' => '巕', '��' => '巑', '��' => '廲', '��' => '攡', '��' => '攠', '��' => '攦', '��' => '攢', '��' => '欋', '��' => '欈', '��' => '欉', '��' => '氍', '��' => '灕', '��' => '灖', '��' => '灗', '��' => '灒', '��' => '爞', '��' => '爟', '��' => '犩', '��' => '獿', '��' => '瓘', '��' => '瓕', '��' => '瓙', '��' => '瓗', '��' => '癭', '��' => '皭', '��' => '礵', '��' => '禴', '��' => '穰', '��' => '穱', '��' => '籗', '��' => '籜', '��' => '籙', '��' => '籛', '��' => '籚', '�@' => '糴', '�A' => '糱', '�B' => '纑', '�C' => '罏', '�D' => '羇', '�E' => '臞', '�F' => '艫', '�G' => '蘴', '�H' => '蘵', '�I' => '蘳', '�J' => '蘬', '�K' => '蘲', '�L' => '蘶', '�M' => '蠬', '�N' => '蠨', '�O' => '蠦', '�P' => '蠪', '�Q' => '蠥', '�R' => '襱', '�S' => '覿', '�T' => '覾', '�U' => '觻', '�V' => '譾', '�W' => '讄', '�X' => '讂', '�Y' => '讆', '�Z' => '讅', '�[' => '譿', '�\\' => '贕', '�]' => '躕', '�^' => '躔', '�_' => '躚', '�`' => '躒', '�a' => '躐', '�b' => '躖', '�c' => '躗', '�d' => '轠', '�e' => '轢', '�f' => '酇', '�g' => '鑌', '�h' => '鑐', '�i' => '鑊', '�j' => '鑋', '�k' => '鑏', '�l' => '鑇', '�m' => '鑅', '�n' => '鑈', '�o' => '鑉', '�p' => '鑆', '�q' => '霿', '�r' => '韣', '�s' => '顪', '�t' => '顩', '�u' => '飋', '�v' => '饔', '�w' => '饛', '�x' => '驎', '�y' => '驓', '�z' => '驔', '�{' => '驌', '�|' => '驏', '�}' => '驈', '�~' => '驊', '��' => '驉', '��' => '驒', '��' => '驐', '��' => '髐', '��' => '鬙', '��' => '鬫', '��' => '鬻', '��' => '魖', '��' => '魕', '��' => '鱆', '��' => '鱈', '��' => '鰿', '��' => '鱄', '��' => '鰹', '��' => '鰳', '��' => '鱁', '��' => '鰼', '��' => '鰷', '��' => '鰴', '��' => '鰲', '��' => '鰽', '��' => '鰶', '��' => '鷛', '��' => '鷒', '��' => '鷞', '��' => '鷚', '��' => '鷋', '��' => '鷐', '��' => '鷜', '��' => '鷑', '��' => '鷟', '��' => '鷩', '��' => '鷙', '��' => '鷘', '��' => '鷖', '��' => '鷵', '��' => '鷕', '��' => '鷝', '��' => '麶', '��' => '黰', '��' => '鼵', '��' => '鼳', '��' => '鼲', '��' => '齂', '��' => '齫', '��' => '龕', '��' => '龢', '��' => '儽', '��' => '劙', '��' => '壨', '��' => '壧', '��' => '奲', '��' => '孍', '��' => '巘', '��' => '蠯', '��' => '彏', '��' => '戁', '��' => '戃', '��' => '戄', '��' => '攩', '��' => '攥', '��' => '斖', '��' => '曫', '��' => '欑', '��' => '欒', '��' => '欏', '��' => '毊', '��' => '灛', '��' => '灚', '��' => '爢', '��' => '玂', '��' => '玁', '��' => '玃', '��' => '癰', '��' => '矔', '��' => '籧', '��' => '籦', '��' => '纕', '��' => '艬', '��' => '蘺', '��' => '虀', '��' => '蘹', '��' => '蘼', '��' => '蘱', '��' => '蘻', '��' => '蘾', '��' => '蠰', '��' => '蠲', '��' => '蠮', '��' => '蠳', '��' => '襶', '��' => '襴', '��' => '襳', '��' => '觾', '�@' => '讌', '�A' => '讎', '�B' => '讋', '�C' => '讈', '�D' => '豅', '�E' => '贙', '�F' => '躘', '�G' => '轤', '�H' => '轣', '�I' => '醼', '�J' => '鑢', '�K' => '鑕', '�L' => '鑝', '�M' => '鑗', '�N' => '鑞', '�O' => '韄', '�P' => '韅', '�Q' => '頀', '�R' => '驖', '�S' => '驙', '�T' => '鬞', '�U' => '鬟', '�V' => '鬠', '�W' => '鱒', '�X' => '鱘', '�Y' => '鱐', '�Z' => '鱊', '�[' => '鱍', '�\\' => '鱋', '�]' => '鱕', '�^' => '鱙', '�_' => '鱌', '�`' => '鱎', '�a' => '鷻', '�b' => '鷷', '�c' => '鷯', '�d' => '鷣', '�e' => '鷫', '�f' => '鷸', '�g' => '鷤', '�h' => '鷶', '�i' => '鷡', '�j' => '鷮', '�k' => '鷦', '�l' => '鷲', '�m' => '鷰', '�n' => '鷢', '�o' => '鷬', '�p' => '鷴', '�q' => '鷳', '�r' => '鷨', '�s' => '鷭', '�t' => '黂', '�u' => '黐', '�v' => '黲', '�w' => '黳', '�x' => '鼆', '�y' => '鼜', '�z' => '鼸', '�{' => '鼷', '�|' => '鼶', '�}' => '齃', '�~' => '齏', '��' => '齱', '��' => '齰', '��' => '齮', '��' => '齯', '��' => '囓', '��' => '囍', '��' => '孎', '��' => '屭', '��' => '攭', '��' => '曭', '��' => '曮', '��' => '欓', '��' => '灟', '��' => '灡', '��' => '灝', '��' => '灠', '��' => '爣', '��' => '瓛', '��' => '瓥', '��' => '矕', '��' => '礸', '��' => '禷', '��' => '禶', '��' => '籪', '��' => '纗', '��' => '羉', '��' => '艭', '��' => '虃', '��' => '蠸', '��' => '蠷', '��' => '蠵', '��' => '衋', '��' => '讔', '��' => '讕', '��' => '躞', '��' => '躟', '��' => '躠', '��' => '躝', '��' => '醾', '��' => '醽', '��' => '釂', '��' => '鑫', '��' => '鑨', '��' => '鑩', '��' => '雥', '��' => '靆', '��' => '靃', '��' => '靇', '��' => '韇', '��' => '韥', '��' => '驞', '��' => '髕', '��' => '魙', '��' => '鱣', '��' => '鱧', '��' => '鱦', '��' => '鱢', '��' => '鱞', '��' => '鱠', '��' => '鸂', '��' => '鷾', '��' => '鸇', '��' => '鸃', '��' => '鸆', '��' => '鸅', '��' => '鸀', '��' => '鸁', '��' => '鸉', '��' => '鷿', '��' => '鷽', '��' => '鸄', '��' => '麠', '��' => '鼞', '��' => '齆', '��' => '齴', '��' => '齵', '��' => '齶', '��' => '囔', '��' => '攮', '��' => '斸', '��' => '欘', '��' => '欙', '��' => '欗', '��' => '欚', '��' => '灢', '��' => '爦', '��' => '犪', '��' => '矘', '��' => '矙', '��' => '礹', '��' => '籩', '��' => '籫', '��' => '糶', '��' => '纚', '�@' => '纘', '�A' => '纛', '�B' => '纙', '�C' => '臠', '�D' => '臡', '�E' => '虆', '�F' => '虇', '�G' => '虈', '�H' => '襹', '�I' => '襺', '�J' => '襼', '�K' => '襻', '�L' => '觿', '�M' => '讘', '�N' => '讙', '�O' => '躥', '�P' => '躤', '�Q' => '躣', '�R' => '鑮', '�S' => '鑭', '�T' => '鑯', '�U' => '鑱', '�V' => '鑳', '�W' => '靉', '�X' => '顲', '�Y' => '饟', '�Z' => '鱨', '�[' => '鱮', '�\\' => '鱭', '�]' => '鸋', '�^' => '鸍', '�_' => '鸐', '�`' => '鸏', '�a' => '鸒', '�b' => '鸑', '�c' => '麡', '�d' => '黵', '�e' => '鼉', '�f' => '齇', '�g' => '齸', '�h' => '齻', '�i' => '齺', '�j' => '齹', '�k' => '圞', '�l' => '灦', '�m' => '籯', '�n' => '蠼', '�o' => '趲', '�p' => '躦', '�q' => '釃', '�r' => '鑴', '�s' => '鑸', '�t' => '鑶', '�u' => '鑵', '�v' => '驠', '�w' => '鱴', '�x' => '鱳', '�y' => '鱱', '�z' => '鱵', '�{' => '鸔', '�|' => '鸓', '�}' => '黶', '�~' => '鼊', '��' => '龤', '��' => '灨', '��' => '灥', '��' => '糷', '��' => '虪', '��' => '蠾', '��' => '蠽', '��' => '蠿', '��' => '讞', '��' => '貜', '��' => '躩', '��' => '軉', '��' => '靋', '��' => '顳', '��' => '顴', '��' => '飌', '��' => '饡', '��' => '馫', '��' => '驤', '��' => '驦', '��' => '驧', '��' => '鬤', '��' => '鸕', '��' => '鸗', '��' => '齈', '��' => '戇', '��' => '欞', '��' => '爧', '��' => '虌', '��' => '躨', '��' => '钂', '��' => '钀', '��' => '钁', '��' => '驩', '��' => '驨', '��' => '鬮', '��' => '鸙', '��' => '爩', '��' => '虋', '��' => '讟', '��' => '钃', '��' => '鱹', '��' => '麷', '��' => '癵', '��' => '驫', '��' => '鱺', '��' => '鸝', '��' => '灩', '��' => '灪', '��' => '麤', '��' => '齾', '��' => '齉', '��' => '龘', ); $result =& $data; unset($data); return $result; PK2A#]s1 ���>vendor/symfony/polyfill-iconv/Resources/charset/from.cp037.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', ' ' => '', ' ' => '', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => '', '!' => '', '"' => '', '#' => '', '$' => '', '%' => ' ', '&' => '', '\'' => '', '(' => '', ')' => '', '*' => '', '+' => '', ',' => '', '-' => '', '.' => '', '/' => '', 0 => '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', 6 => '', 7 => '', 8 => '', 9 => '', ':' => '', ';' => '', '<' => '', '=' => '', '>' => '', '?' => '', '@' => ' ', 'A' => ' ', 'B' => 'â', 'C' => 'ä', 'D' => 'à', 'E' => 'á', 'F' => 'ã', 'G' => 'å', 'H' => 'ç', 'I' => 'ñ', 'J' => '¢', 'K' => '.', 'L' => '<', 'M' => '(', 'N' => '+', 'O' => '|', 'P' => '&', 'Q' => 'é', 'R' => 'ê', 'S' => 'ë', 'T' => 'è', 'U' => 'í', 'V' => 'î', 'W' => 'ï', 'X' => 'ì', 'Y' => 'ß', 'Z' => '!', '[' => '$', '\\' => '*', ']' => ')', '^' => ';', '_' => '¬', '`' => '-', 'a' => '/', 'b' => 'Â', 'c' => 'Ä', 'd' => 'À', 'e' => 'Á', 'f' => 'Ã', 'g' => 'Å', 'h' => 'Ç', 'i' => 'Ñ', 'j' => '¦', 'k' => ',', 'l' => '%', 'm' => '_', 'n' => '>', 'o' => '?', 'p' => 'ø', 'q' => 'É', 'r' => 'Ê', 's' => 'Ë', 't' => 'È', 'u' => 'Í', 'v' => 'Î', 'w' => 'Ï', 'x' => 'Ì', 'y' => '`', 'z' => ':', '{' => '#', '|' => '@', '}' => '\'', '~' => '=', '' => '"', '�' => 'Ø', '�' => 'a', '�' => 'b', '�' => 'c', '�' => 'd', '�' => 'e', '�' => 'f', '�' => 'g', '�' => 'h', '�' => 'i', '�' => '«', '�' => '»', '�' => 'ð', '�' => 'ý', '�' => 'þ', '�' => '±', '�' => '°', '�' => 'j', '�' => 'k', '�' => 'l', '�' => 'm', '�' => 'n', '�' => 'o', '�' => 'p', '�' => 'q', '�' => 'r', '�' => 'ª', '�' => 'º', '�' => 'æ', '�' => '¸', '�' => 'Æ', '�' => '¤', '�' => 'µ', '�' => '~', '�' => 's', '�' => 't', '�' => 'u', '�' => 'v', '�' => 'w', '�' => 'x', '�' => 'y', '�' => 'z', '�' => '¡', '�' => '¿', '�' => 'Ð', '�' => 'Ý', '�' => 'Þ', '�' => '®', '�' => '^', '�' => '£', '�' => '¥', '�' => '·', '�' => '©', '�' => '§', '�' => '¶', '�' => '¼', '�' => '½', '�' => '¾', '�' => '[', '�' => ']', '�' => '¯', '�' => '¨', '�' => '´', '�' => '×', '�' => '{', '�' => 'A', '�' => 'B', '�' => 'C', '�' => 'D', '�' => 'E', '�' => 'F', '�' => 'G', '�' => 'H', '�' => 'I', '�' => '', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'ó', '�' => 'õ', '�' => '}', '�' => 'J', '�' => 'K', '�' => 'L', '�' => 'M', '�' => 'N', '�' => 'O', '�' => 'P', '�' => 'Q', '�' => 'R', '�' => '¹', '�' => 'û', '�' => 'ü', '�' => 'ù', '�' => 'ú', '�' => 'ÿ', '�' => '\\', '�' => '÷', '�' => 'S', '�' => 'T', '�' => 'U', '�' => 'V', '�' => 'W', '�' => 'X', '�' => 'Y', '�' => 'Z', '�' => '²', '�' => 'Ô', '�' => 'Ö', '�' => 'Ò', '�' => 'Ó', '�' => 'Õ', '�' => '0', '�' => '1', '�' => '2', '�' => '3', '�' => '4', '�' => '5', '�' => '6', '�' => '7', '�' => '8', '�' => '9', '�' => '³', '�' => 'Û', '�' => 'Ü', '�' => 'Ù', '�' => 'Ú', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]0pIq��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp500.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', ' ' => '', ' ' => '', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => '', '!' => '', '"' => '', '#' => '', '$' => '', '%' => ' ', '&' => '', '\'' => '', '(' => '', ')' => '', '*' => '', '+' => '', ',' => '', '-' => '', '.' => '', '/' => '', 0 => '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', 6 => '', 7 => '', 8 => '', 9 => '', ':' => '', ';' => '', '<' => '', '=' => '', '>' => '', '?' => '', '@' => ' ', 'A' => ' ', 'B' => 'â', 'C' => 'ä', 'D' => 'à', 'E' => 'á', 'F' => 'ã', 'G' => 'å', 'H' => 'ç', 'I' => 'ñ', 'J' => '[', 'K' => '.', 'L' => '<', 'M' => '(', 'N' => '+', 'O' => '!', 'P' => '&', 'Q' => 'é', 'R' => 'ê', 'S' => 'ë', 'T' => 'è', 'U' => 'í', 'V' => 'î', 'W' => 'ï', 'X' => 'ì', 'Y' => 'ß', 'Z' => ']', '[' => '$', '\\' => '*', ']' => ')', '^' => ';', '_' => '^', '`' => '-', 'a' => '/', 'b' => 'Â', 'c' => 'Ä', 'd' => 'À', 'e' => 'Á', 'f' => 'Ã', 'g' => 'Å', 'h' => 'Ç', 'i' => 'Ñ', 'j' => '¦', 'k' => ',', 'l' => '%', 'm' => '_', 'n' => '>', 'o' => '?', 'p' => 'ø', 'q' => 'É', 'r' => 'Ê', 's' => 'Ë', 't' => 'È', 'u' => 'Í', 'v' => 'Î', 'w' => 'Ï', 'x' => 'Ì', 'y' => '`', 'z' => ':', '{' => '#', '|' => '@', '}' => '\'', '~' => '=', '' => '"', '�' => 'Ø', '�' => 'a', '�' => 'b', '�' => 'c', '�' => 'd', '�' => 'e', '�' => 'f', '�' => 'g', '�' => 'h', '�' => 'i', '�' => '«', '�' => '»', '�' => 'ð', '�' => 'ý', '�' => 'þ', '�' => '±', '�' => '°', '�' => 'j', '�' => 'k', '�' => 'l', '�' => 'm', '�' => 'n', '�' => 'o', '�' => 'p', '�' => 'q', '�' => 'r', '�' => 'ª', '�' => 'º', '�' => 'æ', '�' => '¸', '�' => 'Æ', '�' => '¤', '�' => 'µ', '�' => '~', '�' => 's', '�' => 't', '�' => 'u', '�' => 'v', '�' => 'w', '�' => 'x', '�' => 'y', '�' => 'z', '�' => '¡', '�' => '¿', '�' => 'Ð', '�' => 'Ý', '�' => 'Þ', '�' => '®', '�' => '¢', '�' => '£', '�' => '¥', '�' => '·', '�' => '©', '�' => '§', '�' => '¶', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¬', '�' => '|', '�' => '¯', '�' => '¨', '�' => '´', '�' => '×', '�' => '{', '�' => 'A', '�' => 'B', '�' => 'C', '�' => 'D', '�' => 'E', '�' => 'F', '�' => 'G', '�' => 'H', '�' => 'I', '�' => '', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'ó', '�' => 'õ', '�' => '}', '�' => 'J', '�' => 'K', '�' => 'L', '�' => 'M', '�' => 'N', '�' => 'O', '�' => 'P', '�' => 'Q', '�' => 'R', '�' => '¹', '�' => 'û', '�' => 'ü', '�' => 'ù', '�' => 'ú', '�' => 'ÿ', '�' => '\\', '�' => '÷', '�' => 'S', '�' => 'T', '�' => 'U', '�' => 'V', '�' => 'W', '�' => 'X', '�' => 'Y', '�' => 'Z', '�' => '²', '�' => 'Ô', '�' => 'Ö', '�' => 'Ò', '�' => 'Ó', '�' => 'Õ', '�' => '0', '�' => '1', '�' => '2', '�' => '3', '�' => '4', '�' => '5', '�' => '6', '�' => '7', '�' => '8', '�' => '9', '�' => '³', '�' => 'Û', '�' => 'Ü', '�' => 'Ù', '�' => 'Ú', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]����>vendor/symfony/polyfill-iconv/Resources/charset/from.cp424.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', ' ' => '', ' ' => '', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => '', '!' => '', '"' => '', '#' => '', '$' => '', '%' => ' ', '&' => '', '\'' => '', '(' => '', ')' => '', '*' => '', '+' => '', ',' => '', '-' => '', '.' => '', '/' => '', 0 => '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', 6 => '', 7 => '', 8 => '', 9 => '', ':' => '', ';' => '', '<' => '', '=' => '', '>' => '', '?' => '', '@' => ' ', 'A' => 'א', 'B' => 'ב', 'C' => 'ג', 'D' => 'ד', 'E' => 'ה', 'F' => 'ו', 'G' => 'ז', 'H' => 'ח', 'I' => 'ט', 'J' => '¢', 'K' => '.', 'L' => '<', 'M' => '(', 'N' => '+', 'O' => '|', 'P' => '&', 'Q' => 'י', 'R' => 'ך', 'S' => 'כ', 'T' => 'ל', 'U' => 'ם', 'V' => 'מ', 'W' => 'ן', 'X' => 'נ', 'Y' => 'ס', 'Z' => '!', '[' => '$', '\\' => '*', ']' => ')', '^' => ';', '_' => '¬', '`' => '-', 'a' => '/', 'b' => 'ע', 'c' => 'ף', 'd' => 'פ', 'e' => 'ץ', 'f' => 'צ', 'g' => 'ק', 'h' => 'ר', 'i' => 'ש', 'j' => '¦', 'k' => ',', 'l' => '%', 'm' => '_', 'n' => '>', 'o' => '?', 'q' => 'ת', 't' => ' ', 'x' => '‗', 'y' => '`', 'z' => ':', '{' => '#', '|' => '@', '}' => '\'', '~' => '=', '' => '"', '�' => 'a', '�' => 'b', '�' => 'c', '�' => 'd', '�' => 'e', '�' => 'f', '�' => 'g', '�' => 'h', '�' => 'i', '�' => '«', '�' => '»', '�' => '±', '�' => '°', '�' => 'j', '�' => 'k', '�' => 'l', '�' => 'm', '�' => 'n', '�' => 'o', '�' => 'p', '�' => 'q', '�' => 'r', '�' => '¸', '�' => '¤', '�' => 'µ', '�' => '~', '�' => 's', '�' => 't', '�' => 'u', '�' => 'v', '�' => 'w', '�' => 'x', '�' => 'y', '�' => 'z', '�' => '®', '�' => '^', '�' => '£', '�' => '¥', '�' => '·', '�' => '©', '�' => '§', '�' => '¶', '�' => '¼', '�' => '½', '�' => '¾', '�' => '[', '�' => ']', '�' => '¯', '�' => '¨', '�' => '´', '�' => '×', '�' => '{', '�' => 'A', '�' => 'B', '�' => 'C', '�' => 'D', '�' => 'E', '�' => 'F', '�' => 'G', '�' => 'H', '�' => 'I', '�' => '', '�' => '}', '�' => 'J', '�' => 'K', '�' => 'L', '�' => 'M', '�' => 'N', '�' => 'O', '�' => 'P', '�' => 'Q', '�' => 'R', '�' => '¹', '�' => '\\', '�' => '÷', '�' => 'S', '�' => 'T', '�' => 'U', '�' => 'V', '�' => 'W', '�' => 'X', '�' => 'Y', '�' => 'Z', '�' => '²', '�' => '0', '�' => '1', '�' => '2', '�' => '3', '�' => '4', '�' => '5', '�' => '6', '�' => '7', '�' => '8', '�' => '9', '�' => '³', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]U���zz>vendor/symfony/polyfill-iconv/Resources/charset/from.cp856.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'א', '�' => 'ב', '�' => 'ג', '�' => 'ד', '�' => 'ה', '�' => 'ו', '�' => 'ז', '�' => 'ח', '�' => 'ט', '�' => 'י', '�' => 'ך', '�' => 'כ', '�' => 'ל', '�' => 'ם', '�' => 'מ', '�' => 'ן', '�' => 'נ', '�' => 'ס', '�' => 'ע', '�' => 'ף', '�' => 'פ', '�' => 'ץ', '�' => 'צ', '�' => 'ק', '�' => 'ר', '�' => 'ש', '�' => 'ת', '�' => '£', '�' => '×', '�' => '®', '�' => '¬', '�' => '½', '�' => '¼', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '©', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '¢', '�' => '¥', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '¤', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '¦', '�' => '▀', '�' => 'µ', '�' => '¯', '�' => '´', '�' => '', '�' => '±', '�' => '‗', '�' => '¾', '�' => '¶', '�' => '§', '�' => '÷', '�' => '¸', '�' => '°', '�' => '¨', '�' => '·', '�' => '¹', '�' => '³', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]��i�R R >vendor/symfony/polyfill-iconv/Resources/charset/from.cp874.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '…', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => ' ', '�' => 'ก', '�' => 'ข', '�' => 'ฃ', '�' => 'ค', '�' => 'ฅ', '�' => 'ฆ', '�' => 'ง', '�' => 'จ', '�' => 'ฉ', '�' => 'ช', '�' => 'ซ', '�' => 'ฌ', '�' => 'ญ', '�' => 'ฎ', '�' => 'ฏ', '�' => 'ฐ', '�' => 'ฑ', '�' => 'ฒ', '�' => 'ณ', '�' => 'ด', '�' => 'ต', '�' => 'ถ', '�' => 'ท', '�' => 'ธ', '�' => 'น', '�' => 'บ', '�' => 'ป', '�' => 'ผ', '�' => 'ฝ', '�' => 'พ', '�' => 'ฟ', '�' => 'ภ', '�' => 'ม', '�' => 'ย', '�' => 'ร', '�' => 'ฤ', '�' => 'ล', '�' => 'ฦ', '�' => 'ว', '�' => 'ศ', '�' => 'ษ', '�' => 'ส', '�' => 'ห', '�' => 'ฬ', '�' => 'อ', '�' => 'ฮ', '�' => 'ฯ', '�' => 'ะ', '�' => 'ั', '�' => 'า', '�' => 'ำ', '�' => 'ิ', '�' => 'ี', '�' => 'ึ', '�' => 'ื', '�' => 'ุ', '�' => 'ู', '�' => 'ฺ', '�' => '฿', '�' => 'เ', '�' => 'แ', '�' => 'โ', '�' => 'ใ', '�' => 'ไ', '�' => 'ๅ', '�' => 'ๆ', '�' => '็', '�' => '่', '�' => '้', '�' => '๊', '�' => '๋', '�' => '์', '�' => 'ํ', '�' => '๎', '�' => '๏', '�' => '๐', '�' => '๑', '�' => '๒', '�' => '๓', '�' => '๔', '�' => '๕', '�' => '๖', '�' => '๗', '�' => '๘', '�' => '๙', '�' => '๚', '�' => '๛', ); $result =& $data; unset($data); return $result; PK2A#]�~V��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp950.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�@' => ' ', '�A' => ',', '�B' => '、', '�C' => '。', '�D' => '.', '�E' => '‧', '�F' => ';', '�G' => ':', '�H' => '?', '�I' => '!', '�J' => '︰', '�K' => '…', '�L' => '‥', '�M' => '﹐', '�N' => '﹑', '�O' => '﹒', '�P' => '·', '�Q' => '﹔', '�R' => '﹕', '�S' => '﹖', '�T' => '﹗', '�U' => '|', '�V' => '–', '�W' => '︱', '�X' => '—', '�Y' => '︳', '�Z' => '╴', '�[' => '︴', '�\\' => '﹏', '�]' => '(', '�^' => ')', '�_' => '︵', '�`' => '︶', '�a' => '{', '�b' => '}', '�c' => '︷', '�d' => '︸', '�e' => '〔', '�f' => '〕', '�g' => '︹', '�h' => '︺', '�i' => '【', '�j' => '】', '�k' => '︻', '�l' => '︼', '�m' => '《', '�n' => '》', '�o' => '︽', '�p' => '︾', '�q' => '〈', '�r' => '〉', '�s' => '︿', '�t' => '﹀', '�u' => '「', '�v' => '」', '�w' => '﹁', '�x' => '﹂', '�y' => '『', '�z' => '』', '�{' => '﹃', '�|' => '﹄', '�}' => '﹙', '�~' => '﹚', '��' => '﹛', '��' => '﹜', '��' => '﹝', '��' => '﹞', '��' => '‘', '��' => '’', '��' => '“', '��' => '”', '��' => '〝', '��' => '〞', '��' => '‵', '��' => '′', '��' => '#', '��' => '&', '��' => '*', '��' => '※', '��' => '§', '��' => '〃', '��' => '○', '��' => '●', '��' => '△', '��' => '▲', '��' => '◎', '��' => '☆', '��' => '★', '��' => '◇', '��' => '◆', '��' => '□', '��' => '■', '��' => '▽', '��' => '▼', '��' => '㊣', '��' => '℅', '��' => '¯', '��' => ' ̄', '��' => '_', '��' => 'ˍ', '��' => '﹉', '��' => '﹊', '��' => '﹍', '��' => '﹎', '��' => '﹋', '��' => '﹌', '��' => '﹟', '��' => '﹠', '��' => '﹡', '��' => '+', '��' => '-', '��' => '×', '��' => '÷', '��' => '±', '��' => '√', '��' => '<', '��' => '>', '��' => '=', '��' => '≦', '��' => '≧', '��' => '≠', '��' => '∞', '��' => '≒', '��' => '≡', '��' => '﹢', '��' => '﹣', '��' => '﹤', '��' => '﹥', '��' => '﹦', '��' => '~', '��' => '∩', '��' => '∪', '��' => '⊥', '��' => '∠', '��' => '∟', '��' => '⊿', '��' => '㏒', '��' => '㏑', '��' => '∫', '��' => '∮', '��' => '∵', '��' => '∴', '��' => '♀', '��' => '♂', '��' => '⊕', '��' => '⊙', '��' => '↑', '��' => '↓', '��' => '←', '��' => '→', '��' => '↖', '��' => '↗', '��' => '↙', '��' => '↘', '��' => '∥', '��' => '∣', '��' => '/', '�@' => '\', '�A' => '∕', '�B' => '﹨', '�C' => '$', '�D' => '¥', '�E' => '〒', '�F' => '¢', '�G' => '£', '�H' => '%', '�I' => '@', '�J' => '℃', '�K' => '℉', '�L' => '﹩', '�M' => '﹪', '�N' => '﹫', '�O' => '㏕', '�P' => '㎜', '�Q' => '㎝', '�R' => '㎞', '�S' => '㏎', '�T' => '㎡', '�U' => '㎎', '�V' => '㎏', '�W' => '㏄', '�X' => '°', '�Y' => '兙', '�Z' => '兛', '�[' => '兞', '�\\' => '兝', '�]' => '兡', '�^' => '兣', '�_' => '嗧', '�`' => '瓩', '�a' => '糎', '�b' => '▁', '�c' => '▂', '�d' => '▃', '�e' => '▄', '�f' => '▅', '�g' => '▆', '�h' => '▇', '�i' => '█', '�j' => '▏', '�k' => '▎', '�l' => '▍', '�m' => '▌', '�n' => '▋', '�o' => '▊', '�p' => '▉', '�q' => '┼', '�r' => '┴', '�s' => '┬', '�t' => '┤', '�u' => '├', '�v' => '▔', '�w' => '─', '�x' => '│', '�y' => '▕', '�z' => '┌', '�{' => '┐', '�|' => '└', '�}' => '┘', '�~' => '╭', '��' => '╮', '��' => '╰', '��' => '╯', '��' => '═', '��' => '╞', '��' => '╪', '��' => '╡', '��' => '◢', '��' => '◣', '��' => '◥', '��' => '◤', '��' => '╱', '��' => '╲', '��' => '╳', '��' => '0', '��' => '1', '��' => '2', '��' => '3', '��' => '4', '��' => '5', '��' => '6', '��' => '7', '��' => '8', '��' => '9', '��' => 'Ⅰ', '��' => 'Ⅱ', '��' => 'Ⅲ', '��' => 'Ⅳ', '��' => 'Ⅴ', '��' => 'Ⅵ', '��' => 'Ⅶ', '��' => 'Ⅷ', '��' => 'Ⅸ', '��' => 'Ⅹ', '��' => '〡', '��' => '〢', '��' => '〣', '��' => '〤', '��' => '〥', '��' => '〦', '��' => '〧', '��' => '〨', '��' => '〩', '��' => '十', '��' => '卄', '��' => '卅', '��' => 'A', '��' => 'B', '��' => 'C', '��' => 'D', '��' => 'E', '��' => 'F', '��' => 'G', '��' => 'H', '��' => 'I', '��' => 'J', '��' => 'K', '��' => 'L', '��' => 'M', '��' => 'N', '��' => 'O', '��' => 'P', '��' => 'Q', '��' => 'R', '��' => 'S', '��' => 'T', '��' => 'U', '��' => 'V', '��' => 'W', '��' => 'X', '��' => 'Y', '��' => 'Z', '��' => 'a', '��' => 'b', '��' => 'c', '��' => 'd', '��' => 'e', '��' => 'f', '��' => 'g', '��' => 'h', '��' => 'i', '��' => 'j', '��' => 'k', '��' => 'l', '��' => 'm', '��' => 'n', '��' => 'o', '��' => 'p', '��' => 'q', '��' => 'r', '��' => 's', '��' => 't', '��' => 'u', '��' => 'v', '�@' => 'w', '�A' => 'x', '�B' => 'y', '�C' => 'z', '�D' => 'Α', '�E' => 'Β', '�F' => 'Γ', '�G' => 'Δ', '�H' => 'Ε', '�I' => 'Ζ', '�J' => 'Η', '�K' => 'Θ', '�L' => 'Ι', '�M' => 'Κ', '�N' => 'Λ', '�O' => 'Μ', '�P' => 'Ν', '�Q' => 'Ξ', '�R' => 'Ο', '�S' => 'Π', '�T' => 'Ρ', '�U' => 'Σ', '�V' => 'Τ', '�W' => 'Υ', '�X' => 'Φ', '�Y' => 'Χ', '�Z' => 'Ψ', '�[' => 'Ω', '�\\' => 'α', '�]' => 'β', '�^' => 'γ', '�_' => 'δ', '�`' => 'ε', '�a' => 'ζ', '�b' => 'η', '�c' => 'θ', '�d' => 'ι', '�e' => 'κ', '�f' => 'λ', '�g' => 'μ', '�h' => 'ν', '�i' => 'ξ', '�j' => 'ο', '�k' => 'π', '�l' => 'ρ', '�m' => 'σ', '�n' => 'τ', '�o' => 'υ', '�p' => 'φ', '�q' => 'χ', '�r' => 'ψ', '�s' => 'ω', '�t' => 'ㄅ', '�u' => 'ㄆ', '�v' => 'ㄇ', '�w' => 'ㄈ', '�x' => 'ㄉ', '�y' => 'ㄊ', '�z' => 'ㄋ', '�{' => 'ㄌ', '�|' => 'ㄍ', '�}' => 'ㄎ', '�~' => 'ㄏ', '��' => 'ㄐ', '��' => 'ㄑ', '��' => 'ㄒ', '��' => 'ㄓ', '��' => 'ㄔ', '��' => 'ㄕ', '��' => 'ㄖ', '��' => 'ㄗ', '��' => 'ㄘ', '��' => 'ㄙ', '��' => 'ㄚ', '��' => 'ㄛ', '��' => 'ㄜ', '��' => 'ㄝ', '��' => 'ㄞ', '��' => 'ㄟ', '��' => 'ㄠ', '��' => 'ㄡ', '��' => 'ㄢ', '��' => 'ㄣ', '��' => 'ㄤ', '��' => 'ㄥ', '��' => 'ㄦ', '��' => 'ㄧ', '��' => 'ㄨ', '��' => 'ㄩ', '��' => '˙', '��' => 'ˉ', '��' => 'ˊ', '��' => 'ˇ', '��' => 'ˋ', '��' => '€', '�@' => '一', '�A' => '乙', '�B' => '丁', '�C' => '七', '�D' => '乃', '�E' => '九', '�F' => '了', '�G' => '二', '�H' => '人', '�I' => '儿', '�J' => '入', '�K' => '八', '�L' => '几', '�M' => '刀', '�N' => '刁', '�O' => '力', '�P' => '匕', '�Q' => '十', '�R' => '卜', '�S' => '又', '�T' => '三', '�U' => '下', '�V' => '丈', '�W' => '上', '�X' => '丫', '�Y' => '丸', '�Z' => '凡', '�[' => '久', '�\\' => '么', '�]' => '也', '�^' => '乞', '�_' => '于', '�`' => '亡', '�a' => '兀', '�b' => '刃', '�c' => '勺', '�d' => '千', '�e' => '叉', '�f' => '口', '�g' => '土', '�h' => '士', '�i' => '夕', '�j' => '大', '�k' => '女', '�l' => '子', '�m' => '孑', '�n' => '孓', '�o' => '寸', '�p' => '小', '�q' => '尢', '�r' => '尸', '�s' => '山', '�t' => '川', '�u' => '工', '�v' => '己', '�w' => '已', '�x' => '巳', '�y' => '巾', '�z' => '干', '�{' => '廾', '�|' => '弋', '�}' => '弓', '�~' => '才', '��' => '丑', '��' => '丐', '��' => '不', '��' => '中', '��' => '丰', '��' => '丹', '��' => '之', '��' => '尹', '��' => '予', '��' => '云', '��' => '井', '��' => '互', '��' => '五', '��' => '亢', '��' => '仁', '��' => '什', '��' => '仃', '��' => '仆', '��' => '仇', '��' => '仍', '��' => '今', '��' => '介', '��' => '仄', '��' => '元', '��' => '允', '��' => '內', '��' => '六', '��' => '兮', '��' => '公', '��' => '冗', '��' => '凶', '��' => '分', '��' => '切', '��' => '刈', '��' => '勻', '��' => '勾', '��' => '勿', '��' => '化', '��' => '匹', '��' => '午', '��' => '升', '��' => '卅', '��' => '卞', '��' => '厄', '��' => '友', '��' => '及', '��' => '反', '��' => '壬', '��' => '天', '��' => '夫', '��' => '太', '��' => '夭', '��' => '孔', '��' => '少', '��' => '尤', '��' => '尺', '��' => '屯', '��' => '巴', '��' => '幻', '��' => '廿', '��' => '弔', '��' => '引', '��' => '心', '��' => '戈', '��' => '戶', '��' => '手', '��' => '扎', '��' => '支', '��' => '文', '��' => '斗', '��' => '斤', '��' => '方', '��' => '日', '��' => '曰', '��' => '月', '��' => '木', '��' => '欠', '��' => '止', '��' => '歹', '��' => '毋', '��' => '比', '��' => '毛', '��' => '氏', '��' => '水', '��' => '火', '��' => '爪', '��' => '父', '��' => '爻', '��' => '片', '��' => '牙', '��' => '牛', '��' => '犬', '��' => '王', '��' => '丙', '�@' => '世', '�A' => '丕', '�B' => '且', '�C' => '丘', '�D' => '主', '�E' => '乍', '�F' => '乏', '�G' => '乎', '�H' => '以', '�I' => '付', '�J' => '仔', '�K' => '仕', '�L' => '他', '�M' => '仗', '�N' => '代', '�O' => '令', '�P' => '仙', '�Q' => '仞', '�R' => '充', '�S' => '兄', '�T' => '冉', '�U' => '冊', '�V' => '冬', '�W' => '凹', '�X' => '出', '�Y' => '凸', '�Z' => '刊', '�[' => '加', '�\\' => '功', '�]' => '包', '�^' => '匆', '�_' => '北', '�`' => '匝', '�a' => '仟', '�b' => '半', '�c' => '卉', '�d' => '卡', '�e' => '占', '�f' => '卯', '�g' => '卮', '�h' => '去', '�i' => '可', '�j' => '古', '�k' => '右', '�l' => '召', '�m' => '叮', '�n' => '叩', '�o' => '叨', '�p' => '叼', '�q' => '司', '�r' => '叵', '�s' => '叫', '�t' => '另', '�u' => '只', '�v' => '史', '�w' => '叱', '�x' => '台', '�y' => '句', '�z' => '叭', '�{' => '叻', '�|' => '四', '�}' => '囚', '�~' => '外', '��' => '央', '��' => '失', '��' => '奴', '��' => '奶', '��' => '孕', '��' => '它', '��' => '尼', '��' => '巨', '��' => '巧', '��' => '左', '��' => '市', '��' => '布', '��' => '平', '��' => '幼', '��' => '弁', '��' => '弘', '��' => '弗', '��' => '必', '��' => '戊', '��' => '打', '��' => '扔', '��' => '扒', '��' => '扑', '��' => '斥', '��' => '旦', '��' => '朮', '��' => '本', '��' => '未', '��' => '末', '��' => '札', '��' => '正', '��' => '母', '��' => '民', '��' => '氐', '��' => '永', '��' => '汁', '��' => '汀', '��' => '氾', '��' => '犯', '��' => '玄', '��' => '玉', '��' => '瓜', '��' => '瓦', '��' => '甘', '��' => '生', '��' => '用', '��' => '甩', '��' => '田', '��' => '由', '��' => '甲', '��' => '申', '��' => '疋', '��' => '白', '��' => '皮', '��' => '皿', '��' => '目', '��' => '矛', '��' => '矢', '��' => '石', '��' => '示', '��' => '禾', '��' => '穴', '��' => '立', '��' => '丞', '��' => '丟', '��' => '乒', '��' => '乓', '��' => '乩', '��' => '亙', '��' => '交', '��' => '亦', '��' => '亥', '��' => '仿', '��' => '伉', '��' => '伙', '��' => '伊', '��' => '伕', '��' => '伍', '��' => '伐', '��' => '休', '��' => '伏', '��' => '仲', '��' => '件', '��' => '任', '��' => '仰', '��' => '仳', '��' => '份', '��' => '企', '��' => '伋', '��' => '光', '��' => '兇', '��' => '兆', '��' => '先', '��' => '全', '�@' => '共', '�A' => '再', '�B' => '冰', '�C' => '列', '�D' => '刑', '�E' => '划', '�F' => '刎', '�G' => '刖', '�H' => '劣', '�I' => '匈', '�J' => '匡', '�K' => '匠', '�L' => '印', '�M' => '危', '�N' => '吉', '�O' => '吏', '�P' => '同', '�Q' => '吊', '�R' => '吐', '�S' => '吁', '�T' => '吋', '�U' => '各', '�V' => '向', '�W' => '名', '�X' => '合', '�Y' => '吃', '�Z' => '后', '�[' => '吆', '�\\' => '吒', '�]' => '因', '�^' => '回', '�_' => '囝', '�`' => '圳', '�a' => '地', '�b' => '在', '�c' => '圭', '�d' => '圬', '�e' => '圯', '�f' => '圩', '�g' => '夙', '�h' => '多', '�i' => '夷', '�j' => '夸', '�k' => '妄', '�l' => '奸', '�m' => '妃', '�n' => '好', '�o' => '她', '�p' => '如', '�q' => '妁', '�r' => '字', '�s' => '存', '�t' => '宇', '�u' => '守', '�v' => '宅', '�w' => '安', '�x' => '寺', '�y' => '尖', '�z' => '屹', '�{' => '州', '�|' => '帆', '�}' => '并', '�~' => '年', '��' => '式', '��' => '弛', '��' => '忙', '��' => '忖', '��' => '戎', '��' => '戌', '��' => '戍', '��' => '成', '��' => '扣', '��' => '扛', '��' => '托', '��' => '收', '��' => '早', '��' => '旨', '��' => '旬', '��' => '旭', '��' => '曲', '��' => '曳', '��' => '有', '��' => '朽', '��' => '朴', '��' => '朱', '��' => '朵', '��' => '次', '��' => '此', '��' => '死', '��' => '氖', '��' => '汝', '��' => '汗', '��' => '汙', '��' => '江', '��' => '池', '��' => '汐', '��' => '汕', '��' => '污', '��' => '汛', '��' => '汍', '��' => '汎', '��' => '灰', '��' => '牟', '��' => '牝', '��' => '百', '��' => '竹', '��' => '米', '��' => '糸', '��' => '缶', '��' => '羊', '��' => '羽', '��' => '老', '��' => '考', '��' => '而', '��' => '耒', '��' => '耳', '��' => '聿', '��' => '肉', '��' => '肋', '��' => '肌', '��' => '臣', '��' => '自', '��' => '至', '��' => '臼', '��' => '舌', '��' => '舛', '��' => '舟', '��' => '艮', '��' => '色', '��' => '艾', '��' => '虫', '��' => '血', '��' => '行', '��' => '衣', '��' => '西', '��' => '阡', '��' => '串', '��' => '亨', '��' => '位', '��' => '住', '��' => '佇', '��' => '佗', '��' => '佞', '��' => '伴', '��' => '佛', '��' => '何', '��' => '估', '��' => '佐', '��' => '佑', '��' => '伽', '��' => '伺', '��' => '伸', '��' => '佃', '��' => '佔', '��' => '似', '��' => '但', '��' => '佣', '�@' => '作', '�A' => '你', '�B' => '伯', '�C' => '低', '�D' => '伶', '�E' => '余', '�F' => '佝', '�G' => '佈', '�H' => '佚', '�I' => '兌', '�J' => '克', '�K' => '免', '�L' => '兵', '�M' => '冶', '�N' => '冷', '�O' => '別', '�P' => '判', '�Q' => '利', '�R' => '刪', '�S' => '刨', '�T' => '劫', '�U' => '助', '�V' => '努', '�W' => '劬', '�X' => '匣', '�Y' => '即', '�Z' => '卵', '�[' => '吝', '�\\' => '吭', '�]' => '吞', '�^' => '吾', '�_' => '否', '�`' => '呎', '�a' => '吧', '�b' => '呆', '�c' => '呃', '�d' => '吳', '�e' => '呈', '�f' => '呂', '�g' => '君', '�h' => '吩', '�i' => '告', '�j' => '吹', '�k' => '吻', '�l' => '吸', '�m' => '吮', '�n' => '吵', '�o' => '吶', '�p' => '吠', '�q' => '吼', '�r' => '呀', '�s' => '吱', '�t' => '含', '�u' => '吟', '�v' => '听', '�w' => '囪', '�x' => '困', '�y' => '囤', '�z' => '囫', '�{' => '坊', '�|' => '坑', '�}' => '址', '�~' => '坍', '��' => '均', '��' => '坎', '��' => '圾', '��' => '坐', '��' => '坏', '��' => '圻', '��' => '壯', '��' => '夾', '��' => '妝', '��' => '妒', '��' => '妨', '��' => '妞', '��' => '妣', '��' => '妙', '��' => '妖', '��' => '妍', '��' => '妤', '��' => '妓', '��' => '妊', '��' => '妥', '��' => '孝', '��' => '孜', '��' => '孚', '��' => '孛', '��' => '完', '��' => '宋', '��' => '宏', '��' => '尬', '��' => '局', '��' => '屁', '��' => '尿', '��' => '尾', '��' => '岐', '��' => '岑', '��' => '岔', '��' => '岌', '��' => '巫', '��' => '希', '��' => '序', '��' => '庇', '��' => '床', '��' => '廷', '��' => '弄', '��' => '弟', '��' => '彤', '��' => '形', '��' => '彷', '��' => '役', '��' => '忘', '��' => '忌', '��' => '志', '��' => '忍', '��' => '忱', '��' => '快', '��' => '忸', '��' => '忪', '��' => '戒', '��' => '我', '��' => '抄', '��' => '抗', '��' => '抖', '��' => '技', '��' => '扶', '��' => '抉', '��' => '扭', '��' => '把', '��' => '扼', '��' => '找', '��' => '批', '��' => '扳', '��' => '抒', '��' => '扯', '��' => '折', '��' => '扮', '��' => '投', '��' => '抓', '��' => '抑', '��' => '抆', '��' => '改', '��' => '攻', '��' => '攸', '��' => '旱', '��' => '更', '��' => '束', '��' => '李', '��' => '杏', '��' => '材', '��' => '村', '��' => '杜', '��' => '杖', '��' => '杞', '��' => '杉', '��' => '杆', '��' => '杠', '�@' => '杓', '�A' => '杗', '�B' => '步', '�C' => '每', '�D' => '求', '�E' => '汞', '�F' => '沙', '�G' => '沁', '�H' => '沈', '�I' => '沉', '�J' => '沅', '�K' => '沛', '�L' => '汪', '�M' => '決', '�N' => '沐', '�O' => '汰', '�P' => '沌', '�Q' => '汨', '�R' => '沖', '�S' => '沒', '�T' => '汽', '�U' => '沃', '�V' => '汲', '�W' => '汾', '�X' => '汴', '�Y' => '沆', '�Z' => '汶', '�[' => '沍', '�\\' => '沔', '�]' => '沘', '�^' => '沂', '�_' => '灶', '�`' => '灼', '�a' => '災', '�b' => '灸', '�c' => '牢', '�d' => '牡', '�e' => '牠', '�f' => '狄', '�g' => '狂', '�h' => '玖', '�i' => '甬', '�j' => '甫', '�k' => '男', '�l' => '甸', '�m' => '皂', '�n' => '盯', '�o' => '矣', '�p' => '私', '�q' => '秀', '�r' => '禿', '�s' => '究', '�t' => '系', '�u' => '罕', '�v' => '肖', '�w' => '肓', '�x' => '肝', '�y' => '肘', '�z' => '肛', '�{' => '肚', '�|' => '育', '�}' => '良', '�~' => '芒', '��' => '芋', '��' => '芍', '��' => '見', '��' => '角', '��' => '言', '��' => '谷', '��' => '豆', '��' => '豕', '��' => '貝', '��' => '赤', '��' => '走', '��' => '足', '��' => '身', '��' => '車', '��' => '辛', '��' => '辰', '��' => '迂', '��' => '迆', '��' => '迅', '��' => '迄', '��' => '巡', '��' => '邑', '��' => '邢', '��' => '邪', '��' => '邦', '��' => '那', '��' => '酉', '��' => '釆', '��' => '里', '��' => '防', '��' => '阮', '��' => '阱', '��' => '阪', '��' => '阬', '��' => '並', '��' => '乖', '��' => '乳', '��' => '事', '��' => '些', '��' => '亞', '��' => '享', '��' => '京', '��' => '佯', '��' => '依', '��' => '侍', '��' => '佳', '��' => '使', '��' => '佬', '��' => '供', '��' => '例', '��' => '來', '��' => '侃', '��' => '佰', '��' => '併', '��' => '侈', '��' => '佩', '��' => '佻', '��' => '侖', '��' => '佾', '��' => '侏', '��' => '侑', '��' => '佺', '��' => '兔', '��' => '兒', '��' => '兕', '��' => '兩', '��' => '具', '��' => '其', '��' => '典', '��' => '冽', '��' => '函', '��' => '刻', '��' => '券', '��' => '刷', '��' => '刺', '��' => '到', '��' => '刮', '��' => '制', '��' => '剁', '��' => '劾', '��' => '劻', '��' => '卒', '��' => '協', '��' => '卓', '��' => '卑', '��' => '卦', '��' => '卷', '��' => '卸', '��' => '卹', '��' => '取', '��' => '叔', '��' => '受', '��' => '味', '��' => '呵', '�@' => '咖', '�A' => '呸', '�B' => '咕', '�C' => '咀', '�D' => '呻', '�E' => '呷', '�F' => '咄', '�G' => '咒', '�H' => '咆', '�I' => '呼', '�J' => '咐', '�K' => '呱', '�L' => '呶', '�M' => '和', '�N' => '咚', '�O' => '呢', '�P' => '周', '�Q' => '咋', '�R' => '命', '�S' => '咎', '�T' => '固', '�U' => '垃', '�V' => '坷', '�W' => '坪', '�X' => '坩', '�Y' => '坡', '�Z' => '坦', '�[' => '坤', '�\\' => '坼', '�]' => '夜', '�^' => '奉', '�_' => '奇', '�`' => '奈', '�a' => '奄', '�b' => '奔', '�c' => '妾', '�d' => '妻', '�e' => '委', '�f' => '妹', '�g' => '妮', '�h' => '姑', '�i' => '姆', '�j' => '姐', '�k' => '姍', '�l' => '始', '�m' => '姓', '�n' => '姊', '�o' => '妯', '�p' => '妳', '�q' => '姒', '�r' => '姅', '�s' => '孟', '�t' => '孤', '�u' => '季', '�v' => '宗', '�w' => '定', '�x' => '官', '�y' => '宜', '�z' => '宙', '�{' => '宛', '�|' => '尚', '�}' => '屈', '�~' => '居', '��' => '屆', '��' => '岷', '��' => '岡', '��' => '岸', '��' => '岩', '��' => '岫', '��' => '岱', '��' => '岳', '��' => '帘', '��' => '帚', '��' => '帖', '��' => '帕', '��' => '帛', '��' => '帑', '��' => '幸', '��' => '庚', '��' => '店', '��' => '府', '��' => '底', '��' => '庖', '��' => '延', '��' => '弦', '��' => '弧', '��' => '弩', '��' => '往', '��' => '征', '��' => '彿', '��' => '彼', '��' => '忝', '��' => '忠', '��' => '忽', '��' => '念', '��' => '忿', '��' => '怏', '��' => '怔', '��' => '怯', '��' => '怵', '��' => '怖', '��' => '怪', '��' => '怕', '��' => '怡', '��' => '性', '��' => '怩', '��' => '怫', '��' => '怛', '��' => '或', '��' => '戕', '��' => '房', '��' => '戾', '��' => '所', '��' => '承', '��' => '拉', '��' => '拌', '��' => '拄', '��' => '抿', '��' => '拂', '��' => '抹', '��' => '拒', '��' => '招', '��' => '披', '��' => '拓', '��' => '拔', '��' => '拋', '��' => '拈', '��' => '抨', '��' => '抽', '��' => '押', '��' => '拐', '��' => '拙', '��' => '拇', '��' => '拍', '��' => '抵', '��' => '拚', '��' => '抱', '��' => '拘', '��' => '拖', '��' => '拗', '��' => '拆', '��' => '抬', '��' => '拎', '��' => '放', '��' => '斧', '��' => '於', '��' => '旺', '��' => '昔', '��' => '易', '��' => '昌', '��' => '昆', '��' => '昂', '��' => '明', '��' => '昀', '��' => '昏', '��' => '昕', '��' => '昊', '�@' => '昇', '�A' => '服', '�B' => '朋', '�C' => '杭', '�D' => '枋', '�E' => '枕', '�F' => '東', '�G' => '果', '�H' => '杳', '�I' => '杷', '�J' => '枇', '�K' => '枝', '�L' => '林', '�M' => '杯', '�N' => '杰', '�O' => '板', '�P' => '枉', '�Q' => '松', '�R' => '析', '�S' => '杵', '�T' => '枚', '�U' => '枓', '�V' => '杼', '�W' => '杪', '�X' => '杲', '�Y' => '欣', '�Z' => '武', '�[' => '歧', '�\\' => '歿', '�]' => '氓', '�^' => '氛', '�_' => '泣', '�`' => '注', '�a' => '泳', '�b' => '沱', '�c' => '泌', '�d' => '泥', '�e' => '河', '�f' => '沽', '�g' => '沾', '�h' => '沼', '�i' => '波', '�j' => '沫', '�k' => '法', '�l' => '泓', '�m' => '沸', '�n' => '泄', '�o' => '油', '�p' => '況', '�q' => '沮', '�r' => '泗', '�s' => '泅', '�t' => '泱', '�u' => '沿', '�v' => '治', '�w' => '泡', '�x' => '泛', '�y' => '泊', '�z' => '沬', '�{' => '泯', '�|' => '泜', '�}' => '泖', '�~' => '泠', '��' => '炕', '��' => '炎', '��' => '炒', '��' => '炊', '��' => '炙', '��' => '爬', '��' => '爭', '��' => '爸', '��' => '版', '��' => '牧', '��' => '物', '��' => '狀', '��' => '狎', '��' => '狙', '��' => '狗', '��' => '狐', '��' => '玩', '��' => '玨', '��' => '玟', '��' => '玫', '��' => '玥', '��' => '甽', '��' => '疝', '��' => '疙', '��' => '疚', '��' => '的', '��' => '盂', '��' => '盲', '��' => '直', '��' => '知', '��' => '矽', '��' => '社', '��' => '祀', '��' => '祁', '��' => '秉', '��' => '秈', '��' => '空', '��' => '穹', '��' => '竺', '��' => '糾', '��' => '罔', '��' => '羌', '��' => '羋', '��' => '者', '��' => '肺', '��' => '肥', '��' => '肢', '��' => '肱', '��' => '股', '��' => '肫', '��' => '肩', '��' => '肴', '��' => '肪', '��' => '肯', '��' => '臥', '��' => '臾', '��' => '舍', '��' => '芳', '��' => '芝', '��' => '芙', '��' => '芭', '��' => '芽', '��' => '芟', '��' => '芹', '��' => '花', '��' => '芬', '��' => '芥', '��' => '芯', '��' => '芸', '��' => '芣', '��' => '芰', '��' => '芾', '��' => '芷', '��' => '虎', '��' => '虱', '��' => '初', '��' => '表', '��' => '軋', '��' => '迎', '��' => '返', '��' => '近', '��' => '邵', '��' => '邸', '��' => '邱', '��' => '邶', '��' => '采', '��' => '金', '��' => '長', '��' => '門', '��' => '阜', '��' => '陀', '��' => '阿', '��' => '阻', '��' => '附', '�@' => '陂', '�A' => '隹', '�B' => '雨', '�C' => '青', '�D' => '非', '�E' => '亟', '�F' => '亭', '�G' => '亮', '�H' => '信', '�I' => '侵', '�J' => '侯', '�K' => '便', '�L' => '俠', '�M' => '俑', '�N' => '俏', '�O' => '保', '�P' => '促', '�Q' => '侶', '�R' => '俘', '�S' => '俟', '�T' => '俊', '�U' => '俗', '�V' => '侮', '�W' => '俐', '�X' => '俄', '�Y' => '係', '�Z' => '俚', '�[' => '俎', '�\\' => '俞', '�]' => '侷', '�^' => '兗', '�_' => '冒', '�`' => '冑', '�a' => '冠', '�b' => '剎', '�c' => '剃', '�d' => '削', '�e' => '前', '�f' => '剌', '�g' => '剋', '�h' => '則', '�i' => '勇', '�j' => '勉', '�k' => '勃', '�l' => '勁', '�m' => '匍', '�n' => '南', '�o' => '卻', '�p' => '厚', '�q' => '叛', '�r' => '咬', '�s' => '哀', '�t' => '咨', '�u' => '哎', '�v' => '哉', '�w' => '咸', '�x' => '咦', '�y' => '咳', '�z' => '哇', '�{' => '哂', '�|' => '咽', '�}' => '咪', '�~' => '品', '��' => '哄', '��' => '哈', '��' => '咯', '��' => '咫', '��' => '咱', '��' => '咻', '��' => '咩', '��' => '咧', '��' => '咿', '��' => '囿', '��' => '垂', '��' => '型', '��' => '垠', '��' => '垣', '��' => '垢', '��' => '城', '��' => '垮', '��' => '垓', '��' => '奕', '��' => '契', '��' => '奏', '��' => '奎', '��' => '奐', '��' => '姜', '��' => '姘', '��' => '姿', '��' => '姣', '��' => '姨', '��' => '娃', '��' => '姥', '��' => '姪', '��' => '姚', '��' => '姦', '��' => '威', '��' => '姻', '��' => '孩', '��' => '宣', '��' => '宦', '��' => '室', '��' => '客', '��' => '宥', '��' => '封', '��' => '屎', '��' => '屏', '��' => '屍', '��' => '屋', '��' => '峙', '��' => '峒', '��' => '巷', '��' => '帝', '��' => '帥', '��' => '帟', '��' => '幽', '��' => '庠', '��' => '度', '��' => '建', '��' => '弈', '��' => '弭', '��' => '彥', '��' => '很', '��' => '待', '��' => '徊', '��' => '律', '��' => '徇', '��' => '後', '��' => '徉', '��' => '怒', '��' => '思', '��' => '怠', '��' => '急', '��' => '怎', '��' => '怨', '��' => '恍', '��' => '恰', '��' => '恨', '��' => '恢', '��' => '恆', '��' => '恃', '��' => '恬', '��' => '恫', '��' => '恪', '��' => '恤', '��' => '扁', '��' => '拜', '��' => '挖', '��' => '按', '��' => '拼', '��' => '拭', '��' => '持', '��' => '拮', '��' => '拽', '��' => '指', '��' => '拱', '��' => '拷', '�@' => '拯', '�A' => '括', '�B' => '拾', '�C' => '拴', '�D' => '挑', '�E' => '挂', '�F' => '政', '�G' => '故', '�H' => '斫', '�I' => '施', '�J' => '既', '�K' => '春', '�L' => '昭', '�M' => '映', '�N' => '昧', '�O' => '是', '�P' => '星', '�Q' => '昨', '�R' => '昱', '�S' => '昤', '�T' => '曷', '�U' => '柿', '�V' => '染', '�W' => '柱', '�X' => '柔', '�Y' => '某', '�Z' => '柬', '�[' => '架', '�\\' => '枯', '�]' => '柵', '�^' => '柩', '�_' => '柯', '�`' => '柄', '�a' => '柑', '�b' => '枴', '�c' => '柚', '�d' => '查', '�e' => '枸', '�f' => '柏', '�g' => '柞', '�h' => '柳', '�i' => '枰', '�j' => '柙', '�k' => '柢', '�l' => '柝', '�m' => '柒', '�n' => '歪', '�o' => '殃', '�p' => '殆', '�q' => '段', '�r' => '毒', '�s' => '毗', '�t' => '氟', '�u' => '泉', '�v' => '洋', '�w' => '洲', '�x' => '洪', '�y' => '流', '�z' => '津', '�{' => '洌', '�|' => '洱', '�}' => '洞', '�~' => '洗', '��' => '活', '��' => '洽', '��' => '派', '��' => '洶', '��' => '洛', '��' => '泵', '��' => '洹', '��' => '洧', '��' => '洸', '��' => '洩', '��' => '洮', '��' => '洵', '��' => '洎', '��' => '洫', '��' => '炫', '��' => '為', '��' => '炳', '��' => '炬', '��' => '炯', '��' => '炭', '��' => '炸', '��' => '炮', '��' => '炤', '��' => '爰', '��' => '牲', '��' => '牯', '��' => '牴', '��' => '狩', '��' => '狠', '��' => '狡', '��' => '玷', '��' => '珊', '��' => '玻', '��' => '玲', '��' => '珍', '��' => '珀', '��' => '玳', '��' => '甚', '��' => '甭', '��' => '畏', '��' => '界', '��' => '畎', '��' => '畋', '��' => '疫', '��' => '疤', '��' => '疥', '��' => '疢', '��' => '疣', '��' => '癸', '��' => '皆', '��' => '皇', '��' => '皈', '��' => '盈', '��' => '盆', '��' => '盃', '��' => '盅', '��' => '省', '��' => '盹', '��' => '相', '��' => '眉', '��' => '看', '��' => '盾', '��' => '盼', '��' => '眇', '��' => '矜', '��' => '砂', '��' => '研', '��' => '砌', '��' => '砍', '��' => '祆', '��' => '祉', '��' => '祈', '��' => '祇', '��' => '禹', '��' => '禺', '��' => '科', '��' => '秒', '��' => '秋', '��' => '穿', '��' => '突', '��' => '竿', '��' => '竽', '��' => '籽', '��' => '紂', '��' => '紅', '��' => '紀', '��' => '紉', '��' => '紇', '��' => '約', '��' => '紆', '��' => '缸', '��' => '美', '��' => '羿', '��' => '耄', '�@' => '耐', '�A' => '耍', '�B' => '耑', '�C' => '耶', '�D' => '胖', '�E' => '胥', '�F' => '胚', '�G' => '胃', '�H' => '胄', '�I' => '背', '�J' => '胡', '�K' => '胛', '�L' => '胎', '�M' => '胞', '�N' => '胤', '�O' => '胝', '�P' => '致', '�Q' => '舢', '�R' => '苧', '�S' => '范', '�T' => '茅', '�U' => '苣', '�V' => '苛', '�W' => '苦', '�X' => '茄', '�Y' => '若', '�Z' => '茂', '�[' => '茉', '�\\' => '苒', '�]' => '苗', '�^' => '英', '�_' => '茁', '�`' => '苜', '�a' => '苔', '�b' => '苑', '�c' => '苞', '�d' => '苓', '�e' => '苟', '�f' => '苯', '�g' => '茆', '�h' => '虐', '�i' => '虹', '�j' => '虻', '�k' => '虺', '�l' => '衍', '�m' => '衫', '�n' => '要', '�o' => '觔', '�p' => '計', '�q' => '訂', '�r' => '訃', '�s' => '貞', '�t' => '負', '�u' => '赴', '�v' => '赳', '�w' => '趴', '�x' => '軍', '�y' => '軌', '�z' => '述', '�{' => '迦', '�|' => '迢', '�}' => '迪', '�~' => '迥', '��' => '迭', '��' => '迫', '��' => '迤', '��' => '迨', '��' => '郊', '��' => '郎', '��' => '郁', '��' => '郃', '��' => '酋', '��' => '酊', '��' => '重', '��' => '閂', '��' => '限', '��' => '陋', '��' => '陌', '��' => '降', '��' => '面', '��' => '革', '��' => '韋', '��' => '韭', '��' => '音', '��' => '頁', '��' => '風', '��' => '飛', '��' => '食', '��' => '首', '��' => '香', '��' => '乘', '��' => '亳', '��' => '倌', '��' => '倍', '��' => '倣', '��' => '俯', '��' => '倦', '��' => '倥', '��' => '俸', '��' => '倩', '��' => '倖', '��' => '倆', '��' => '值', '��' => '借', '��' => '倚', '��' => '倒', '��' => '們', '��' => '俺', '��' => '倀', '��' => '倔', '��' => '倨', '��' => '俱', '��' => '倡', '��' => '個', '��' => '候', '��' => '倘', '��' => '俳', '��' => '修', '��' => '倭', '��' => '倪', '��' => '俾', '��' => '倫', '��' => '倉', '��' => '兼', '��' => '冤', '��' => '冥', '��' => '冢', '��' => '凍', '��' => '凌', '��' => '准', '��' => '凋', '��' => '剖', '��' => '剜', '��' => '剔', '��' => '剛', '��' => '剝', '��' => '匪', '��' => '卿', '��' => '原', '��' => '厝', '��' => '叟', '��' => '哨', '��' => '唐', '��' => '唁', '��' => '唷', '��' => '哼', '��' => '哥', '��' => '哲', '��' => '唆', '��' => '哺', '��' => '唔', '��' => '哩', '��' => '哭', '��' => '員', '��' => '唉', '��' => '哮', '��' => '哪', '�@' => '哦', '�A' => '唧', '�B' => '唇', '�C' => '哽', '�D' => '唏', '�E' => '圃', '�F' => '圄', '�G' => '埂', '�H' => '埔', '�I' => '埋', '�J' => '埃', '�K' => '堉', '�L' => '夏', '�M' => '套', '�N' => '奘', '�O' => '奚', '�P' => '娑', '�Q' => '娘', '�R' => '娜', '�S' => '娟', '�T' => '娛', '�U' => '娓', '�V' => '姬', '�W' => '娠', '�X' => '娣', '�Y' => '娩', '�Z' => '娥', '�[' => '娌', '�\\' => '娉', '�]' => '孫', '�^' => '屘', '�_' => '宰', '�`' => '害', '�a' => '家', '�b' => '宴', '�c' => '宮', '�d' => '宵', '�e' => '容', '�f' => '宸', '�g' => '射', '�h' => '屑', '�i' => '展', '�j' => '屐', '�k' => '峭', '�l' => '峽', '�m' => '峻', '�n' => '峪', '�o' => '峨', '�p' => '峰', '�q' => '島', '�r' => '崁', '�s' => '峴', '�t' => '差', '�u' => '席', '�v' => '師', '�w' => '庫', '�x' => '庭', '�y' => '座', '�z' => '弱', '�{' => '徒', '�|' => '徑', '�}' => '徐', '�~' => '恙', '��' => '恣', '��' => '恥', '��' => '恐', '��' => '恕', '��' => '恭', '��' => '恩', '��' => '息', '��' => '悄', '��' => '悟', '��' => '悚', '��' => '悍', '��' => '悔', '��' => '悌', '��' => '悅', '��' => '悖', '��' => '扇', '��' => '拳', '��' => '挈', '��' => '拿', '��' => '捎', '��' => '挾', '��' => '振', '��' => '捕', '��' => '捂', '��' => '捆', '��' => '捏', '��' => '捉', '��' => '挺', '��' => '捐', '��' => '挽', '��' => '挪', '��' => '挫', '��' => '挨', '��' => '捍', '��' => '捌', '��' => '效', '��' => '敉', '��' => '料', '��' => '旁', '��' => '旅', '��' => '時', '��' => '晉', '��' => '晏', '��' => '晃', '��' => '晒', '��' => '晌', '��' => '晅', '��' => '晁', '��' => '書', '��' => '朔', '��' => '朕', '��' => '朗', '��' => '校', '��' => '核', '��' => '案', '��' => '框', '��' => '桓', '��' => '根', '��' => '桂', '��' => '桔', '��' => '栩', '��' => '梳', '��' => '栗', '��' => '桌', '��' => '桑', '��' => '栽', '��' => '柴', '��' => '桐', '��' => '桀', '��' => '格', '��' => '桃', '��' => '株', '��' => '桅', '��' => '栓', '��' => '栘', '��' => '桁', '��' => '殊', '��' => '殉', '��' => '殷', '��' => '氣', '��' => '氧', '��' => '氨', '��' => '氦', '��' => '氤', '��' => '泰', '��' => '浪', '��' => '涕', '��' => '消', '��' => '涇', '��' => '浦', '��' => '浸', '��' => '海', '��' => '浙', '��' => '涓', '�@' => '浬', '�A' => '涉', '�B' => '浮', '�C' => '浚', '�D' => '浴', '�E' => '浩', '�F' => '涌', '�G' => '涊', '�H' => '浹', '�I' => '涅', '�J' => '浥', '�K' => '涔', '�L' => '烊', '�M' => '烘', '�N' => '烤', '�O' => '烙', '�P' => '烈', '�Q' => '烏', '�R' => '爹', '�S' => '特', '�T' => '狼', '�U' => '狹', '�V' => '狽', '�W' => '狸', '�X' => '狷', '�Y' => '玆', '�Z' => '班', '�[' => '琉', '�\\' => '珮', '�]' => '珠', '�^' => '珪', '�_' => '珞', '�`' => '畔', '�a' => '畝', '�b' => '畜', '�c' => '畚', '�d' => '留', '�e' => '疾', '�f' => '病', '�g' => '症', '�h' => '疲', '�i' => '疳', '�j' => '疽', '�k' => '疼', '�l' => '疹', '�m' => '痂', '�n' => '疸', '�o' => '皋', '�p' => '皰', '�q' => '益', '�r' => '盍', '�s' => '盎', '�t' => '眩', '�u' => '真', '�v' => '眠', '�w' => '眨', '�x' => '矩', '�y' => '砰', '�z' => '砧', '�{' => '砸', '�|' => '砝', '�}' => '破', '�~' => '砷', '��' => '砥', '��' => '砭', '��' => '砠', '��' => '砟', '��' => '砲', '��' => '祕', '��' => '祐', '��' => '祠', '��' => '祟', '��' => '祖', '��' => '神', '��' => '祝', '��' => '祗', '��' => '祚', '��' => '秤', '��' => '秣', '��' => '秧', '��' => '租', '��' => '秦', '��' => '秩', '��' => '秘', '��' => '窄', '��' => '窈', '��' => '站', '��' => '笆', '��' => '笑', '��' => '粉', '��' => '紡', '��' => '紗', '��' => '紋', '��' => '紊', '��' => '素', '��' => '索', '��' => '純', '��' => '紐', '��' => '紕', '��' => '級', '��' => '紜', '��' => '納', '��' => '紙', '��' => '紛', '��' => '缺', '��' => '罟', '��' => '羔', '��' => '翅', '��' => '翁', '��' => '耆', '��' => '耘', '��' => '耕', '��' => '耙', '��' => '耗', '��' => '耽', '��' => '耿', '��' => '胱', '��' => '脂', '��' => '胰', '��' => '脅', '��' => '胭', '��' => '胴', '��' => '脆', '��' => '胸', '��' => '胳', '��' => '脈', '��' => '能', '��' => '脊', '��' => '胼', '��' => '胯', '��' => '臭', '��' => '臬', '��' => '舀', '��' => '舐', '��' => '航', '��' => '舫', '��' => '舨', '��' => '般', '��' => '芻', '��' => '茫', '��' => '荒', '��' => '荔', '��' => '荊', '��' => '茸', '��' => '荐', '��' => '草', '��' => '茵', '��' => '茴', '��' => '荏', '��' => '茲', '��' => '茹', '��' => '茶', '��' => '茗', '��' => '荀', '��' => '茱', '��' => '茨', '��' => '荃', '�@' => '虔', '�A' => '蚊', '�B' => '蚪', '�C' => '蚓', '�D' => '蚤', '�E' => '蚩', '�F' => '蚌', '�G' => '蚣', '�H' => '蚜', '�I' => '衰', '�J' => '衷', '�K' => '袁', '�L' => '袂', '�M' => '衽', '�N' => '衹', '�O' => '記', '�P' => '訐', '�Q' => '討', '�R' => '訌', '�S' => '訕', '�T' => '訊', '�U' => '託', '�V' => '訓', '�W' => '訖', '�X' => '訏', '�Y' => '訑', '�Z' => '豈', '�[' => '豺', '�\\' => '豹', '�]' => '財', '�^' => '貢', '�_' => '起', '�`' => '躬', '�a' => '軒', '�b' => '軔', '�c' => '軏', '�d' => '辱', '�e' => '送', '�f' => '逆', '�g' => '迷', '�h' => '退', '�i' => '迺', '�j' => '迴', '�k' => '逃', '�l' => '追', '�m' => '逅', '�n' => '迸', '�o' => '邕', '�p' => '郡', '�q' => '郝', '�r' => '郢', '�s' => '酒', '�t' => '配', '�u' => '酌', '�v' => '釘', '�w' => '針', '�x' => '釗', '�y' => '釜', '�z' => '釙', '�{' => '閃', '�|' => '院', '�}' => '陣', '�~' => '陡', '��' => '陛', '��' => '陝', '��' => '除', '��' => '陘', '��' => '陞', '��' => '隻', '��' => '飢', '��' => '馬', '��' => '骨', '��' => '高', '��' => '鬥', '��' => '鬲', '��' => '鬼', '��' => '乾', '��' => '偺', '��' => '偽', '��' => '停', '��' => '假', '��' => '偃', '��' => '偌', '��' => '做', '��' => '偉', '��' => '健', '��' => '偶', '��' => '偎', '��' => '偕', '��' => '偵', '��' => '側', '��' => '偷', '��' => '偏', '��' => '倏', '��' => '偯', '��' => '偭', '��' => '兜', '��' => '冕', '��' => '凰', '��' => '剪', '��' => '副', '��' => '勒', '��' => '務', '��' => '勘', '��' => '動', '��' => '匐', '��' => '匏', '��' => '匙', '��' => '匿', '��' => '區', '��' => '匾', '��' => '參', '��' => '曼', '��' => '商', '��' => '啪', '��' => '啦', '��' => '啄', '��' => '啞', '��' => '啡', '��' => '啃', '��' => '啊', '��' => '唱', '��' => '啖', '��' => '問', '��' => '啕', '��' => '唯', '��' => '啤', '��' => '唸', '��' => '售', '��' => '啜', '��' => '唬', '��' => '啣', '��' => '唳', '��' => '啁', '��' => '啗', '��' => '圈', '��' => '國', '��' => '圉', '��' => '域', '��' => '堅', '��' => '堊', '��' => '堆', '��' => '埠', '��' => '埤', '��' => '基', '��' => '堂', '��' => '堵', '��' => '執', '��' => '培', '��' => '夠', '��' => '奢', '��' => '娶', '��' => '婁', '��' => '婉', '��' => '婦', '��' => '婪', '��' => '婀', '�@' => '娼', '�A' => '婢', '�B' => '婚', '�C' => '婆', '�D' => '婊', '�E' => '孰', '�F' => '寇', '�G' => '寅', '�H' => '寄', '�I' => '寂', '�J' => '宿', '�K' => '密', '�L' => '尉', '�M' => '專', '�N' => '將', '�O' => '屠', '�P' => '屜', '�Q' => '屝', '�R' => '崇', '�S' => '崆', '�T' => '崎', '�U' => '崛', '�V' => '崖', '�W' => '崢', '�X' => '崑', '�Y' => '崩', '�Z' => '崔', '�[' => '崙', '�\\' => '崤', '�]' => '崧', '�^' => '崗', '�_' => '巢', '�`' => '常', '�a' => '帶', '�b' => '帳', '�c' => '帷', '�d' => '康', '�e' => '庸', '�f' => '庶', '�g' => '庵', '�h' => '庾', '�i' => '張', '�j' => '強', '�k' => '彗', '�l' => '彬', '�m' => '彩', '�n' => '彫', '�o' => '得', '�p' => '徙', '�q' => '從', '�r' => '徘', '�s' => '御', '�t' => '徠', '�u' => '徜', '�v' => '恿', '�w' => '患', '�x' => '悉', '�y' => '悠', '�z' => '您', '�{' => '惋', '�|' => '悴', '�}' => '惦', '�~' => '悽', '��' => '情', '��' => '悻', '��' => '悵', '��' => '惜', '��' => '悼', '��' => '惘', '��' => '惕', '��' => '惆', '��' => '惟', '��' => '悸', '��' => '惚', '��' => '惇', '��' => '戚', '��' => '戛', '��' => '扈', '��' => '掠', '��' => '控', '��' => '捲', '��' => '掖', '��' => '探', '��' => '接', '��' => '捷', '��' => '捧', '��' => '掘', '��' => '措', '��' => '捱', '��' => '掩', '��' => '掉', '��' => '掃', '��' => '掛', '��' => '捫', '��' => '推', '��' => '掄', '��' => '授', '��' => '掙', '��' => '採', '��' => '掬', '��' => '排', '��' => '掏', '��' => '掀', '��' => '捻', '��' => '捩', '��' => '捨', '��' => '捺', '��' => '敝', '��' => '敖', '��' => '救', '��' => '教', '��' => '敗', '��' => '啟', '��' => '敏', '��' => '敘', '��' => '敕', '��' => '敔', '��' => '斜', '��' => '斛', '��' => '斬', '��' => '族', '��' => '旋', '��' => '旌', '��' => '旎', '��' => '晝', '��' => '晚', '��' => '晤', '��' => '晨', '��' => '晦', '��' => '晞', '��' => '曹', '��' => '勗', '��' => '望', '��' => '梁', '��' => '梯', '��' => '梢', '��' => '梓', '��' => '梵', '��' => '桿', '��' => '桶', '��' => '梱', '��' => '梧', '��' => '梗', '��' => '械', '��' => '梃', '��' => '棄', '��' => '梭', '��' => '梆', '��' => '梅', '��' => '梔', '��' => '條', '��' => '梨', '��' => '梟', '��' => '梡', '��' => '梂', '��' => '欲', '��' => '殺', '�@' => '毫', '�A' => '毬', '�B' => '氫', '�C' => '涎', '�D' => '涼', '�E' => '淳', '�F' => '淙', '�G' => '液', '�H' => '淡', '�I' => '淌', '�J' => '淤', '�K' => '添', '�L' => '淺', '�M' => '清', '�N' => '淇', '�O' => '淋', '�P' => '涯', '�Q' => '淑', '�R' => '涮', '�S' => '淞', '�T' => '淹', '�U' => '涸', '�V' => '混', '�W' => '淵', '�X' => '淅', '�Y' => '淒', '�Z' => '渚', '�[' => '涵', '�\\' => '淚', '�]' => '淫', '�^' => '淘', '�_' => '淪', '�`' => '深', '�a' => '淮', '�b' => '淨', '�c' => '淆', '�d' => '淄', '�e' => '涪', '�f' => '淬', '�g' => '涿', '�h' => '淦', '�i' => '烹', '�j' => '焉', '�k' => '焊', '�l' => '烽', '�m' => '烯', '�n' => '爽', '�o' => '牽', '�p' => '犁', '�q' => '猜', '�r' => '猛', '�s' => '猖', '�t' => '猓', '�u' => '猙', '�v' => '率', '�w' => '琅', '�x' => '琊', '�y' => '球', '�z' => '理', '�{' => '現', '�|' => '琍', '�}' => '瓠', '�~' => '瓶', '��' => '瓷', '��' => '甜', '��' => '產', '��' => '略', '��' => '畦', '��' => '畢', '��' => '異', '��' => '疏', '��' => '痔', '��' => '痕', '��' => '疵', '��' => '痊', '��' => '痍', '��' => '皎', '��' => '盔', '��' => '盒', '��' => '盛', '��' => '眷', '��' => '眾', '��' => '眼', '��' => '眶', '��' => '眸', '��' => '眺', '��' => '硫', '��' => '硃', '��' => '硎', '��' => '祥', '��' => '票', '��' => '祭', '��' => '移', '��' => '窒', '��' => '窕', '��' => '笠', '��' => '笨', '��' => '笛', '��' => '第', '��' => '符', '��' => '笙', '��' => '笞', '��' => '笮', '��' => '粒', '��' => '粗', '��' => '粕', '��' => '絆', '��' => '絃', '��' => '統', '��' => '紮', '��' => '紹', '��' => '紼', '��' => '絀', '��' => '細', '��' => '紳', '��' => '組', '��' => '累', '��' => '終', '��' => '紲', '��' => '紱', '��' => '缽', '��' => '羞', '��' => '羚', '��' => '翌', '��' => '翎', '��' => '習', '��' => '耜', '��' => '聊', '��' => '聆', '��' => '脯', '��' => '脖', '��' => '脣', '��' => '脫', '��' => '脩', '��' => '脰', '��' => '脤', '��' => '舂', '��' => '舵', '��' => '舷', '��' => '舶', '��' => '船', '��' => '莎', '��' => '莞', '��' => '莘', '��' => '荸', '��' => '莢', '��' => '莖', '��' => '莽', '��' => '莫', '��' => '莒', '��' => '莊', '��' => '莓', '��' => '莉', '��' => '莠', '��' => '荷', '��' => '荻', '��' => '荼', '�@' => '莆', '�A' => '莧', '�B' => '處', '�C' => '彪', '�D' => '蛇', '�E' => '蛀', '�F' => '蚶', '�G' => '蛄', '�H' => '蚵', '�I' => '蛆', '�J' => '蛋', '�K' => '蚱', '�L' => '蚯', '�M' => '蛉', '�N' => '術', '�O' => '袞', '�P' => '袈', '�Q' => '被', '�R' => '袒', '�S' => '袖', '�T' => '袍', '�U' => '袋', '�V' => '覓', '�W' => '規', '�X' => '訪', '�Y' => '訝', '�Z' => '訣', '�[' => '訥', '�\\' => '許', '�]' => '設', '�^' => '訟', '�_' => '訛', '�`' => '訢', '�a' => '豉', '�b' => '豚', '�c' => '販', '�d' => '責', '�e' => '貫', '�f' => '貨', '�g' => '貪', '�h' => '貧', '�i' => '赧', '�j' => '赦', '�k' => '趾', '�l' => '趺', '�m' => '軛', '�n' => '軟', '�o' => '這', '�p' => '逍', '�q' => '通', '�r' => '逗', '�s' => '連', '�t' => '速', '�u' => '逝', '�v' => '逐', '�w' => '逕', '�x' => '逞', '�y' => '造', '�z' => '透', '�{' => '逢', '�|' => '逖', '�}' => '逛', '�~' => '途', '��' => '部', '��' => '郭', '��' => '都', '��' => '酗', '��' => '野', '��' => '釵', '��' => '釦', '��' => '釣', '��' => '釧', '��' => '釭', '��' => '釩', '��' => '閉', '��' => '陪', '��' => '陵', '��' => '陳', '��' => '陸', '��' => '陰', '��' => '陴', '��' => '陶', '��' => '陷', '��' => '陬', '��' => '雀', '��' => '雪', '��' => '雩', '��' => '章', '��' => '竟', '��' => '頂', '��' => '頃', '��' => '魚', '��' => '鳥', '��' => '鹵', '��' => '鹿', '��' => '麥', '��' => '麻', '��' => '傢', '��' => '傍', '��' => '傅', '��' => '備', '��' => '傑', '��' => '傀', '��' => '傖', '��' => '傘', '��' => '傚', '��' => '最', '��' => '凱', '��' => '割', '��' => '剴', '��' => '創', '��' => '剩', '��' => '勞', '��' => '勝', '��' => '勛', '��' => '博', '��' => '厥', '��' => '啻', '��' => '喀', '��' => '喧', '��' => '啼', '��' => '喊', '��' => '喝', '��' => '喘', '��' => '喂', '��' => '喜', '��' => '喪', '��' => '喔', '��' => '喇', '��' => '喋', '��' => '喃', '��' => '喳', '��' => '單', '��' => '喟', '��' => '唾', '��' => '喲', '��' => '喚', '��' => '喻', '��' => '喬', '��' => '喱', '��' => '啾', '��' => '喉', '��' => '喫', '��' => '喙', '��' => '圍', '��' => '堯', '��' => '堪', '��' => '場', '��' => '堤', '��' => '堰', '��' => '報', '��' => '堡', '��' => '堝', '��' => '堠', '��' => '壹', '��' => '壺', '��' => '奠', '�@' => '婷', '�A' => '媚', '�B' => '婿', '�C' => '媒', '�D' => '媛', '�E' => '媧', '�F' => '孳', '�G' => '孱', '�H' => '寒', '�I' => '富', '�J' => '寓', '�K' => '寐', '�L' => '尊', '�M' => '尋', '�N' => '就', '�O' => '嵌', '�P' => '嵐', '�Q' => '崴', '�R' => '嵇', '�S' => '巽', '�T' => '幅', '�U' => '帽', '�V' => '幀', '�W' => '幃', '�X' => '幾', '�Y' => '廊', '�Z' => '廁', '�[' => '廂', '�\\' => '廄', '�]' => '弼', '�^' => '彭', '�_' => '復', '�`' => '循', '�a' => '徨', '�b' => '惑', '�c' => '惡', '�d' => '悲', '�e' => '悶', '�f' => '惠', '�g' => '愜', '�h' => '愣', '�i' => '惺', '�j' => '愕', '�k' => '惰', '�l' => '惻', '�m' => '惴', '�n' => '慨', '�o' => '惱', '�p' => '愎', '�q' => '惶', '�r' => '愉', '�s' => '愀', '�t' => '愒', '�u' => '戟', '�v' => '扉', '�w' => '掣', '�x' => '掌', '�y' => '描', '�z' => '揀', '�{' => '揩', '�|' => '揉', '�}' => '揆', '�~' => '揍', '��' => '插', '��' => '揣', '��' => '提', '��' => '握', '��' => '揖', '��' => '揭', '��' => '揮', '��' => '捶', '��' => '援', '��' => '揪', '��' => '換', '��' => '摒', '��' => '揚', '��' => '揹', '��' => '敞', '��' => '敦', '��' => '敢', '��' => '散', '��' => '斑', '��' => '斐', '��' => '斯', '��' => '普', '��' => '晰', '��' => '晴', '��' => '晶', '��' => '景', '��' => '暑', '��' => '智', '��' => '晾', '��' => '晷', '��' => '曾', '��' => '替', '��' => '期', '��' => '朝', '��' => '棺', '��' => '棕', '��' => '棠', '��' => '棘', '��' => '棗', '��' => '椅', '��' => '棟', '��' => '棵', '��' => '森', '��' => '棧', '��' => '棹', '��' => '棒', '��' => '棲', '��' => '棣', '��' => '棋', '��' => '棍', '��' => '植', '��' => '椒', '��' => '椎', '��' => '棉', '��' => '棚', '��' => '楮', '��' => '棻', '��' => '款', '��' => '欺', '��' => '欽', '��' => '殘', '��' => '殖', '��' => '殼', '��' => '毯', '��' => '氮', '��' => '氯', '��' => '氬', '��' => '港', '��' => '游', '��' => '湔', '��' => '渡', '��' => '渲', '��' => '湧', '��' => '湊', '��' => '渠', '��' => '渥', '��' => '渣', '��' => '減', '��' => '湛', '��' => '湘', '��' => '渤', '��' => '湖', '��' => '湮', '��' => '渭', '��' => '渦', '��' => '湯', '��' => '渴', '��' => '湍', '��' => '渺', '��' => '測', '��' => '湃', '��' => '渝', '��' => '渾', '��' => '滋', '�@' => '溉', '�A' => '渙', '�B' => '湎', '�C' => '湣', '�D' => '湄', '�E' => '湲', '�F' => '湩', '�G' => '湟', '�H' => '焙', '�I' => '焚', '�J' => '焦', '�K' => '焰', '�L' => '無', '�M' => '然', '�N' => '煮', '�O' => '焜', '�P' => '牌', '�Q' => '犄', '�R' => '犀', '�S' => '猶', '�T' => '猥', '�U' => '猴', '�V' => '猩', '�W' => '琺', '�X' => '琪', '�Y' => '琳', '�Z' => '琢', '�[' => '琥', '�\\' => '琵', '�]' => '琶', '�^' => '琴', '�_' => '琯', '�`' => '琛', '�a' => '琦', '�b' => '琨', '�c' => '甥', '�d' => '甦', '�e' => '畫', '�f' => '番', '�g' => '痢', '�h' => '痛', '�i' => '痣', '�j' => '痙', '�k' => '痘', '�l' => '痞', '�m' => '痠', '�n' => '登', '�o' => '發', '�p' => '皖', '�q' => '皓', '�r' => '皴', '�s' => '盜', '�t' => '睏', '�u' => '短', '�v' => '硝', '�w' => '硬', '�x' => '硯', '�y' => '稍', '�z' => '稈', '�{' => '程', '�|' => '稅', '�}' => '稀', '�~' => '窘', '��' => '窗', '��' => '窖', '��' => '童', '��' => '竣', '��' => '等', '��' => '策', '��' => '筆', '��' => '筐', '��' => '筒', '��' => '答', '��' => '筍', '��' => '筋', '��' => '筏', '��' => '筑', '��' => '粟', '��' => '粥', '��' => '絞', '��' => '結', '��' => '絨', '��' => '絕', '��' => '紫', '��' => '絮', '��' => '絲', '��' => '絡', '��' => '給', '��' => '絢', '��' => '絰', '��' => '絳', '��' => '善', '��' => '翔', '��' => '翕', '��' => '耋', '��' => '聒', '��' => '肅', '��' => '腕', '��' => '腔', '��' => '腋', '��' => '腑', '��' => '腎', '��' => '脹', '��' => '腆', '��' => '脾', '��' => '腌', '��' => '腓', '��' => '腴', '��' => '舒', '��' => '舜', '��' => '菩', '��' => '萃', '��' => '菸', '��' => '萍', '��' => '菠', '��' => '菅', '��' => '萋', '��' => '菁', '��' => '華', '��' => '菱', '��' => '菴', '��' => '著', '��' => '萊', '��' => '菰', '��' => '萌', '��' => '菌', '��' => '菽', '��' => '菲', '��' => '菊', '��' => '萸', '��' => '萎', '��' => '萄', '��' => '菜', '��' => '萇', '��' => '菔', '��' => '菟', '��' => '虛', '��' => '蛟', '��' => '蛙', '��' => '蛭', '��' => '蛔', '��' => '蛛', '��' => '蛤', '��' => '蛐', '��' => '蛞', '��' => '街', '��' => '裁', '��' => '裂', '��' => '袱', '��' => '覃', '��' => '視', '��' => '註', '��' => '詠', '��' => '評', '��' => '詞', '��' => '証', '��' => '詁', '�@' => '詔', '�A' => '詛', '�B' => '詐', '�C' => '詆', '�D' => '訴', '�E' => '診', '�F' => '訶', '�G' => '詖', '�H' => '象', '�I' => '貂', '�J' => '貯', '�K' => '貼', '�L' => '貳', '�M' => '貽', '�N' => '賁', '�O' => '費', '�P' => '賀', '�Q' => '貴', '�R' => '買', '�S' => '貶', '�T' => '貿', '�U' => '貸', '�V' => '越', '�W' => '超', '�X' => '趁', '�Y' => '跎', '�Z' => '距', '�[' => '跋', '�\\' => '跚', '�]' => '跑', '�^' => '跌', '�_' => '跛', '�`' => '跆', '�a' => '軻', '�b' => '軸', '�c' => '軼', '�d' => '辜', '�e' => '逮', '�f' => '逵', '�g' => '週', '�h' => '逸', '�i' => '進', '�j' => '逶', '�k' => '鄂', '�l' => '郵', '�m' => '鄉', '�n' => '郾', '�o' => '酣', '�p' => '酥', '�q' => '量', '�r' => '鈔', '�s' => '鈕', '�t' => '鈣', '�u' => '鈉', '�v' => '鈞', '�w' => '鈍', '�x' => '鈐', '�y' => '鈇', '�z' => '鈑', '�{' => '閔', '�|' => '閏', '�}' => '開', '�~' => '閑', '��' => '間', '��' => '閒', '��' => '閎', '��' => '隊', '��' => '階', '��' => '隋', '��' => '陽', '��' => '隅', '��' => '隆', '��' => '隍', '��' => '陲', '��' => '隄', '��' => '雁', '��' => '雅', '��' => '雄', '��' => '集', '��' => '雇', '��' => '雯', '��' => '雲', '��' => '韌', '��' => '項', '��' => '順', '��' => '須', '��' => '飧', '��' => '飪', '��' => '飯', '��' => '飩', '��' => '飲', '��' => '飭', '��' => '馮', '��' => '馭', '��' => '黃', '��' => '黍', '��' => '黑', '��' => '亂', '��' => '傭', '��' => '債', '��' => '傲', '��' => '傳', '��' => '僅', '��' => '傾', '��' => '催', '��' => '傷', '��' => '傻', '��' => '傯', '��' => '僇', '��' => '剿', '��' => '剷', '��' => '剽', '��' => '募', '��' => '勦', '��' => '勤', '��' => '勢', '��' => '勣', '��' => '匯', '��' => '嗟', '��' => '嗨', '��' => '嗓', '��' => '嗦', '��' => '嗎', '��' => '嗜', '��' => '嗇', '��' => '嗑', '��' => '嗣', '��' => '嗤', '��' => '嗯', '��' => '嗚', '��' => '嗡', '��' => '嗅', '��' => '嗆', '��' => '嗥', '��' => '嗉', '��' => '園', '��' => '圓', '��' => '塞', '��' => '塑', '��' => '塘', '��' => '塗', '��' => '塚', '��' => '塔', '��' => '填', '��' => '塌', '��' => '塭', '��' => '塊', '��' => '塢', '��' => '塒', '��' => '塋', '��' => '奧', '��' => '嫁', '��' => '嫉', '��' => '嫌', '��' => '媾', '��' => '媽', '��' => '媼', '�@' => '媳', '�A' => '嫂', '�B' => '媲', '�C' => '嵩', '�D' => '嵯', '�E' => '幌', '�F' => '幹', '�G' => '廉', '�H' => '廈', '�I' => '弒', '�J' => '彙', '�K' => '徬', '�L' => '微', '�M' => '愚', '�N' => '意', '�O' => '慈', '�P' => '感', '�Q' => '想', '�R' => '愛', '�S' => '惹', '�T' => '愁', '�U' => '愈', '�V' => '慎', '�W' => '慌', '�X' => '慄', '�Y' => '慍', '�Z' => '愾', '�[' => '愴', '�\\' => '愧', '�]' => '愍', '�^' => '愆', '�_' => '愷', '�`' => '戡', '�a' => '戢', '�b' => '搓', '�c' => '搾', '�d' => '搞', '�e' => '搪', '�f' => '搭', '�g' => '搽', '�h' => '搬', '�i' => '搏', '�j' => '搜', '�k' => '搔', '�l' => '損', '�m' => '搶', '�n' => '搖', '�o' => '搗', '�p' => '搆', '�q' => '敬', '�r' => '斟', '�s' => '新', '�t' => '暗', '�u' => '暉', '�v' => '暇', '�w' => '暈', '�x' => '暖', '�y' => '暄', '�z' => '暘', '�{' => '暍', '�|' => '會', '�}' => '榔', '�~' => '業', '��' => '楚', '��' => '楷', '��' => '楠', '��' => '楔', '��' => '極', '��' => '椰', '��' => '概', '��' => '楊', '��' => '楨', '��' => '楫', '��' => '楞', '��' => '楓', '��' => '楹', '��' => '榆', '��' => '楝', '��' => '楣', '��' => '楛', '��' => '歇', '��' => '歲', '��' => '毀', '��' => '殿', '��' => '毓', '��' => '毽', '��' => '溢', '��' => '溯', '��' => '滓', '��' => '溶', '��' => '滂', '��' => '源', '��' => '溝', '��' => '滇', '��' => '滅', '��' => '溥', '��' => '溘', '��' => '溼', '��' => '溺', '��' => '溫', '��' => '滑', '��' => '準', '��' => '溜', '��' => '滄', '��' => '滔', '��' => '溪', '��' => '溧', '��' => '溴', '��' => '煎', '��' => '煙', '��' => '煩', '��' => '煤', '��' => '煉', '��' => '照', '��' => '煜', '��' => '煬', '��' => '煦', '��' => '煌', '��' => '煥', '��' => '煞', '��' => '煆', '��' => '煨', '��' => '煖', '��' => '爺', '��' => '牒', '��' => '猷', '��' => '獅', '��' => '猿', '��' => '猾', '��' => '瑯', '��' => '瑚', '��' => '瑕', '��' => '瑟', '��' => '瑞', '��' => '瑁', '��' => '琿', '��' => '瑙', '��' => '瑛', '��' => '瑜', '��' => '當', '��' => '畸', '��' => '瘀', '��' => '痰', '��' => '瘁', '��' => '痲', '��' => '痱', '��' => '痺', '��' => '痿', '��' => '痴', '��' => '痳', '��' => '盞', '��' => '盟', '��' => '睛', '��' => '睫', '��' => '睦', '��' => '睞', '��' => '督', '�@' => '睹', '�A' => '睪', '�B' => '睬', '�C' => '睜', '�D' => '睥', '�E' => '睨', '�F' => '睢', '�G' => '矮', '�H' => '碎', '�I' => '碰', '�J' => '碗', '�K' => '碘', '�L' => '碌', '�M' => '碉', '�N' => '硼', '�O' => '碑', '�P' => '碓', '�Q' => '硿', '�R' => '祺', '�S' => '祿', '�T' => '禁', '�U' => '萬', '�V' => '禽', '�W' => '稜', '�X' => '稚', '�Y' => '稠', '�Z' => '稔', '�[' => '稟', '�\\' => '稞', '�]' => '窟', '�^' => '窠', '�_' => '筷', '�`' => '節', '�a' => '筠', '�b' => '筮', '�c' => '筧', '�d' => '粱', '�e' => '粳', '�f' => '粵', '�g' => '經', '�h' => '絹', '�i' => '綑', '�j' => '綁', '�k' => '綏', '�l' => '絛', '�m' => '置', '�n' => '罩', '�o' => '罪', '�p' => '署', '�q' => '義', '�r' => '羨', '�s' => '群', '�t' => '聖', '�u' => '聘', '�v' => '肆', '�w' => '肄', '�x' => '腱', '�y' => '腰', '�z' => '腸', '�{' => '腥', '�|' => '腮', '�}' => '腳', '�~' => '腫', '��' => '腹', '��' => '腺', '��' => '腦', '��' => '舅', '��' => '艇', '��' => '蒂', '��' => '葷', '��' => '落', '��' => '萱', '��' => '葵', '��' => '葦', '��' => '葫', '��' => '葉', '��' => '葬', '��' => '葛', '��' => '萼', '��' => '萵', '��' => '葡', '��' => '董', '��' => '葩', '��' => '葭', '��' => '葆', '��' => '虞', '��' => '虜', '��' => '號', '��' => '蛹', '��' => '蜓', '��' => '蜈', '��' => '蜇', '��' => '蜀', '��' => '蛾', '��' => '蛻', '��' => '蜂', '��' => '蜃', '��' => '蜆', '��' => '蜊', '��' => '衙', '��' => '裟', '��' => '裔', '��' => '裙', '��' => '補', '��' => '裘', '��' => '裝', '��' => '裡', '��' => '裊', '��' => '裕', '��' => '裒', '��' => '覜', '��' => '解', '��' => '詫', '��' => '該', '��' => '詳', '��' => '試', '��' => '詩', '��' => '詰', '��' => '誇', '��' => '詼', '��' => '詣', '��' => '誠', '��' => '話', '��' => '誅', '��' => '詭', '��' => '詢', '��' => '詮', '��' => '詬', '��' => '詹', '��' => '詻', '��' => '訾', '��' => '詨', '��' => '豢', '��' => '貊', '��' => '貉', '��' => '賊', '��' => '資', '��' => '賈', '��' => '賄', '��' => '貲', '��' => '賃', '��' => '賂', '��' => '賅', '��' => '跡', '��' => '跟', '��' => '跨', '��' => '路', '��' => '跳', '��' => '跺', '��' => '跪', '��' => '跤', '��' => '跦', '��' => '躲', '��' => '較', '��' => '載', '��' => '軾', '��' => '輊', '�@' => '辟', '�A' => '農', '�B' => '運', '�C' => '遊', '�D' => '道', '�E' => '遂', '�F' => '達', '�G' => '逼', '�H' => '違', '�I' => '遐', '�J' => '遇', '�K' => '遏', '�L' => '過', '�M' => '遍', '�N' => '遑', '�O' => '逾', '�P' => '遁', '�Q' => '鄒', '�R' => '鄗', '�S' => '酬', '�T' => '酪', '�U' => '酩', '�V' => '釉', '�W' => '鈷', '�X' => '鉗', '�Y' => '鈸', '�Z' => '鈽', '�[' => '鉀', '�\\' => '鈾', '�]' => '鉛', '�^' => '鉋', '�_' => '鉤', '�`' => '鉑', '�a' => '鈴', '�b' => '鉉', '�c' => '鉍', '�d' => '鉅', '�e' => '鈹', '�f' => '鈿', '�g' => '鉚', '�h' => '閘', '�i' => '隘', '�j' => '隔', '�k' => '隕', '�l' => '雍', '�m' => '雋', '�n' => '雉', '�o' => '雊', '�p' => '雷', '�q' => '電', '�r' => '雹', '�s' => '零', '�t' => '靖', '�u' => '靴', '�v' => '靶', '�w' => '預', '�x' => '頑', '�y' => '頓', '�z' => '頊', '�{' => '頒', '�|' => '頌', '�}' => '飼', '�~' => '飴', '��' => '飽', '��' => '飾', '��' => '馳', '��' => '馱', '��' => '馴', '��' => '髡', '��' => '鳩', '��' => '麂', '��' => '鼎', '��' => '鼓', '��' => '鼠', '��' => '僧', '��' => '僮', '��' => '僥', '��' => '僖', '��' => '僭', '��' => '僚', '��' => '僕', '��' => '像', '��' => '僑', '��' => '僱', '��' => '僎', '��' => '僩', '��' => '兢', '��' => '凳', '��' => '劃', '��' => '劂', '��' => '匱', '��' => '厭', '��' => '嗾', '��' => '嘀', '��' => '嘛', '��' => '嘗', '��' => '嗽', '��' => '嘔', '��' => '嘆', '��' => '嘉', '��' => '嘍', '��' => '嘎', '��' => '嗷', '��' => '嘖', '��' => '嘟', '��' => '嘈', '��' => '嘐', '��' => '嗶', '��' => '團', '��' => '圖', '��' => '塵', '��' => '塾', '��' => '境', '��' => '墓', '��' => '墊', '��' => '塹', '��' => '墅', '��' => '塽', '��' => '壽', '��' => '夥', '��' => '夢', '��' => '夤', '��' => '奪', '��' => '奩', '��' => '嫡', '��' => '嫦', '��' => '嫩', '��' => '嫗', '��' => '嫖', '��' => '嫘', '��' => '嫣', '��' => '孵', '��' => '寞', '��' => '寧', '��' => '寡', '��' => '寥', '��' => '實', '��' => '寨', '��' => '寢', '��' => '寤', '��' => '察', '��' => '對', '��' => '屢', '��' => '嶄', '��' => '嶇', '��' => '幛', '��' => '幣', '��' => '幕', '��' => '幗', '��' => '幔', '��' => '廓', '��' => '廖', '��' => '弊', '��' => '彆', '��' => '彰', '��' => '徹', '��' => '慇', '�@' => '愿', '�A' => '態', '�B' => '慷', '�C' => '慢', '�D' => '慣', '�E' => '慟', '�F' => '慚', '�G' => '慘', '�H' => '慵', '�I' => '截', '�J' => '撇', '�K' => '摘', '�L' => '摔', '�M' => '撤', '�N' => '摸', '�O' => '摟', '�P' => '摺', '�Q' => '摑', '�R' => '摧', '�S' => '搴', '�T' => '摭', '�U' => '摻', '�V' => '敲', '�W' => '斡', '�X' => '旗', '�Y' => '旖', '�Z' => '暢', '�[' => '暨', '�\\' => '暝', '�]' => '榜', '�^' => '榨', '�_' => '榕', '�`' => '槁', '�a' => '榮', '�b' => '槓', '�c' => '構', '�d' => '榛', '�e' => '榷', '�f' => '榻', '�g' => '榫', '�h' => '榴', '�i' => '槐', '�j' => '槍', '�k' => '榭', '�l' => '槌', '�m' => '榦', '�n' => '槃', '�o' => '榣', '�p' => '歉', '�q' => '歌', '�r' => '氳', '�s' => '漳', '�t' => '演', '�u' => '滾', '�v' => '漓', '�w' => '滴', '�x' => '漩', '�y' => '漾', '�z' => '漠', '�{' => '漬', '�|' => '漏', '�}' => '漂', '�~' => '漢', '��' => '滿', '��' => '滯', '��' => '漆', '��' => '漱', '��' => '漸', '��' => '漲', '��' => '漣', '��' => '漕', '��' => '漫', '��' => '漯', '��' => '澈', '��' => '漪', '��' => '滬', '��' => '漁', '��' => '滲', '��' => '滌', '��' => '滷', '��' => '熔', '��' => '熙', '��' => '煽', '��' => '熊', '��' => '熄', '��' => '熒', '��' => '爾', '��' => '犒', '��' => '犖', '��' => '獄', '��' => '獐', '��' => '瑤', '��' => '瑣', '��' => '瑪', '��' => '瑰', '��' => '瑭', '��' => '甄', '��' => '疑', '��' => '瘧', '��' => '瘍', '��' => '瘋', '��' => '瘉', '��' => '瘓', '��' => '盡', '��' => '監', '��' => '瞄', '��' => '睽', '��' => '睿', '��' => '睡', '��' => '磁', '��' => '碟', '��' => '碧', '��' => '碳', '��' => '碩', '��' => '碣', '��' => '禎', '��' => '福', '��' => '禍', '��' => '種', '��' => '稱', '��' => '窪', '��' => '窩', '��' => '竭', '��' => '端', '��' => '管', '��' => '箕', '��' => '箋', '��' => '筵', '��' => '算', '��' => '箝', '��' => '箔', '��' => '箏', '��' => '箸', '��' => '箇', '��' => '箄', '��' => '粹', '��' => '粽', '��' => '精', '��' => '綻', '��' => '綰', '��' => '綜', '��' => '綽', '��' => '綾', '��' => '綠', '��' => '緊', '��' => '綴', '��' => '網', '��' => '綱', '��' => '綺', '��' => '綢', '��' => '綿', '��' => '綵', '��' => '綸', '��' => '維', '��' => '緒', '��' => '緇', '��' => '綬', '�@' => '罰', '�A' => '翠', '�B' => '翡', '�C' => '翟', '�D' => '聞', '�E' => '聚', '�F' => '肇', '�G' => '腐', '�H' => '膀', '�I' => '膏', '�J' => '膈', '�K' => '膊', '�L' => '腿', '�M' => '膂', '�N' => '臧', '�O' => '臺', '�P' => '與', '�Q' => '舔', '�R' => '舞', '�S' => '艋', '�T' => '蓉', '�U' => '蒿', '�V' => '蓆', '�W' => '蓄', '�X' => '蒙', '�Y' => '蒞', '�Z' => '蒲', '�[' => '蒜', '�\\' => '蓋', '�]' => '蒸', '�^' => '蓀', '�_' => '蓓', '�`' => '蒐', '�a' => '蒼', '�b' => '蓑', '�c' => '蓊', '�d' => '蜿', '�e' => '蜜', '�f' => '蜻', '�g' => '蜢', '�h' => '蜥', '�i' => '蜴', '�j' => '蜘', '�k' => '蝕', '�l' => '蜷', '�m' => '蜩', '�n' => '裳', '�o' => '褂', '�p' => '裴', '�q' => '裹', '�r' => '裸', '�s' => '製', '�t' => '裨', '�u' => '褚', '�v' => '裯', '�w' => '誦', '�x' => '誌', '�y' => '語', '�z' => '誣', '�{' => '認', '�|' => '誡', '�}' => '誓', '�~' => '誤', '��' => '說', '��' => '誥', '��' => '誨', '��' => '誘', '��' => '誑', '��' => '誚', '��' => '誧', '��' => '豪', '��' => '貍', '��' => '貌', '��' => '賓', '��' => '賑', '��' => '賒', '��' => '赫', '��' => '趙', '��' => '趕', '��' => '跼', '��' => '輔', '��' => '輒', '��' => '輕', '��' => '輓', '��' => '辣', '��' => '遠', '��' => '遘', '��' => '遜', '��' => '遣', '��' => '遙', '��' => '遞', '��' => '遢', '��' => '遝', '��' => '遛', '��' => '鄙', '��' => '鄘', '��' => '鄞', '��' => '酵', '��' => '酸', '��' => '酷', '��' => '酴', '��' => '鉸', '��' => '銀', '��' => '銅', '��' => '銘', '��' => '銖', '��' => '鉻', '��' => '銓', '��' => '銜', '��' => '銨', '��' => '鉼', '��' => '銑', '��' => '閡', '��' => '閨', '��' => '閩', '��' => '閣', '��' => '閥', '��' => '閤', '��' => '隙', '��' => '障', '��' => '際', '��' => '雌', '��' => '雒', '��' => '需', '��' => '靼', '��' => '鞅', '��' => '韶', '��' => '頗', '��' => '領', '��' => '颯', '��' => '颱', '��' => '餃', '��' => '餅', '��' => '餌', '��' => '餉', '��' => '駁', '��' => '骯', '��' => '骰', '��' => '髦', '��' => '魁', '��' => '魂', '��' => '鳴', '��' => '鳶', '��' => '鳳', '��' => '麼', '��' => '鼻', '��' => '齊', '��' => '億', '��' => '儀', '��' => '僻', '��' => '僵', '��' => '價', '��' => '儂', '��' => '儈', '��' => '儉', '��' => '儅', '��' => '凜', '�@' => '劇', '�A' => '劈', '�B' => '劉', '�C' => '劍', '�D' => '劊', '�E' => '勰', '�F' => '厲', '�G' => '嘮', '�H' => '嘻', '�I' => '嘹', '�J' => '嘲', '�K' => '嘿', '�L' => '嘴', '�M' => '嘩', '�N' => '噓', '�O' => '噎', '�P' => '噗', '�Q' => '噴', '�R' => '嘶', '�S' => '嘯', '�T' => '嘰', '�U' => '墀', '�V' => '墟', '�W' => '增', '�X' => '墳', '�Y' => '墜', '�Z' => '墮', '�[' => '墩', '�\\' => '墦', '�]' => '奭', '�^' => '嬉', '�_' => '嫻', '�`' => '嬋', '�a' => '嫵', '�b' => '嬌', '�c' => '嬈', '�d' => '寮', '�e' => '寬', '�f' => '審', '�g' => '寫', '�h' => '層', '�i' => '履', '�j' => '嶝', '�k' => '嶔', '�l' => '幢', '�m' => '幟', '�n' => '幡', '�o' => '廢', '�p' => '廚', '�q' => '廟', '�r' => '廝', '�s' => '廣', '�t' => '廠', '�u' => '彈', '�v' => '影', '�w' => '德', '�x' => '徵', '�y' => '慶', '�z' => '慧', '�{' => '慮', '�|' => '慝', '�}' => '慕', '�~' => '憂', '��' => '慼', '��' => '慰', '��' => '慫', '��' => '慾', '��' => '憧', '��' => '憐', '��' => '憫', '��' => '憎', '��' => '憬', '��' => '憚', '��' => '憤', '��' => '憔', '��' => '憮', '��' => '戮', '��' => '摩', '��' => '摯', '��' => '摹', '��' => '撞', '��' => '撲', '��' => '撈', '��' => '撐', '��' => '撰', '��' => '撥', '��' => '撓', '��' => '撕', '��' => '撩', '��' => '撒', '��' => '撮', '��' => '播', '��' => '撫', '��' => '撚', '��' => '撬', '��' => '撙', '��' => '撢', '��' => '撳', '��' => '敵', '��' => '敷', '��' => '數', '��' => '暮', '��' => '暫', '��' => '暴', '��' => '暱', '��' => '樣', '��' => '樟', '��' => '槨', '��' => '樁', '��' => '樞', '��' => '標', '��' => '槽', '��' => '模', '��' => '樓', '��' => '樊', '��' => '槳', '��' => '樂', '��' => '樅', '��' => '槭', '��' => '樑', '��' => '歐', '��' => '歎', '��' => '殤', '��' => '毅', '��' => '毆', '��' => '漿', '��' => '潼', '��' => '澄', '��' => '潑', '��' => '潦', '��' => '潔', '��' => '澆', '��' => '潭', '��' => '潛', '��' => '潸', '��' => '潮', '��' => '澎', '��' => '潺', '��' => '潰', '��' => '潤', '��' => '澗', '��' => '潘', '��' => '滕', '��' => '潯', '��' => '潠', '��' => '潟', '��' => '熟', '��' => '熬', '��' => '熱', '��' => '熨', '��' => '牖', '��' => '犛', '��' => '獎', '��' => '獗', '��' => '瑩', '��' => '璋', '��' => '璃', '�@' => '瑾', '�A' => '璀', '�B' => '畿', '�C' => '瘠', '�D' => '瘩', '�E' => '瘟', '�F' => '瘤', '�G' => '瘦', '�H' => '瘡', '�I' => '瘢', '�J' => '皚', '�K' => '皺', '�L' => '盤', '�M' => '瞎', '�N' => '瞇', '�O' => '瞌', '�P' => '瞑', '�Q' => '瞋', '�R' => '磋', '�S' => '磅', '�T' => '確', '�U' => '磊', '�V' => '碾', '�W' => '磕', '�X' => '碼', '�Y' => '磐', '�Z' => '稿', '�[' => '稼', '�\\' => '穀', '�]' => '稽', '�^' => '稷', '�_' => '稻', '�`' => '窯', '�a' => '窮', '�b' => '箭', '�c' => '箱', '�d' => '範', '�e' => '箴', '�f' => '篆', '�g' => '篇', '�h' => '篁', '�i' => '箠', '�j' => '篌', '�k' => '糊', '�l' => '締', '�m' => '練', '�n' => '緯', '�o' => '緻', '�p' => '緘', '�q' => '緬', '�r' => '緝', '�s' => '編', '�t' => '緣', '�u' => '線', '�v' => '緞', '�w' => '緩', '�x' => '綞', '�y' => '緙', '�z' => '緲', '�{' => '緹', '�|' => '罵', '�}' => '罷', '�~' => '羯', '��' => '翩', '��' => '耦', '��' => '膛', '��' => '膜', '��' => '膝', '��' => '膠', '��' => '膚', '��' => '膘', '��' => '蔗', '��' => '蔽', '��' => '蔚', '��' => '蓮', '��' => '蔬', '��' => '蔭', '��' => '蔓', '��' => '蔑', '��' => '蔣', '��' => '蔡', '��' => '蔔', '��' => '蓬', '��' => '蔥', '��' => '蓿', '��' => '蔆', '��' => '螂', '��' => '蝴', '��' => '蝶', '��' => '蝠', '��' => '蝦', '��' => '蝸', '��' => '蝨', '��' => '蝙', '��' => '蝗', '��' => '蝌', '��' => '蝓', '��' => '衛', '��' => '衝', '��' => '褐', '��' => '複', '��' => '褒', '��' => '褓', '��' => '褕', '��' => '褊', '��' => '誼', '��' => '諒', '��' => '談', '��' => '諄', '��' => '誕', '��' => '請', '��' => '諸', '��' => '課', '��' => '諉', '��' => '諂', '��' => '調', '��' => '誰', '��' => '論', '��' => '諍', '��' => '誶', '��' => '誹', '��' => '諛', '��' => '豌', '��' => '豎', '��' => '豬', '��' => '賠', '��' => '賞', '��' => '賦', '��' => '賤', '��' => '賬', '��' => '賭', '��' => '賢', '��' => '賣', '��' => '賜', '��' => '質', '��' => '賡', '��' => '赭', '��' => '趟', '��' => '趣', '��' => '踫', '��' => '踐', '��' => '踝', '��' => '踢', '��' => '踏', '��' => '踩', '��' => '踟', '��' => '踡', '��' => '踞', '��' => '躺', '��' => '輝', '��' => '輛', '��' => '輟', '��' => '輩', '��' => '輦', '��' => '輪', '��' => '輜', '��' => '輞', '�@' => '輥', '�A' => '適', '�B' => '遮', '�C' => '遨', '�D' => '遭', '�E' => '遷', '�F' => '鄰', '�G' => '鄭', '�H' => '鄧', '�I' => '鄱', '�J' => '醇', '�K' => '醉', '�L' => '醋', '�M' => '醃', '�N' => '鋅', '�O' => '銻', '�P' => '銷', '�Q' => '鋪', '�R' => '銬', '�S' => '鋤', '�T' => '鋁', '�U' => '銳', '�V' => '銼', '�W' => '鋒', '�X' => '鋇', '�Y' => '鋰', '�Z' => '銲', '�[' => '閭', '�\\' => '閱', '�]' => '霄', '�^' => '霆', '�_' => '震', '�`' => '霉', '�a' => '靠', '�b' => '鞍', '�c' => '鞋', '�d' => '鞏', '�e' => '頡', '�f' => '頫', '�g' => '頜', '�h' => '颳', '�i' => '養', '�j' => '餓', '�k' => '餒', '�l' => '餘', '�m' => '駝', '�n' => '駐', '�o' => '駟', '�p' => '駛', '�q' => '駑', '�r' => '駕', '�s' => '駒', '�t' => '駙', '�u' => '骷', '�v' => '髮', '�w' => '髯', '�x' => '鬧', '�y' => '魅', '�z' => '魄', '�{' => '魷', '�|' => '魯', '�}' => '鴆', '�~' => '鴉', '��' => '鴃', '��' => '麩', '��' => '麾', '��' => '黎', '��' => '墨', '��' => '齒', '��' => '儒', '��' => '儘', '��' => '儔', '��' => '儐', '��' => '儕', '��' => '冀', '��' => '冪', '��' => '凝', '��' => '劑', '��' => '劓', '��' => '勳', '��' => '噙', '��' => '噫', '��' => '噹', '��' => '噩', '��' => '噤', '��' => '噸', '��' => '噪', '��' => '器', '��' => '噥', '��' => '噱', '��' => '噯', '��' => '噬', '��' => '噢', '��' => '噶', '��' => '壁', '��' => '墾', '��' => '壇', '��' => '壅', '��' => '奮', '��' => '嬝', '��' => '嬴', '��' => '學', '��' => '寰', '��' => '導', '��' => '彊', '��' => '憲', '��' => '憑', '��' => '憩', '��' => '憊', '��' => '懍', '��' => '憶', '��' => '憾', '��' => '懊', '��' => '懈', '��' => '戰', '��' => '擅', '��' => '擁', '��' => '擋', '��' => '撻', '��' => '撼', '��' => '據', '��' => '擄', '��' => '擇', '��' => '擂', '��' => '操', '��' => '撿', '��' => '擒', '��' => '擔', '��' => '撾', '��' => '整', '��' => '曆', '��' => '曉', '��' => '暹', '��' => '曄', '��' => '曇', '��' => '暸', '��' => '樽', '��' => '樸', '��' => '樺', '��' => '橙', '��' => '橫', '��' => '橘', '��' => '樹', '��' => '橄', '��' => '橢', '��' => '橡', '��' => '橋', '��' => '橇', '��' => '樵', '��' => '機', '��' => '橈', '��' => '歙', '��' => '歷', '��' => '氅', '��' => '濂', '��' => '澱', '��' => '澡', '�@' => '濃', '�A' => '澤', '�B' => '濁', '�C' => '澧', '�D' => '澳', '�E' => '激', '�F' => '澹', '�G' => '澶', '�H' => '澦', '�I' => '澠', '�J' => '澴', '�K' => '熾', '�L' => '燉', '�M' => '燐', '�N' => '燒', '�O' => '燈', '�P' => '燕', '�Q' => '熹', '�R' => '燎', '�S' => '燙', '�T' => '燜', '�U' => '燃', '�V' => '燄', '�W' => '獨', '�X' => '璜', '�Y' => '璣', '�Z' => '璘', '�[' => '璟', '�\\' => '璞', '�]' => '瓢', '�^' => '甌', '�_' => '甍', '�`' => '瘴', '�a' => '瘸', '�b' => '瘺', '�c' => '盧', '�d' => '盥', '�e' => '瞠', '�f' => '瞞', '�g' => '瞟', '�h' => '瞥', '�i' => '磨', '�j' => '磚', '�k' => '磬', '�l' => '磧', '�m' => '禦', '�n' => '積', '�o' => '穎', '�p' => '穆', '�q' => '穌', '�r' => '穋', '�s' => '窺', '�t' => '篙', '�u' => '簑', '�v' => '築', '�w' => '篤', '�x' => '篛', '�y' => '篡', '�z' => '篩', '�{' => '篦', '�|' => '糕', '�}' => '糖', '�~' => '縊', '��' => '縑', '��' => '縈', '��' => '縛', '��' => '縣', '��' => '縞', '��' => '縝', '��' => '縉', '��' => '縐', '��' => '罹', '��' => '羲', '��' => '翰', '��' => '翱', '��' => '翮', '��' => '耨', '��' => '膳', '��' => '膩', '��' => '膨', '��' => '臻', '��' => '興', '��' => '艘', '��' => '艙', '��' => '蕊', '��' => '蕙', '��' => '蕈', '��' => '蕨', '��' => '蕩', '��' => '蕃', '��' => '蕉', '��' => '蕭', '��' => '蕪', '��' => '蕞', '��' => '螃', '��' => '螟', '��' => '螞', '��' => '螢', '��' => '融', '��' => '衡', '��' => '褪', '��' => '褲', '��' => '褥', '��' => '褫', '��' => '褡', '��' => '親', '��' => '覦', '��' => '諦', '��' => '諺', '��' => '諫', '��' => '諱', '��' => '謀', '��' => '諜', '��' => '諧', '��' => '諮', '��' => '諾', '��' => '謁', '��' => '謂', '��' => '諷', '��' => '諭', '��' => '諳', '��' => '諶', '��' => '諼', '��' => '豫', '��' => '豭', '��' => '貓', '��' => '賴', '��' => '蹄', '��' => '踱', '��' => '踴', '��' => '蹂', '��' => '踹', '��' => '踵', '��' => '輻', '��' => '輯', '��' => '輸', '��' => '輳', '��' => '辨', '��' => '辦', '��' => '遵', '��' => '遴', '��' => '選', '��' => '遲', '��' => '遼', '��' => '遺', '��' => '鄴', '��' => '醒', '��' => '錠', '��' => '錶', '��' => '鋸', '��' => '錳', '��' => '錯', '��' => '錢', '��' => '鋼', '��' => '錫', '��' => '錄', '��' => '錚', '�@' => '錐', '�A' => '錦', '�B' => '錡', '�C' => '錕', '�D' => '錮', '�E' => '錙', '�F' => '閻', '�G' => '隧', '�H' => '隨', '�I' => '險', '�J' => '雕', '�K' => '霎', '�L' => '霑', '�M' => '霖', '�N' => '霍', '�O' => '霓', '�P' => '霏', '�Q' => '靛', '�R' => '靜', '�S' => '靦', '�T' => '鞘', '�U' => '頰', '�V' => '頸', '�W' => '頻', '�X' => '頷', '�Y' => '頭', '�Z' => '頹', '�[' => '頤', '�\\' => '餐', '�]' => '館', '�^' => '餞', '�_' => '餛', '�`' => '餡', '�a' => '餚', '�b' => '駭', '�c' => '駢', '�d' => '駱', '�e' => '骸', '�f' => '骼', '�g' => '髻', '�h' => '髭', '�i' => '鬨', '�j' => '鮑', '�k' => '鴕', '�l' => '鴣', '�m' => '鴦', '�n' => '鴨', '�o' => '鴒', '�p' => '鴛', '�q' => '默', '�r' => '黔', '�s' => '龍', '�t' => '龜', '�u' => '優', '�v' => '償', '�w' => '儡', '�x' => '儲', '�y' => '勵', '�z' => '嚎', '�{' => '嚀', '�|' => '嚐', '�}' => '嚅', '�~' => '嚇', '��' => '嚏', '��' => '壕', '��' => '壓', '��' => '壑', '��' => '壎', '��' => '嬰', '��' => '嬪', '��' => '嬤', '��' => '孺', '��' => '尷', '��' => '屨', '��' => '嶼', '��' => '嶺', '��' => '嶽', '��' => '嶸', '��' => '幫', '��' => '彌', '��' => '徽', '��' => '應', '��' => '懂', '��' => '懇', '��' => '懦', '��' => '懋', '��' => '戲', '��' => '戴', '��' => '擎', '��' => '擊', '��' => '擘', '��' => '擠', '��' => '擰', '��' => '擦', '��' => '擬', '��' => '擱', '��' => '擢', '��' => '擭', '��' => '斂', '��' => '斃', '��' => '曙', '��' => '曖', '��' => '檀', '��' => '檔', '��' => '檄', '��' => '檢', '��' => '檜', '��' => '櫛', '��' => '檣', '��' => '橾', '��' => '檗', '��' => '檐', '��' => '檠', '��' => '歜', '��' => '殮', '��' => '毚', '��' => '氈', '��' => '濘', '��' => '濱', '��' => '濟', '��' => '濠', '��' => '濛', '��' => '濤', '��' => '濫', '��' => '濯', '��' => '澀', '��' => '濬', '��' => '濡', '��' => '濩', '��' => '濕', '��' => '濮', '��' => '濰', '��' => '燧', '��' => '營', '��' => '燮', '��' => '燦', '��' => '燥', '��' => '燭', '��' => '燬', '��' => '燴', '��' => '燠', '��' => '爵', '��' => '牆', '��' => '獰', '��' => '獲', '��' => '璩', '��' => '環', '��' => '璦', '��' => '璨', '��' => '癆', '��' => '療', '��' => '癌', '��' => '盪', '��' => '瞳', '��' => '瞪', '��' => '瞰', '��' => '瞬', '�@' => '瞧', '�A' => '瞭', '�B' => '矯', '�C' => '磷', '�D' => '磺', '�E' => '磴', '�F' => '磯', '�G' => '礁', '�H' => '禧', '�I' => '禪', '�J' => '穗', '�K' => '窿', '�L' => '簇', '�M' => '簍', '�N' => '篾', '�O' => '篷', '�P' => '簌', '�Q' => '篠', '�R' => '糠', '�S' => '糜', '�T' => '糞', '�U' => '糢', '�V' => '糟', '�W' => '糙', '�X' => '糝', '�Y' => '縮', '�Z' => '績', '�[' => '繆', '�\\' => '縷', '�]' => '縲', '�^' => '繃', '�_' => '縫', '�`' => '總', '�a' => '縱', '�b' => '繅', '�c' => '繁', '�d' => '縴', '�e' => '縹', '�f' => '繈', '�g' => '縵', '�h' => '縿', '�i' => '縯', '�j' => '罄', '�k' => '翳', '�l' => '翼', '�m' => '聱', '�n' => '聲', '�o' => '聰', '�p' => '聯', '�q' => '聳', '�r' => '臆', '�s' => '臃', '�t' => '膺', '�u' => '臂', '�v' => '臀', '�w' => '膿', '�x' => '膽', '�y' => '臉', '�z' => '膾', '�{' => '臨', '�|' => '舉', '�}' => '艱', '�~' => '薪', '��' => '薄', '��' => '蕾', '��' => '薜', '��' => '薑', '��' => '薔', '��' => '薯', '��' => '薛', '��' => '薇', '��' => '薨', '��' => '薊', '��' => '虧', '��' => '蟀', '��' => '蟑', '��' => '螳', '��' => '蟒', '��' => '蟆', '��' => '螫', '��' => '螻', '��' => '螺', '��' => '蟈', '��' => '蟋', '��' => '褻', '��' => '褶', '��' => '襄', '��' => '褸', '��' => '褽', '��' => '覬', '��' => '謎', '��' => '謗', '��' => '謙', '��' => '講', '��' => '謊', '��' => '謠', '��' => '謝', '��' => '謄', '��' => '謐', '��' => '豁', '��' => '谿', '��' => '豳', '��' => '賺', '��' => '賽', '��' => '購', '��' => '賸', '��' => '賻', '��' => '趨', '��' => '蹉', '��' => '蹋', '��' => '蹈', '��' => '蹊', '��' => '轄', '��' => '輾', '��' => '轂', '��' => '轅', '��' => '輿', '��' => '避', '��' => '遽', '��' => '還', '��' => '邁', '��' => '邂', '��' => '邀', '��' => '鄹', '��' => '醣', '��' => '醞', '��' => '醜', '��' => '鍍', '��' => '鎂', '��' => '錨', '��' => '鍵', '��' => '鍊', '��' => '鍥', '��' => '鍋', '��' => '錘', '��' => '鍾', '��' => '鍬', '��' => '鍛', '��' => '鍰', '��' => '鍚', '��' => '鍔', '��' => '闊', '��' => '闋', '��' => '闌', '��' => '闈', '��' => '闆', '��' => '隱', '��' => '隸', '��' => '雖', '��' => '霜', '��' => '霞', '��' => '鞠', '��' => '韓', '��' => '顆', '��' => '颶', '��' => '餵', '��' => '騁', '�@' => '駿', '�A' => '鮮', '�B' => '鮫', '�C' => '鮪', '�D' => '鮭', '�E' => '鴻', '�F' => '鴿', '�G' => '麋', '�H' => '黏', '�I' => '點', '�J' => '黜', '�K' => '黝', '�L' => '黛', '�M' => '鼾', '�N' => '齋', '�O' => '叢', '�P' => '嚕', '�Q' => '嚮', '�R' => '壙', '�S' => '壘', '�T' => '嬸', '�U' => '彝', '�V' => '懣', '�W' => '戳', '�X' => '擴', '�Y' => '擲', '�Z' => '擾', '�[' => '攆', '�\\' => '擺', '�]' => '擻', '�^' => '擷', '�_' => '斷', '�`' => '曜', '�a' => '朦', '�b' => '檳', '�c' => '檬', '�d' => '櫃', '�e' => '檻', '�f' => '檸', '�g' => '櫂', '�h' => '檮', '�i' => '檯', '�j' => '歟', '�k' => '歸', '�l' => '殯', '�m' => '瀉', '�n' => '瀋', '�o' => '濾', '�p' => '瀆', '�q' => '濺', '�r' => '瀑', '�s' => '瀏', '�t' => '燻', '�u' => '燼', '�v' => '燾', '�w' => '燸', '�x' => '獷', '�y' => '獵', '�z' => '璧', '�{' => '璿', '�|' => '甕', '�}' => '癖', '�~' => '癘', '¡' => '癒', '¢' => '瞽', '£' => '瞿', '¤' => '瞻', '¥' => '瞼', '¦' => '礎', '§' => '禮', '¨' => '穡', '©' => '穢', 'ª' => '穠', '«' => '竄', '¬' => '竅', '' => '簫', '®' => '簧', '¯' => '簪', '°' => '簞', '±' => '簣', '²' => '簡', '³' => '糧', '´' => '織', 'µ' => '繕', '¶' => '繞', '·' => '繚', '¸' => '繡', '¹' => '繒', 'º' => '繙', '»' => '罈', '¼' => '翹', '½' => '翻', '¾' => '職', '¿' => '聶', '�' => '臍', '�' => '臏', '��' => '舊', '��' => '藏', '��' => '薩', '��' => '藍', '��' => '藐', '��' => '藉', '��' => '薰', '��' => '薺', '��' => '薹', '��' => '薦', '��' => '蟯', '��' => '蟬', '��' => '蟲', '��' => '蟠', '��' => '覆', '��' => '覲', '��' => '觴', '��' => '謨', '��' => '謹', '��' => '謬', '��' => '謫', '��' => '豐', '��' => '贅', '��' => '蹙', '��' => '蹣', '��' => '蹦', '��' => '蹤', '��' => '蹟', '��' => '蹕', '��' => '軀', '��' => '轉', '��' => '轍', '��' => '邇', '��' => '邃', '��' => '邈', '��' => '醫', '��' => '醬', '��' => '釐', '��' => '鎔', '��' => '鎊', '��' => '鎖', '��' => '鎢', '��' => '鎳', '��' => '鎮', '��' => '鎬', '��' => '鎰', '��' => '鎘', '��' => '鎚', '��' => '鎗', '��' => '闔', '��' => '闖', '�' => '闐', '�' => '闕', '�' => '離', '�' => '雜', '�' => '雙', '�' => '雛', '�' => '雞', '�' => '霤', '�' => '鞣', '�' => '鞦', '�@' => '鞭', '�A' => '韹', '�B' => '額', '�C' => '顏', '�D' => '題', '�E' => '顎', '�F' => '顓', '�G' => '颺', '�H' => '餾', '�I' => '餿', '�J' => '餽', '�K' => '餮', '�L' => '馥', '�M' => '騎', '�N' => '髁', '�O' => '鬃', '�P' => '鬆', '�Q' => '魏', '�R' => '魎', '�S' => '魍', '�T' => '鯊', '�U' => '鯉', '�V' => '鯽', '�W' => '鯈', '�X' => '鯀', '�Y' => '鵑', '�Z' => '鵝', '�[' => '鵠', '�\\' => '黠', '�]' => '鼕', '�^' => '鼬', '�_' => '儳', '�`' => '嚥', '�a' => '壞', '�b' => '壟', '�c' => '壢', '�d' => '寵', '�e' => '龐', '�f' => '廬', '�g' => '懲', '�h' => '懷', '�i' => '懶', '�j' => '懵', '�k' => '攀', '�l' => '攏', '�m' => '曠', '�n' => '曝', '�o' => '櫥', '�p' => '櫝', '�q' => '櫚', '�r' => '櫓', '�s' => '瀛', '�t' => '瀟', '�u' => '瀨', '�v' => '瀚', '�w' => '瀝', '�x' => '瀕', '�y' => '瀘', '�z' => '爆', '�{' => '爍', '�|' => '牘', '�}' => '犢', '�~' => '獸', 'á' => '獺', 'â' => '璽', 'ã' => '瓊', 'ä' => '瓣', 'å' => '疇', 'æ' => '疆', 'ç' => '癟', 'è' => '癡', 'é' => '矇', 'ê' => '礙', 'ë' => '禱', 'ì' => '穫', 'í' => '穩', 'î' => '簾', 'ï' => '簿', 'ð' => '簸', 'ñ' => '簽', 'ò' => '簷', 'ó' => '籀', 'ô' => '繫', 'õ' => '繭', 'ö' => '繹', '÷' => '繩', 'ø' => '繪', 'ù' => '羅', 'ú' => '繳', 'û' => '羶', 'ü' => '羹', 'ý' => '羸', 'þ' => '臘', 'ÿ' => '藩', '�' => '藝', '�' => '藪', '��' => '藕', '��' => '藤', '��' => '藥', '��' => '藷', '��' => '蟻', '��' => '蠅', '��' => '蠍', '��' => '蟹', '��' => '蟾', '��' => '襠', '��' => '襟', '��' => '襖', '��' => '襞', '��' => '譁', '��' => '譜', '��' => '識', '��' => '證', '��' => '譚', '��' => '譎', '��' => '譏', '��' => '譆', '��' => '譙', '��' => '贈', '��' => '贊', '��' => '蹼', '��' => '蹲', '��' => '躇', '��' => '蹶', '��' => '蹬', '��' => '蹺', '��' => '蹴', '��' => '轔', '��' => '轎', '��' => '辭', '��' => '邊', '��' => '邋', '��' => '醱', '��' => '醮', '��' => '鏡', '��' => '鏑', '��' => '鏟', '��' => '鏃', '��' => '鏈', '��' => '鏜', '��' => '鏝', '��' => '鏖', '��' => '鏢', '��' => '鏍', '��' => '鏘', '��' => '鏤', '��' => '鏗', '�' => '鏨', '�' => '關', '�' => '隴', '�' => '難', '�' => '霪', '�' => '霧', '�' => '靡', '�' => '韜', '�' => '韻', '�' => '類', '�@' => '願', '�A' => '顛', '�B' => '颼', '�C' => '饅', '�D' => '饉', '�E' => '騖', '�F' => '騙', '�G' => '鬍', '�H' => '鯨', '�I' => '鯧', '�J' => '鯖', '�K' => '鯛', '�L' => '鶉', '�M' => '鵡', '�N' => '鵲', '�O' => '鵪', '�P' => '鵬', '�Q' => '麒', '�R' => '麗', '�S' => '麓', '�T' => '麴', '�U' => '勸', '�V' => '嚨', '�W' => '嚷', '�X' => '嚶', '�Y' => '嚴', '�Z' => '嚼', '�[' => '壤', '�\\' => '孀', '�]' => '孃', '�^' => '孽', '�_' => '寶', '�`' => '巉', '�a' => '懸', '�b' => '懺', '�c' => '攘', '�d' => '攔', '�e' => '攙', '�f' => '曦', '�g' => '朧', '�h' => '櫬', '�i' => '瀾', '�j' => '瀰', '�k' => '瀲', '�l' => '爐', '�m' => '獻', '�n' => '瓏', '�o' => '癢', '�p' => '癥', '�q' => '礦', '�r' => '礪', '�s' => '礬', '�t' => '礫', '�u' => '竇', '�v' => '競', '�w' => '籌', '�x' => '籃', '�y' => '籍', '�z' => '糯', '�{' => '糰', '�|' => '辮', '�}' => '繽', '�~' => '繼', 'ġ' => '纂', 'Ģ' => '罌', 'ģ' => '耀', 'Ĥ' => '臚', 'ĥ' => '艦', 'Ħ' => '藻', 'ħ' => '藹', 'Ĩ' => '蘑', 'ĩ' => '藺', 'Ī' => '蘆', 'ī' => '蘋', 'Ĭ' => '蘇', 'ĭ' => '蘊', 'Į' => '蠔', 'į' => '蠕', 'İ' => '襤', 'ı' => '覺', 'IJ' => '觸', 'ij' => '議', 'Ĵ' => '譬', 'ĵ' => '警', 'Ķ' => '譯', 'ķ' => '譟', 'ĸ' => '譫', 'Ĺ' => '贏', 'ĺ' => '贍', 'Ļ' => '躉', 'ļ' => '躁', 'Ľ' => '躅', 'ľ' => '躂', 'Ŀ' => '醴', '�' => '釋', '�' => '鐘', '��' => '鐃', '��' => '鏽', '��' => '闡', '��' => '霰', '��' => '飄', '��' => '饒', '��' => '饑', '��' => '馨', '��' => '騫', '��' => '騰', '��' => '騷', '��' => '騵', '��' => '鰓', '��' => '鰍', '��' => '鹹', '��' => '麵', '��' => '黨', '��' => '鼯', '��' => '齟', '��' => '齣', '��' => '齡', '��' => '儷', '��' => '儸', '��' => '囁', '��' => '囀', '��' => '囂', '��' => '夔', '��' => '屬', '��' => '巍', '��' => '懼', '��' => '懾', '��' => '攝', '��' => '攜', '��' => '斕', '��' => '曩', '��' => '櫻', '��' => '欄', '��' => '櫺', '��' => '殲', '��' => '灌', '��' => '爛', '��' => '犧', '��' => '瓖', '��' => '瓔', '��' => '癩', '��' => '矓', '��' => '籐', '��' => '纏', '��' => '續', '��' => '羼', '��' => '蘗', '�' => '蘭', '�' => '蘚', '�' => '蠣', '�' => '蠢', '�' => '蠡', '�' => '蠟', '�' => '襪', '�' => '襬', '�' => '覽', '�' => '譴', '�@' => '護', '�A' => '譽', '�B' => '贓', '�C' => '躊', '�D' => '躍', '�E' => '躋', '�F' => '轟', '�G' => '辯', '�H' => '醺', '�I' => '鐮', '�J' => '鐳', '�K' => '鐵', '�L' => '鐺', '�M' => '鐸', '�N' => '鐲', '�O' => '鐫', '�P' => '闢', '�Q' => '霸', '�R' => '霹', '�S' => '露', '�T' => '響', '�U' => '顧', '�V' => '顥', '�W' => '饗', '�X' => '驅', '�Y' => '驃', '�Z' => '驀', '�[' => '騾', '�\\' => '髏', '�]' => '魔', '�^' => '魑', '�_' => '鰭', '�`' => '鰥', '�a' => '鶯', '�b' => '鶴', '�c' => '鷂', '�d' => '鶸', '�e' => '麝', '�f' => '黯', '�g' => '鼙', '�h' => '齜', '�i' => '齦', '�j' => '齧', '�k' => '儼', '�l' => '儻', '�m' => '囈', '�n' => '囊', '�o' => '囉', '�p' => '孿', '�q' => '巔', '�r' => '巒', '�s' => '彎', '�t' => '懿', '�u' => '攤', '�v' => '權', '�w' => '歡', '�x' => '灑', '�y' => '灘', '�z' => '玀', '�{' => '瓤', '�|' => '疊', '�}' => '癮', '�~' => '癬', 'š' => '禳', 'Ţ' => '籠', 'ţ' => '籟', 'Ť' => '聾', 'ť' => '聽', 'Ŧ' => '臟', 'ŧ' => '襲', 'Ũ' => '襯', 'ũ' => '觼', 'Ū' => '讀', 'ū' => '贖', 'Ŭ' => '贗', 'ŭ' => '躑', 'Ů' => '躓', 'ů' => '轡', 'Ű' => '酈', 'ű' => '鑄', 'Ų' => '鑑', 'ų' => '鑒', 'Ŵ' => '霽', 'ŵ' => '霾', 'Ŷ' => '韃', 'ŷ' => '韁', 'Ÿ' => '顫', 'Ź' => '饕', 'ź' => '驕', 'Ż' => '驍', 'ż' => '髒', 'Ž' => '鬚', 'ž' => '鱉', 'ſ' => '鰱', '�' => '鰾', '�' => '鰻', '��' => '鷓', '��' => '鷗', '��' => '鼴', '��' => '齬', '��' => '齪', '��' => '龔', '��' => '囌', '��' => '巖', '��' => '戀', '��' => '攣', '��' => '攫', '��' => '攪', '��' => '曬', '��' => '欐', '��' => '瓚', '��' => '竊', '��' => '籤', '��' => '籣', '��' => '籥', '��' => '纓', '��' => '纖', '��' => '纔', '��' => '臢', '��' => '蘸', '��' => '蘿', '��' => '蠱', '��' => '變', '��' => '邐', '��' => '邏', '��' => '鑣', '��' => '鑠', '��' => '鑤', '��' => '靨', '��' => '顯', '��' => '饜', '��' => '驚', '��' => '驛', '��' => '驗', '��' => '髓', '��' => '體', '��' => '髑', '��' => '鱔', '��' => '鱗', '��' => '鱖', '��' => '鷥', '��' => '麟', '��' => '黴', '��' => '囑', '��' => '壩', '��' => '攬', '��' => '灞', '�' => '癱', '�' => '癲', '�' => '矗', '�' => '罐', '�' => '羈', '�' => '蠶', '�' => '蠹', '�' => '衢', '�' => '讓', '�' => '讒', '�@' => '讖', '�A' => '艷', '�B' => '贛', '�C' => '釀', '�D' => '鑪', '�E' => '靂', '�F' => '靈', '�G' => '靄', '�H' => '韆', '�I' => '顰', '�J' => '驟', '�K' => '鬢', '�L' => '魘', '�M' => '鱟', '�N' => '鷹', '�O' => '鷺', '�P' => '鹼', '�Q' => '鹽', '�R' => '鼇', '�S' => '齷', '�T' => '齲', '�U' => '廳', '�V' => '欖', '�W' => '灣', '�X' => '籬', '�Y' => '籮', '�Z' => '蠻', '�[' => '觀', '�\\' => '躡', '�]' => '釁', '�^' => '鑲', '�_' => '鑰', '�`' => '顱', '�a' => '饞', '�b' => '髖', '�c' => '鬣', '�d' => '黌', '�e' => '灤', '�f' => '矚', '�g' => '讚', '�h' => '鑷', '�i' => '韉', '�j' => '驢', '�k' => '驥', '�l' => '纜', '�m' => '讜', '�n' => '躪', '�o' => '釅', '�p' => '鑽', '�q' => '鑾', '�r' => '鑼', '�s' => '鱷', '�t' => '鱸', '�u' => '黷', '�v' => '豔', '�w' => '鑿', '�x' => '鸚', '�y' => '爨', '�z' => '驪', '�{' => '鬱', '�|' => '鸛', '�}' => '鸞', '�~' => '籲', '�@' => '乂', '�A' => '乜', '�B' => '凵', '�C' => '匚', '�D' => '厂', '�E' => '万', '�F' => '丌', '�G' => '乇', '�H' => '亍', '�I' => '囗', '�J' => '兀', '�K' => '屮', '�L' => '彳', '�M' => '丏', '�N' => '冇', '�O' => '与', '�P' => '丮', '�Q' => '亓', '�R' => '仂', '�S' => '仉', '�T' => '仈', '�U' => '冘', '�V' => '勼', '�W' => '卬', '�X' => '厹', '�Y' => '圠', '�Z' => '夃', '�[' => '夬', '�\\' => '尐', '�]' => '巿', '�^' => '旡', '�_' => '殳', '�`' => '毌', '�a' => '气', '�b' => '爿', '�c' => '丱', '�d' => '丼', '�e' => '仨', '�f' => '仜', '�g' => '仩', '�h' => '仡', '�i' => '仝', '�j' => '仚', '�k' => '刌', '�l' => '匜', '�m' => '卌', '�n' => '圢', '�o' => '圣', '�p' => '夗', '�q' => '夯', '�r' => '宁', '�s' => '宄', '�t' => '尒', '�u' => '尻', '�v' => '屴', '�w' => '屳', '�x' => '帄', '�y' => '庀', '�z' => '庂', '�{' => '忉', '�|' => '戉', '�}' => '扐', '�~' => '氕', 'ɡ' => '氶', 'ɢ' => '汃', 'ɣ' => '氿', 'ɤ' => '氻', 'ɥ' => '犮', 'ɦ' => '犰', 'ɧ' => '玊', 'ɨ' => '禸', 'ɩ' => '肊', 'ɪ' => '阞', 'ɫ' => '伎', 'ɬ' => '优', 'ɭ' => '伬', 'ɮ' => '仵', 'ɯ' => '伔', 'ɰ' => '仱', 'ɱ' => '伀', 'ɲ' => '价', 'ɳ' => '伈', 'ɴ' => '伝', 'ɵ' => '伂', 'ɶ' => '伅', 'ɷ' => '伢', 'ɸ' => '伓', 'ɹ' => '伄', 'ɺ' => '仴', 'ɻ' => '伒', 'ɼ' => '冱', 'ɽ' => '刓', 'ɾ' => '刉', 'ɿ' => '刐', '�' => '劦', '�' => '匢', '��' => '匟', '��' => '卍', '��' => '厊', '��' => '吇', '��' => '囡', '��' => '囟', '��' => '圮', '��' => '圪', '��' => '圴', '��' => '夼', '��' => '妀', '��' => '奼', '��' => '妅', '��' => '奻', '��' => '奾', '��' => '奷', '��' => '奿', '��' => '孖', '��' => '尕', '��' => '尥', '��' => '屼', '��' => '屺', '��' => '屻', '��' => '屾', '��' => '巟', '��' => '幵', '��' => '庄', '��' => '异', '��' => '弚', '��' => '彴', '��' => '忕', '��' => '忔', '��' => '忏', '��' => '扜', '��' => '扞', '��' => '扤', '��' => '扡', '��' => '扦', '��' => '扢', '��' => '扙', '��' => '扠', '��' => '扚', '��' => '扥', '��' => '旯', '��' => '旮', '��' => '朾', '��' => '朹', '��' => '朸', '��' => '朻', '��' => '机', '��' => '朿', '�' => '朼', '�' => '朳', '�' => '氘', '�' => '汆', '�' => '汒', '�' => '汜', '�' => '汏', '�' => '汊', '�' => '汔', '�' => '汋', '�@' => '汌', '�A' => '灱', '�B' => '牞', '�C' => '犴', '�D' => '犵', '�E' => '玎', '�F' => '甪', '�G' => '癿', '�H' => '穵', '�I' => '网', '�J' => '艸', '�K' => '艼', '�L' => '芀', '�M' => '艽', '�N' => '艿', '�O' => '虍', '�P' => '襾', '�Q' => '邙', '�R' => '邗', '�S' => '邘', '�T' => '邛', '�U' => '邔', '�V' => '阢', '�W' => '阤', '�X' => '阠', '�Y' => '阣', '�Z' => '佖', '�[' => '伻', '�\\' => '佢', '�]' => '佉', '�^' => '体', '�_' => '佤', '�`' => '伾', '�a' => '佧', '�b' => '佒', '�c' => '佟', '�d' => '佁', '�e' => '佘', '�f' => '伭', '�g' => '伳', '�h' => '伿', '�i' => '佡', '�j' => '冏', '�k' => '冹', '�l' => '刜', '�m' => '刞', '�n' => '刡', '�o' => '劭', '�p' => '劮', '�q' => '匉', '�r' => '卣', '�s' => '卲', '�t' => '厎', '�u' => '厏', '�v' => '吰', '�w' => '吷', '�x' => '吪', '�y' => '呔', '�z' => '呅', '�{' => '吙', '�|' => '吜', '�}' => '吥', '�~' => '吘', 'ʡ' => '吽', 'ʢ' => '呏', 'ʣ' => '呁', 'ʤ' => '吨', 'ʥ' => '吤', 'ʦ' => '呇', 'ʧ' => '囮', 'ʨ' => '囧', 'ʩ' => '囥', 'ʪ' => '坁', 'ʫ' => '坅', 'ʬ' => '坌', 'ʭ' => '坉', 'ʮ' => '坋', 'ʯ' => '坒', 'ʰ' => '夆', 'ʱ' => '奀', 'ʲ' => '妦', 'ʳ' => '妘', 'ʴ' => '妠', 'ʵ' => '妗', 'ʶ' => '妎', 'ʷ' => '妢', 'ʸ' => '妐', 'ʹ' => '妏', 'ʺ' => '妧', 'ʻ' => '妡', 'ʼ' => '宎', 'ʽ' => '宒', 'ʾ' => '尨', 'ʿ' => '尪', '�' => '岍', '�' => '岏', '��' => '岈', '��' => '岋', '��' => '岉', '��' => '岒', '��' => '岊', '��' => '岆', '��' => '岓', '��' => '岕', '��' => '巠', '��' => '帊', '��' => '帎', '��' => '庋', '��' => '庉', '��' => '庌', '��' => '庈', '��' => '庍', '��' => '弅', '��' => '弝', '��' => '彸', '��' => '彶', '��' => '忒', '��' => '忑', '��' => '忐', '��' => '忭', '��' => '忨', '��' => '忮', '��' => '忳', '��' => '忡', '��' => '忤', '��' => '忣', '��' => '忺', '��' => '忯', '��' => '忷', '��' => '忻', '��' => '怀', '��' => '忴', '��' => '戺', '��' => '抃', '��' => '抌', '��' => '抎', '��' => '抏', '��' => '抔', '��' => '抇', '��' => '扱', '��' => '扻', '��' => '扺', '��' => '扰', '��' => '抁', '��' => '抈', '��' => '扷', '��' => '扽', '�' => '扲', '�' => '扴', '�' => '攷', '�' => '旰', '�' => '旴', '�' => '旳', '�' => '旲', '�' => '旵', '�' => '杅', '�' => '杇', '�@' => '杙', '�A' => '杕', '�B' => '杌', '�C' => '杈', '�D' => '杝', '�E' => '杍', '�F' => '杚', '�G' => '杋', '�H' => '毐', '�I' => '氙', '�J' => '氚', '�K' => '汸', '�L' => '汧', '�M' => '汫', '�N' => '沄', '�O' => '沋', '�P' => '沏', '�Q' => '汱', '�R' => '汯', '�S' => '汩', '�T' => '沚', '�U' => '汭', '�V' => '沇', '�W' => '沕', '�X' => '沜', '�Y' => '汦', '�Z' => '汳', '�[' => '汥', '�\\' => '汻', '�]' => '沎', '�^' => '灴', '�_' => '灺', '�`' => '牣', '�a' => '犿', '�b' => '犽', '�c' => '狃', '�d' => '狆', '�e' => '狁', '�f' => '犺', '�g' => '狅', '�h' => '玕', '�i' => '玗', '�j' => '玓', '�k' => '玔', '�l' => '玒', '�m' => '町', '�n' => '甹', '�o' => '疔', '�p' => '疕', '�q' => '皁', '�r' => '礽', '�s' => '耴', '�t' => '肕', '�u' => '肙', '�v' => '肐', '�w' => '肒', '�x' => '肜', '�y' => '芐', '�z' => '芏', '�{' => '芅', '�|' => '芎', '�}' => '芑', '�~' => '芓', 'ˡ' => '芊', 'ˢ' => '芃', 'ˣ' => '芄', 'ˤ' => '豸', '˥' => '迉', '˦' => '辿', '˧' => '邟', '˨' => '邡', '˩' => '邥', '˪' => '邞', '˫' => '邧', 'ˬ' => '邠', '˭' => '阰', 'ˮ' => '阨', '˯' => '阯', '˰' => '阭', '˱' => '丳', '˲' => '侘', '˳' => '佼', '˴' => '侅', '˵' => '佽', '˶' => '侀', '˷' => '侇', '˸' => '佶', '˹' => '佴', '˺' => '侉', '˻' => '侄', '˼' => '佷', '˽' => '佌', '˾' => '侗', '˿' => '佪', '�' => '侚', '�' => '佹', '��' => '侁', '��' => '佸', '��' => '侐', '��' => '侜', '��' => '侔', '��' => '侞', '��' => '侒', '��' => '侂', '��' => '侕', '��' => '佫', '��' => '佮', '��' => '冞', '��' => '冼', '��' => '冾', '��' => '刵', '��' => '刲', '��' => '刳', '��' => '剆', '��' => '刱', '��' => '劼', '��' => '匊', '��' => '匋', '��' => '匼', '��' => '厒', '��' => '厔', '��' => '咇', '��' => '呿', '��' => '咁', '��' => '咑', '��' => '咂', '��' => '咈', '��' => '呫', '��' => '呺', '��' => '呾', '��' => '呥', '��' => '呬', '��' => '呴', '��' => '呦', '��' => '咍', '��' => '呯', '��' => '呡', '��' => '呠', '��' => '咘', '��' => '呣', '��' => '呧', '��' => '呤', '��' => '囷', '��' => '囹', '��' => '坯', '��' => '坲', '��' => '坭', '�' => '坫', '�' => '坱', '�' => '坰', '�' => '坶', '�' => '垀', '�' => '坵', '�' => '坻', '�' => '坳', '�' => '坴', '�' => '坢', '�@' => '坨', '�A' => '坽', '�B' => '夌', '�C' => '奅', '�D' => '妵', '�E' => '妺', '�F' => '姏', '�G' => '姎', '�H' => '妲', '�I' => '姌', '�J' => '姁', '�K' => '妶', '�L' => '妼', '�M' => '姃', '�N' => '姖', '�O' => '妱', '�P' => '妽', '�Q' => '姀', '�R' => '姈', '�S' => '妴', '�T' => '姇', '�U' => '孢', '�V' => '孥', '�W' => '宓', '�X' => '宕', '�Y' => '屄', '�Z' => '屇', '�[' => '岮', '�\\' => '岤', '�]' => '岠', '�^' => '岵', '�_' => '岯', '�`' => '岨', '�a' => '岬', '�b' => '岟', '�c' => '岣', '�d' => '岭', '�e' => '岢', '�f' => '岪', '�g' => '岧', '�h' => '岝', '�i' => '岥', '�j' => '岶', '�k' => '岰', '�l' => '岦', '�m' => '帗', '�n' => '帔', '�o' => '帙', '�p' => '弨', '�q' => '弢', '�r' => '弣', '�s' => '弤', '�t' => '彔', '�u' => '徂', '�v' => '彾', '�w' => '彽', '�x' => '忞', '�y' => '忥', '�z' => '怭', '�{' => '怦', '�|' => '怙', '�}' => '怲', '�~' => '怋', '̡' => '怴', '̢' => '怊', '̣' => '怗', '̤' => '怳', '̥' => '怚', '̦' => '怞', '̧' => '怬', '̨' => '怢', '̩' => '怍', '̪' => '怐', '̫' => '怮', '̬' => '怓', '̭' => '怑', '̮' => '怌', '̯' => '怉', '̰' => '怜', '̱' => '戔', '̲' => '戽', '̳' => '抭', '̴' => '抴', '̵' => '拑', '̶' => '抾', '̷' => '抪', '̸' => '抶', '̹' => '拊', '̺' => '抮', '̻' => '抳', '̼' => '抯', '̽' => '抻', '̾' => '抩', '̿' => '抰', '�' => '抸', '�' => '攽', '��' => '斨', '��' => '斻', '��' => '昉', '��' => '旼', '��' => '昄', '��' => '昒', '��' => '昈', '��' => '旻', '��' => '昃', '��' => '昋', '��' => '昍', '��' => '昅', '��' => '旽', '��' => '昑', '��' => '昐', '��' => '曶', '��' => '朊', '��' => '枅', '��' => '杬', '��' => '枎', '��' => '枒', '��' => '杶', '��' => '杻', '��' => '枘', '��' => '枆', '��' => '构', '��' => '杴', '��' => '枍', '��' => '枌', '��' => '杺', '��' => '枟', '��' => '枑', '��' => '枙', '��' => '枃', '��' => '杽', '��' => '极', '��' => '杸', '��' => '杹', '��' => '枔', '��' => '欥', '��' => '殀', '��' => '歾', '��' => '毞', '��' => '氝', '��' => '沓', '��' => '泬', '��' => '泫', '��' => '泮', '��' => '泙', '��' => '沶', '��' => '泔', '�' => '沭', '�' => '泧', '�' => '沷', '�' => '泐', '�' => '泂', '�' => '沺', '�' => '泃', '�' => '泆', '�' => '泭', '�' => '泲', '�@' => '泒', '�A' => '泝', '�B' => '沴', '�C' => '沊', '�D' => '沝', '�E' => '沀', '�F' => '泞', '�G' => '泀', '�H' => '洰', '�I' => '泍', '�J' => '泇', '�K' => '沰', '�L' => '泹', '�M' => '泏', '�N' => '泩', '�O' => '泑', '�P' => '炔', '�Q' => '炘', '�R' => '炅', '�S' => '炓', '�T' => '炆', '�U' => '炄', '�V' => '炑', '�W' => '炖', '�X' => '炂', '�Y' => '炚', '�Z' => '炃', '�[' => '牪', '�\\' => '狖', '�]' => '狋', '�^' => '狘', '�_' => '狉', '�`' => '狜', '�a' => '狒', '�b' => '狔', '�c' => '狚', '�d' => '狌', '�e' => '狑', '�f' => '玤', '�g' => '玡', '�h' => '玭', '�i' => '玦', '�j' => '玢', '�k' => '玠', '�l' => '玬', '�m' => '玝', '�n' => '瓝', '�o' => '瓨', '�p' => '甿', '�q' => '畀', '�r' => '甾', '�s' => '疌', '�t' => '疘', '�u' => '皯', '�v' => '盳', '�w' => '盱', '�x' => '盰', '�y' => '盵', '�z' => '矸', '�{' => '矼', '�|' => '矹', '�}' => '矻', '�~' => '矺', '͡' => '矷', '͢' => '祂', 'ͣ' => '礿', 'ͤ' => '秅', 'ͥ' => '穸', 'ͦ' => '穻', 'ͧ' => '竻', 'ͨ' => '籵', 'ͩ' => '糽', 'ͪ' => '耵', 'ͫ' => '肏', 'ͬ' => '肮', 'ͭ' => '肣', 'ͮ' => '肸', 'ͯ' => '肵', 'Ͱ' => '肭', 'ͱ' => '舠', 'Ͳ' => '芠', 'ͳ' => '苀', 'ʹ' => '芫', '͵' => '芚', 'Ͷ' => '芘', 'ͷ' => '芛', '' => '芵', '' => '芧', 'ͺ' => '芮', 'ͻ' => '芼', 'ͼ' => '芞', 'ͽ' => '芺', ';' => '芴', 'Ϳ' => '芨', '�' => '芡', '�' => '芩', '��' => '苂', '��' => '芤', '��' => '苃', '��' => '芶', '��' => '芢', '��' => '虰', '��' => '虯', '��' => '虭', '��' => '虮', '��' => '豖', '��' => '迒', '��' => '迋', '��' => '迓', '��' => '迍', '��' => '迖', '��' => '迕', '��' => '迗', '��' => '邲', '��' => '邴', '��' => '邯', '��' => '邳', '��' => '邰', '��' => '阹', '��' => '阽', '��' => '阼', '��' => '阺', '��' => '陃', '��' => '俍', '��' => '俅', '��' => '俓', '��' => '侲', '��' => '俉', '��' => '俋', '��' => '俁', '��' => '俔', '��' => '俜', '��' => '俙', '��' => '侻', '��' => '侳', '��' => '俛', '��' => '俇', '��' => '俖', '��' => '侺', '��' => '俀', '��' => '侹', '��' => '俬', '��' => '剄', '��' => '剉', '��' => '勀', '��' => '勂', '��' => '匽', '�' => '卼', '�' => '厗', '�' => '厖', '�' => '厙', '�' => '厘', '�' => '咺', '�' => '咡', '�' => '咭', '�' => '咥', '�' => '哏', '�@' => '哃', '�A' => '茍', '�B' => '咷', '�C' => '咮', '�D' => '哖', '�E' => '咶', '�F' => '哅', '�G' => '哆', '�H' => '咠', '�I' => '呰', '�J' => '咼', '�K' => '咢', '�L' => '咾', '�M' => '呲', '�N' => '哞', '�O' => '咰', '�P' => '垵', '�Q' => '垞', '�R' => '垟', '�S' => '垤', '�T' => '垌', '�U' => '垗', '�V' => '垝', '�W' => '垛', '�X' => '垔', '�Y' => '垘', '�Z' => '垏', '�[' => '垙', '�\\' => '垥', '�]' => '垚', '�^' => '垕', '�_' => '壴', '�`' => '复', '�a' => '奓', '�b' => '姡', '�c' => '姞', '�d' => '姮', '�e' => '娀', '�f' => '姱', '�g' => '姝', '�h' => '姺', '�i' => '姽', '�j' => '姼', '�k' => '姶', '�l' => '姤', '�m' => '姲', '�n' => '姷', '�o' => '姛', '�p' => '姩', '�q' => '姳', '�r' => '姵', '�s' => '姠', '�t' => '姾', '�u' => '姴', '�v' => '姭', '�w' => '宨', '�x' => '屌', '�y' => '峐', '�z' => '峘', '�{' => '峌', '�|' => '峗', '�}' => '峋', '�~' => '峛', 'Ρ' => '峞', '' => '峚', 'Σ' => '峉', 'Τ' => '峇', 'Υ' => '峊', 'Φ' => '峖', 'Χ' => '峓', 'Ψ' => '峔', 'Ω' => '峏', 'Ϊ' => '峈', 'Ϋ' => '峆', 'ά' => '峎', 'έ' => '峟', 'ή' => '峸', 'ί' => '巹', 'ΰ' => '帡', 'α' => '帢', 'β' => '帣', 'γ' => '帠', 'δ' => '帤', 'ε' => '庰', 'ζ' => '庤', 'η' => '庢', 'θ' => '庛', 'ι' => '庣', 'κ' => '庥', 'λ' => '弇', 'μ' => '弮', 'ν' => '彖', 'ξ' => '徆', 'ο' => '怷', '�' => '怹', '�' => '恔', '��' => '恲', '��' => '恞', '��' => '恅', '��' => '恓', '��' => '恇', '��' => '恉', '��' => '恛', '��' => '恌', '��' => '恀', '��' => '恂', '��' => '恟', '��' => '怤', '��' => '恄', '��' => '恘', '��' => '恦', '��' => '恮', '��' => '扂', '��' => '扃', '��' => '拏', '��' => '挍', '��' => '挋', '��' => '拵', '��' => '挎', '��' => '挃', '��' => '拫', '��' => '拹', '��' => '挏', '��' => '挌', '��' => '拸', '��' => '拶', '��' => '挀', '��' => '挓', '��' => '挔', '��' => '拺', '��' => '挕', '��' => '拻', '��' => '拰', '��' => '敁', '��' => '敃', '��' => '斪', '��' => '斿', '��' => '昶', '��' => '昡', '��' => '昲', '��' => '昵', '��' => '昜', '��' => '昦', '��' => '昢', '��' => '昳', '��' => '昫', '��' => '昺', '�' => '昝', '�' => '昴', '�' => '昹', '�' => '昮', '�' => '朏', '�' => '朐', '�' => '柁', '�' => '柲', '�' => '柈', '�' => '枺', '�@' => '柜', '�A' => '枻', '�B' => '柸', '�C' => '柘', '�D' => '柀', '�E' => '枷', '�F' => '柅', '�G' => '柫', '�H' => '柤', '�I' => '柟', '�J' => '枵', '�K' => '柍', '�L' => '枳', '�M' => '柷', '�N' => '柶', '�O' => '柮', '�P' => '柣', '�Q' => '柂', '�R' => '枹', '�S' => '柎', '�T' => '柧', '�U' => '柰', '�V' => '枲', '�W' => '柼', '�X' => '柆', '�Y' => '柭', '�Z' => '柌', '�[' => '枮', '�\\' => '柦', '�]' => '柛', '�^' => '柺', '�_' => '柉', '�`' => '柊', '�a' => '柃', '�b' => '柪', '�c' => '柋', '�d' => '欨', '�e' => '殂', '�f' => '殄', '�g' => '殶', '�h' => '毖', '�i' => '毘', '�j' => '毠', '�k' => '氠', '�l' => '氡', '�m' => '洨', '�n' => '洴', '�o' => '洭', '�p' => '洟', '�q' => '洼', '�r' => '洿', '�s' => '洒', '�t' => '洊', '�u' => '泚', '�v' => '洳', '�w' => '洄', '�x' => '洙', '�y' => '洺', '�z' => '洚', '�{' => '洑', '�|' => '洀', '�}' => '洝', '�~' => '浂', 'ϡ' => '洁', 'Ϣ' => '洘', 'ϣ' => '洷', 'Ϥ' => '洃', 'ϥ' => '洏', 'Ϧ' => '浀', 'ϧ' => '洇', 'Ϩ' => '洠', 'ϩ' => '洬', 'Ϫ' => '洈', 'ϫ' => '洢', 'Ϭ' => '洉', 'ϭ' => '洐', 'Ϯ' => '炷', 'ϯ' => '炟', 'ϰ' => '炾', 'ϱ' => '炱', 'ϲ' => '炰', 'ϳ' => '炡', 'ϴ' => '炴', 'ϵ' => '炵', '϶' => '炩', 'Ϸ' => '牁', 'ϸ' => '牉', 'Ϲ' => '牊', 'Ϻ' => '牬', 'ϻ' => '牰', 'ϼ' => '牳', 'Ͻ' => '牮', 'Ͼ' => '狊', 'Ͽ' => '狤', '�' => '狨', '�' => '狫', '��' => '狟', '��' => '狪', '��' => '狦', '��' => '狣', '��' => '玅', '��' => '珌', '��' => '珂', '��' => '珈', '��' => '珅', '��' => '玹', '��' => '玶', '��' => '玵', '��' => '玴', '��' => '珫', '��' => '玿', '��' => '珇', '��' => '玾', '��' => '珃', '��' => '珆', '��' => '玸', '��' => '珋', '��' => '瓬', '��' => '瓮', '��' => '甮', '��' => '畇', '��' => '畈', '��' => '疧', '��' => '疪', '��' => '癹', '��' => '盄', '��' => '眈', '��' => '眃', '��' => '眄', '��' => '眅', '��' => '眊', '��' => '盷', '��' => '盻', '��' => '盺', '��' => '矧', '��' => '矨', '��' => '砆', '��' => '砑', '��' => '砒', '��' => '砅', '��' => '砐', '��' => '砏', '��' => '砎', '��' => '砉', '��' => '砃', '��' => '砓', '��' => '祊', '�' => '祌', '�' => '祋', '�' => '祅', '�' => '祄', '�' => '秕', '�' => '种', '�' => '秏', '�' => '秖', '�' => '秎', '�' => '窀', '�@' => '穾', '�A' => '竑', '�B' => '笀', '�C' => '笁', '�D' => '籺', '�E' => '籸', '�F' => '籹', '�G' => '籿', '�H' => '粀', '�I' => '粁', '�J' => '紃', '�K' => '紈', '�L' => '紁', '�M' => '罘', '�N' => '羑', '�O' => '羍', '�P' => '羾', '�Q' => '耇', '�R' => '耎', '�S' => '耏', '�T' => '耔', '�U' => '耷', '�V' => '胘', '�W' => '胇', '�X' => '胠', '�Y' => '胑', '�Z' => '胈', '�[' => '胂', '�\\' => '胐', '�]' => '胅', '�^' => '胣', '�_' => '胙', '�`' => '胜', '�a' => '胊', '�b' => '胕', '�c' => '胉', '�d' => '胏', '�e' => '胗', '�f' => '胦', '�g' => '胍', '�h' => '臿', '�i' => '舡', '�j' => '芔', '�k' => '苙', '�l' => '苾', '�m' => '苹', '�n' => '茇', '�o' => '苨', '�p' => '茀', '�q' => '苕', '�r' => '茺', '�s' => '苫', '�t' => '苖', '�u' => '苴', '�v' => '苬', '�w' => '苡', '�x' => '苲', '�y' => '苵', '�z' => '茌', '�{' => '苻', '�|' => '苶', '�}' => '苰', '�~' => '苪', 'С' => '苤', 'Т' => '苠', 'У' => '苺', 'Ф' => '苳', 'Х' => '苭', 'Ц' => '虷', 'Ч' => '虴', 'Ш' => '虼', 'Щ' => '虳', 'Ъ' => '衁', 'Ы' => '衎', 'Ь' => '衧', 'Э' => '衪', 'Ю' => '衩', 'Я' => '觓', 'а' => '訄', 'б' => '訇', 'в' => '赲', 'г' => '迣', 'д' => '迡', 'е' => '迮', 'ж' => '迠', 'з' => '郱', 'и' => '邽', 'й' => '邿', 'к' => '郕', 'л' => '郅', 'м' => '邾', 'н' => '郇', 'о' => '郋', 'п' => '郈', '�' => '釔', '�' => '釓', '��' => '陔', '��' => '陏', '��' => '陑', '��' => '陓', '��' => '陊', '��' => '陎', '��' => '倞', '��' => '倅', '��' => '倇', '��' => '倓', '��' => '倢', '��' => '倰', '��' => '倛', '��' => '俵', '��' => '俴', '��' => '倳', '��' => '倷', '��' => '倬', '��' => '俶', '��' => '俷', '��' => '倗', '��' => '倜', '��' => '倠', '��' => '倧', '��' => '倵', '��' => '倯', '��' => '倱', '��' => '倎', '��' => '党', '��' => '冔', '��' => '冓', '��' => '凊', '��' => '凄', '��' => '凅', '��' => '凈', '��' => '凎', '��' => '剡', '��' => '剚', '��' => '剒', '��' => '剞', '��' => '剟', '��' => '剕', '��' => '剢', '��' => '勍', '��' => '匎', '��' => '厞', '��' => '唦', '��' => '哢', '��' => '唗', '��' => '唒', '��' => '哧', '�' => '哳', '�' => '哤', '�' => '唚', '�' => '哿', '�' => '唄', '�' => '唈', '�' => '哫', '�' => '唑', '�' => '唅', '�' => '哱', '�@' => '唊', '�A' => '哻', '�B' => '哷', '�C' => '哸', '�D' => '哠', '�E' => '唎', '�F' => '唃', '�G' => '唋', '�H' => '圁', '�I' => '圂', '�J' => '埌', '�K' => '堲', '�L' => '埕', '�M' => '埒', '�N' => '垺', '�O' => '埆', '�P' => '垽', '�Q' => '垼', '�R' => '垸', '�S' => '垶', '�T' => '垿', '�U' => '埇', '�V' => '埐', '�W' => '垹', '�X' => '埁', '�Y' => '夎', '�Z' => '奊', '�[' => '娙', '�\\' => '娖', '�]' => '娭', '�^' => '娮', '�_' => '娕', '�`' => '娏', '�a' => '娗', '�b' => '娊', '�c' => '娞', '�d' => '娳', '�e' => '孬', '�f' => '宧', '�g' => '宭', '�h' => '宬', '�i' => '尃', '�j' => '屖', '�k' => '屔', '�l' => '峬', '�m' => '峿', '�n' => '峮', '�o' => '峱', '�p' => '峷', '�q' => '崀', '�r' => '峹', '�s' => '帩', '�t' => '帨', '�u' => '庨', '�v' => '庮', '�w' => '庪', '�x' => '庬', '�y' => '弳', '�z' => '弰', '�{' => '彧', '�|' => '恝', '�}' => '恚', '�~' => '恧', 'ѡ' => '恁', 'Ѣ' => '悢', 'ѣ' => '悈', 'Ѥ' => '悀', 'ѥ' => '悒', 'Ѧ' => '悁', 'ѧ' => '悝', 'Ѩ' => '悃', 'ѩ' => '悕', 'Ѫ' => '悛', 'ѫ' => '悗', 'Ѭ' => '悇', 'ѭ' => '悜', 'Ѯ' => '悎', 'ѯ' => '戙', 'Ѱ' => '扆', 'ѱ' => '拲', 'Ѳ' => '挐', 'ѳ' => '捖', 'Ѵ' => '挬', 'ѵ' => '捄', 'Ѷ' => '捅', 'ѷ' => '挶', 'Ѹ' => '捃', 'ѹ' => '揤', 'Ѻ' => '挹', 'ѻ' => '捋', 'Ѽ' => '捊', 'ѽ' => '挼', 'Ѿ' => '挩', 'ѿ' => '捁', '�' => '挴', '�' => '捘', '��' => '捔', '��' => '捙', '��' => '挭', '��' => '捇', '��' => '挳', '��' => '捚', '��' => '捑', '��' => '挸', '��' => '捗', '��' => '捀', '��' => '捈', '��' => '敊', '��' => '敆', '��' => '旆', '��' => '旃', '��' => '旄', '��' => '旂', '��' => '晊', '��' => '晟', '��' => '晇', '��' => '晑', '��' => '朒', '��' => '朓', '��' => '栟', '��' => '栚', '��' => '桉', '��' => '栲', '��' => '栳', '��' => '栻', '��' => '桋', '��' => '桏', '��' => '栖', '��' => '栱', '��' => '栜', '��' => '栵', '��' => '栫', '��' => '栭', '��' => '栯', '��' => '桎', '��' => '桄', '��' => '栴', '��' => '栝', '��' => '栒', '��' => '栔', '��' => '栦', '��' => '栨', '��' => '栮', '��' => '桍', '��' => '栺', '��' => '栥', '��' => '栠', '�' => '欬', '�' => '欯', '�' => '欭', '�' => '欱', '�' => '欴', '�' => '歭', '�' => '肂', '�' => '殈', '�' => '毦', '�' => '毤', '�@' => '毨', '�A' => '毣', '�B' => '毢', '�C' => '毧', '�D' => '氥', '�E' => '浺', '�F' => '浣', '�G' => '浤', '�H' => '浶', '�I' => '洍', '�J' => '浡', '�K' => '涒', '�L' => '浘', '�M' => '浢', '�N' => '浭', '�O' => '浯', '�P' => '涑', '�Q' => '涍', '�R' => '淯', '�S' => '浿', '�T' => '涆', '�U' => '浞', '�V' => '浧', '�W' => '浠', '�X' => '涗', '�Y' => '浰', '�Z' => '浼', '�[' => '浟', '�\\' => '涂', '�]' => '涘', '�^' => '洯', '�_' => '浨', '�`' => '涋', '�a' => '浾', '�b' => '涀', '�c' => '涄', '�d' => '洖', '�e' => '涃', '�f' => '浻', '�g' => '浽', '�h' => '浵', '�i' => '涐', '�j' => '烜', '�k' => '烓', '�l' => '烑', '�m' => '烝', '�n' => '烋', '�o' => '缹', '�p' => '烢', '�q' => '烗', '�r' => '烒', '�s' => '烞', '�t' => '烠', '�u' => '烔', '�v' => '烍', '�w' => '烅', '�x' => '烆', '�y' => '烇', '�z' => '烚', '�{' => '烎', '�|' => '烡', '�}' => '牂', '�~' => '牸', 'ҡ' => '牷', 'Ң' => '牶', 'ң' => '猀', 'Ҥ' => '狺', 'ҥ' => '狴', 'Ҧ' => '狾', 'ҧ' => '狶', 'Ҩ' => '狳', 'ҩ' => '狻', 'Ҫ' => '猁', 'ҫ' => '珓', 'Ҭ' => '珙', 'ҭ' => '珥', 'Ү' => '珖', 'ү' => '玼', 'Ұ' => '珧', 'ұ' => '珣', 'Ҳ' => '珩', 'ҳ' => '珜', 'Ҵ' => '珒', 'ҵ' => '珛', 'Ҷ' => '珔', 'ҷ' => '珝', 'Ҹ' => '珚', 'ҹ' => '珗', 'Һ' => '珘', 'һ' => '珨', 'Ҽ' => '瓞', 'ҽ' => '瓟', 'Ҿ' => '瓴', 'ҿ' => '瓵', '�' => '甡', '�' => '畛', '��' => '畟', '��' => '疰', '��' => '痁', '��' => '疻', '��' => '痄', '��' => '痀', '��' => '疿', '��' => '疶', '��' => '疺', '��' => '皊', '��' => '盉', '��' => '眝', '��' => '眛', '��' => '眐', '��' => '眓', '��' => '眒', '��' => '眣', '��' => '眑', '��' => '眕', '��' => '眙', '��' => '眚', '��' => '眢', '��' => '眧', '��' => '砣', '��' => '砬', '��' => '砢', '��' => '砵', '��' => '砯', '��' => '砨', '��' => '砮', '��' => '砫', '��' => '砡', '��' => '砩', '��' => '砳', '��' => '砪', '��' => '砱', '��' => '祔', '��' => '祛', '��' => '祏', '��' => '祜', '��' => '祓', '��' => '祒', '��' => '祑', '��' => '秫', '��' => '秬', '��' => '秠', '��' => '秮', '��' => '秭', '��' => '秪', '��' => '秜', '��' => '秞', '�' => '秝', '�' => '窆', '�' => '窉', '�' => '窅', '�' => '窋', '�' => '窌', '�' => '窊', '�' => '窇', '�' => '竘', '�' => '笐', '�@' => '笄', '�A' => '笓', '�B' => '笅', '�C' => '笏', '�D' => '笈', '�E' => '笊', '�F' => '笎', '�G' => '笉', '�H' => '笒', '�I' => '粄', '�J' => '粑', '�K' => '粊', '�L' => '粌', '�M' => '粈', '�N' => '粍', '�O' => '粅', '�P' => '紞', '�Q' => '紝', '�R' => '紑', '�S' => '紎', '�T' => '紘', '�U' => '紖', '�V' => '紓', '�W' => '紟', '�X' => '紒', '�Y' => '紏', '�Z' => '紌', '�[' => '罜', '�\\' => '罡', '�]' => '罞', '�^' => '罠', '�_' => '罝', '�`' => '罛', '�a' => '羖', '�b' => '羒', '�c' => '翃', '�d' => '翂', '�e' => '翀', '�f' => '耖', '�g' => '耾', '�h' => '耹', '�i' => '胺', '�j' => '胲', '�k' => '胹', '�l' => '胵', '�m' => '脁', '�n' => '胻', '�o' => '脀', '�p' => '舁', '�q' => '舯', '�r' => '舥', '�s' => '茳', '�t' => '茭', '�u' => '荄', '�v' => '茙', '�w' => '荑', '�x' => '茥', '�y' => '荖', '�z' => '茿', '�{' => '荁', '�|' => '茦', '�}' => '茜', '�~' => '茢', 'ӡ' => '荂', 'Ӣ' => '荎', 'ӣ' => '茛', 'Ӥ' => '茪', 'ӥ' => '茈', 'Ӧ' => '茼', 'ӧ' => '荍', 'Ө' => '茖', 'ө' => '茤', 'Ӫ' => '茠', 'ӫ' => '茷', 'Ӭ' => '茯', 'ӭ' => '茩', 'Ӯ' => '荇', 'ӯ' => '荅', 'Ӱ' => '荌', 'ӱ' => '荓', 'Ӳ' => '茞', 'ӳ' => '茬', 'Ӵ' => '荋', 'ӵ' => '茧', 'Ӷ' => '荈', 'ӷ' => '虓', 'Ӹ' => '虒', 'ӹ' => '蚢', 'Ӻ' => '蚨', 'ӻ' => '蚖', 'Ӽ' => '蚍', 'ӽ' => '蚑', 'Ӿ' => '蚞', 'ӿ' => '蚇', '�' => '蚗', '�' => '蚆', '��' => '蚋', '��' => '蚚', '��' => '蚅', '��' => '蚥', '��' => '蚙', '��' => '蚡', '��' => '蚧', '��' => '蚕', '��' => '蚘', '��' => '蚎', '��' => '蚝', '��' => '蚐', '��' => '蚔', '��' => '衃', '��' => '衄', '��' => '衭', '��' => '衵', '��' => '衶', '��' => '衲', '��' => '袀', '��' => '衱', '��' => '衿', '��' => '衯', '��' => '袃', '��' => '衾', '��' => '衴', '��' => '衼', '��' => '訒', '��' => '豇', '��' => '豗', '��' => '豻', '��' => '貤', '��' => '貣', '��' => '赶', '��' => '赸', '��' => '趵', '��' => '趷', '��' => '趶', '��' => '軑', '��' => '軓', '��' => '迾', '��' => '迵', '��' => '适', '��' => '迿', '��' => '迻', '��' => '逄', '��' => '迼', '��' => '迶', '��' => '郖', '��' => '郠', '��' => '郙', '�' => '郚', '�' => '郣', '�' => '郟', '�' => '郥', '�' => '郘', '�' => '郛', '�' => '郗', '�' => '郜', '�' => '郤', '�' => '酐', '�@' => '酎', '�A' => '酏', '�B' => '釕', '�C' => '釢', '�D' => '釚', '�E' => '陜', '�F' => '陟', '�G' => '隼', '�H' => '飣', '�I' => '髟', '�J' => '鬯', '�K' => '乿', '�L' => '偰', '�M' => '偪', '�N' => '偡', '�O' => '偞', '�P' => '偠', '�Q' => '偓', '�R' => '偋', '�S' => '偝', '�T' => '偲', '�U' => '偈', '�V' => '偍', '�W' => '偁', '�X' => '偛', '�Y' => '偊', '�Z' => '偢', '�[' => '倕', '�\\' => '偅', '�]' => '偟', '�^' => '偩', '�_' => '偫', '�`' => '偣', '�a' => '偤', '�b' => '偆', '�c' => '偀', '�d' => '偮', '�e' => '偳', '�f' => '偗', '�g' => '偑', '�h' => '凐', '�i' => '剫', '�j' => '剭', '�k' => '剬', '�l' => '剮', '�m' => '勖', '�n' => '勓', '�o' => '匭', '�p' => '厜', '�q' => '啵', '�r' => '啶', '�s' => '唼', '�t' => '啍', '�u' => '啐', '�v' => '唴', '�w' => '唪', '�x' => '啑', '�y' => '啢', '�z' => '唶', '�{' => '唵', '�|' => '唰', '�}' => '啒', '�~' => '啅', 'ԡ' => '唌', 'Ԣ' => '唲', 'ԣ' => '啥', 'Ԥ' => '啎', 'ԥ' => '唹', 'Ԧ' => '啈', 'ԧ' => '唭', 'Ԩ' => '唻', 'ԩ' => '啀', 'Ԫ' => '啋', 'ԫ' => '圊', 'Ԭ' => '圇', 'ԭ' => '埻', 'Ԯ' => '堔', 'ԯ' => '埢', '' => '埶', 'Ա' => '埜', 'Բ' => '埴', 'Գ' => '堀', 'Դ' => '埭', 'Ե' => '埽', 'Զ' => '堈', 'Է' => '埸', 'Ը' => '堋', 'Թ' => '埳', 'Ժ' => '埏', 'Ի' => '堇', 'Լ' => '埮', 'Խ' => '埣', 'Ծ' => '埲', 'Կ' => '埥', '�' => '埬', '�' => '埡', '��' => '堎', '��' => '埼', '��' => '堐', '��' => '埧', '��' => '堁', '��' => '堌', '��' => '埱', '��' => '埩', '��' => '埰', '��' => '堍', '��' => '堄', '��' => '奜', '��' => '婠', '��' => '婘', '��' => '婕', '��' => '婧', '��' => '婞', '��' => '娸', '��' => '娵', '��' => '婭', '��' => '婐', '��' => '婟', '��' => '婥', '��' => '婬', '��' => '婓', '��' => '婤', '��' => '婗', '��' => '婃', '��' => '婝', '��' => '婒', '��' => '婄', '��' => '婛', '��' => '婈', '��' => '媎', '��' => '娾', '��' => '婍', '��' => '娹', '��' => '婌', '��' => '婰', '��' => '婩', '��' => '婇', '��' => '婑', '��' => '婖', '��' => '婂', '��' => '婜', '��' => '孲', '��' => '孮', '��' => '寁', '��' => '寀', '��' => '屙', '��' => '崞', '�' => '崋', '�' => '崝', '�' => '崚', '�' => '崠', '�' => '崌', '�' => '崨', '�' => '崍', '�' => '崦', '�' => '崥', '�' => '崏', '�@' => '崰', '�A' => '崒', '�B' => '崣', '�C' => '崟', '�D' => '崮', '�E' => '帾', '�F' => '帴', '�G' => '庱', '�H' => '庴', '�I' => '庹', '�J' => '庲', '�K' => '庳', '�L' => '弶', '�M' => '弸', '�N' => '徛', '�O' => '徖', '�P' => '徟', '�Q' => '悊', '�R' => '悐', '�S' => '悆', '�T' => '悾', '�U' => '悰', '�V' => '悺', '�W' => '惓', '�X' => '惔', '�Y' => '惏', '�Z' => '惤', '�[' => '惙', '�\\' => '惝', '�]' => '惈', '�^' => '悱', '�_' => '惛', '�`' => '悷', '�a' => '惊', '�b' => '悿', '�c' => '惃', '�d' => '惍', '�e' => '惀', '�f' => '挲', '�g' => '捥', '�h' => '掊', '�i' => '掂', '�j' => '捽', '�k' => '掽', '�l' => '掞', '�m' => '掭', '�n' => '掝', '�o' => '掗', '�p' => '掫', '�q' => '掎', '�r' => '捯', '�s' => '掇', '�t' => '掐', '�u' => '据', '�v' => '掯', '�w' => '捵', '�x' => '掜', '�y' => '捭', '�z' => '掮', '�{' => '捼', '�|' => '掤', '�}' => '挻', '�~' => '掟', 'ա' => '捸', 'բ' => '掅', 'գ' => '掁', 'դ' => '掑', 'ե' => '掍', 'զ' => '捰', 'է' => '敓', 'ը' => '旍', 'թ' => '晥', 'ժ' => '晡', 'ի' => '晛', 'լ' => '晙', 'խ' => '晜', 'ծ' => '晢', 'կ' => '朘', 'հ' => '桹', 'ձ' => '梇', 'ղ' => '梐', 'ճ' => '梜', 'մ' => '桭', 'յ' => '桮', 'ն' => '梮', 'շ' => '梫', 'ո' => '楖', 'չ' => '桯', 'պ' => '梣', 'ջ' => '梬', 'ռ' => '梩', 'ս' => '桵', 'վ' => '桴', 'տ' => '梲', '�' => '梏', '�' => '桷', '��' => '梒', '��' => '桼', '��' => '桫', '��' => '桲', '��' => '梪', '��' => '梀', '��' => '桱', '��' => '桾', '��' => '梛', '��' => '梖', '��' => '梋', '��' => '梠', '��' => '梉', '��' => '梤', '��' => '桸', '��' => '桻', '��' => '梑', '��' => '梌', '��' => '梊', '��' => '桽', '��' => '欶', '��' => '欳', '��' => '欷', '��' => '欸', '��' => '殑', '��' => '殏', '��' => '殍', '��' => '殎', '��' => '殌', '��' => '氪', '��' => '淀', '��' => '涫', '��' => '涴', '��' => '涳', '��' => '湴', '��' => '涬', '��' => '淩', '��' => '淢', '��' => '涷', '��' => '淶', '��' => '淔', '��' => '渀', '��' => '淈', '��' => '淠', '��' => '淟', '��' => '淖', '��' => '涾', '��' => '淥', '��' => '淜', '��' => '淝', '��' => '淛', '�' => '淴', '�' => '淊', '�' => '涽', '�' => '淭', '�' => '淰', '�' => '涺', '�' => '淕', '�' => '淂', '�' => '淏', '�' => '淉', '�@' => '淐', '�A' => '淲', '�B' => '淓', '�C' => '淽', '�D' => '淗', '�E' => '淍', '�F' => '淣', '�G' => '涻', '�H' => '烺', '�I' => '焍', '�J' => '烷', '�K' => '焗', '�L' => '烴', '�M' => '焌', '�N' => '烰', '�O' => '焄', '�P' => '烳', '�Q' => '焐', '�R' => '烼', '�S' => '烿', '�T' => '焆', '�U' => '焓', '�V' => '焀', '�W' => '烸', '�X' => '烶', '�Y' => '焋', '�Z' => '焂', '�[' => '焎', '�\\' => '牾', '�]' => '牻', '�^' => '牼', '�_' => '牿', '�`' => '猝', '�a' => '猗', '�b' => '猇', '�c' => '猑', '�d' => '猘', '�e' => '猊', '�f' => '猈', '�g' => '狿', '�h' => '猏', '�i' => '猞', '�j' => '玈', '�k' => '珶', '�l' => '珸', '�m' => '珵', '�n' => '琄', '�o' => '琁', '�p' => '珽', '�q' => '琇', '�r' => '琀', '�s' => '珺', '�t' => '珼', '�u' => '珿', '�v' => '琌', '�w' => '琋', '�x' => '珴', '�y' => '琈', '�z' => '畤', '�{' => '畣', '�|' => '痎', '�}' => '痒', '�~' => '痏', '֡' => '痋', '֢' => '痌', '֣' => '痑', '֤' => '痐', '֥' => '皏', '֦' => '皉', '֧' => '盓', '֨' => '眹', '֩' => '眯', '֪' => '眭', '֫' => '眱', '֬' => '眲', '֭' => '眴', '֮' => '眳', '֯' => '眽', 'ְ' => '眥', 'ֱ' => '眻', 'ֲ' => '眵', 'ֳ' => '硈', 'ִ' => '硒', 'ֵ' => '硉', 'ֶ' => '硍', 'ַ' => '硊', 'ָ' => '硌', 'ֹ' => '砦', 'ֺ' => '硅', 'ֻ' => '硐', 'ּ' => '祤', 'ֽ' => '祧', '־' => '祩', 'ֿ' => '祪', '�' => '祣', '�' => '祫', '��' => '祡', '��' => '离', '��' => '秺', '��' => '秸', '��' => '秶', '��' => '秷', '��' => '窏', '��' => '窔', '��' => '窐', '��' => '笵', '��' => '筇', '��' => '笴', '��' => '笥', '��' => '笰', '��' => '笢', '��' => '笤', '��' => '笳', '��' => '笘', '��' => '笪', '��' => '笝', '��' => '笱', '��' => '笫', '��' => '笭', '��' => '笯', '��' => '笲', '��' => '笸', '��' => '笚', '��' => '笣', '��' => '粔', '��' => '粘', '��' => '粖', '��' => '粣', '��' => '紵', '��' => '紽', '��' => '紸', '��' => '紶', '��' => '紺', '��' => '絅', '��' => '紬', '��' => '紩', '��' => '絁', '��' => '絇', '��' => '紾', '��' => '紿', '��' => '絊', '��' => '紻', '��' => '紨', '��' => '罣', '��' => '羕', '��' => '羜', '��' => '羝', '�' => '羛', '�' => '翊', '�' => '翋', '�' => '翍', '�' => '翐', '�' => '翑', '�' => '翇', '�' => '翏', '�' => '翉', '�' => '耟', '�@' => '耞', '�A' => '耛', '�B' => '聇', '�C' => '聃', '�D' => '聈', '�E' => '脘', '�F' => '脥', '�G' => '脙', '�H' => '脛', '�I' => '脭', '�J' => '脟', '�K' => '脬', '�L' => '脞', '�M' => '脡', '�N' => '脕', '�O' => '脧', '�P' => '脝', '�Q' => '脢', '�R' => '舑', '�S' => '舸', '�T' => '舳', '�U' => '舺', '�V' => '舴', '�W' => '舲', '�X' => '艴', '�Y' => '莐', '�Z' => '莣', '�[' => '莨', '�\\' => '莍', '�]' => '荺', '�^' => '荳', '�_' => '莤', '�`' => '荴', '�a' => '莏', '�b' => '莁', '�c' => '莕', '�d' => '莙', '�e' => '荵', '�f' => '莔', '�g' => '莩', '�h' => '荽', '�i' => '莃', '�j' => '莌', '�k' => '莝', '�l' => '莛', '�m' => '莪', '�n' => '莋', '�o' => '荾', '�p' => '莥', '�q' => '莯', '�r' => '莈', '�s' => '莗', '�t' => '莰', '�u' => '荿', '�v' => '莦', '�w' => '莇', '�x' => '莮', '�y' => '荶', '�z' => '莚', '�{' => '虙', '�|' => '虖', '�}' => '蚿', '�~' => '蚷', 'ס' => '蛂', 'ע' => '蛁', 'ף' => '蛅', 'פ' => '蚺', 'ץ' => '蚰', 'צ' => '蛈', 'ק' => '蚹', 'ר' => '蚳', 'ש' => '蚸', 'ת' => '蛌', '' => '蚴', '' => '蚻', '' => '蚼', '' => '蛃', 'ׯ' => '蚽', 'װ' => '蚾', 'ױ' => '衒', 'ײ' => '袉', '׳' => '袕', '״' => '袨', '' => '袢', '' => '袪', '' => '袚', '' => '袑', '' => '袡', '' => '袟', '' => '袘', '' => '袧', '' => '袙', '' => '袛', '' => '袗', '�' => '袤', '�' => '袬', '��' => '袌', '��' => '袓', '��' => '袎', '��' => '覂', '��' => '觖', '��' => '觙', '��' => '觕', '��' => '訰', '��' => '訧', '��' => '訬', '��' => '訞', '��' => '谹', '��' => '谻', '��' => '豜', '��' => '豝', '��' => '豽', '��' => '貥', '��' => '赽', '��' => '赻', '��' => '赹', '��' => '趼', '��' => '跂', '��' => '趹', '��' => '趿', '��' => '跁', '��' => '軘', '��' => '軞', '��' => '軝', '��' => '軜', '��' => '軗', '��' => '軠', '��' => '軡', '��' => '逤', '��' => '逋', '��' => '逑', '��' => '逜', '��' => '逌', '��' => '逡', '��' => '郯', '��' => '郪', '��' => '郰', '��' => '郴', '��' => '郲', '��' => '郳', '��' => '郔', '��' => '郫', '��' => '郬', '��' => '郩', '��' => '酖', '��' => '酘', '��' => '酚', '�' => '酓', '�' => '酕', '�' => '釬', '�' => '釴', '�' => '釱', '�' => '釳', '�' => '釸', '�' => '釤', '�' => '釹', '�' => '釪', '�@' => '釫', '�A' => '釷', '�B' => '釨', '�C' => '釮', '�D' => '镺', '�E' => '閆', '�F' => '閈', '�G' => '陼', '�H' => '陭', '�I' => '陫', '�J' => '陱', '�K' => '陯', '�L' => '隿', '�M' => '靪', '�N' => '頄', '�O' => '飥', '�P' => '馗', '�Q' => '傛', '�R' => '傕', '�S' => '傔', '�T' => '傞', '�U' => '傋', '�V' => '傣', '�W' => '傃', '�X' => '傌', '�Y' => '傎', '�Z' => '傝', '�[' => '偨', '�\\' => '傜', '�]' => '傒', '�^' => '傂', '�_' => '傇', '�`' => '兟', '�a' => '凔', '�b' => '匒', '�c' => '匑', '�d' => '厤', '�e' => '厧', '�f' => '喑', '�g' => '喨', '�h' => '喥', '�i' => '喭', '�j' => '啷', '�k' => '噅', '�l' => '喢', '�m' => '喓', '�n' => '喈', '�o' => '喏', '�p' => '喵', '�q' => '喁', '�r' => '喣', '�s' => '喒', '�t' => '喤', '�u' => '啽', '�v' => '喌', '�w' => '喦', '�x' => '啿', '�y' => '喕', '�z' => '喡', '�{' => '喎', '�|' => '圌', '�}' => '堩', '�~' => '堷', 'ء' => '堙', 'آ' => '堞', 'أ' => '堧', 'ؤ' => '堣', 'إ' => '堨', 'ئ' => '埵', 'ا' => '塈', 'ب' => '堥', 'ة' => '堜', 'ت' => '堛', 'ث' => '堳', 'ج' => '堿', 'ح' => '堶', 'خ' => '堮', 'د' => '堹', 'ذ' => '堸', 'ر' => '堭', 'ز' => '堬', 'س' => '堻', 'ش' => '奡', 'ص' => '媯', 'ض' => '媔', 'ط' => '媟', 'ظ' => '婺', 'ع' => '媢', 'غ' => '媞', 'ػ' => '婸', 'ؼ' => '媦', 'ؽ' => '婼', 'ؾ' => '媥', 'ؿ' => '媬', '�' => '媕', '�' => '媮', '��' => '娷', '��' => '媄', '��' => '媊', '��' => '媗', '��' => '媃', '��' => '媋', '��' => '媩', '��' => '婻', '��' => '婽', '��' => '媌', '��' => '媜', '��' => '媏', '��' => '媓', '��' => '媝', '��' => '寪', '��' => '寍', '��' => '寋', '��' => '寔', '��' => '寑', '��' => '寊', '��' => '寎', '��' => '尌', '��' => '尰', '��' => '崷', '��' => '嵃', '��' => '嵫', '��' => '嵁', '��' => '嵋', '��' => '崿', '��' => '崵', '��' => '嵑', '��' => '嵎', '��' => '嵕', '��' => '崳', '��' => '崺', '��' => '嵒', '��' => '崽', '��' => '崱', '��' => '嵙', '��' => '嵂', '��' => '崹', '��' => '嵉', '��' => '崸', '��' => '崼', '��' => '崲', '��' => '崶', '��' => '嵀', '��' => '嵅', '��' => '幄', '��' => '幁', '��' => '彘', '�' => '徦', '�' => '徥', '�' => '徫', '�' => '惉', '�' => '悹', '�' => '惌', '�' => '惢', '�' => '惎', '�' => '惄', '�' => '愔', '�@' => '惲', '�A' => '愊', '�B' => '愖', '�C' => '愅', '�D' => '惵', '�E' => '愓', '�F' => '惸', '�G' => '惼', '�H' => '惾', '�I' => '惁', '�J' => '愃', '�K' => '愘', '�L' => '愝', '�M' => '愐', '�N' => '惿', '�O' => '愄', '�P' => '愋', '�Q' => '扊', '�R' => '掔', '�S' => '掱', '�T' => '掰', '�U' => '揎', '�V' => '揥', '�W' => '揨', '�X' => '揯', '�Y' => '揃', '�Z' => '撝', '�[' => '揳', '�\\' => '揊', '�]' => '揠', '�^' => '揶', '�_' => '揕', '�`' => '揲', '�a' => '揵', '�b' => '摡', '�c' => '揟', '�d' => '掾', '�e' => '揝', '�f' => '揜', '�g' => '揄', '�h' => '揘', '�i' => '揓', '�j' => '揂', '�k' => '揇', '�l' => '揌', '�m' => '揋', '�n' => '揈', '�o' => '揰', '�p' => '揗', '�q' => '揙', '�r' => '攲', '�s' => '敧', '�t' => '敪', '�u' => '敤', '�v' => '敜', '�w' => '敨', '�x' => '敥', '�y' => '斌', '�z' => '斝', '�{' => '斞', '�|' => '斮', '�}' => '旐', '�~' => '旒', '١' => '晼', '٢' => '晬', '٣' => '晻', '٤' => '暀', '٥' => '晱', '٦' => '晹', '٧' => '晪', '٨' => '晲', '٩' => '朁', '٪' => '椌', '٫' => '棓', '٬' => '椄', '٭' => '棜', 'ٮ' => '椪', 'ٯ' => '棬', 'ٰ' => '棪', 'ٱ' => '棱', 'ٲ' => '椏', 'ٳ' => '棖', 'ٴ' => '棷', 'ٵ' => '棫', 'ٶ' => '棤', 'ٷ' => '棶', 'ٸ' => '椓', 'ٹ' => '椐', 'ٺ' => '棳', 'ٻ' => '棡', 'ټ' => '椇', 'ٽ' => '棌', 'پ' => '椈', 'ٿ' => '楰', '�' => '梴', '�' => '椑', '��' => '棯', '��' => '棆', '��' => '椔', '��' => '棸', '��' => '棐', '��' => '棽', '��' => '棼', '��' => '棨', '��' => '椋', '��' => '椊', '��' => '椗', '��' => '棎', '��' => '棈', '��' => '棝', '��' => '棞', '��' => '棦', '��' => '棴', '��' => '棑', '��' => '椆', '��' => '棔', '��' => '棩', '��' => '椕', '��' => '椥', '��' => '棇', '��' => '欹', '��' => '欻', '��' => '欿', '��' => '欼', '��' => '殔', '��' => '殗', '��' => '殙', '��' => '殕', '��' => '殽', '��' => '毰', '��' => '毲', '��' => '毳', '��' => '氰', '��' => '淼', '��' => '湆', '��' => '湇', '��' => '渟', '��' => '湉', '��' => '溈', '��' => '渼', '��' => '渽', '��' => '湅', '��' => '湢', '��' => '渫', '��' => '渿', '��' => '湁', '��' => '湝', '�' => '湳', '�' => '渜', '�' => '渳', '�' => '湋', '�' => '湀', '�' => '湑', '�' => '渻', '�' => '渃', '�' => '渮', '�' => '湞', '�@' => '湨', '�A' => '湜', '�B' => '湡', '�C' => '渱', '�D' => '渨', '�E' => '湠', '�F' => '湱', '�G' => '湫', '�H' => '渹', '�I' => '渢', '�J' => '渰', '�K' => '湓', '�L' => '湥', '�M' => '渧', '�N' => '湸', '�O' => '湤', '�P' => '湷', '�Q' => '湕', '�R' => '湹', '�S' => '湒', '�T' => '湦', '�U' => '渵', '�V' => '渶', '�W' => '湚', '�X' => '焠', '�Y' => '焞', '�Z' => '焯', '�[' => '烻', '�\\' => '焮', '�]' => '焱', '�^' => '焣', '�_' => '焥', '�`' => '焢', '�a' => '焲', '�b' => '焟', '�c' => '焨', '�d' => '焺', '�e' => '焛', '�f' => '牋', '�g' => '牚', '�h' => '犈', '�i' => '犉', '�j' => '犆', '�k' => '犅', '�l' => '犋', '�m' => '猒', '�n' => '猋', '�o' => '猰', '�p' => '猢', '�q' => '猱', '�r' => '猳', '�s' => '猧', '�t' => '猲', '�u' => '猭', '�v' => '猦', '�w' => '猣', '�x' => '猵', '�y' => '猌', '�z' => '琮', '�{' => '琬', '�|' => '琰', '�}' => '琫', '�~' => '琖', 'ڡ' => '琚', 'ڢ' => '琡', 'ڣ' => '琭', 'ڤ' => '琱', 'ڥ' => '琤', 'ڦ' => '琣', 'ڧ' => '琝', 'ڨ' => '琩', 'ک' => '琠', 'ڪ' => '琲', 'ګ' => '瓻', 'ڬ' => '甯', 'ڭ' => '畯', 'ڮ' => '畬', 'گ' => '痧', 'ڰ' => '痚', 'ڱ' => '痡', 'ڲ' => '痦', 'ڳ' => '痝', 'ڴ' => '痟', 'ڵ' => '痤', 'ڶ' => '痗', 'ڷ' => '皕', 'ڸ' => '皒', 'ڹ' => '盚', 'ں' => '睆', 'ڻ' => '睇', 'ڼ' => '睄', 'ڽ' => '睍', 'ھ' => '睅', 'ڿ' => '睊', '�' => '睎', '�' => '睋', '��' => '睌', '��' => '矞', '��' => '矬', '��' => '硠', '��' => '硤', '��' => '硥', '��' => '硜', '��' => '硭', '��' => '硱', '��' => '硪', '��' => '确', '��' => '硰', '��' => '硩', '��' => '硨', '��' => '硞', '��' => '硢', '��' => '祴', '��' => '祳', '��' => '祲', '��' => '祰', '��' => '稂', '��' => '稊', '��' => '稃', '��' => '稌', '��' => '稄', '��' => '窙', '��' => '竦', '��' => '竤', '��' => '筊', '��' => '笻', '��' => '筄', '��' => '筈', '��' => '筌', '��' => '筎', '��' => '筀', '��' => '筘', '��' => '筅', '��' => '粢', '��' => '粞', '��' => '粨', '��' => '粡', '��' => '絘', '��' => '絯', '��' => '絣', '��' => '絓', '��' => '絖', '��' => '絧', '��' => '絪', '��' => '絏', '��' => '絭', '��' => '絜', '�' => '絫', '�' => '絒', '�' => '絔', '�' => '絩', '�' => '絑', '�' => '絟', '�' => '絎', '�' => '缾', '�' => '缿', '�' => '罥', '�@' => '罦', '�A' => '羢', '�B' => '羠', '�C' => '羡', '�D' => '翗', '�E' => '聑', '�F' => '聏', '�G' => '聐', '�H' => '胾', '�I' => '胔', '�J' => '腃', '�K' => '腊', '�L' => '腒', '�M' => '腏', '�N' => '腇', '�O' => '脽', '�P' => '腍', '�Q' => '脺', '�R' => '臦', '�S' => '臮', '�T' => '臷', '�U' => '臸', '�V' => '臹', '�W' => '舄', '�X' => '舼', '�Y' => '舽', '�Z' => '舿', '�[' => '艵', '�\\' => '茻', '�]' => '菏', '�^' => '菹', '�_' => '萣', '�`' => '菀', '�a' => '菨', '�b' => '萒', '�c' => '菧', '�d' => '菤', '�e' => '菼', '�f' => '菶', '�g' => '萐', '�h' => '菆', '�i' => '菈', '�j' => '菫', '�k' => '菣', '�l' => '莿', '�m' => '萁', '�n' => '菝', '�o' => '菥', '�p' => '菘', '�q' => '菿', '�r' => '菡', '�s' => '菋', '�t' => '菎', '�u' => '菖', '�v' => '菵', '�w' => '菉', '�x' => '萉', '�y' => '萏', '�z' => '菞', '�{' => '萑', '�|' => '萆', '�}' => '菂', '�~' => '菳', 'ۡ' => '菕', 'ۢ' => '菺', 'ۣ' => '菇', 'ۤ' => '菑', 'ۥ' => '菪', 'ۦ' => '萓', 'ۧ' => '菃', 'ۨ' => '菬', '۩' => '菮', '۪' => '菄', '۫' => '菻', '۬' => '菗', 'ۭ' => '菢', 'ۮ' => '萛', 'ۯ' => '菛', '۰' => '菾', '۱' => '蛘', '۲' => '蛢', '۳' => '蛦', '۴' => '蛓', '۵' => '蛣', '۶' => '蛚', '۷' => '蛪', '۸' => '蛝', '۹' => '蛫', 'ۺ' => '蛜', 'ۻ' => '蛬', 'ۼ' => '蛩', '۽' => '蛗', '۾' => '蛨', 'ۿ' => '蛑', '�' => '衈', '�' => '衖', '��' => '衕', '��' => '袺', '��' => '裗', '��' => '袹', '��' => '袸', '��' => '裀', '��' => '袾', '��' => '袶', '��' => '袼', '��' => '袷', '��' => '袽', '��' => '袲', '��' => '褁', '��' => '裉', '��' => '覕', '��' => '覘', '��' => '覗', '��' => '觝', '��' => '觚', '��' => '觛', '��' => '詎', '��' => '詍', '��' => '訹', '��' => '詙', '��' => '詀', '��' => '詗', '��' => '詘', '��' => '詄', '��' => '詅', '��' => '詒', '��' => '詈', '��' => '詑', '��' => '詊', '��' => '詌', '��' => '詏', '��' => '豟', '��' => '貁', '��' => '貀', '��' => '貺', '��' => '貾', '��' => '貰', '��' => '貹', '��' => '貵', '��' => '趄', '��' => '趀', '��' => '趉', '��' => '跘', '��' => '跓', '��' => '跍', '��' => '跇', '��' => '跖', '�' => '跜', '�' => '跏', '�' => '跕', '�' => '跙', '�' => '跈', '�' => '跗', '�' => '跅', '�' => '軯', '�' => '軷', '�' => '軺', '�@' => '軹', '�A' => '軦', '�B' => '軮', '�C' => '軥', '�D' => '軵', '�E' => '軧', '�F' => '軨', '�G' => '軶', '�H' => '軫', '�I' => '軱', '�J' => '軬', '�K' => '軴', '�L' => '軩', '�M' => '逭', '�N' => '逴', '�O' => '逯', '�P' => '鄆', '�Q' => '鄬', '�R' => '鄄', '�S' => '郿', '�T' => '郼', '�U' => '鄈', '�V' => '郹', '�W' => '郻', '�X' => '鄁', '�Y' => '鄀', '�Z' => '鄇', '�[' => '鄅', '�\\' => '鄃', '�]' => '酡', '�^' => '酤', '�_' => '酟', '�`' => '酢', '�a' => '酠', '�b' => '鈁', '�c' => '鈊', '�d' => '鈥', '�e' => '鈃', '�f' => '鈚', '�g' => '鈦', '�h' => '鈏', '�i' => '鈌', '�j' => '鈀', '�k' => '鈒', '�l' => '釿', '�m' => '釽', '�n' => '鈆', '�o' => '鈄', '�p' => '鈧', '�q' => '鈂', '�r' => '鈜', '�s' => '鈤', '�t' => '鈙', '�u' => '鈗', '�v' => '鈅', '�w' => '鈖', '�x' => '镻', '�y' => '閍', '�z' => '閌', '�{' => '閐', '�|' => '隇', '�}' => '陾', '�~' => '隈', 'ܡ' => '隉', 'ܢ' => '隃', 'ܣ' => '隀', 'ܤ' => '雂', 'ܥ' => '雈', 'ܦ' => '雃', 'ܧ' => '雱', 'ܨ' => '雰', 'ܩ' => '靬', 'ܪ' => '靰', 'ܫ' => '靮', 'ܬ' => '頇', 'ܭ' => '颩', 'ܮ' => '飫', 'ܯ' => '鳦', 'ܰ' => '黹', 'ܱ' => '亃', 'ܲ' => '亄', 'ܳ' => '亶', 'ܴ' => '傽', 'ܵ' => '傿', 'ܶ' => '僆', 'ܷ' => '傮', 'ܸ' => '僄', 'ܹ' => '僊', 'ܺ' => '傴', 'ܻ' => '僈', 'ܼ' => '僂', 'ܽ' => '傰', 'ܾ' => '僁', 'ܿ' => '傺', '�' => '傱', '�' => '僋', '��' => '僉', '��' => '傶', '��' => '傸', '��' => '凗', '��' => '剺', '��' => '剸', '��' => '剻', '��' => '剼', '��' => '嗃', '��' => '嗛', '��' => '嗌', '��' => '嗐', '��' => '嗋', '��' => '嗊', '��' => '嗝', '��' => '嗀', '��' => '嗔', '��' => '嗄', '��' => '嗩', '��' => '喿', '��' => '嗒', '��' => '喍', '��' => '嗏', '��' => '嗕', '��' => '嗢', '��' => '嗖', '��' => '嗈', '��' => '嗲', '��' => '嗍', '��' => '嗙', '��' => '嗂', '��' => '圔', '��' => '塓', '��' => '塨', '��' => '塤', '��' => '塏', '��' => '塍', '��' => '塉', '��' => '塯', '��' => '塕', '��' => '塎', '��' => '塝', '��' => '塙', '��' => '塥', '��' => '塛', '��' => '堽', '��' => '塣', '��' => '塱', '��' => '壼', '��' => '嫇', '��' => '嫄', '�' => '嫋', '�' => '媺', '�' => '媸', '�' => '媱', '�' => '媵', '�' => '媰', '�' => '媿', '�' => '嫈', '�' => '媻', '�' => '嫆', '�@' => '媷', '�A' => '嫀', '�B' => '嫊', '�C' => '媴', '�D' => '媶', '�E' => '嫍', '�F' => '媹', '�G' => '媐', '�H' => '寖', '�I' => '寘', '�J' => '寙', '�K' => '尟', '�L' => '尳', '�M' => '嵱', '�N' => '嵣', '�O' => '嵊', '�P' => '嵥', '�Q' => '嵲', '�R' => '嵬', '�S' => '嵞', '�T' => '嵨', '�U' => '嵧', '�V' => '嵢', '�W' => '巰', '�X' => '幏', '�Y' => '幎', '�Z' => '幊', '�[' => '幍', '�\\' => '幋', '�]' => '廅', '�^' => '廌', '�_' => '廆', '�`' => '廋', '�a' => '廇', '�b' => '彀', '�c' => '徯', '�d' => '徭', '�e' => '惷', '�f' => '慉', '�g' => '慊', '�h' => '愫', '�i' => '慅', '�j' => '愶', '�k' => '愲', '�l' => '愮', '�m' => '慆', '�n' => '愯', '�o' => '慏', '�p' => '愩', '�q' => '慀', '�r' => '戠', '�s' => '酨', '�t' => '戣', '�u' => '戥', '�v' => '戤', '�w' => '揅', '�x' => '揱', '�y' => '揫', '�z' => '搐', '�{' => '搒', '�|' => '搉', '�}' => '搠', '�~' => '搤', 'ݡ' => '搳', 'ݢ' => '摃', 'ݣ' => '搟', 'ݤ' => '搕', 'ݥ' => '搘', 'ݦ' => '搹', 'ݧ' => '搷', 'ݨ' => '搢', 'ݩ' => '搣', 'ݪ' => '搌', 'ݫ' => '搦', 'ݬ' => '搰', 'ݭ' => '搨', 'ݮ' => '摁', 'ݯ' => '搵', 'ݰ' => '搯', 'ݱ' => '搊', 'ݲ' => '搚', 'ݳ' => '摀', 'ݴ' => '搥', 'ݵ' => '搧', 'ݶ' => '搋', 'ݷ' => '揧', 'ݸ' => '搛', 'ݹ' => '搮', 'ݺ' => '搡', 'ݻ' => '搎', 'ݼ' => '敯', 'ݽ' => '斒', 'ݾ' => '旓', 'ݿ' => '暆', '�' => '暌', '�' => '暕', '��' => '暐', '��' => '暋', '��' => '暊', '��' => '暙', '��' => '暔', '��' => '晸', '��' => '朠', '��' => '楦', '��' => '楟', '��' => '椸', '��' => '楎', '��' => '楢', '��' => '楱', '��' => '椿', '��' => '楅', '��' => '楪', '��' => '椹', '��' => '楂', '��' => '楗', '��' => '楙', '��' => '楺', '��' => '楈', '��' => '楉', '��' => '椵', '��' => '楬', '��' => '椳', '��' => '椽', '��' => '楥', '��' => '棰', '��' => '楸', '��' => '椴', '��' => '楩', '��' => '楀', '��' => '楯', '��' => '楄', '��' => '楶', '��' => '楘', '��' => '楁', '��' => '楴', '��' => '楌', '��' => '椻', '��' => '楋', '��' => '椷', '��' => '楜', '��' => '楏', '��' => '楑', '��' => '椲', '��' => '楒', '��' => '椯', '��' => '楻', '��' => '椼', '�' => '歆', '�' => '歅', '�' => '歃', '�' => '歂', '�' => '歈', '�' => '歁', '�' => '殛', '�' => '嗀', '�' => '毻', '�' => '毼', '�@' => '毹', '�A' => '毷', '�B' => '毸', '�C' => '溛', '�D' => '滖', '�E' => '滈', '�F' => '溏', '�G' => '滀', '�H' => '溟', '�I' => '溓', '�J' => '溔', '�K' => '溠', '�L' => '溱', '�M' => '溹', '�N' => '滆', '�O' => '滒', '�P' => '溽', '�Q' => '滁', '�R' => '溞', '�S' => '滉', '�T' => '溷', '�U' => '溰', '�V' => '滍', '�W' => '溦', '�X' => '滏', '�Y' => '溲', '�Z' => '溾', '�[' => '滃', '�\\' => '滜', '�]' => '滘', '�^' => '溙', '�_' => '溒', '�`' => '溎', '�a' => '溍', '�b' => '溤', '�c' => '溡', '�d' => '溿', '�e' => '溳', '�f' => '滐', '�g' => '滊', '�h' => '溗', '�i' => '溮', '�j' => '溣', '�k' => '煇', '�l' => '煔', '�m' => '煒', '�n' => '煣', '�o' => '煠', '�p' => '煁', '�q' => '煝', '�r' => '煢', '�s' => '煲', '�t' => '煸', '�u' => '煪', '�v' => '煡', '�w' => '煂', '�x' => '煘', '�y' => '煃', '�z' => '煋', '�{' => '煰', '�|' => '煟', '�}' => '煐', '�~' => '煓', 'ޡ' => '煄', 'ޢ' => '煍', 'ޣ' => '煚', 'ޤ' => '牏', 'ޥ' => '犍', 'ަ' => '犌', 'ާ' => '犑', 'ި' => '犐', 'ީ' => '犎', 'ު' => '猼', 'ޫ' => '獂', 'ެ' => '猻', 'ޭ' => '猺', 'ޮ' => '獀', 'ޯ' => '獊', 'ް' => '獉', 'ޱ' => '瑄', '' => '瑊', '' => '瑋', '' => '瑒', '' => '瑑', '' => '瑗', '' => '瑀', '' => '瑏', '' => '瑐', '' => '瑎', '' => '瑂', '' => '瑆', '' => '瑍', '' => '瑔', '' => '瓡', '�' => '瓿', '�' => '瓾', '��' => '瓽', '��' => '甝', '��' => '畹', '��' => '畷', '��' => '榃', '��' => '痯', '��' => '瘏', '��' => '瘃', '��' => '痷', '��' => '痾', '��' => '痼', '��' => '痹', '��' => '痸', '��' => '瘐', '��' => '痻', '��' => '痶', '��' => '痭', '��' => '痵', '��' => '痽', '��' => '皙', '��' => '皵', '��' => '盝', '��' => '睕', '��' => '睟', '��' => '睠', '��' => '睒', '��' => '睖', '��' => '睚', '��' => '睩', '��' => '睧', '��' => '睔', '��' => '睙', '��' => '睭', '��' => '矠', '��' => '碇', '��' => '碚', '��' => '碔', '��' => '碏', '��' => '碄', '��' => '碕', '��' => '碅', '��' => '碆', '��' => '碡', '��' => '碃', '��' => '硹', '��' => '碙', '��' => '碀', '��' => '碖', '��' => '硻', '��' => '祼', '��' => '禂', '�' => '祽', '�' => '祹', '�' => '稑', '�' => '稘', '�' => '稙', '�' => '稒', '�' => '稗', '�' => '稕', '�' => '稢', '�' => '稓', '�@' => '稛', '�A' => '稐', '�B' => '窣', '�C' => '窢', '�D' => '窞', '�E' => '竫', '�F' => '筦', '�G' => '筤', '�H' => '筭', '�I' => '筴', '�J' => '筩', '�K' => '筲', '�L' => '筥', '�M' => '筳', '�N' => '筱', '�O' => '筰', '�P' => '筡', '�Q' => '筸', '�R' => '筶', '�S' => '筣', '�T' => '粲', '�U' => '粴', '�V' => '粯', '�W' => '綈', '�X' => '綆', '�Y' => '綀', '�Z' => '綍', '�[' => '絿', '�\\' => '綅', '�]' => '絺', '�^' => '綎', '�_' => '絻', '�`' => '綃', '�a' => '絼', '�b' => '綌', '�c' => '綔', '�d' => '綄', '�e' => '絽', '�f' => '綒', '�g' => '罭', '�h' => '罫', '�i' => '罧', '�j' => '罨', '�k' => '罬', '�l' => '羦', '�m' => '羥', '�n' => '羧', '�o' => '翛', '�p' => '翜', '�q' => '耡', '�r' => '腤', '�s' => '腠', '�t' => '腷', '�u' => '腜', '�v' => '腩', '�w' => '腛', '�x' => '腢', '�y' => '腲', '�z' => '朡', '�{' => '腞', '�|' => '腶', '�}' => '腧', '�~' => '腯', 'ߡ' => '腄', 'ߢ' => '腡', 'ߣ' => '舝', 'ߤ' => '艉', 'ߥ' => '艄', 'ߦ' => '艀', 'ߧ' => '艂', 'ߨ' => '艅', 'ߩ' => '蓱', 'ߪ' => '萿', '߫' => '葖', '߬' => '葶', '߭' => '葹', '߮' => '蒏', '߯' => '蒍', '߰' => '葥', '߱' => '葑', '߲' => '葀', '߳' => '蒆', 'ߴ' => '葧', 'ߵ' => '萰', '߶' => '葍', '߷' => '葽', '߸' => '葚', '߹' => '葙', 'ߺ' => '葴', '' => '葳', '' => '葝', '߽' => '蔇', '߾' => '葞', '߿' => '萷', '�' => '萺', '�' => '萴', '��' => '葺', '��' => '葃', '��' => '葸', '��' => '萲', '��' => '葅', '��' => '萩', '��' => '菙', '��' => '葋', '��' => '萯', '��' => '葂', '��' => '萭', '��' => '葟', '��' => '葰', '��' => '萹', '��' => '葎', '��' => '葌', '��' => '葒', '��' => '葯', '��' => '蓅', '��' => '蒎', '��' => '萻', '��' => '葇', '��' => '萶', '��' => '萳', '��' => '葨', '��' => '葾', '��' => '葄', '��' => '萫', '��' => '葠', '��' => '葔', '��' => '葮', '��' => '葐', '��' => '蜋', '��' => '蜄', '��' => '蛷', '��' => '蜌', '��' => '蛺', '��' => '蛖', '��' => '蛵', '��' => '蝍', '��' => '蛸', '��' => '蜎', '��' => '蜉', '��' => '蜁', '��' => '蛶', '��' => '蜍', '��' => '蜅', '��' => '裖', '��' => '裋', '��' => '裍', '��' => '裎', '�' => '裞', '�' => '裛', '�' => '裚', '�' => '裌', '�' => '裐', '�' => '覅', '�' => '覛', '�' => '觟', '�' => '觥', '�' => '觤', '�@' => '觡', '�A' => '觠', '�B' => '觢', '�C' => '觜', '�D' => '触', '�E' => '詶', '�F' => '誆', '�G' => '詿', '�H' => '詡', '�I' => '訿', '�J' => '詷', '�K' => '誂', '�L' => '誄', '�M' => '詵', '�N' => '誃', '�O' => '誁', '�P' => '詴', '�Q' => '詺', '�R' => '谼', '�S' => '豋', '�T' => '豊', '�U' => '豥', '�V' => '豤', '�W' => '豦', '�X' => '貆', '�Y' => '貄', '�Z' => '貅', '�[' => '賌', '�\\' => '赨', '�]' => '赩', '�^' => '趑', '�_' => '趌', '�`' => '趎', '�a' => '趏', '�b' => '趍', '�c' => '趓', '�d' => '趔', '�e' => '趐', '�f' => '趒', '�g' => '跰', '�h' => '跠', '�i' => '跬', '�j' => '跱', '�k' => '跮', '�l' => '跐', '�m' => '跩', '�n' => '跣', '�o' => '跢', '�p' => '跧', '�q' => '跲', '�r' => '跫', '�s' => '跴', '�t' => '輆', '�u' => '軿', '�v' => '輁', '�w' => '輀', '�x' => '輅', '�y' => '輇', '�z' => '輈', '�{' => '輂', '�|' => '輋', '�}' => '遒', '�~' => '逿', '�' => '遄', '�' => '遉', '�' => '逽', '�' => '鄐', '�' => '鄍', '�' => '鄏', '�' => '鄑', '�' => '鄖', '�' => '鄔', '�' => '鄋', '�' => '鄎', '�' => '酮', '�' => '酯', '�' => '鉈', '�' => '鉒', '�' => '鈰', '�' => '鈺', '�' => '鉦', '�' => '鈳', '�' => '鉥', '�' => '鉞', '�' => '銃', '�' => '鈮', '�' => '鉊', '�' => '鉆', '�' => '鉭', '�' => '鉬', '�' => '鉏', '�' => '鉠', '�' => '鉧', '�' => '鉯', '�' => '鈶', '�' => '鉡', '��' => '鉰', '��' => '鈱', '��' => '鉔', '��' => '鉣', '��' => '鉐', '��' => '鉲', '��' => '鉎', '��' => '鉓', '��' => '鉌', '��' => '鉖', '��' => '鈲', '��' => '閟', '��' => '閜', '��' => '閞', '��' => '閛', '��' => '隒', '��' => '隓', '��' => '隑', '��' => '隗', '��' => '雎', '��' => '雺', '��' => '雽', '��' => '雸', '��' => '雵', '��' => '靳', '��' => '靷', '��' => '靸', '��' => '靲', '��' => '頏', '��' => '頍', '��' => '頎', '��' => '颬', '��' => '飶', '��' => '飹', '��' => '馯', '��' => '馲', '��' => '馰', '��' => '馵', '��' => '骭', '��' => '骫', '��' => '魛', '��' => '鳪', '��' => '鳭', '��' => '鳧', '��' => '麀', '��' => '黽', '��' => '僦', '��' => '僔', '��' => '僗', '��' => '僨', '��' => '僳', '�' => '僛', '�' => '僪', '�' => '僝', '�' => '僤', '�' => '僓', '�' => '僬', '�' => '僰', '�' => '僯', '�' => '僣', '�' => '僠', '�@' => '凘', '�A' => '劀', '�B' => '劁', '�C' => '勩', '�D' => '勫', '�E' => '匰', '�F' => '厬', '�G' => '嘧', '�H' => '嘕', '�I' => '嘌', '�J' => '嘒', '�K' => '嗼', '�L' => '嘏', '�M' => '嘜', '�N' => '嘁', '�O' => '嘓', '�P' => '嘂', '�Q' => '嗺', '�R' => '嘝', '�S' => '嘄', '�T' => '嗿', '�U' => '嗹', '�V' => '墉', '�W' => '塼', '�X' => '墐', '�Y' => '墘', '�Z' => '墆', '�[' => '墁', '�\\' => '塿', '�]' => '塴', '�^' => '墋', '�_' => '塺', '�`' => '墇', '�a' => '墑', '�b' => '墎', '�c' => '塶', '�d' => '墂', '�e' => '墈', '�f' => '塻', '�g' => '墔', '�h' => '墏', '�i' => '壾', '�j' => '奫', '�k' => '嫜', '�l' => '嫮', '�m' => '嫥', '�n' => '嫕', '�o' => '嫪', '�p' => '嫚', '�q' => '嫭', '�r' => '嫫', '�s' => '嫳', '�t' => '嫢', '�u' => '嫠', '�v' => '嫛', '�w' => '嫬', '�x' => '嫞', '�y' => '嫝', '�z' => '嫙', '�{' => '嫨', '�|' => '嫟', '�}' => '孷', '�~' => '寠', '�' => '寣', '�' => '屣', '�' => '嶂', '�' => '嶀', '�' => '嵽', '�' => '嶆', '�' => '嵺', '�' => '嶁', '�' => '嵷', '�' => '嶊', '�' => '嶉', '�' => '嶈', '�' => '嵾', '�' => '嵼', '�' => '嶍', '�' => '嵹', '�' => '嵿', '�' => '幘', '�' => '幙', '�' => '幓', '�' => '廘', '�' => '廑', '�' => '廗', '�' => '廎', '�' => '廜', '�' => '廕', '�' => '廙', '�' => '廒', '�' => '廔', '�' => '彄', '�' => '彃', '�' => '彯', '�' => '徶', '��' => '愬', '��' => '愨', '��' => '慁', '��' => '慞', '��' => '慱', '��' => '慳', '��' => '慒', '��' => '慓', '��' => '慲', '��' => '慬', '��' => '憀', '��' => '慴', '��' => '慔', '��' => '慺', '��' => '慛', '��' => '慥', '��' => '愻', '��' => '慪', '��' => '慡', '��' => '慖', '��' => '戩', '��' => '戧', '��' => '戫', '��' => '搫', '��' => '摍', '��' => '摛', '��' => '摝', '��' => '摴', '��' => '摶', '��' => '摲', '��' => '摳', '��' => '摽', '��' => '摵', '��' => '摦', '��' => '撦', '��' => '摎', '��' => '撂', '��' => '摞', '��' => '摜', '��' => '摋', '��' => '摓', '��' => '摠', '��' => '摐', '��' => '摿', '��' => '搿', '��' => '摬', '��' => '摫', '��' => '摙', '��' => '摥', '��' => '摷', '��' => '敳', '�' => '斠', '�' => '暡', '�' => '暠', '�' => '暟', '�' => '朅', '�' => '朄', '�' => '朢', '�' => '榱', '�' => '榶', '�' => '槉', '�@' => '榠', '�A' => '槎', '�B' => '榖', '�C' => '榰', '�D' => '榬', '�E' => '榼', '�F' => '榑', '�G' => '榙', '�H' => '榎', '�I' => '榧', '�J' => '榍', '�K' => '榩', '�L' => '榾', '�M' => '榯', '�N' => '榿', '�O' => '槄', '�P' => '榽', '�Q' => '榤', '�R' => '槔', '�S' => '榹', '�T' => '槊', '�U' => '榚', '�V' => '槏', '�W' => '榳', '�X' => '榓', '�Y' => '榪', '�Z' => '榡', '�[' => '榞', '�\\' => '槙', '�]' => '榗', '�^' => '榐', '�_' => '槂', '�`' => '榵', '�a' => '榥', '�b' => '槆', '�c' => '歊', '�d' => '歍', '�e' => '歋', '�f' => '殞', '�g' => '殟', '�h' => '殠', '�i' => '毃', '�j' => '毄', '�k' => '毾', '�l' => '滎', '�m' => '滵', '�n' => '滱', '�o' => '漃', '�p' => '漥', '�q' => '滸', '�r' => '漷', '�s' => '滻', '�t' => '漮', '�u' => '漉', '�v' => '潎', '�w' => '漙', '�x' => '漚', '�y' => '漧', '�z' => '漘', '�{' => '漻', '�|' => '漒', '�}' => '滭', '�~' => '漊', '�' => '漶', '�' => '潳', '�' => '滹', '�' => '滮', '�' => '漭', '�' => '潀', '�' => '漰', '�' => '漼', '�' => '漵', '�' => '滫', '�' => '漇', '�' => '漎', '�' => '潃', '�' => '漅', '�' => '滽', '�' => '滶', '�' => '漹', '�' => '漜', '�' => '滼', '�' => '漺', '�' => '漟', '�' => '漍', '�' => '漞', '�' => '漈', '�' => '漡', '�' => '熇', '�' => '熐', '�' => '熉', '�' => '熀', '�' => '熅', '�' => '熂', '�' => '熏', '�' => '煻', '��' => '熆', '��' => '熁', '��' => '熗', '��' => '牄', '��' => '牓', '��' => '犗', '��' => '犕', '��' => '犓', '��' => '獃', '��' => '獍', '��' => '獑', '��' => '獌', '��' => '瑢', '��' => '瑳', '��' => '瑱', '��' => '瑵', '��' => '瑲', '��' => '瑧', '��' => '瑮', '��' => '甀', '��' => '甂', '��' => '甃', '��' => '畽', '��' => '疐', '��' => '瘖', '��' => '瘈', '��' => '瘌', '��' => '瘕', '��' => '瘑', '��' => '瘊', '��' => '瘔', '��' => '皸', '��' => '瞁', '��' => '睼', '��' => '瞅', '��' => '瞂', '��' => '睮', '��' => '瞀', '��' => '睯', '��' => '睾', '��' => '瞃', '��' => '碲', '��' => '碪', '��' => '碴', '��' => '碭', '��' => '碨', '��' => '硾', '��' => '碫', '��' => '碞', '��' => '碥', '��' => '碠', '�' => '碬', '�' => '碢', '�' => '碤', '�' => '禘', '�' => '禊', '�' => '禋', '�' => '禖', '�' => '禕', '�' => '禔', '�' => '禓', '�@' => '禗', '�A' => '禈', '�B' => '禒', '�C' => '禐', '�D' => '稫', '�E' => '穊', '�F' => '稰', '�G' => '稯', '�H' => '稨', '�I' => '稦', '�J' => '窨', '�K' => '窫', '�L' => '窬', '�M' => '竮', '�N' => '箈', '�O' => '箜', '�P' => '箊', '�Q' => '箑', '�R' => '箐', '�S' => '箖', '�T' => '箍', '�U' => '箌', '�V' => '箛', '�W' => '箎', '�X' => '箅', '�Y' => '箘', '�Z' => '劄', '�[' => '箙', '�\\' => '箤', '�]' => '箂', '�^' => '粻', '�_' => '粿', '�`' => '粼', '�a' => '粺', '�b' => '綧', '�c' => '綷', '�d' => '緂', '�e' => '綣', '�f' => '綪', '�g' => '緁', '�h' => '緀', '�i' => '緅', '�j' => '綝', '�k' => '緎', '�l' => '緄', '�m' => '緆', '�n' => '緋', '�o' => '緌', '�p' => '綯', '�q' => '綹', '�r' => '綖', '�s' => '綼', '�t' => '綟', '�u' => '綦', '�v' => '綮', '�w' => '綩', '�x' => '綡', '�y' => '緉', '�z' => '罳', '�{' => '翢', '�|' => '翣', '�}' => '翥', '�~' => '翞', '�' => '耤', '�' => '聝', '�' => '聜', '�' => '膉', '�' => '膆', '�' => '膃', '�' => '膇', '�' => '膍', '�' => '膌', '�' => '膋', '�' => '舕', '�' => '蒗', '�' => '蒤', '�' => '蒡', '�' => '蒟', '�' => '蒺', '�' => '蓎', '�' => '蓂', '�' => '蒬', '�' => '蒮', '�' => '蒫', '�' => '蒹', '�' => '蒴', '�' => '蓁', '�' => '蓍', '�' => '蒪', '�' => '蒚', '�' => '蒱', '�' => '蓐', '�' => '蒝', '�' => '蒧', '�' => '蒻', '�' => '蒢', '��' => '蒔', '��' => '蓇', '��' => '蓌', '��' => '蒛', '��' => '蒩', '��' => '蒯', '��' => '蒨', '��' => '蓖', '��' => '蒘', '��' => '蒶', '��' => '蓏', '��' => '蒠', '��' => '蓗', '��' => '蓔', '��' => '蓒', '��' => '蓛', '��' => '蒰', '��' => '蒑', '��' => '虡', '��' => '蜳', '��' => '蜣', '��' => '蜨', '��' => '蝫', '��' => '蝀', '��' => '蜮', '��' => '蜞', '��' => '蜡', '��' => '蜙', '��' => '蜛', '��' => '蝃', '��' => '蜬', '��' => '蝁', '��' => '蜾', '��' => '蝆', '��' => '蜠', '��' => '蜲', '��' => '蜪', '��' => '蜭', '��' => '蜼', '��' => '蜒', '��' => '蜺', '��' => '蜱', '��' => '蜵', '��' => '蝂', '��' => '蜦', '��' => '蜧', '��' => '蜸', '��' => '蜤', '��' => '蜚', '��' => '蜰', '��' => '蜑', '�' => '裷', '�' => '裧', '�' => '裱', '�' => '裲', '�' => '裺', '�' => '裾', '�' => '裮', '�' => '裼', '�' => '裶', '�' => '裻', '�@' => '裰', '�A' => '裬', '�B' => '裫', '�C' => '覝', '�D' => '覡', '�E' => '覟', '�F' => '覞', '�G' => '觩', '�H' => '觫', '�I' => '觨', '�J' => '誫', '�K' => '誙', '�L' => '誋', '�M' => '誒', '�N' => '誏', '�O' => '誖', '�P' => '谽', '�Q' => '豨', '�R' => '豩', '�S' => '賕', '�T' => '賏', '�U' => '賗', '�V' => '趖', '�W' => '踉', '�X' => '踂', '�Y' => '跿', '�Z' => '踍', '�[' => '跽', '�\\' => '踊', '�]' => '踃', '�^' => '踇', '�_' => '踆', '�`' => '踅', '�a' => '跾', '�b' => '踀', '�c' => '踄', '�d' => '輐', '�e' => '輑', '�f' => '輎', '�g' => '輍', '�h' => '鄣', '�i' => '鄜', '�j' => '鄠', '�k' => '鄢', '�l' => '鄟', '�m' => '鄝', '�n' => '鄚', '�o' => '鄤', '�p' => '鄡', '�q' => '鄛', '�r' => '酺', '�s' => '酲', '�t' => '酹', '�u' => '酳', '�v' => '銥', '�w' => '銤', '�x' => '鉶', '�y' => '銛', '�z' => '鉺', '�{' => '銠', '�|' => '銔', '�}' => '銪', '�~' => '銍', '�' => '銦', '�' => '銚', '�' => '銫', '�' => '鉹', '�' => '銗', '�' => '鉿', '�' => '銣', '�' => '鋮', '�' => '銎', '�' => '銂', '�' => '銕', '�' => '銢', '�' => '鉽', '�' => '銈', '�' => '銡', '�' => '銊', '�' => '銆', '�' => '銌', '�' => '銙', '�' => '銧', '�' => '鉾', '�' => '銇', '�' => '銩', '�' => '銝', '�' => '銋', '�' => '鈭', '�' => '隞', '�' => '隡', '�' => '雿', '�' => '靘', '�' => '靽', '�' => '靺', '�' => '靾', '��' => '鞃', '��' => '鞀', '��' => '鞂', '��' => '靻', '��' => '鞄', '��' => '鞁', '��' => '靿', '��' => '韎', '��' => '韍', '��' => '頖', '��' => '颭', '��' => '颮', '��' => '餂', '��' => '餀', '��' => '餇', '��' => '馝', '��' => '馜', '��' => '駃', '��' => '馹', '��' => '馻', '��' => '馺', '��' => '駂', '��' => '馽', '��' => '駇', '��' => '骱', '��' => '髣', '��' => '髧', '��' => '鬾', '��' => '鬿', '��' => '魠', '��' => '魡', '��' => '魟', '��' => '鳱', '��' => '鳲', '��' => '鳵', '��' => '麧', '��' => '僿', '��' => '儃', '��' => '儰', '��' => '僸', '��' => '儆', '��' => '儇', '��' => '僶', '��' => '僾', '��' => '儋', '��' => '儌', '��' => '僽', '��' => '儊', '��' => '劋', '��' => '劌', '��' => '勱', '�' => '勯', '�' => '噈', '�' => '噂', '�' => '噌', '�' => '嘵', '�' => '噁', '�' => '噊', '�' => '噉', '�' => '噆', '�' => '噘', '�@' => '噚', '�A' => '噀', '�B' => '嘳', '�C' => '嘽', '�D' => '嘬', '�E' => '嘾', '�F' => '嘸', '�G' => '嘪', '�H' => '嘺', '�I' => '圚', '�J' => '墫', '�K' => '墝', '�L' => '墱', '�M' => '墠', '�N' => '墣', '�O' => '墯', '�P' => '墬', '�Q' => '墥', '�R' => '墡', '�S' => '壿', '�T' => '嫿', '�U' => '嫴', '�V' => '嫽', '�W' => '嫷', '�X' => '嫶', '�Y' => '嬃', '�Z' => '嫸', '�[' => '嬂', '�\\' => '嫹', '�]' => '嬁', '�^' => '嬇', '�_' => '嬅', '�`' => '嬏', '�a' => '屧', '�b' => '嶙', '�c' => '嶗', '�d' => '嶟', '�e' => '嶒', '�f' => '嶢', '�g' => '嶓', '�h' => '嶕', '�i' => '嶠', '�j' => '嶜', '�k' => '嶡', '�l' => '嶚', '�m' => '嶞', '�n' => '幩', '�o' => '幝', '�p' => '幠', '�q' => '幜', '�r' => '緳', '�s' => '廛', '�t' => '廞', '�u' => '廡', '�v' => '彉', '�w' => '徲', '�x' => '憋', '�y' => '憃', '�z' => '慹', '�{' => '憱', '�|' => '憰', '�}' => '憢', '�~' => '憉', '�' => '憛', '�' => '憓', '�' => '憯', '�' => '憭', '�' => '憟', '�' => '憒', '�' => '憪', '�' => '憡', '�' => '憍', '�' => '慦', '�' => '憳', '�' => '戭', '�' => '摮', '�' => '摰', '�' => '撖', '�' => '撠', '�' => '撅', '�' => '撗', '�' => '撜', '�' => '撏', '�' => '撋', '�' => '撊', '�' => '撌', '�' => '撣', '�' => '撟', '�' => '摨', '�' => '撱', '�' => '撘', '�' => '敶', '�' => '敺', '�' => '敹', '�' => '敻', '�' => '斲', '��' => '斳', '��' => '暵', '��' => '暰', '��' => '暩', '��' => '暲', '��' => '暷', '��' => '暪', '��' => '暯', '��' => '樀', '��' => '樆', '��' => '樗', '��' => '槥', '��' => '槸', '��' => '樕', '��' => '槱', '��' => '槤', '��' => '樠', '��' => '槿', '��' => '槬', '��' => '槢', '��' => '樛', '��' => '樝', '��' => '槾', '��' => '樧', '��' => '槲', '��' => '槮', '��' => '樔', '��' => '槷', '��' => '槧', '��' => '橀', '��' => '樈', '��' => '槦', '��' => '槻', '��' => '樍', '��' => '槼', '��' => '槫', '��' => '樉', '��' => '樄', '��' => '樘', '��' => '樥', '��' => '樏', '��' => '槶', '��' => '樦', '��' => '樇', '��' => '槴', '��' => '樖', '��' => '歑', '��' => '殥', '��' => '殣', '��' => '殢', '��' => '殦', '�' => '氁', '�' => '氀', '�' => '毿', '�' => '氂', '�' => '潁', '�' => '漦', '�' => '潾', '�' => '澇', '�' => '濆', '�' => '澒', '�@' => '澍', '�A' => '澉', '�B' => '澌', '�C' => '潢', '�D' => '潏', '�E' => '澅', '�F' => '潚', '�G' => '澖', '�H' => '潶', '�I' => '潬', '�J' => '澂', '�K' => '潕', '�L' => '潲', '�M' => '潒', '�N' => '潐', '�O' => '潗', '�P' => '澔', '�Q' => '澓', '�R' => '潝', '�S' => '漀', '�T' => '潡', '�U' => '潫', '�V' => '潽', '�W' => '潧', '�X' => '澐', '�Y' => '潓', '�Z' => '澋', '�[' => '潩', '�\\' => '潿', '�]' => '澕', '�^' => '潣', '�_' => '潷', '�`' => '潪', '�a' => '潻', '�b' => '熲', '�c' => '熯', '�d' => '熛', '�e' => '熰', '�f' => '熠', '�g' => '熚', '�h' => '熩', '�i' => '熵', '�j' => '熝', '�k' => '熥', '�l' => '熞', '�m' => '熤', '�n' => '熡', '�o' => '熪', '�p' => '熜', '�q' => '熧', '�r' => '熳', '�s' => '犘', '�t' => '犚', '�u' => '獘', '�v' => '獒', '�w' => '獞', '�x' => '獟', '�y' => '獠', '�z' => '獝', '�{' => '獛', '�|' => '獡', '�}' => '獚', '�~' => '獙', '�' => '獢', '�' => '璇', '�' => '璉', '�' => '璊', '�' => '璆', '�' => '璁', '�' => '瑽', '�' => '璅', '�' => '璈', '�' => '瑼', '�' => '瑹', '�' => '甈', '�' => '甇', '�' => '畾', '�' => '瘥', '�' => '瘞', '�' => '瘙', '�' => '瘝', '�' => '瘜', '�' => '瘣', '�' => '瘚', '�' => '瘨', '�' => '瘛', '�' => '皜', '�' => '皝', '�' => '皞', '�' => '皛', '�' => '瞍', '�' => '瞏', '�' => '瞉', '�' => '瞈', '�' => '磍', '�' => '碻', '��' => '磏', '��' => '磌', '��' => '磑', '��' => '磎', '��' => '磔', '��' => '磈', '��' => '磃', '��' => '磄', '��' => '磉', '��' => '禚', '��' => '禡', '��' => '禠', '��' => '禜', '��' => '禢', '��' => '禛', '��' => '歶', '��' => '稹', '��' => '窲', '��' => '窴', '��' => '窳', '��' => '箷', '��' => '篋', '��' => '箾', '��' => '箬', '��' => '篎', '��' => '箯', '��' => '箹', '��' => '篊', '��' => '箵', '��' => '糅', '��' => '糈', '��' => '糌', '��' => '糋', '��' => '緷', '��' => '緛', '��' => '緪', '��' => '緧', '��' => '緗', '��' => '緡', '��' => '縃', '��' => '緺', '��' => '緦', '��' => '緶', '��' => '緱', '��' => '緰', '��' => '緮', '��' => '緟', '��' => '罶', '��' => '羬', '��' => '羰', '��' => '羭', '�' => '翭', '�' => '翫', '�' => '翪', '�' => '翬', '�' => '翦', '�' => '翨', '�' => '聤', '�' => '聧', '�' => '膣', '�' => '膟', '�@' => '膞', '�A' => '膕', '�B' => '膢', '�C' => '膙', '�D' => '膗', '�E' => '舖', '�F' => '艏', '�G' => '艓', '�H' => '艒', '�I' => '艐', '�J' => '艎', '�K' => '艑', '�L' => '蔤', '�M' => '蔻', '�N' => '蔏', '�O' => '蔀', '�P' => '蔩', '�Q' => '蔎', '�R' => '蔉', '�S' => '蔍', '�T' => '蔟', '�U' => '蔊', '�V' => '蔧', '�W' => '蔜', '�X' => '蓻', '�Y' => '蔫', '�Z' => '蓺', '�[' => '蔈', '�\\' => '蔌', '�]' => '蓴', '�^' => '蔪', '�_' => '蓲', '�`' => '蔕', '�a' => '蓷', '�b' => '蓫', '�c' => '蓳', '�d' => '蓼', '�e' => '蔒', '�f' => '蓪', '�g' => '蓩', '�h' => '蔖', '�i' => '蓾', '�j' => '蔨', '�k' => '蔝', '�l' => '蔮', '�m' => '蔂', '�n' => '蓽', '�o' => '蔞', '�p' => '蓶', '�q' => '蔱', '�r' => '蔦', '�s' => '蓧', '�t' => '蓨', '�u' => '蓰', '�v' => '蓯', '�w' => '蓹', '�x' => '蔘', '�y' => '蔠', '�z' => '蔰', '�{' => '蔋', '�|' => '蔙', '�}' => '蔯', '�~' => '虢', '�' => '蝖', '�' => '蝣', '�' => '蝤', '�' => '蝷', '�' => '蟡', '�' => '蝳', '�' => '蝘', '�' => '蝔', '�' => '蝛', '�' => '蝒', '�' => '蝡', '�' => '蝚', '�' => '蝑', '�' => '蝞', '�' => '蝭', '�' => '蝪', '�' => '蝐', '�' => '蝎', '�' => '蝟', '�' => '蝝', '�' => '蝯', '�' => '蝬', '�' => '蝺', '�' => '蝮', '�' => '蝜', '�' => '蝥', '�' => '蝏', '�' => '蝻', '�' => '蝵', '�' => '蝢', '�' => '蝧', '�' => '蝩', '�' => '衚', '��' => '褅', '��' => '褌', '��' => '褔', '��' => '褋', '��' => '褗', '��' => '褘', '��' => '褙', '��' => '褆', '��' => '褖', '��' => '褑', '��' => '褎', '��' => '褉', '��' => '覢', '��' => '覤', '��' => '覣', '��' => '觭', '��' => '觰', '��' => '觬', '��' => '諏', '��' => '諆', '��' => '誸', '��' => '諓', '��' => '諑', '��' => '諔', '��' => '諕', '��' => '誻', '��' => '諗', '��' => '誾', '��' => '諀', '��' => '諅', '��' => '諘', '��' => '諃', '��' => '誺', '��' => '誽', '��' => '諙', '��' => '谾', '��' => '豍', '��' => '貏', '��' => '賥', '��' => '賟', '��' => '賙', '��' => '賨', '��' => '賚', '��' => '賝', '��' => '賧', '��' => '趠', '��' => '趜', '��' => '趡', '��' => '趛', '��' => '踠', '��' => '踣', '�' => '踥', '�' => '踤', '�' => '踮', '�' => '踕', '�' => '踛', '�' => '踖', '�' => '踑', '�' => '踙', '�' => '踦', '�' => '踧', '�@' => '踔', '�A' => '踒', '�B' => '踘', '�C' => '踓', '�D' => '踜', '�E' => '踗', '�F' => '踚', '�G' => '輬', '�H' => '輤', '�I' => '輘', '�J' => '輚', '�K' => '輠', '�L' => '輣', '�M' => '輖', '�N' => '輗', '�O' => '遳', '�P' => '遰', '�Q' => '遯', '�R' => '遧', '�S' => '遫', '�T' => '鄯', '�U' => '鄫', '�V' => '鄩', '�W' => '鄪', '�X' => '鄲', '�Y' => '鄦', '�Z' => '鄮', '�[' => '醅', '�\\' => '醆', '�]' => '醊', '�^' => '醁', '�_' => '醂', '�`' => '醄', '�a' => '醀', '�b' => '鋐', '�c' => '鋃', '�d' => '鋄', '�e' => '鋀', '�f' => '鋙', '�g' => '銶', '�h' => '鋏', '�i' => '鋱', '�j' => '鋟', '�k' => '鋘', '�l' => '鋩', '�m' => '鋗', '�n' => '鋝', '�o' => '鋌', '�p' => '鋯', '�q' => '鋂', '�r' => '鋨', '�s' => '鋊', '�t' => '鋈', '�u' => '鋎', '�v' => '鋦', '�w' => '鋍', '�x' => '鋕', '�y' => '鋉', '�z' => '鋠', '�{' => '鋞', '�|' => '鋧', '�}' => '鋑', '�~' => '鋓', '�' => '銵', '�' => '鋡', '�' => '鋆', '�' => '銴', '�' => '镼', '�' => '閬', '�' => '閫', '�' => '閮', '�' => '閰', '�' => '隤', '�' => '隢', '�' => '雓', '�' => '霅', '�' => '霈', '�' => '霂', '�' => '靚', '�' => '鞊', '�' => '鞎', '�' => '鞈', '�' => '韐', '�' => '韏', '�' => '頞', '�' => '頝', '�' => '頦', '�' => '頩', '�' => '頨', '�' => '頠', '�' => '頛', '�' => '頧', '�' => '颲', '�' => '餈', '�' => '飺', '�' => '餑', '��' => '餔', '��' => '餖', '��' => '餗', '��' => '餕', '��' => '駜', '��' => '駍', '��' => '駏', '��' => '駓', '��' => '駔', '��' => '駎', '��' => '駉', '��' => '駖', '��' => '駘', '��' => '駋', '��' => '駗', '��' => '駌', '��' => '骳', '��' => '髬', '��' => '髫', '��' => '髳', '��' => '髲', '��' => '髱', '��' => '魆', '��' => '魃', '��' => '魧', '��' => '魴', '��' => '魱', '��' => '魦', '��' => '魶', '��' => '魵', '��' => '魰', '��' => '魨', '��' => '魤', '��' => '魬', '��' => '鳼', '��' => '鳺', '��' => '鳽', '��' => '鳿', '��' => '鳷', '��' => '鴇', '��' => '鴀', '��' => '鳹', '��' => '鳻', '��' => '鴈', '��' => '鴅', '��' => '鴄', '��' => '麃', '��' => '黓', '��' => '鼏', '��' => '鼐', '��' => '儜', '�' => '儓', '�' => '儗', '�' => '儚', '�' => '儑', '�' => '凞', '�' => '匴', '�' => '叡', '�' => '噰', '�' => '噠', '�' => '噮', '�@' => '噳', '�A' => '噦', '�B' => '噣', '�C' => '噭', '�D' => '噲', '�E' => '噞', '�F' => '噷', '�G' => '圜', '�H' => '圛', '�I' => '壈', '�J' => '墽', '�K' => '壉', '�L' => '墿', '�M' => '墺', '�N' => '壂', '�O' => '墼', '�P' => '壆', '�Q' => '嬗', '�R' => '嬙', '�S' => '嬛', '�T' => '嬡', '�U' => '嬔', '�V' => '嬓', '�W' => '嬐', '�X' => '嬖', '�Y' => '嬨', '�Z' => '嬚', '�[' => '嬠', '�\\' => '嬞', '�]' => '寯', '�^' => '嶬', '�_' => '嶱', '�`' => '嶩', '�a' => '嶧', '�b' => '嶵', '�c' => '嶰', '�d' => '嶮', '�e' => '嶪', '�f' => '嶨', '�g' => '嶲', '�h' => '嶭', '�i' => '嶯', '�j' => '嶴', '�k' => '幧', '�l' => '幨', '�m' => '幦', '�n' => '幯', '�o' => '廩', '�p' => '廧', '�q' => '廦', '�r' => '廨', '�s' => '廥', '�t' => '彋', '�u' => '徼', '�v' => '憝', '�w' => '憨', '�x' => '憖', '�y' => '懅', '�z' => '憴', '�{' => '懆', '�|' => '懁', '�}' => '懌', '�~' => '憺', '�' => '憿', '�' => '憸', '�' => '憌', '�' => '擗', '�' => '擖', '�' => '擐', '�' => '擏', '�' => '擉', '�' => '撽', '�' => '撉', '�' => '擃', '�' => '擛', '�' => '擳', '�' => '擙', '�' => '攳', '�' => '敿', '�' => '敼', '�' => '斢', '�' => '曈', '�' => '暾', '�' => '曀', '�' => '曊', '�' => '曋', '�' => '曏', '�' => '暽', '�' => '暻', '�' => '暺', '�' => '曌', '�' => '朣', '�' => '樴', '�' => '橦', '�' => '橉', '�' => '橧', '��' => '樲', '��' => '橨', '��' => '樾', '��' => '橝', '��' => '橭', '��' => '橶', '��' => '橛', '��' => '橑', '��' => '樨', '��' => '橚', '��' => '樻', '��' => '樿', '��' => '橁', '��' => '橪', '��' => '橤', '��' => '橐', '��' => '橏', '��' => '橔', '��' => '橯', '��' => '橩', '��' => '橠', '��' => '樼', '��' => '橞', '��' => '橖', '��' => '橕', '��' => '橍', '��' => '橎', '��' => '橆', '��' => '歕', '��' => '歔', '��' => '歖', '��' => '殧', '��' => '殪', '��' => '殫', '��' => '毈', '��' => '毇', '��' => '氄', '��' => '氃', '��' => '氆', '��' => '澭', '��' => '濋', '��' => '澣', '��' => '濇', '��' => '澼', '��' => '濎', '��' => '濈', '��' => '潞', '��' => '濄', '��' => '澽', '��' => '澞', '��' => '濊', '�' => '澨', '�' => '瀄', '�' => '澥', '�' => '澮', '�' => '澺', '�' => '澬', '�' => '澪', '�' => '濏', '�' => '澿', '�' => '澸', '�@' => '澢', '�A' => '濉', '�B' => '澫', '�C' => '濍', '�D' => '澯', '�E' => '澲', '�F' => '澰', '�G' => '燅', '�H' => '燂', '�I' => '熿', '�J' => '熸', '�K' => '燖', '�L' => '燀', '�M' => '燁', '�N' => '燋', '�O' => '燔', '�P' => '燊', '�Q' => '燇', '�R' => '燏', '�S' => '熽', '�T' => '燘', '�U' => '熼', '�V' => '燆', '�W' => '燚', '�X' => '燛', '�Y' => '犝', '�Z' => '犞', '�[' => '獩', '�\\' => '獦', '�]' => '獧', '�^' => '獬', '�_' => '獥', '�`' => '獫', '�a' => '獪', '�b' => '瑿', '�c' => '璚', '�d' => '璠', '�e' => '璔', '�f' => '璒', '�g' => '璕', '�h' => '璡', '�i' => '甋', '�j' => '疀', '�k' => '瘯', '�l' => '瘭', '�m' => '瘱', '�n' => '瘽', '�o' => '瘳', '�p' => '瘼', '�q' => '瘵', '�r' => '瘲', '�s' => '瘰', '�t' => '皻', '�u' => '盦', '�v' => '瞚', '�w' => '瞝', '�x' => '瞡', '�y' => '瞜', '�z' => '瞛', '�{' => '瞢', '�|' => '瞣', '�}' => '瞕', '�~' => '瞙', '�' => '瞗', '�' => '磝', '�' => '磩', '�' => '磥', '�' => '磪', '�' => '磞', '�' => '磣', '�' => '磛', '�' => '磡', '�' => '磢', '�' => '磭', '�' => '磟', '�' => '磠', '�' => '禤', '�' => '穄', '�' => '穈', '�' => '穇', '�' => '窶', '�' => '窸', '�' => '窵', '�' => '窱', '�' => '窷', '�' => '篞', '�' => '篣', '�' => '篧', '�' => '篝', '�' => '篕', '�' => '篥', '�' => '篚', '�' => '篨', '�' => '篹', '�' => '篔', '�' => '篪', '��' => '篢', '��' => '篜', '��' => '篫', '��' => '篘', '��' => '篟', '��' => '糒', '��' => '糔', '��' => '糗', '��' => '糐', '��' => '糑', '��' => '縒', '��' => '縡', '��' => '縗', '��' => '縌', '��' => '縟', '��' => '縠', '��' => '縓', '��' => '縎', '��' => '縜', '��' => '縕', '��' => '縚', '��' => '縢', '��' => '縋', '��' => '縏', '��' => '縖', '��' => '縍', '��' => '縔', '��' => '縥', '��' => '縤', '��' => '罃', '��' => '罻', '��' => '罼', '��' => '罺', '��' => '羱', '��' => '翯', '��' => '耪', '��' => '耩', '��' => '聬', '��' => '膱', '��' => '膦', '��' => '膮', '��' => '膹', '��' => '膵', '��' => '膫', '��' => '膰', '��' => '膬', '��' => '膴', '��' => '膲', '��' => '膷', '��' => '膧', '��' => '臲', '�' => '艕', '�' => '艖', '�' => '艗', '�' => '蕖', '�' => '蕅', '�' => '蕫', '�' => '蕍', '�' => '蕓', '�' => '蕡', '�' => '蕘', '�@' => '蕀', '�A' => '蕆', '�B' => '蕤', '�C' => '蕁', '�D' => '蕢', '�E' => '蕄', '�F' => '蕑', '�G' => '蕇', '�H' => '蕣', '�I' => '蔾', '�J' => '蕛', '�K' => '蕱', '�L' => '蕎', '�M' => '蕮', '�N' => '蕵', '�O' => '蕕', '�P' => '蕧', '�Q' => '蕠', '�R' => '薌', '�S' => '蕦', '�T' => '蕝', '�U' => '蕔', '�V' => '蕥', '�W' => '蕬', '�X' => '虣', '�Y' => '虥', '�Z' => '虤', '�[' => '螛', '�\\' => '螏', '�]' => '螗', '�^' => '螓', '�_' => '螒', '�`' => '螈', '�a' => '螁', '�b' => '螖', '�c' => '螘', '�d' => '蝹', '�e' => '螇', '�f' => '螣', '�g' => '螅', '�h' => '螐', '�i' => '螑', '�j' => '螝', '�k' => '螄', '�l' => '螔', '�m' => '螜', '�n' => '螚', '�o' => '螉', '�p' => '褞', '�q' => '褦', '�r' => '褰', '�s' => '褭', '�t' => '褮', '�u' => '褧', '�v' => '褱', '�w' => '褢', '�x' => '褩', '�y' => '褣', '�z' => '褯', '�{' => '褬', '�|' => '褟', '�}' => '觱', '�~' => '諠', '�' => '諢', '�' => '諲', '�' => '諴', '�' => '諵', '�' => '諝', '�' => '謔', '�' => '諤', '�' => '諟', '�' => '諰', '�' => '諈', '�' => '諞', '�' => '諡', '�' => '諨', '�' => '諿', '�' => '諯', '�' => '諻', '�' => '貑', '�' => '貒', '�' => '貐', '�' => '賵', '�' => '賮', '�' => '賱', '�' => '賰', '�' => '賳', '�' => '赬', '�' => '赮', '�' => '趥', '�' => '趧', '�' => '踳', '�' => '踾', '�' => '踸', '�' => '蹀', '�' => '蹅', '��' => '踶', '��' => '踼', '��' => '踽', '��' => '蹁', '��' => '踰', '��' => '踿', '��' => '躽', '��' => '輶', '��' => '輮', '��' => '輵', '��' => '輲', '��' => '輹', '��' => '輷', '��' => '輴', '��' => '遶', '��' => '遹', '��' => '遻', '��' => '邆', '��' => '郺', '��' => '鄳', '��' => '鄵', '��' => '鄶', '��' => '醓', '��' => '醐', '��' => '醑', '��' => '醍', '��' => '醏', '��' => '錧', '��' => '錞', '��' => '錈', '��' => '錟', '��' => '錆', '��' => '錏', '��' => '鍺', '��' => '錸', '��' => '錼', '��' => '錛', '��' => '錣', '��' => '錒', '��' => '錁', '��' => '鍆', '��' => '錭', '��' => '錎', '��' => '錍', '��' => '鋋', '��' => '錝', '��' => '鋺', '��' => '錥', '��' => '錓', '��' => '鋹', '��' => '鋷', '�' => '錴', '�' => '錂', '�' => '錤', '�' => '鋿', '�' => '錩', '�' => '錹', '�' => '錵', '�' => '錪', '�' => '錔', '�' => '錌', '�@' => '錋', '�A' => '鋾', '�B' => '錉', '�C' => '錀', '�D' => '鋻', '�E' => '錖', '�F' => '閼', '�G' => '闍', '�H' => '閾', '�I' => '閹', '�J' => '閺', '�K' => '閶', '�L' => '閿', '�M' => '閵', '�N' => '閽', '�O' => '隩', '�P' => '雔', '�Q' => '霋', '�R' => '霒', '�S' => '霐', '�T' => '鞙', '�U' => '鞗', '�V' => '鞔', '�W' => '韰', '�X' => '韸', '�Y' => '頵', '�Z' => '頯', '�[' => '頲', '�\\' => '餤', '�]' => '餟', '�^' => '餧', '�_' => '餩', '�`' => '馞', '�a' => '駮', '�b' => '駬', '�c' => '駥', '�d' => '駤', '�e' => '駰', '�f' => '駣', '�g' => '駪', '�h' => '駩', '�i' => '駧', '�j' => '骹', '�k' => '骿', '�l' => '骴', '�m' => '骻', '�n' => '髶', '�o' => '髺', '�p' => '髹', '�q' => '髷', '�r' => '鬳', '�s' => '鮀', '�t' => '鮅', '�u' => '鮇', '�v' => '魼', '�w' => '魾', '�x' => '魻', '�y' => '鮂', '�z' => '鮓', '�{' => '鮒', '�|' => '鮐', '�}' => '魺', '�~' => '鮕', '�' => '魽', '�' => '鮈', '�' => '鴥', '�' => '鴗', '�' => '鴠', '�' => '鴞', '�' => '鴔', '�' => '鴩', '�' => '鴝', '�' => '鴘', '�' => '鴢', '�' => '鴐', '�' => '鴙', '�' => '鴟', '�' => '麈', '�' => '麆', '�' => '麇', '�' => '麮', '�' => '麭', '�' => '黕', '�' => '黖', '�' => '黺', '�' => '鼒', '�' => '鼽', '�' => '儦', '�' => '儥', '�' => '儢', '�' => '儤', '�' => '儠', '�' => '儩', '�' => '勴', '�' => '嚓', '�' => '嚌', '��' => '嚍', '��' => '嚆', '��' => '嚄', '��' => '嚃', '��' => '噾', '��' => '嚂', '��' => '噿', '��' => '嚁', '��' => '壖', '��' => '壔', '��' => '壏', '��' => '壒', '��' => '嬭', '��' => '嬥', '��' => '嬲', '��' => '嬣', '��' => '嬬', '��' => '嬧', '��' => '嬦', '��' => '嬯', '��' => '嬮', '��' => '孻', '��' => '寱', '��' => '寲', '��' => '嶷', '��' => '幬', '��' => '幪', '��' => '徾', '��' => '徻', '��' => '懃', '��' => '憵', '��' => '憼', '��' => '懧', '��' => '懠', '��' => '懥', '��' => '懤', '��' => '懨', '��' => '懞', '��' => '擯', '��' => '擩', '��' => '擣', '��' => '擫', '��' => '擤', '��' => '擨', '��' => '斁', '��' => '斀', '��' => '斶', '��' => '旚', '��' => '曒', '��' => '檍', '��' => '檖', '�' => '檁', '�' => '檥', '�' => '檉', '�' => '檟', '�' => '檛', '�' => '檡', '�' => '檞', '�' => '檇', '�' => '檓', '�' => '檎', '�@' => '檕', '�A' => '檃', '�B' => '檨', '�C' => '檤', '�D' => '檑', '�E' => '橿', '�F' => '檦', '�G' => '檚', '�H' => '檅', '�I' => '檌', '�J' => '檒', '�K' => '歛', '�L' => '殭', '�M' => '氉', '�N' => '濌', '�O' => '澩', '�P' => '濴', '�Q' => '濔', '�R' => '濣', '�S' => '濜', '�T' => '濭', '�U' => '濧', '�V' => '濦', '�W' => '濞', '�X' => '濲', '�Y' => '濝', '�Z' => '濢', '�[' => '濨', '�\\' => '燡', '�]' => '燱', '�^' => '燨', '�_' => '燲', '�`' => '燤', '�a' => '燰', '�b' => '燢', '�c' => '獳', '�d' => '獮', '�e' => '獯', '�f' => '璗', '�g' => '璲', '�h' => '璫', '�i' => '璐', '�j' => '璪', '�k' => '璭', '�l' => '璱', '�m' => '璥', '�n' => '璯', '�o' => '甐', '�p' => '甑', '�q' => '甒', '�r' => '甏', '�s' => '疄', '�t' => '癃', '�u' => '癈', '�v' => '癉', '�w' => '癇', '�x' => '皤', '�y' => '盩', '�z' => '瞵', '�{' => '瞫', '�|' => '瞲', '�}' => '瞷', '�~' => '瞶', '�' => '瞴', '�' => '瞱', '�' => '瞨', '�' => '矰', '�' => '磳', '�' => '磽', '�' => '礂', '�' => '磻', '�' => '磼', '�' => '磲', '�' => '礅', '�' => '磹', '�' => '磾', '�' => '礄', '�' => '禫', '�' => '禨', '�' => '穜', '�' => '穛', '�' => '穖', '�' => '穘', '�' => '穔', '�' => '穚', '�' => '窾', '�' => '竀', '�' => '竁', '�' => '簅', '�' => '簏', '�' => '篲', '�' => '簀', '�' => '篿', '�' => '篻', '�' => '簎', '�' => '篴', '��' => '簋', '��' => '篳', '��' => '簂', '��' => '簉', '��' => '簃', '��' => '簁', '��' => '篸', '��' => '篽', '��' => '簆', '��' => '篰', '��' => '篱', '��' => '簐', '��' => '簊', '��' => '糨', '��' => '縭', '��' => '縼', '��' => '繂', '��' => '縳', '��' => '顈', '��' => '縸', '��' => '縪', '��' => '繉', '��' => '繀', '��' => '繇', '��' => '縩', '��' => '繌', '��' => '縰', '��' => '縻', '��' => '縶', '��' => '繄', '��' => '縺', '��' => '罅', '��' => '罿', '��' => '罾', '��' => '罽', '��' => '翴', '��' => '翲', '��' => '耬', '��' => '膻', '��' => '臄', '��' => '臌', '��' => '臊', '��' => '臅', '��' => '臇', '��' => '膼', '��' => '臩', '��' => '艛', '��' => '艚', '��' => '艜', '��' => '薃', '��' => '薀', '�' => '薏', '�' => '薧', '�' => '薕', '�' => '薠', '�' => '薋', '�' => '薣', '�' => '蕻', '�' => '薤', '�' => '薚', '�' => '薞', '�@' => '蕷', '�A' => '蕼', '�B' => '薉', '�C' => '薡', '�D' => '蕺', '�E' => '蕸', '�F' => '蕗', '�G' => '薎', '�H' => '薖', '�I' => '薆', '�J' => '薍', '�K' => '薙', '�L' => '薝', '�M' => '薁', '�N' => '薢', '�O' => '薂', '�P' => '薈', '�Q' => '薅', '�R' => '蕹', '�S' => '蕶', '�T' => '薘', '�U' => '薐', '�V' => '薟', '�W' => '虨', '�X' => '螾', '�Y' => '螪', '�Z' => '螭', '�[' => '蟅', '�\\' => '螰', '�]' => '螬', '�^' => '螹', '�_' => '螵', '�`' => '螼', '�a' => '螮', '�b' => '蟉', '�c' => '蟃', '�d' => '蟂', '�e' => '蟌', '�f' => '螷', '�g' => '螯', '�h' => '蟄', '�i' => '蟊', '�j' => '螴', '�k' => '螶', '�l' => '螿', '�m' => '螸', '�n' => '螽', '�o' => '蟞', '�p' => '螲', '�q' => '褵', '�r' => '褳', '�s' => '褼', '�t' => '褾', '�u' => '襁', '�v' => '襒', '�w' => '褷', '�x' => '襂', '�y' => '覭', '�z' => '覯', '�{' => '覮', '�|' => '觲', '�}' => '觳', '�~' => '謞', '�' => '謘', '�' => '謖', '�' => '謑', '�' => '謅', '�' => '謋', '�' => '謢', '�' => '謏', '�' => '謒', '�' => '謕', '�' => '謇', '�' => '謍', '�' => '謈', '�' => '謆', '�' => '謜', '�' => '謓', '�' => '謚', '�' => '豏', '�' => '豰', '�' => '豲', '�' => '豱', '�' => '豯', '�' => '貕', '�' => '貔', '�' => '賹', '�' => '赯', '�' => '蹎', '�' => '蹍', '�' => '蹓', '�' => '蹐', '�' => '蹌', '�' => '蹇', '�' => '轃', '�' => '轀', '��' => '邅', '��' => '遾', '��' => '鄸', '��' => '醚', '��' => '醢', '��' => '醛', '��' => '醙', '��' => '醟', '��' => '醡', '��' => '醝', '��' => '醠', '��' => '鎡', '��' => '鎃', '��' => '鎯', '��' => '鍤', '��' => '鍖', '��' => '鍇', '��' => '鍼', '��' => '鍘', '��' => '鍜', '��' => '鍶', '��' => '鍉', '��' => '鍐', '��' => '鍑', '��' => '鍠', '��' => '鍭', '��' => '鎏', '��' => '鍌', '��' => '鍪', '��' => '鍹', '��' => '鍗', '��' => '鍕', '��' => '鍒', '��' => '鍏', '��' => '鍱', '��' => '鍷', '��' => '鍻', '��' => '鍡', '��' => '鍞', '��' => '鍣', '��' => '鍧', '��' => '鎀', '��' => '鍎', '��' => '鍙', '��' => '闇', '��' => '闀', '��' => '闉', '��' => '闃', '��' => '闅', '��' => '閷', '��' => '隮', '�' => '隰', '�' => '隬', '�' => '霠', '�' => '霟', '�' => '霘', '�' => '霝', '�' => '霙', '�' => '鞚', '�' => '鞡', '�' => '鞜', '�@' => '鞞', '�A' => '鞝', '�B' => '韕', '�C' => '韔', '�D' => '韱', '�E' => '顁', '�F' => '顄', '�G' => '顊', '�H' => '顉', '�I' => '顅', '�J' => '顃', '�K' => '餥', '�L' => '餫', '�M' => '餬', '�N' => '餪', '�O' => '餳', '�P' => '餲', '�Q' => '餯', '�R' => '餭', '�S' => '餱', '�T' => '餰', '�U' => '馘', '�V' => '馣', '�W' => '馡', '�X' => '騂', '�Y' => '駺', '�Z' => '駴', '�[' => '駷', '�\\' => '駹', '�]' => '駸', '�^' => '駶', '�_' => '駻', '�`' => '駽', '�a' => '駾', '�b' => '駼', '�c' => '騃', '�d' => '骾', '�e' => '髾', '�f' => '髽', '�g' => '鬁', '�h' => '髼', '�i' => '魈', '�j' => '鮚', '�k' => '鮨', '�l' => '鮞', '�m' => '鮛', '�n' => '鮦', '�o' => '鮡', '�p' => '鮥', '�q' => '鮤', '�r' => '鮆', '�s' => '鮢', '�t' => '鮠', '�u' => '鮯', '�v' => '鴳', '�w' => '鵁', '�x' => '鵧', '�y' => '鴶', '�z' => '鴮', '�{' => '鴯', '�|' => '鴱', '�}' => '鴸', '�~' => '鴰', '�' => '鵅', '�' => '鵂', '�' => '鵃', '�' => '鴾', '�' => '鴷', '�' => '鵀', '�' => '鴽', '�' => '翵', '�' => '鴭', '�' => '麊', '�' => '麉', '�' => '麍', '�' => '麰', '�' => '黈', '�' => '黚', '�' => '黻', '�' => '黿', '�' => '鼤', '�' => '鼣', '�' => '鼢', '�' => '齔', '�' => '龠', '�' => '儱', '�' => '儭', '�' => '儮', '�' => '嚘', '�' => '嚜', '�' => '嚗', '�' => '嚚', '�' => '嚝', '�' => '嚙', '�' => '奰', '�' => '嬼', '��' => '屩', '��' => '屪', '��' => '巀', '��' => '幭', '��' => '幮', '��' => '懘', '��' => '懟', '��' => '懭', '��' => '懮', '��' => '懱', '��' => '懪', '��' => '懰', '��' => '懫', '��' => '懖', '��' => '懩', '��' => '擿', '��' => '攄', '��' => '擽', '��' => '擸', '��' => '攁', '��' => '攃', '��' => '擼', '��' => '斔', '��' => '旛', '��' => '曚', '��' => '曛', '��' => '曘', '��' => '櫅', '��' => '檹', '��' => '檽', '��' => '櫡', '��' => '櫆', '��' => '檺', '��' => '檶', '��' => '檷', '��' => '櫇', '��' => '檴', '��' => '檭', '��' => '歞', '��' => '毉', '��' => '氋', '��' => '瀇', '��' => '瀌', '��' => '瀍', '��' => '瀁', '��' => '瀅', '��' => '瀔', '��' => '瀎', '��' => '濿', '��' => '瀀', '��' => '濻', '�' => '瀦', '�' => '濼', '�' => '濷', '�' => '瀊', '�' => '爁', '�' => '燿', '�' => '燹', '�' => '爃', '�' => '燽', '�' => '獶', '�@' => '璸', '�A' => '瓀', '�B' => '璵', '�C' => '瓁', '�D' => '璾', '�E' => '璶', '�F' => '璻', '�G' => '瓂', '�H' => '甔', '�I' => '甓', '�J' => '癜', '�K' => '癤', '�L' => '癙', '�M' => '癐', '�N' => '癓', '�O' => '癗', '�P' => '癚', '�Q' => '皦', '�R' => '皽', '�S' => '盬', '�T' => '矂', '�U' => '瞺', '�V' => '磿', '�W' => '礌', '�X' => '礓', '�Y' => '礔', '�Z' => '礉', '�[' => '礐', '�\\' => '礒', '�]' => '礑', '�^' => '禭', '�_' => '禬', '�`' => '穟', '�a' => '簜', '�b' => '簩', '�c' => '簙', '�d' => '簠', '�e' => '簟', '�f' => '簭', '�g' => '簝', '�h' => '簦', '�i' => '簨', '�j' => '簢', '�k' => '簥', '�l' => '簰', '�m' => '繜', '�n' => '繐', '�o' => '繖', '�p' => '繣', '�q' => '繘', '�r' => '繢', '�s' => '繟', '�t' => '繑', '�u' => '繠', '�v' => '繗', '�w' => '繓', '�x' => '羵', '�y' => '羳', '�z' => '翷', '�{' => '翸', '�|' => '聵', '�}' => '臑', '�~' => '臒', '�' => '臐', '�' => '艟', '�' => '艞', '�' => '薴', '�' => '藆', '�' => '藀', '�' => '藃', '�' => '藂', '�' => '薳', '�' => '薵', '�' => '薽', '�' => '藇', '�' => '藄', '�' => '薿', '�' => '藋', '�' => '藎', '�' => '藈', '�' => '藅', '�' => '薱', '�' => '薶', '�' => '藒', '�' => '蘤', '�' => '薸', '�' => '薷', '�' => '薾', '�' => '虩', '�' => '蟧', '�' => '蟦', '�' => '蟢', '�' => '蟛', '�' => '蟫', '�' => '蟪', '�' => '蟥', '��' => '蟟', '��' => '蟳', '��' => '蟤', '��' => '蟔', '��' => '蟜', '��' => '蟓', '��' => '蟭', '��' => '蟘', '��' => '蟣', '��' => '螤', '��' => '蟗', '��' => '蟙', '��' => '蠁', '��' => '蟴', '��' => '蟨', '��' => '蟝', '��' => '襓', '��' => '襋', '��' => '襏', '��' => '襌', '��' => '襆', '��' => '襐', '��' => '襑', '��' => '襉', '��' => '謪', '��' => '謧', '��' => '謣', '��' => '謳', '��' => '謰', '��' => '謵', '��' => '譇', '��' => '謯', '��' => '謼', '��' => '謾', '��' => '謱', '��' => '謥', '��' => '謷', '��' => '謦', '��' => '謶', '��' => '謮', '��' => '謤', '��' => '謻', '��' => '謽', '��' => '謺', '��' => '豂', '��' => '豵', '��' => '貙', '��' => '貘', '��' => '貗', '��' => '賾', '��' => '贄', '�' => '贂', '�' => '贀', '�' => '蹜', '�' => '蹢', '�' => '蹠', '�' => '蹗', '�' => '蹖', '�' => '蹞', '�' => '蹥', '�' => '蹧', '�@' => '蹛', '�A' => '蹚', '�B' => '蹡', '�C' => '蹝', '�D' => '蹩', '�E' => '蹔', '�F' => '轆', '�G' => '轇', '�H' => '轈', '�I' => '轋', '�J' => '鄨', '�K' => '鄺', '�L' => '鄻', '�M' => '鄾', '�N' => '醨', '�O' => '醥', '�P' => '醧', '�Q' => '醯', '�R' => '醪', '�S' => '鎵', '�T' => '鎌', '�U' => '鎒', '�V' => '鎷', '�W' => '鎛', '�X' => '鎝', '�Y' => '鎉', '�Z' => '鎧', '�[' => '鎎', '�\\' => '鎪', '�]' => '鎞', '�^' => '鎦', '�_' => '鎕', '�`' => '鎈', '�a' => '鎙', '�b' => '鎟', '�c' => '鎍', '�d' => '鎱', '�e' => '鎑', '�f' => '鎲', '�g' => '鎤', '�h' => '鎨', '�i' => '鎴', '�j' => '鎣', '�k' => '鎥', '�l' => '闒', '�m' => '闓', '�n' => '闑', '�o' => '隳', '�p' => '雗', '�q' => '雚', '�r' => '巂', '�s' => '雟', '�t' => '雘', '�u' => '雝', '�v' => '霣', '�w' => '霢', '�x' => '霥', '�y' => '鞬', '�z' => '鞮', '�{' => '鞨', '�|' => '鞫', '�}' => '鞤', '�~' => '鞪', '�' => '鞢', '�' => '鞥', '�' => '韗', '�' => '韙', '�' => '韖', '�' => '韘', '�' => '韺', '�' => '顐', '�' => '顑', '�' => '顒', '�' => '颸', '�' => '饁', '�' => '餼', '�' => '餺', '�' => '騏', '�' => '騋', '�' => '騉', '�' => '騍', '�' => '騄', '�' => '騑', '�' => '騊', '�' => '騅', '�' => '騇', '�' => '騆', '�' => '髀', '�' => '髜', '�' => '鬈', '�' => '鬄', '�' => '鬅', '�' => '鬩', '�' => '鬵', '�' => '魊', '�' => '魌', '��' => '魋', '��' => '鯇', '��' => '鯆', '��' => '鯃', '��' => '鮿', '��' => '鯁', '��' => '鮵', '��' => '鮸', '��' => '鯓', '��' => '鮶', '��' => '鯄', '��' => '鮹', '��' => '鮽', '��' => '鵜', '��' => '鵓', '��' => '鵏', '��' => '鵊', '��' => '鵛', '��' => '鵋', '��' => '鵙', '��' => '鵖', '��' => '鵌', '��' => '鵗', '��' => '鵒', '��' => '鵔', '��' => '鵟', '��' => '鵘', '��' => '鵚', '��' => '麎', '��' => '麌', '��' => '黟', '��' => '鼁', '��' => '鼀', '��' => '鼖', '��' => '鼥', '��' => '鼫', '��' => '鼪', '��' => '鼩', '��' => '鼨', '��' => '齌', '��' => '齕', '��' => '儴', '��' => '儵', '��' => '劖', '��' => '勷', '��' => '厴', '��' => '嚫', '��' => '嚭', '��' => '嚦', '��' => '嚧', '��' => '嚪', '�' => '嚬', '�' => '壚', '�' => '壝', '�' => '壛', '�' => '夒', '�' => '嬽', '�' => '嬾', '�' => '嬿', '�' => '巃', '�' => '幰', '�@' => '徿', '�A' => '懻', '�B' => '攇', '�C' => '攐', '�D' => '攍', '�E' => '攉', '�F' => '攌', '�G' => '攎', '�H' => '斄', '�I' => '旞', '�J' => '旝', '�K' => '曞', '�L' => '櫧', '�M' => '櫠', '�N' => '櫌', '�O' => '櫑', '�P' => '櫙', '�Q' => '櫋', '�R' => '櫟', '�S' => '櫜', '�T' => '櫐', '�U' => '櫫', '�V' => '櫏', '�W' => '櫍', '�X' => '櫞', '�Y' => '歠', '�Z' => '殰', '�[' => '氌', '�\\' => '瀙', '�]' => '瀧', '�^' => '瀠', '�_' => '瀖', '�`' => '瀫', '�a' => '瀡', '�b' => '瀢', '�c' => '瀣', '�d' => '瀩', '�e' => '瀗', '�f' => '瀤', '�g' => '瀜', '�h' => '瀪', '�i' => '爌', '�j' => '爊', '�k' => '爇', '�l' => '爂', '�m' => '爅', '�n' => '犥', '�o' => '犦', '�p' => '犤', '�q' => '犣', '�r' => '犡', '�s' => '瓋', '�t' => '瓅', '�u' => '璷', '�v' => '瓃', '�w' => '甖', '�x' => '癠', '�y' => '矉', '�z' => '矊', '�{' => '矄', '�|' => '矱', '�}' => '礝', '�~' => '礛', '�' => '礡', '�' => '礜', '�' => '礗', '�' => '礞', '�' => '禰', '�' => '穧', '�' => '穨', '�' => '簳', '�' => '簼', '�' => '簹', '�' => '簬', '�' => '簻', '�' => '糬', '�' => '糪', '�' => '繶', '�' => '繵', '�' => '繸', '�' => '繰', '�' => '繷', '�' => '繯', '�' => '繺', '�' => '繲', '�' => '繴', '�' => '繨', '�' => '罋', '�' => '罊', '�' => '羃', '�' => '羆', '�' => '羷', '�' => '翽', '�' => '翾', '�' => '聸', '�' => '臗', '��' => '臕', '��' => '艤', '��' => '艡', '��' => '艣', '��' => '藫', '��' => '藱', '��' => '藭', '��' => '藙', '��' => '藡', '��' => '藨', '��' => '藚', '��' => '藗', '��' => '藬', '��' => '藲', '��' => '藸', '��' => '藘', '��' => '藟', '��' => '藣', '��' => '藜', '��' => '藑', '��' => '藰', '��' => '藦', '��' => '藯', '��' => '藞', '��' => '藢', '��' => '蠀', '��' => '蟺', '��' => '蠃', '��' => '蟶', '��' => '蟷', '��' => '蠉', '��' => '蠌', '��' => '蠋', '��' => '蠆', '��' => '蟼', '��' => '蠈', '��' => '蟿', '��' => '蠊', '��' => '蠂', '��' => '襢', '��' => '襚', '��' => '襛', '��' => '襗', '��' => '襡', '��' => '襜', '��' => '襘', '��' => '襝', '��' => '襙', '��' => '覈', '��' => '覷', '��' => '覶', '�' => '觶', '�' => '譐', '�' => '譈', '�' => '譊', '�' => '譀', '�' => '譓', '�' => '譖', '�' => '譔', '�' => '譋', '�' => '譕', '�@' => '譑', '�A' => '譂', '�B' => '譒', '�C' => '譗', '�D' => '豃', '�E' => '豷', '�F' => '豶', '�G' => '貚', '�H' => '贆', '�I' => '贇', '�J' => '贉', '�K' => '趬', '�L' => '趪', '�M' => '趭', '�N' => '趫', '�O' => '蹭', '�P' => '蹸', '�Q' => '蹳', '�R' => '蹪', '�S' => '蹯', '�T' => '蹻', '�U' => '軂', '�V' => '轒', '�W' => '轑', '�X' => '轏', '�Y' => '轐', '�Z' => '轓', '�[' => '辴', '�\\' => '酀', '�]' => '鄿', '�^' => '醰', '�_' => '醭', '�`' => '鏞', '�a' => '鏇', '�b' => '鏏', '�c' => '鏂', '�d' => '鏚', '�e' => '鏐', '�f' => '鏹', '�g' => '鏬', '�h' => '鏌', '�i' => '鏙', '�j' => '鎩', '�k' => '鏦', '�l' => '鏊', '�m' => '鏔', '�n' => '鏮', '�o' => '鏣', '�p' => '鏕', '�q' => '鏄', '�r' => '鏎', '�s' => '鏀', '�t' => '鏒', '�u' => '鏧', '�v' => '镽', '�w' => '闚', '�x' => '闛', '�y' => '雡', '�z' => '霩', '�{' => '霫', '�|' => '霬', '�}' => '霨', '�~' => '霦', '�' => '鞳', '�' => '鞷', '�' => '鞶', '�' => '韝', '�' => '韞', '�' => '韟', '�' => '顜', '�' => '顙', '�' => '顝', '�' => '顗', '�' => '颿', '�' => '颽', '�' => '颻', '�' => '颾', '�' => '饈', '�' => '饇', '�' => '饃', '�' => '馦', '�' => '馧', '�' => '騚', '�' => '騕', '�' => '騥', '�' => '騝', '�' => '騤', '�' => '騛', '�' => '騢', '�' => '騠', '�' => '騧', '�' => '騣', '�' => '騞', '�' => '騜', '�' => '騔', '�' => '髂', '��' => '鬋', '��' => '鬊', '��' => '鬎', '��' => '鬌', '��' => '鬷', '��' => '鯪', '��' => '鯫', '��' => '鯠', '��' => '鯞', '��' => '鯤', '��' => '鯦', '��' => '鯢', '��' => '鯰', '��' => '鯔', '��' => '鯗', '��' => '鯬', '��' => '鯜', '��' => '鯙', '��' => '鯥', '��' => '鯕', '��' => '鯡', '��' => '鯚', '��' => '鵷', '��' => '鶁', '��' => '鶊', '��' => '鶄', '��' => '鶈', '��' => '鵱', '��' => '鶀', '��' => '鵸', '��' => '鶆', '��' => '鶋', '��' => '鶌', '��' => '鵽', '��' => '鵫', '��' => '鵴', '��' => '鵵', '��' => '鵰', '��' => '鵩', '��' => '鶅', '��' => '鵳', '��' => '鵻', '��' => '鶂', '��' => '鵯', '��' => '鵹', '��' => '鵿', '��' => '鶇', '��' => '鵨', '��' => '麔', '��' => '麑', '��' => '黀', '�' => '黼', '�' => '鼭', '�' => '齀', '�' => '齁', '�' => '齍', '�' => '齖', '�' => '齗', '�' => '齘', '�' => '匷', '�' => '嚲', '�@' => '嚵', '�A' => '嚳', '�B' => '壣', '�C' => '孅', '�D' => '巆', '�E' => '巇', '�F' => '廮', '�G' => '廯', '�H' => '忀', '�I' => '忁', '�J' => '懹', '�K' => '攗', '�L' => '攖', '�M' => '攕', '�N' => '攓', '�O' => '旟', '�P' => '曨', '�Q' => '曣', '�R' => '曤', '�S' => '櫳', '�T' => '櫰', '�U' => '櫪', '�V' => '櫨', '�W' => '櫹', '�X' => '櫱', '�Y' => '櫮', '�Z' => '櫯', '�[' => '瀼', '�\\' => '瀵', '�]' => '瀯', '�^' => '瀷', '�_' => '瀴', '�`' => '瀱', '�a' => '灂', '�b' => '瀸', '�c' => '瀿', '�d' => '瀺', '�e' => '瀹', '�f' => '灀', '�g' => '瀻', '�h' => '瀳', '�i' => '灁', '�j' => '爓', '�k' => '爔', '�l' => '犨', '�m' => '獽', '�n' => '獼', '�o' => '璺', '�p' => '皫', '�q' => '皪', '�r' => '皾', '�s' => '盭', '�t' => '矌', '�u' => '矎', '�v' => '矏', '�w' => '矍', '�x' => '矲', '�y' => '礥', '�z' => '礣', '�{' => '礧', '�|' => '礨', '�}' => '礤', '�~' => '礩', '�' => '禲', '�' => '穮', '�' => '穬', '�' => '穭', '�' => '竷', '�' => '籉', '�' => '籈', '�' => '籊', '�' => '籇', '�' => '籅', '�' => '糮', '�' => '繻', '�' => '繾', '�' => '纁', '�' => '纀', '�' => '羺', '�' => '翿', '�' => '聹', '�' => '臛', '�' => '臙', '�' => '舋', '�' => '艨', '�' => '艩', '�' => '蘢', '�' => '藿', '�' => '蘁', '�' => '藾', '�' => '蘛', '�' => '蘀', '�' => '藶', '�' => '蘄', '�' => '蘉', '�' => '蘅', '��' => '蘌', '��' => '藽', '��' => '蠙', '��' => '蠐', '��' => '蠑', '��' => '蠗', '��' => '蠓', '��' => '蠖', '��' => '襣', '��' => '襦', '��' => '覹', '��' => '觷', '��' => '譠', '��' => '譪', '��' => '譝', '��' => '譨', '��' => '譣', '��' => '譥', '��' => '譧', '��' => '譭', '��' => '趮', '��' => '躆', '��' => '躈', '��' => '躄', '��' => '轙', '��' => '轖', '��' => '轗', '��' => '轕', '��' => '轘', '��' => '轚', '��' => '邍', '��' => '酃', '��' => '酁', '��' => '醷', '��' => '醵', '��' => '醲', '��' => '醳', '��' => '鐋', '��' => '鐓', '��' => '鏻', '��' => '鐠', '��' => '鐏', '��' => '鐔', '��' => '鏾', '��' => '鐕', '��' => '鐐', '��' => '鐨', '��' => '鐙', '��' => '鐍', '��' => '鏵', '��' => '鐀', '�' => '鏷', '�' => '鐇', '�' => '鐎', '�' => '鐖', '�' => '鐒', '�' => '鏺', '�' => '鐉', '�' => '鏸', '�' => '鐊', '�' => '鏿', '�@' => '鏼', '�A' => '鐌', '�B' => '鏶', '�C' => '鐑', '�D' => '鐆', '�E' => '闞', '�F' => '闠', '�G' => '闟', '�H' => '霮', '�I' => '霯', '�J' => '鞹', '�K' => '鞻', '�L' => '韽', '�M' => '韾', '�N' => '顠', '�O' => '顢', '�P' => '顣', '�Q' => '顟', '�R' => '飁', '�S' => '飂', '�T' => '饐', '�U' => '饎', '�V' => '饙', '�W' => '饌', '�X' => '饋', '�Y' => '饓', '�Z' => '騲', '�[' => '騴', '�\\' => '騱', '�]' => '騬', '�^' => '騪', '�_' => '騶', '�`' => '騩', '�a' => '騮', '�b' => '騸', '�c' => '騭', '�d' => '髇', '�e' => '髊', '�f' => '髆', '�g' => '鬐', '�h' => '鬒', '�i' => '鬑', '�j' => '鰋', '�k' => '鰈', '�l' => '鯷', '�m' => '鰅', '�n' => '鰒', '�o' => '鯸', '�p' => '鱀', '�q' => '鰇', '�r' => '鰎', '�s' => '鰆', '�t' => '鰗', '�u' => '鰔', '�v' => '鰉', '�w' => '鶟', '�x' => '鶙', '�y' => '鶤', '�z' => '鶝', '�{' => '鶒', '�|' => '鶘', '�}' => '鶐', '�~' => '鶛', '��' => '鶠', '��' => '鶔', '��' => '鶜', '��' => '鶪', '��' => '鶗', '��' => '鶡', '��' => '鶚', '��' => '鶢', '��' => '鶨', '��' => '鶞', '��' => '鶣', '��' => '鶿', '��' => '鶩', '��' => '鶖', '��' => '鶦', '��' => '鶧', '��' => '麙', '��' => '麛', '��' => '麚', '��' => '黥', '��' => '黤', '��' => '黧', '��' => '黦', '��' => '鼰', '��' => '鼮', '��' => '齛', '��' => '齠', '��' => '齞', '��' => '齝', '��' => '齙', '��' => '龑', '��' => '儺', '��' => '儹', '��' => '劘', '��' => '劗', '��' => '囃', '��' => '嚽', '��' => '嚾', '��' => '孈', '��' => '孇', '��' => '巋', '��' => '巏', '��' => '廱', '��' => '懽', '��' => '攛', '��' => '欂', '��' => '櫼', '��' => '欃', '��' => '櫸', '��' => '欀', '��' => '灃', '��' => '灄', '��' => '灊', '��' => '灈', '��' => '灉', '��' => '灅', '��' => '灆', '��' => '爝', '��' => '爚', '��' => '爙', '��' => '獾', '��' => '甗', '��' => '癪', '��' => '矐', '��' => '礭', '��' => '礱', '��' => '礯', '��' => '籔', '��' => '籓', '��' => '糲', '��' => '纊', '��' => '纇', '��' => '纈', '��' => '纋', '��' => '纆', '��' => '纍', '��' => '罍', '��' => '羻', '��' => '耰', '��' => '臝', '��' => '蘘', '��' => '蘪', '��' => '蘦', '��' => '蘟', '��' => '蘣', '��' => '蘜', '��' => '蘙', '��' => '蘧', '��' => '蘮', '��' => '蘡', '��' => '蘠', '��' => '蘩', '��' => '蘞', '��' => '蘥', '�@' => '蠩', '�A' => '蠝', '�B' => '蠛', '�C' => '蠠', '�D' => '蠤', '�E' => '蠜', '�F' => '蠫', '�G' => '衊', '�H' => '襭', '�I' => '襩', '�J' => '襮', '�K' => '襫', '�L' => '觺', '�M' => '譹', '�N' => '譸', '�O' => '譅', '�P' => '譺', '�Q' => '譻', '�R' => '贐', '�S' => '贔', '�T' => '趯', '�U' => '躎', '�V' => '躌', '�W' => '轞', '�X' => '轛', '�Y' => '轝', '�Z' => '酆', '�[' => '酄', '�\\' => '酅', '�]' => '醹', '�^' => '鐿', '�_' => '鐻', '�`' => '鐶', '�a' => '鐩', '�b' => '鐽', '�c' => '鐼', '�d' => '鐰', '�e' => '鐹', '�f' => '鐪', '�g' => '鐷', '�h' => '鐬', '�i' => '鑀', '�j' => '鐱', '�k' => '闥', '�l' => '闤', '�m' => '闣', '�n' => '霵', '�o' => '霺', '�p' => '鞿', '�q' => '韡', '�r' => '顤', '�s' => '飉', '�t' => '飆', '�u' => '飀', '�v' => '饘', '�w' => '饖', '�x' => '騹', '�y' => '騽', '�z' => '驆', '�{' => '驄', '�|' => '驂', '�}' => '驁', '�~' => '騺', '��' => '騿', '��' => '髍', '��' => '鬕', '��' => '鬗', '��' => '鬘', '��' => '鬖', '��' => '鬺', '��' => '魒', '��' => '鰫', '��' => '鰝', '��' => '鰜', '��' => '鰬', '��' => '鰣', '��' => '鰨', '��' => '鰩', '��' => '鰤', '��' => '鰡', '��' => '鶷', '��' => '鶶', '��' => '鶼', '��' => '鷁', '��' => '鷇', '��' => '鷊', '��' => '鷏', '��' => '鶾', '��' => '鷅', '��' => '鷃', '��' => '鶻', '��' => '鶵', '��' => '鷎', '��' => '鶹', '��' => '鶺', '��' => '鶬', '��' => '鷈', '��' => '鶱', '��' => '鶭', '��' => '鷌', '��' => '鶳', '��' => '鷍', '��' => '鶲', '��' => '鹺', '��' => '麜', '��' => '黫', '��' => '黮', '��' => '黭', '��' => '鼛', '��' => '鼘', '��' => '鼚', '��' => '鼱', '��' => '齎', '��' => '齥', '��' => '齤', '��' => '龒', '��' => '亹', '��' => '囆', '��' => '囅', '��' => '囋', '��' => '奱', '��' => '孋', '��' => '孌', '��' => '巕', '��' => '巑', '��' => '廲', '��' => '攡', '��' => '攠', '��' => '攦', '��' => '攢', '��' => '欋', '��' => '欈', '��' => '欉', '��' => '氍', '��' => '灕', '��' => '灖', '��' => '灗', '��' => '灒', '��' => '爞', '��' => '爟', '��' => '犩', '��' => '獿', '��' => '瓘', '��' => '瓕', '��' => '瓙', '��' => '瓗', '��' => '癭', '��' => '皭', '��' => '礵', '��' => '禴', '��' => '穰', '��' => '穱', '��' => '籗', '��' => '籜', '��' => '籙', '��' => '籛', '��' => '籚', '�@' => '糴', '�A' => '糱', '�B' => '纑', '�C' => '罏', '�D' => '羇', '�E' => '臞', '�F' => '艫', '�G' => '蘴', '�H' => '蘵', '�I' => '蘳', '�J' => '蘬', '�K' => '蘲', '�L' => '蘶', '�M' => '蠬', '�N' => '蠨', '�O' => '蠦', '�P' => '蠪', '�Q' => '蠥', '�R' => '襱', '�S' => '覿', '�T' => '覾', '�U' => '觻', '�V' => '譾', '�W' => '讄', '�X' => '讂', '�Y' => '讆', '�Z' => '讅', '�[' => '譿', '�\\' => '贕', '�]' => '躕', '�^' => '躔', '�_' => '躚', '�`' => '躒', '�a' => '躐', '�b' => '躖', '�c' => '躗', '�d' => '轠', '�e' => '轢', '�f' => '酇', '�g' => '鑌', '�h' => '鑐', '�i' => '鑊', '�j' => '鑋', '�k' => '鑏', '�l' => '鑇', '�m' => '鑅', '�n' => '鑈', '�o' => '鑉', '�p' => '鑆', '�q' => '霿', '�r' => '韣', '�s' => '顪', '�t' => '顩', '�u' => '飋', '�v' => '饔', '�w' => '饛', '�x' => '驎', '�y' => '驓', '�z' => '驔', '�{' => '驌', '�|' => '驏', '�}' => '驈', '�~' => '驊', '��' => '驉', '��' => '驒', '��' => '驐', '��' => '髐', '��' => '鬙', '��' => '鬫', '��' => '鬻', '��' => '魖', '��' => '魕', '��' => '鱆', '��' => '鱈', '��' => '鰿', '��' => '鱄', '��' => '鰹', '��' => '鰳', '��' => '鱁', '��' => '鰼', '��' => '鰷', '��' => '鰴', '��' => '鰲', '��' => '鰽', '��' => '鰶', '��' => '鷛', '��' => '鷒', '��' => '鷞', '��' => '鷚', '��' => '鷋', '��' => '鷐', '��' => '鷜', '��' => '鷑', '��' => '鷟', '��' => '鷩', '��' => '鷙', '��' => '鷘', '��' => '鷖', '��' => '鷵', '��' => '鷕', '��' => '鷝', '��' => '麶', '��' => '黰', '��' => '鼵', '��' => '鼳', '��' => '鼲', '��' => '齂', '��' => '齫', '��' => '龕', '��' => '龢', '��' => '儽', '��' => '劙', '��' => '壨', '��' => '壧', '��' => '奲', '��' => '孍', '��' => '巘', '��' => '蠯', '��' => '彏', '��' => '戁', '��' => '戃', '��' => '戄', '��' => '攩', '��' => '攥', '��' => '斖', '��' => '曫', '��' => '欑', '��' => '欒', '��' => '欏', '��' => '毊', '��' => '灛', '��' => '灚', '��' => '爢', '��' => '玂', '��' => '玁', '��' => '玃', '��' => '癰', '��' => '矔', '��' => '籧', '��' => '籦', '��' => '纕', '��' => '艬', '��' => '蘺', '��' => '虀', '��' => '蘹', '��' => '蘼', '��' => '蘱', '��' => '蘻', '��' => '蘾', '��' => '蠰', '��' => '蠲', '��' => '蠮', '��' => '蠳', '��' => '襶', '��' => '襴', '��' => '襳', '��' => '觾', '�@' => '讌', '�A' => '讎', '�B' => '讋', '�C' => '讈', '�D' => '豅', '�E' => '贙', '�F' => '躘', '�G' => '轤', '�H' => '轣', '�I' => '醼', '�J' => '鑢', '�K' => '鑕', '�L' => '鑝', '�M' => '鑗', '�N' => '鑞', '�O' => '韄', '�P' => '韅', '�Q' => '頀', '�R' => '驖', '�S' => '驙', '�T' => '鬞', '�U' => '鬟', '�V' => '鬠', '�W' => '鱒', '�X' => '鱘', '�Y' => '鱐', '�Z' => '鱊', '�[' => '鱍', '�\\' => '鱋', '�]' => '鱕', '�^' => '鱙', '�_' => '鱌', '�`' => '鱎', '�a' => '鷻', '�b' => '鷷', '�c' => '鷯', '�d' => '鷣', '�e' => '鷫', '�f' => '鷸', '�g' => '鷤', '�h' => '鷶', '�i' => '鷡', '�j' => '鷮', '�k' => '鷦', '�l' => '鷲', '�m' => '鷰', '�n' => '鷢', '�o' => '鷬', '�p' => '鷴', '�q' => '鷳', '�r' => '鷨', '�s' => '鷭', '�t' => '黂', '�u' => '黐', '�v' => '黲', '�w' => '黳', '�x' => '鼆', '�y' => '鼜', '�z' => '鼸', '�{' => '鼷', '�|' => '鼶', '�}' => '齃', '�~' => '齏', '��' => '齱', '��' => '齰', '��' => '齮', '��' => '齯', '��' => '囓', '��' => '囍', '��' => '孎', '��' => '屭', '��' => '攭', '��' => '曭', '��' => '曮', '��' => '欓', '��' => '灟', '��' => '灡', '��' => '灝', '��' => '灠', '��' => '爣', '��' => '瓛', '��' => '瓥', '��' => '矕', '��' => '礸', '��' => '禷', '��' => '禶', '��' => '籪', '��' => '纗', '��' => '羉', '��' => '艭', '��' => '虃', '��' => '蠸', '��' => '蠷', '��' => '蠵', '��' => '衋', '��' => '讔', '��' => '讕', '��' => '躞', '��' => '躟', '��' => '躠', '��' => '躝', '��' => '醾', '��' => '醽', '��' => '釂', '��' => '鑫', '��' => '鑨', '��' => '鑩', '��' => '雥', '��' => '靆', '��' => '靃', '��' => '靇', '��' => '韇', '��' => '韥', '��' => '驞', '��' => '髕', '��' => '魙', '��' => '鱣', '��' => '鱧', '��' => '鱦', '��' => '鱢', '��' => '鱞', '��' => '鱠', '��' => '鸂', '��' => '鷾', '��' => '鸇', '��' => '鸃', '��' => '鸆', '��' => '鸅', '��' => '鸀', '��' => '鸁', '��' => '鸉', '��' => '鷿', '��' => '鷽', '��' => '鸄', '��' => '麠', '��' => '鼞', '��' => '齆', '��' => '齴', '��' => '齵', '��' => '齶', '��' => '囔', '��' => '攮', '��' => '斸', '��' => '欘', '��' => '欙', '��' => '欗', '��' => '欚', '��' => '灢', '��' => '爦', '��' => '犪', '��' => '矘', '��' => '矙', '��' => '礹', '��' => '籩', '��' => '籫', '��' => '糶', '��' => '纚', '�@' => '纘', '�A' => '纛', '�B' => '纙', '�C' => '臠', '�D' => '臡', '�E' => '虆', '�F' => '虇', '�G' => '虈', '�H' => '襹', '�I' => '襺', '�J' => '襼', '�K' => '襻', '�L' => '觿', '�M' => '讘', '�N' => '讙', '�O' => '躥', '�P' => '躤', '�Q' => '躣', '�R' => '鑮', '�S' => '鑭', '�T' => '鑯', '�U' => '鑱', '�V' => '鑳', '�W' => '靉', '�X' => '顲', '�Y' => '饟', '�Z' => '鱨', '�[' => '鱮', '�\\' => '鱭', '�]' => '鸋', '�^' => '鸍', '�_' => '鸐', '�`' => '鸏', '�a' => '鸒', '�b' => '鸑', '�c' => '麡', '�d' => '黵', '�e' => '鼉', '�f' => '齇', '�g' => '齸', '�h' => '齻', '�i' => '齺', '�j' => '齹', '�k' => '圞', '�l' => '灦', '�m' => '籯', '�n' => '蠼', '�o' => '趲', '�p' => '躦', '�q' => '釃', '�r' => '鑴', '�s' => '鑸', '�t' => '鑶', '�u' => '鑵', '�v' => '驠', '�w' => '鱴', '�x' => '鱳', '�y' => '鱱', '�z' => '鱵', '�{' => '鸔', '�|' => '鸓', '�}' => '黶', '�~' => '鼊', '��' => '龤', '��' => '灨', '��' => '灥', '��' => '糷', '��' => '虪', '��' => '蠾', '��' => '蠽', '��' => '蠿', '��' => '讞', '��' => '貜', '��' => '躩', '��' => '軉', '��' => '靋', '��' => '顳', '��' => '顴', '��' => '飌', '��' => '饡', '��' => '馫', '��' => '驤', '��' => '驦', '��' => '驧', '��' => '鬤', '��' => '鸕', '��' => '鸗', '��' => '齈', '��' => '戇', '��' => '欞', '��' => '爧', '��' => '虌', '��' => '躨', '��' => '钂', '��' => '钀', '��' => '钁', '��' => '驩', '��' => '驨', '��' => '鬮', '��' => '鸙', '��' => '爩', '��' => '虋', '��' => '讟', '��' => '钃', '��' => '鱹', '��' => '麷', '��' => '癵', '��' => '驫', '��' => '鱺', '��' => '鸝', '��' => '灩', '��' => '灪', '��' => '麤', '��' => '齾', '��' => '齉', '��' => '龘', '��' => '碁', '��' => '銹', '��' => '裏', '��' => '墻', '��' => '恒', '��' => '粧', '��' => '嫺', '��' => '╔', '��' => '╦', '��' => '╗', '��' => '╠', '��' => '╬', '��' => '╣', '��' => '╚', '��' => '╩', '��' => '╝', '��' => '╒', '��' => '╤', '��' => '╕', '��' => '╞', '��' => '╪', '��' => '╡', '��' => '╘', '��' => '╧', '��' => '╛', '��' => '╓', '��' => '╥', '��' => '╖', '��' => '╟', '��' => '╫', '��' => '╢', '��' => '╙', '��' => '╨', '��' => '╜', '��' => '║', '��' => '═', '��' => '╭', '��' => '╮', '��' => '╰', '��' => '╯', '��' => '▓', ); $result =& $data; unset($data); return $result; PK2A#]1M���Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-16.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ą', '�' => 'ą', '�' => 'Ł', '�' => '€', '�' => '„', '�' => 'Š', '�' => '§', '�' => 'š', '�' => '©', '�' => 'Ș', '�' => '«', '�' => 'Ź', '�' => '', '�' => 'ź', '�' => 'Ż', '�' => '°', '�' => '±', '�' => 'Č', '�' => 'ł', '�' => 'Ž', '�' => '”', '�' => '¶', '�' => '·', '�' => 'ž', '�' => 'č', '�' => 'ș', '�' => '»', '�' => 'Œ', '�' => 'œ', '�' => 'Ÿ', '�' => 'ż', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ă', '�' => 'Ä', '�' => 'Ć', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Đ', '�' => 'Ń', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Ő', '�' => 'Ö', '�' => 'Ś', '�' => 'Ű', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ę', '�' => 'Ț', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ă', '�' => 'ä', '�' => 'ć', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'đ', '�' => 'ń', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'ő', '�' => 'ö', '�' => 'ś', '�' => 'ű', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ę', '�' => 'ț', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]Z�����Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1251.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ђ', '�' => 'Ѓ', '�' => '‚', '�' => 'ѓ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => '€', '�' => '‰', '�' => 'Љ', '�' => '‹', '�' => 'Њ', '�' => 'Ќ', '�' => 'Ћ', '�' => 'Џ', '�' => 'ђ', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '™', '�' => 'љ', '�' => '›', '�' => 'њ', '�' => 'ќ', '�' => 'ћ', '�' => 'џ', '�' => ' ', '�' => 'Ў', '�' => 'ў', '�' => 'Ј', '�' => '¤', '�' => 'Ґ', '�' => '¦', '�' => '§', '�' => 'Ё', '�' => '©', '�' => 'Є', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => 'Ї', '�' => '°', '�' => '±', '�' => 'І', '�' => 'і', '�' => 'ґ', '�' => 'µ', '�' => '¶', '�' => '·', '�' => 'ё', '�' => '№', '�' => 'є', '�' => '»', '�' => 'ј', '�' => 'Ѕ', '�' => 'ѕ', '�' => 'ї', '�' => 'А', '�' => 'Б', '�' => 'В', '�' => 'Г', '�' => 'Д', '�' => 'Е', '�' => 'Ж', '�' => 'З', '�' => 'И', '�' => 'Й', '�' => 'К', '�' => 'Л', '�' => 'М', '�' => 'Н', '�' => 'О', '�' => 'П', '�' => 'Р', '�' => 'С', '�' => 'Т', '�' => 'У', '�' => 'Ф', '�' => 'Х', '�' => 'Ц', '�' => 'Ч', '�' => 'Ш', '�' => 'Щ', '�' => 'Ъ', '�' => 'Ы', '�' => 'Ь', '�' => 'Э', '�' => 'Ю', '�' => 'Я', '�' => 'а', '�' => 'б', '�' => 'в', '�' => 'г', '�' => 'д', '�' => 'е', '�' => 'ж', '�' => 'з', '�' => 'и', '�' => 'й', '�' => 'к', '�' => 'л', '�' => 'м', '�' => 'н', '�' => 'о', '�' => 'п', '�' => 'р', '�' => 'с', '�' => 'т', '�' => 'у', '�' => 'ф', '�' => 'х', '�' => 'ц', '�' => 'ч', '�' => 'ш', '�' => 'щ', '�' => 'ъ', '�' => 'ы', '�' => 'ь', '�' => 'э', '�' => 'ю', '�' => 'я', ); $result =& $data; unset($data); return $result; PK2A#]�JT��Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1256.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => 'پ', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => 'ˆ', '�' => '‰', '�' => 'ٹ', '�' => '‹', '�' => 'Œ', '�' => 'چ', '�' => 'ژ', '�' => 'ڈ', '�' => 'گ', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => 'ک', '�' => '™', '�' => 'ڑ', '�' => '›', '�' => 'œ', '�' => '', '�' => '', '�' => 'ں', '�' => ' ', '�' => '،', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ھ', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => '؛', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '؟', '�' => 'ہ', '�' => 'ء', '�' => 'آ', '�' => 'أ', '�' => 'ؤ', '�' => 'إ', '�' => 'ئ', '�' => 'ا', '�' => 'ب', '�' => 'ة', '�' => 'ت', '�' => 'ث', '�' => 'ج', '�' => 'ح', '�' => 'خ', '�' => 'د', '�' => 'ذ', '�' => 'ر', '�' => 'ز', '�' => 'س', '�' => 'ش', '�' => 'ص', '�' => 'ض', '�' => '×', '�' => 'ط', '�' => 'ظ', '�' => 'ع', '�' => 'غ', '�' => 'ـ', '�' => 'ف', '�' => 'ق', '�' => 'ك', '�' => 'à', '�' => 'ل', '�' => 'â', '�' => 'م', '�' => 'ن', '�' => 'ه', '�' => 'و', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ى', '�' => 'ي', '�' => 'î', '�' => 'ï', '�' => 'ً', '�' => 'ٌ', '�' => 'ٍ', '�' => 'َ', '�' => 'ô', '�' => 'ُ', '�' => 'ِ', '�' => '÷', '�' => 'ّ', '�' => 'ù', '�' => 'ْ', '�' => 'û', '�' => 'ü', '�' => '', '�' => '', '�' => 'ے', ); $result =& $data; unset($data); return $result; PK2A#]2���Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-11.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'ก', '�' => 'ข', '�' => 'ฃ', '�' => 'ค', '�' => 'ฅ', '�' => 'ฆ', '�' => 'ง', '�' => 'จ', '�' => 'ฉ', '�' => 'ช', '�' => 'ซ', '�' => 'ฌ', '�' => 'ญ', '�' => 'ฎ', '�' => 'ฏ', '�' => 'ฐ', '�' => 'ฑ', '�' => 'ฒ', '�' => 'ณ', '�' => 'ด', '�' => 'ต', '�' => 'ถ', '�' => 'ท', '�' => 'ธ', '�' => 'น', '�' => 'บ', '�' => 'ป', '�' => 'ผ', '�' => 'ฝ', '�' => 'พ', '�' => 'ฟ', '�' => 'ภ', '�' => 'ม', '�' => 'ย', '�' => 'ร', '�' => 'ฤ', '�' => 'ล', '�' => 'ฦ', '�' => 'ว', '�' => 'ศ', '�' => 'ษ', '�' => 'ส', '�' => 'ห', '�' => 'ฬ', '�' => 'อ', '�' => 'ฮ', '�' => 'ฯ', '�' => 'ะ', '�' => 'ั', '�' => 'า', '�' => 'ำ', '�' => 'ิ', '�' => 'ี', '�' => 'ึ', '�' => 'ื', '�' => 'ุ', '�' => 'ู', '�' => 'ฺ', '�' => '฿', '�' => 'เ', '�' => 'แ', '�' => 'โ', '�' => 'ใ', '�' => 'ไ', '�' => 'ๅ', '�' => 'ๆ', '�' => '็', '�' => '่', '�' => '้', '�' => '๊', '�' => '๋', '�' => '์', '�' => 'ํ', '�' => '๎', '�' => '๏', '�' => '๐', '�' => '๑', '�' => '๒', '�' => '๓', '�' => '๔', '�' => '๕', '�' => '๖', '�' => '๗', '�' => '๘', '�' => '๙', '�' => '๚', '�' => '๛', ); $result =& $data; unset($data); return $result; PK2A#]�O�NNEvendor/symfony/polyfill-iconv/Resources/charset/from.windows-1258.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => 'ˆ', '�' => '‰', '�' => '‹', '�' => 'Œ', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '˜', '�' => '™', '�' => '›', '�' => 'œ', '�' => 'Ÿ', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => 'º', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ă', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => '̀', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Đ', '�' => 'Ñ', '�' => '̉', '�' => 'Ó', '�' => 'Ô', '�' => 'Ơ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ư', '�' => '̃', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ă', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => '́', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'đ', '�' => 'ñ', '�' => '̣', '�' => 'ó', '�' => 'ô', '�' => 'ơ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ư', '�' => '₫', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]0��/��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp850.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'à', '�' => 'å', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'ï', '�' => 'î', '�' => 'ì', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'û', '�' => 'ù', '�' => 'ÿ', '�' => 'Ö', '�' => 'Ü', '�' => 'ø', '�' => '£', '�' => 'Ø', '�' => '×', '�' => 'ƒ', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'ª', '�' => 'º', '�' => '¿', '�' => '®', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'Á', '�' => 'Â', '�' => 'À', '�' => '©', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '¢', '�' => '¥', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'ã', '�' => 'Ã', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '¤', '�' => 'ð', '�' => 'Ð', '�' => 'Ê', '�' => 'Ë', '�' => 'È', '�' => 'ı', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '¦', '�' => 'Ì', '�' => '▀', '�' => 'Ó', '�' => 'ß', '�' => 'Ô', '�' => 'Ò', '�' => 'õ', '�' => 'Õ', '�' => 'µ', '�' => 'þ', '�' => 'Þ', '�' => 'Ú', '�' => 'Û', '�' => 'Ù', '�' => 'ý', '�' => 'Ý', '�' => '¯', '�' => '´', '�' => '', '�' => '±', '�' => '‗', '�' => '¾', '�' => '¶', '�' => '§', '�' => '÷', '�' => '¸', '�' => '°', '�' => '¨', '�' => '·', '�' => '¹', '�' => '³', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�r�P��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp857.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'à', '�' => 'å', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'ï', '�' => 'î', '�' => 'ı', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'û', '�' => 'ù', '�' => 'İ', '�' => 'Ö', '�' => 'Ü', '�' => 'ø', '�' => '£', '�' => 'Ø', '�' => 'Ş', '�' => 'ş', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'Ğ', '�' => 'ğ', '�' => '¿', '�' => '®', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'Á', '�' => 'Â', '�' => 'À', '�' => '©', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '¢', '�' => '¥', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'ã', '�' => 'Ã', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '¤', '�' => 'º', '�' => 'ª', '�' => 'Ê', '�' => 'Ë', '�' => 'È', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '¦', '�' => 'Ì', '�' => '▀', '�' => 'Ó', '�' => 'ß', '�' => 'Ô', '�' => 'Ò', '�' => 'õ', '�' => 'Õ', '�' => 'µ', '�' => '×', '�' => 'Ú', '�' => 'Û', '�' => 'Ù', '�' => 'ì', '�' => 'ÿ', '�' => '¯', '�' => '´', '�' => '', '�' => '±', '�' => '¾', '�' => '¶', '�' => '§', '�' => '÷', '�' => '¸', '�' => '°', '�' => '¨', '�' => '·', '�' => '¹', '�' => '³', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�wg���>vendor/symfony/polyfill-iconv/Resources/charset/from.cp775.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ć', '�' => 'ü', '�' => 'é', '�' => 'ā', '�' => 'ä', '�' => 'ģ', '�' => 'å', '�' => 'ć', '�' => 'ł', '�' => 'ē', '�' => 'Ŗ', '�' => 'ŗ', '�' => 'ī', '�' => 'Ź', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ō', '�' => 'ö', '�' => 'Ģ', '�' => '¢', '�' => 'Ś', '�' => 'ś', '�' => 'Ö', '�' => 'Ü', '�' => 'ø', '�' => '£', '�' => 'Ø', '�' => '×', '�' => '¤', '�' => 'Ā', '�' => 'Ī', '�' => 'ó', '�' => 'Ż', '�' => 'ż', '�' => 'ź', '�' => '”', '�' => '¦', '�' => '©', '�' => '®', '�' => '¬', '�' => '½', '�' => '¼', '�' => 'Ł', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'Ą', '�' => 'Č', '�' => 'Ę', '�' => 'Ė', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => 'Į', '�' => 'Š', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'Ų', '�' => 'Ū', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => 'Ž', '�' => 'ą', '�' => 'č', '�' => 'ę', '�' => 'ė', '�' => 'į', '�' => 'š', '�' => 'ų', '�' => 'ū', '�' => 'ž', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'Ó', '�' => 'ß', '�' => 'Ō', '�' => 'Ń', '�' => 'õ', '�' => 'Õ', '�' => 'µ', '�' => 'ń', '�' => 'Ķ', '�' => 'ķ', '�' => 'Ļ', '�' => 'ļ', '�' => 'ņ', '�' => 'Ē', '�' => 'Ņ', '�' => '’', '�' => '', '�' => '±', '�' => '“', '�' => '¾', '�' => '¶', '�' => '§', '�' => '÷', '�' => '„', '�' => '°', '�' => '∙', '�' => '·', '�' => '¹', '�' => '³', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]��&��Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-5.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ё', '�' => 'Ђ', '�' => 'Ѓ', '�' => 'Є', '�' => 'Ѕ', '�' => 'І', '�' => 'Ї', '�' => 'Ј', '�' => 'Љ', '�' => 'Њ', '�' => 'Ћ', '�' => 'Ќ', '�' => '', '�' => 'Ў', '�' => 'Џ', '�' => 'А', '�' => 'Б', '�' => 'В', '�' => 'Г', '�' => 'Д', '�' => 'Е', '�' => 'Ж', '�' => 'З', '�' => 'И', '�' => 'Й', '�' => 'К', '�' => 'Л', '�' => 'М', '�' => 'Н', '�' => 'О', '�' => 'П', '�' => 'Р', '�' => 'С', '�' => 'Т', '�' => 'У', '�' => 'Ф', '�' => 'Х', '�' => 'Ц', '�' => 'Ч', '�' => 'Ш', '�' => 'Щ', '�' => 'Ъ', '�' => 'Ы', '�' => 'Ь', '�' => 'Э', '�' => 'Ю', '�' => 'Я', '�' => 'а', '�' => 'б', '�' => 'в', '�' => 'г', '�' => 'д', '�' => 'е', '�' => 'ж', '�' => 'з', '�' => 'и', '�' => 'й', '�' => 'к', '�' => 'л', '�' => 'м', '�' => 'н', '�' => 'о', '�' => 'п', '�' => 'р', '�' => 'с', '�' => 'т', '�' => 'у', '�' => 'ф', '�' => 'х', '�' => 'ц', '�' => 'ч', '�' => 'ш', '�' => 'щ', '�' => 'ъ', '�' => 'ы', '�' => 'ь', '�' => 'э', '�' => 'ю', '�' => 'я', '�' => '№', '�' => 'ё', '�' => 'ђ', '�' => 'ѓ', '�' => 'є', '�' => 'ѕ', '�' => 'і', '�' => 'ї', '�' => 'ј', '�' => 'љ', '�' => 'њ', '�' => 'ћ', '�' => 'ќ', '�' => '§', '�' => 'ў', '�' => 'џ', ); $result =& $data; unset($data); return $result; PK2A#]���|>vendor/symfony/polyfill-iconv/Resources/charset/from.cp862.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'א', '�' => 'ב', '�' => 'ג', '�' => 'ד', '�' => 'ה', '�' => 'ו', '�' => 'ז', '�' => 'ח', '�' => 'ט', '�' => 'י', '�' => 'ך', '�' => 'כ', '�' => 'ל', '�' => 'ם', '�' => 'מ', '�' => 'ן', '�' => 'נ', '�' => 'ס', '�' => 'ע', '�' => 'ף', '�' => 'פ', '�' => 'ץ', '�' => 'צ', '�' => 'ק', '�' => 'ר', '�' => 'ש', '�' => 'ת', '�' => '¢', '�' => '£', '�' => '¥', '�' => '₧', '�' => 'ƒ', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'ª', '�' => 'º', '�' => '¿', '�' => '⌐', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�����Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-2.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ą', '�' => '˘', '�' => 'Ł', '�' => '¤', '�' => 'Ľ', '�' => 'Ś', '�' => '§', '�' => '¨', '�' => 'Š', '�' => 'Ş', '�' => 'Ť', '�' => 'Ź', '�' => '', '�' => 'Ž', '�' => 'Ż', '�' => '°', '�' => 'ą', '�' => '˛', '�' => 'ł', '�' => '´', '�' => 'ľ', '�' => 'ś', '�' => 'ˇ', '�' => '¸', '�' => 'š', '�' => 'ş', '�' => 'ť', '�' => 'ź', '�' => '˝', '�' => 'ž', '�' => 'ż', '�' => 'Ŕ', '�' => 'Á', '�' => 'Â', '�' => 'Ă', '�' => 'Ä', '�' => 'Ĺ', '�' => 'Ć', '�' => 'Ç', '�' => 'Č', '�' => 'É', '�' => 'Ę', '�' => 'Ë', '�' => 'Ě', '�' => 'Í', '�' => 'Î', '�' => 'Ď', '�' => 'Đ', '�' => 'Ń', '�' => 'Ň', '�' => 'Ó', '�' => 'Ô', '�' => 'Ő', '�' => 'Ö', '�' => '×', '�' => 'Ř', '�' => 'Ů', '�' => 'Ú', '�' => 'Ű', '�' => 'Ü', '�' => 'Ý', '�' => 'Ţ', '�' => 'ß', '�' => 'ŕ', '�' => 'á', '�' => 'â', '�' => 'ă', '�' => 'ä', '�' => 'ĺ', '�' => 'ć', '�' => 'ç', '�' => 'č', '�' => 'é', '�' => 'ę', '�' => 'ë', '�' => 'ě', '�' => 'í', '�' => 'î', '�' => 'ď', '�' => 'đ', '�' => 'ń', '�' => 'ň', '�' => 'ó', '�' => 'ô', '�' => 'ő', '�' => 'ö', '�' => '÷', '�' => 'ř', '�' => 'ů', '�' => 'ú', '�' => 'ű', '�' => 'ü', '�' => 'ý', '�' => 'ţ', '�' => '˙', ); $result =& $data; unset($data); return $result; PK2A#]?��*>vendor/symfony/polyfill-iconv/Resources/charset/from.cp865.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'à', '�' => 'å', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'ï', '�' => 'î', '�' => 'ì', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'û', '�' => 'ù', '�' => 'ÿ', '�' => 'Ö', '�' => 'Ü', '�' => 'ø', '�' => '£', '�' => 'Ø', '�' => '₧', '�' => 'ƒ', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'ª', '�' => 'º', '�' => '¿', '�' => '⌐', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '¤', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]ͩ�Ū�Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-8.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => '×', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => '÷', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '‗', '�' => 'א', '�' => 'ב', '�' => 'ג', '�' => 'ד', '�' => 'ה', '�' => 'ו', '�' => 'ז', '�' => 'ח', '�' => 'ט', '�' => 'י', '�' => 'ך', '�' => 'כ', '�' => 'ל', '�' => 'ם', '�' => 'מ', '�' => 'ן', '�' => 'נ', '�' => 'ס', '�' => 'ע', '�' => 'ף', '�' => 'פ', '�' => 'ץ', '�' => 'צ', '�' => 'ק', '�' => 'ר', '�' => 'ש', '�' => 'ת', '�' => '', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]z;�7��Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-1.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => 'º', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ð', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ý', '�' => 'Þ', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ð', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ý', '�' => 'þ', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]��D;��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp866.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'А', '�' => 'Б', '�' => 'В', '�' => 'Г', '�' => 'Д', '�' => 'Е', '�' => 'Ж', '�' => 'З', '�' => 'И', '�' => 'Й', '�' => 'К', '�' => 'Л', '�' => 'М', '�' => 'Н', '�' => 'О', '�' => 'П', '�' => 'Р', '�' => 'С', '�' => 'Т', '�' => 'У', '�' => 'Ф', '�' => 'Х', '�' => 'Ц', '�' => 'Ч', '�' => 'Ш', '�' => 'Щ', '�' => 'Ъ', '�' => 'Ы', '�' => 'Ь', '�' => 'Э', '�' => 'Ю', '�' => 'Я', '�' => 'а', '�' => 'б', '�' => 'в', '�' => 'г', '�' => 'д', '�' => 'е', '�' => 'ж', '�' => 'з', '�' => 'и', '�' => 'й', '�' => 'к', '�' => 'л', '�' => 'м', '�' => 'н', '�' => 'о', '�' => 'п', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'р', '�' => 'с', '�' => 'т', '�' => 'у', '�' => 'ф', '�' => 'х', '�' => 'ц', '�' => 'ч', '�' => 'ш', '�' => 'щ', '�' => 'ъ', '�' => 'ы', '�' => 'ь', '�' => 'э', '�' => 'ю', '�' => 'я', '�' => 'Ё', '�' => 'ё', '�' => 'Є', '�' => 'є', '�' => 'Ї', '�' => 'ї', '�' => 'Ў', '�' => 'ў', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => '№', '�' => '¤', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]\��� Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-6.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '¤', '�' => '،', '�' => '', '�' => '؛', '�' => '؟', '�' => 'ء', '�' => 'آ', '�' => 'أ', '�' => 'ؤ', '�' => 'إ', '�' => 'ئ', '�' => 'ا', '�' => 'ب', '�' => 'ة', '�' => 'ت', '�' => 'ث', '�' => 'ج', '�' => 'ح', '�' => 'خ', '�' => 'د', '�' => 'ذ', '�' => 'ر', '�' => 'ز', '�' => 'س', '�' => 'ش', '�' => 'ص', '�' => 'ض', '�' => 'ط', '�' => 'ظ', '�' => 'ع', '�' => 'غ', '�' => 'ـ', '�' => 'ف', '�' => 'ق', '�' => 'ك', '�' => 'ل', '�' => 'م', '�' => 'ن', '�' => 'ه', '�' => 'و', '�' => 'ى', '�' => 'ي', '�' => 'ً', '�' => 'ٌ', '�' => 'ٍ', '�' => 'َ', '�' => 'ُ', '�' => 'ِ', '�' => 'ّ', '�' => 'ْ', ); $result =& $data; unset($data); return $result; PK2A#]K��W>vendor/symfony/polyfill-iconv/Resources/charset/from.cp861.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'à', '�' => 'å', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'Ð', '�' => 'ð', '�' => 'Þ', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ô', '�' => 'ö', '�' => 'þ', '�' => 'û', '�' => 'Ý', '�' => 'ý', '�' => 'Ö', '�' => 'Ü', '�' => 'ø', '�' => '£', '�' => 'Ø', '�' => '₧', '�' => 'ƒ', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'Á', '�' => 'Í', '�' => 'Ó', '�' => 'Ú', '�' => '¿', '�' => '⌐', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]���@~ ~ Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1255.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => 'ˆ', '�' => '‰', '�' => '‹', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '˜', '�' => '™', '�' => '›', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '₪', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => '×', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => '÷', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'ְ', '�' => 'ֱ', '�' => 'ֲ', '�' => 'ֳ', '�' => 'ִ', '�' => 'ֵ', '�' => 'ֶ', '�' => 'ַ', '�' => 'ָ', '�' => 'ֹ', '�' => 'ֻ', '�' => 'ּ', '�' => 'ֽ', '�' => '־', '�' => 'ֿ', '�' => '׀', '�' => 'ׁ', '�' => 'ׂ', '�' => '׃', '�' => 'װ', '�' => 'ױ', '�' => 'ײ', '�' => '׳', '�' => '״', '�' => 'א', '�' => 'ב', '�' => 'ג', '�' => 'ד', '�' => 'ה', '�' => 'ו', '�' => 'ז', '�' => 'ח', '�' => 'ט', '�' => 'י', '�' => 'ך', '�' => 'כ', '�' => 'ל', '�' => 'ם', '�' => 'מ', '�' => 'ן', '�' => 'נ', '�' => 'ס', '�' => 'ע', '�' => 'ף', '�' => 'פ', '�' => 'ץ', '�' => 'צ', '�' => 'ק', '�' => 'ר', '�' => 'ש', '�' => 'ת', '�' => '', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]XQ����Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-15.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '€', '�' => '¥', '�' => 'Š', '�' => '§', '�' => 'š', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => 'Ž', '�' => 'µ', '�' => '¶', '�' => '·', '�' => 'ž', '�' => '¹', '�' => 'º', '�' => '»', '�' => 'Œ', '�' => 'œ', '�' => 'Ÿ', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ð', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ý', '�' => 'Þ', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ð', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ý', '�' => 'þ', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]ȫ�ˉ�Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1252.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => 'ˆ', '�' => '‰', '�' => 'Š', '�' => '‹', '�' => 'Œ', '�' => 'Ž', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '˜', '�' => '™', '�' => 'š', '�' => '›', '�' => 'œ', '�' => 'ž', '�' => 'Ÿ', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => 'º', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ð', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ý', '�' => 'Þ', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ð', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ý', '�' => 'þ', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]��U(>vendor/symfony/polyfill-iconv/Resources/charset/from.cp437.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'à', '�' => 'å', '�' => 'ç', '�' => 'ê', '�' => 'ë', '�' => 'è', '�' => 'ï', '�' => 'î', '�' => 'ì', '�' => 'Ä', '�' => 'Å', '�' => 'É', '�' => 'æ', '�' => 'Æ', '�' => 'ô', '�' => 'ö', '�' => 'ò', '�' => 'û', '�' => 'ù', '�' => 'ÿ', '�' => 'Ö', '�' => 'Ü', '�' => '¢', '�' => '£', '�' => '¥', '�' => '₧', '�' => 'ƒ', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'ª', '�' => 'º', '�' => '¿', '�' => '⌐', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]d�lAllCvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-7.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '‘', '�' => '’', '�' => '£', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => '«', '�' => '¬', '�' => '', '�' => '―', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '΄', '�' => '΅', '�' => 'Ά', '�' => '·', '�' => 'Έ', '�' => 'Ή', '�' => 'Ί', '�' => '»', '�' => 'Ό', '�' => '½', '�' => 'Ύ', '�' => 'Ώ', '�' => 'ΐ', '�' => 'Α', '�' => 'Β', '�' => 'Γ', '�' => 'Δ', '�' => 'Ε', '�' => 'Ζ', '�' => 'Η', '�' => 'Θ', '�' => 'Ι', '�' => 'Κ', '�' => 'Λ', '�' => 'Μ', '�' => 'Ν', '�' => 'Ξ', '�' => 'Ο', '�' => 'Π', '�' => 'Ρ', '�' => 'Σ', '�' => 'Τ', '�' => 'Υ', '�' => 'Φ', '�' => 'Χ', '�' => 'Ψ', '�' => 'Ω', '�' => 'Ϊ', '�' => 'Ϋ', '�' => 'ά', '�' => 'έ', '�' => 'ή', '�' => 'ί', '�' => 'ΰ', '�' => 'α', '�' => 'β', '�' => 'γ', '�' => 'δ', '�' => 'ε', '�' => 'ζ', '�' => 'η', '�' => 'θ', '�' => 'ι', '�' => 'κ', '�' => 'λ', '�' => 'μ', '�' => 'ν', '�' => 'ξ', '�' => 'ο', '�' => 'π', '�' => 'ρ', '�' => 'ς', '�' => 'σ', '�' => 'τ', '�' => 'υ', '�' => 'φ', '�' => 'χ', '�' => 'ψ', '�' => 'ω', '�' => 'ϊ', '�' => 'ϋ', '�' => 'ό', '�' => 'ύ', '�' => 'ώ', ); $result =& $data; unset($data); return $result; PK2A#]�>��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp860.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ã', '�' => 'à', '�' => 'Á', '�' => 'ç', '�' => 'ê', '�' => 'Ê', '�' => 'è', '�' => 'Í', '�' => 'Ô', '�' => 'ì', '�' => 'Ã', '�' => 'Â', '�' => 'É', '�' => 'À', '�' => 'È', '�' => 'ô', '�' => 'õ', '�' => 'ò', '�' => 'Ú', '�' => 'ù', '�' => 'Ì', '�' => 'Õ', '�' => 'Ü', '�' => '¢', '�' => '£', '�' => 'Ù', '�' => '₧', '�' => 'Ó', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'ñ', '�' => 'Ñ', '�' => 'ª', '�' => 'º', '�' => '¿', '�' => 'Ò', '�' => '¬', '�' => '½', '�' => '¼', '�' => '¡', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'α', '�' => 'ß', '�' => 'Γ', '�' => 'π', '�' => 'Σ', '�' => 'σ', '�' => 'µ', '�' => 'τ', '�' => 'Φ', '�' => 'Θ', '�' => 'Ω', '�' => 'δ', '�' => '∞', '�' => 'φ', '�' => 'ε', '�' => '∩', '�' => '≡', '�' => '±', '�' => '≥', '�' => '≤', '�' => '⌠', '�' => '⌡', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�K��\\>vendor/symfony/polyfill-iconv/Resources/charset/from.cp869.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ά', '�' => '·', '�' => '¬', '�' => '¦', '�' => '‘', '�' => '’', '�' => 'Έ', '�' => '―', '�' => 'Ή', '�' => 'Ί', '�' => 'Ϊ', '�' => 'Ό', '�' => 'Ύ', '�' => 'Ϋ', '�' => '©', '�' => 'Ώ', '�' => '²', '�' => '³', '�' => 'ά', '�' => '£', '�' => 'έ', '�' => 'ή', '�' => 'ί', '�' => 'ϊ', '�' => 'ΐ', '�' => 'ό', '�' => 'ύ', '�' => 'Α', '�' => 'Β', '�' => 'Γ', '�' => 'Δ', '�' => 'Ε', '�' => 'Ζ', '�' => 'Η', '�' => '½', '�' => 'Θ', '�' => 'Ι', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'Κ', '�' => 'Λ', '�' => 'Μ', '�' => 'Ν', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => 'Ξ', '�' => 'Ο', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'Π', '�' => 'Ρ', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => 'Σ', '�' => 'Τ', '�' => 'Υ', '�' => 'Φ', '�' => 'Χ', '�' => 'Ψ', '�' => 'Ω', '�' => 'α', '�' => 'β', '�' => 'γ', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => 'δ', '�' => 'ε', '�' => '▀', '�' => 'ζ', '�' => 'η', '�' => 'θ', '�' => 'ι', '�' => 'κ', '�' => 'λ', '�' => 'μ', '�' => 'ν', '�' => 'ξ', '�' => 'ο', '�' => 'π', '�' => 'ρ', '�' => 'σ', '�' => 'ς', '�' => 'τ', '�' => '΄', '�' => '', '�' => '±', '�' => 'υ', '�' => 'φ', '�' => 'χ', '�' => '§', '�' => 'ψ', '�' => '΅', '�' => '°', '�' => '¨', '�' => 'ω', '�' => 'ϋ', '�' => 'ΰ', '�' => 'ώ', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#] uň��?vendor/symfony/polyfill-iconv/Resources/charset/from.koi8-r.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '─', '�' => '│', '�' => '┌', '�' => '┐', '�' => '└', '�' => '┘', '�' => '├', '�' => '┤', '�' => '┬', '�' => '┴', '�' => '┼', '�' => '▀', '�' => '▄', '�' => '█', '�' => '▌', '�' => '▐', '�' => '░', '�' => '▒', '�' => '▓', '�' => '⌠', '�' => '■', '�' => '∙', '�' => '√', '�' => '≈', '�' => '≤', '�' => '≥', '�' => ' ', '�' => '⌡', '�' => '°', '�' => '²', '�' => '·', '�' => '÷', '�' => '═', '�' => '║', '�' => '╒', '�' => 'ё', '�' => '╓', '�' => '╔', '�' => '╕', '�' => '╖', '�' => '╗', '�' => '╘', '�' => '╙', '�' => '╚', '�' => '╛', '�' => '╜', '�' => '╝', '�' => '╞', '�' => '╟', '�' => '╠', '�' => '╡', '�' => 'Ё', '�' => '╢', '�' => '╣', '�' => '╤', '�' => '╥', '�' => '╦', '�' => '╧', '�' => '╨', '�' => '╩', '�' => '╪', '�' => '╫', '�' => '╬', '�' => '©', '�' => 'ю', '�' => 'а', '�' => 'б', '�' => 'ц', '�' => 'д', '�' => 'е', '�' => 'ф', '�' => 'г', '�' => 'х', '�' => 'и', '�' => 'й', '�' => 'к', '�' => 'л', '�' => 'м', '�' => 'н', '�' => 'о', '�' => 'п', '�' => 'я', '�' => 'р', '�' => 'с', '�' => 'т', '�' => 'у', '�' => 'ж', '�' => 'в', '�' => 'ь', '�' => 'ы', '�' => 'з', '�' => 'ш', '�' => 'э', '�' => 'щ', '�' => 'ч', '�' => 'ъ', '�' => 'Ю', '�' => 'А', '�' => 'Б', '�' => 'Ц', '�' => 'Д', '�' => 'Е', '�' => 'Ф', '�' => 'Г', '�' => 'Х', '�' => 'И', '�' => 'Й', '�' => 'К', '�' => 'Л', '�' => 'М', '�' => 'Н', '�' => 'О', '�' => 'П', '�' => 'Я', '�' => 'Р', '�' => 'С', '�' => 'Т', '�' => 'У', '�' => 'Ж', '�' => 'В', '�' => 'Ь', '�' => 'Ы', '�' => 'З', '�' => 'Ш', '�' => 'Э', '�' => 'Щ', '�' => 'Ч', '�' => 'Ъ', ); $result =& $data; unset($data); return $result; PK2A#]B4���?vendor/symfony/polyfill-iconv/Resources/charset/from.koi8-u.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '─', '�' => '│', '�' => '┌', '�' => '┐', '�' => '└', '�' => '┘', '�' => '├', '�' => '┤', '�' => '┬', '�' => '┴', '�' => '┼', '�' => '▀', '�' => '▄', '�' => '█', '�' => '▌', '�' => '▐', '�' => '░', '�' => '▒', '�' => '▓', '�' => '⌠', '�' => '■', '�' => '•', '�' => '√', '�' => '≈', '�' => '≤', '�' => '≥', '�' => ' ', '�' => '⌡', '�' => '°', '�' => '²', '�' => '·', '�' => '÷', '�' => '═', '�' => '║', '�' => '╒', '�' => 'ё', '�' => 'є', '�' => '╔', '�' => 'і', '�' => 'ї', '�' => '╗', '�' => '╘', '�' => '╙', '�' => '╚', '�' => '╛', '�' => 'ґ', '�' => '╝', '�' => '╞', '�' => '╟', '�' => '╠', '�' => '╡', '�' => 'Ё', '�' => 'Ѓ', '�' => '╣', '�' => 'І', '�' => 'Ї', '�' => '╦', '�' => '╧', '�' => '╨', '�' => '╩', '�' => '╪', '�' => 'Ґ', '�' => '╬', '�' => '©', '�' => 'ю', '�' => 'а', '�' => 'б', '�' => 'ц', '�' => 'д', '�' => 'е', '�' => 'ф', '�' => 'г', '�' => 'х', '�' => 'и', '�' => 'й', '�' => 'к', '�' => 'л', '�' => 'м', '�' => 'н', '�' => 'о', '�' => 'п', '�' => 'я', '�' => 'р', '�' => 'с', '�' => 'т', '�' => 'у', '�' => 'ж', '�' => 'в', '�' => 'ь', '�' => 'ы', '�' => 'з', '�' => 'ш', '�' => 'э', '�' => 'щ', '�' => 'ч', '�' => 'ъ', '�' => 'Ю', '�' => 'А', '�' => 'Б', '�' => 'Ц', '�' => 'Д', '�' => 'Е', '�' => 'Ф', '�' => 'Г', '�' => 'Х', '�' => 'И', '�' => 'Й', '�' => 'К', '�' => 'Л', '�' => 'М', '�' => 'Н', '�' => 'О', '�' => 'П', '�' => 'Я', '�' => 'Р', '�' => 'С', '�' => 'Т', '�' => 'У', '�' => 'Ж', '�' => 'В', '�' => 'Ь', '�' => 'Ы', '�' => 'З', '�' => 'Ш', '�' => 'Э', '�' => 'Щ', '�' => 'Ч', '�' => 'Ъ', ); $result =& $data; unset($data); return $result; PK2A#]�D�H;�;�>vendor/symfony/polyfill-iconv/Resources/charset/from.cp936.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�@' => '丂', '�A' => '丄', '�B' => '丅', '�C' => '丆', '�D' => '丏', '�E' => '丒', '�F' => '丗', '�G' => '丟', '�H' => '丠', '�I' => '両', '�J' => '丣', '�K' => '並', '�L' => '丩', '�M' => '丮', '�N' => '丯', '�O' => '丱', '�P' => '丳', '�Q' => '丵', '�R' => '丷', '�S' => '丼', '�T' => '乀', '�U' => '乁', '�V' => '乂', '�W' => '乄', '�X' => '乆', '�Y' => '乊', '�Z' => '乑', '�[' => '乕', '�\\' => '乗', '�]' => '乚', '�^' => '乛', '�_' => '乢', '�`' => '乣', '�a' => '乤', '�b' => '乥', '�c' => '乧', '�d' => '乨', '�e' => '乪', '�f' => '乫', '�g' => '乬', '�h' => '乭', '�i' => '乮', '�j' => '乯', '�k' => '乲', '�l' => '乴', '�m' => '乵', '�n' => '乶', '�o' => '乷', '�p' => '乸', '�q' => '乹', '�r' => '乺', '�s' => '乻', '�t' => '乼', '�u' => '乽', '�v' => '乿', '�w' => '亀', '�x' => '亁', '�y' => '亂', '�z' => '亃', '�{' => '亄', '�|' => '亅', '�}' => '亇', '�~' => '亊', '��' => '亐', '��' => '亖', '��' => '亗', '��' => '亙', '��' => '亜', '��' => '亝', '��' => '亞', '��' => '亣', '��' => '亪', '��' => '亯', '��' => '亰', '��' => '亱', '��' => '亴', '��' => '亶', '��' => '亷', '��' => '亸', '��' => '亹', '��' => '亼', '��' => '亽', '��' => '亾', '��' => '仈', '��' => '仌', '��' => '仏', '��' => '仐', '��' => '仒', '��' => '仚', '��' => '仛', '��' => '仜', '��' => '仠', '��' => '仢', '��' => '仦', '��' => '仧', '��' => '仩', '��' => '仭', '��' => '仮', '��' => '仯', '��' => '仱', '��' => '仴', '��' => '仸', '��' => '仹', '��' => '仺', '��' => '仼', '��' => '仾', '��' => '伀', '��' => '伂', '��' => '伃', '��' => '伄', '��' => '伅', '��' => '伆', '��' => '伇', '��' => '伈', '��' => '伋', '��' => '伌', '��' => '伒', '��' => '伓', '��' => '伔', '��' => '伕', '��' => '伖', '��' => '伜', '��' => '伝', '��' => '伡', '��' => '伣', '��' => '伨', '��' => '伩', '��' => '伬', '��' => '伭', '��' => '伮', '��' => '伱', '��' => '伳', '��' => '伵', '��' => '伷', '��' => '伹', '��' => '伻', '��' => '伾', '��' => '伿', '��' => '佀', '��' => '佁', '��' => '佂', '��' => '佄', '��' => '佅', '��' => '佇', '��' => '佈', '��' => '佉', '��' => '佊', '��' => '佋', '��' => '佌', '��' => '佒', '��' => '佔', '��' => '佖', '��' => '佡', '��' => '佢', '��' => '佦', '��' => '佨', '��' => '佪', '��' => '佫', '��' => '佭', '��' => '佮', '��' => '佱', '��' => '佲', '��' => '併', '��' => '佷', '��' => '佸', '��' => '佹', '��' => '佺', '��' => '佽', '��' => '侀', '��' => '侁', '��' => '侂', '��' => '侅', '��' => '來', '��' => '侇', '��' => '侊', '��' => '侌', '��' => '侎', '��' => '侐', '��' => '侒', '��' => '侓', '��' => '侕', '��' => '侖', '��' => '侘', '��' => '侙', '��' => '侚', '��' => '侜', '��' => '侞', '��' => '侟', '��' => '価', '��' => '侢', '�@' => '侤', '�A' => '侫', '�B' => '侭', '�C' => '侰', '�D' => '侱', '�E' => '侲', '�F' => '侳', '�G' => '侴', '�H' => '侶', '�I' => '侷', '�J' => '侸', '�K' => '侹', '�L' => '侺', '�M' => '侻', '�N' => '侼', '�O' => '侽', '�P' => '侾', '�Q' => '俀', '�R' => '俁', '�S' => '係', '�T' => '俆', '�U' => '俇', '�V' => '俈', '�W' => '俉', '�X' => '俋', '�Y' => '俌', '�Z' => '俍', '�[' => '俒', '�\\' => '俓', '�]' => '俔', '�^' => '俕', '�_' => '俖', '�`' => '俙', '�a' => '俛', '�b' => '俠', '�c' => '俢', '�d' => '俤', '�e' => '俥', '�f' => '俧', '�g' => '俫', '�h' => '俬', '�i' => '俰', '�j' => '俲', '�k' => '俴', '�l' => '俵', '�m' => '俶', '�n' => '俷', '�o' => '俹', '�p' => '俻', '�q' => '俼', '�r' => '俽', '�s' => '俿', '�t' => '倀', '�u' => '倁', '�v' => '倂', '�w' => '倃', '�x' => '倄', '�y' => '倅', '�z' => '倆', '�{' => '倇', '�|' => '倈', '�}' => '倉', '�~' => '倊', '��' => '個', '��' => '倎', '��' => '倐', '��' => '們', '��' => '倓', '��' => '倕', '��' => '倖', '��' => '倗', '��' => '倛', '��' => '倝', '��' => '倞', '��' => '倠', '��' => '倢', '��' => '倣', '��' => '値', '��' => '倧', '��' => '倫', '��' => '倯', '��' => '倰', '��' => '倱', '��' => '倲', '��' => '倳', '��' => '倴', '��' => '倵', '��' => '倶', '��' => '倷', '��' => '倸', '��' => '倹', '��' => '倻', '��' => '倽', '��' => '倿', '��' => '偀', '��' => '偁', '��' => '偂', '��' => '偄', '��' => '偅', '��' => '偆', '��' => '偉', '��' => '偊', '��' => '偋', '��' => '偍', '��' => '偐', '��' => '偑', '��' => '偒', '��' => '偓', '��' => '偔', '��' => '偖', '��' => '偗', '��' => '偘', '��' => '偙', '��' => '偛', '��' => '偝', '��' => '偞', '��' => '偟', '��' => '偠', '��' => '偡', '��' => '偢', '��' => '偣', '��' => '偤', '��' => '偦', '��' => '偧', '��' => '偨', '��' => '偩', '��' => '偪', '��' => '偫', '��' => '偭', '��' => '偮', '��' => '偯', '��' => '偰', '��' => '偱', '��' => '偲', '��' => '偳', '��' => '側', '��' => '偵', '��' => '偸', '��' => '偹', '��' => '偺', '��' => '偼', '��' => '偽', '��' => '傁', '��' => '傂', '��' => '傃', '��' => '傄', '��' => '傆', '��' => '傇', '��' => '傉', '��' => '傊', '��' => '傋', '��' => '傌', '��' => '傎', '��' => '傏', '��' => '傐', '��' => '傑', '��' => '傒', '��' => '傓', '��' => '傔', '��' => '傕', '��' => '傖', '��' => '傗', '��' => '傘', '��' => '備', '��' => '傚', '��' => '傛', '��' => '傜', '��' => '傝', '��' => '傞', '��' => '傟', '��' => '傠', '��' => '傡', '��' => '傢', '��' => '傤', '��' => '傦', '��' => '傪', '��' => '傫', '��' => '傭', '��' => '傮', '��' => '傯', '��' => '傰', '��' => '傱', '��' => '傳', '��' => '傴', '��' => '債', '��' => '傶', '��' => '傷', '��' => '傸', '��' => '傹', '��' => '傼', '�@' => '傽', '�A' => '傾', '�B' => '傿', '�C' => '僀', '�D' => '僁', '�E' => '僂', '�F' => '僃', '�G' => '僄', '�H' => '僅', '�I' => '僆', '�J' => '僇', '�K' => '僈', '�L' => '僉', '�M' => '僊', '�N' => '僋', '�O' => '僌', '�P' => '働', '�Q' => '僎', '�R' => '僐', '�S' => '僑', '�T' => '僒', '�U' => '僓', '�V' => '僔', '�W' => '僕', '�X' => '僗', '�Y' => '僘', '�Z' => '僙', '�[' => '僛', '�\\' => '僜', '�]' => '僝', '�^' => '僞', '�_' => '僟', '�`' => '僠', '�a' => '僡', '�b' => '僢', '�c' => '僣', '�d' => '僤', '�e' => '僥', '�f' => '僨', '�g' => '僩', '�h' => '僪', '�i' => '僫', '�j' => '僯', '�k' => '僰', '�l' => '僱', '�m' => '僲', '�n' => '僴', '�o' => '僶', '�p' => '僷', '�q' => '僸', '�r' => '價', '�s' => '僺', '�t' => '僼', '�u' => '僽', '�v' => '僾', '�w' => '僿', '�x' => '儀', '�y' => '儁', '�z' => '儂', '�{' => '儃', '�|' => '億', '�}' => '儅', '�~' => '儈', '��' => '儉', '��' => '儊', '��' => '儌', '��' => '儍', '��' => '儎', '��' => '儏', '��' => '儐', '��' => '儑', '��' => '儓', '��' => '儔', '��' => '儕', '��' => '儖', '��' => '儗', '��' => '儘', '��' => '儙', '��' => '儚', '��' => '儛', '��' => '儜', '��' => '儝', '��' => '儞', '��' => '償', '��' => '儠', '��' => '儢', '��' => '儣', '��' => '儤', '��' => '儥', '��' => '儦', '��' => '儧', '��' => '儨', '��' => '儩', '��' => '優', '��' => '儫', '��' => '儬', '��' => '儭', '��' => '儮', '��' => '儯', '��' => '儰', '��' => '儱', '��' => '儲', '��' => '儳', '��' => '儴', '��' => '儵', '��' => '儶', '��' => '儷', '��' => '儸', '��' => '儹', '��' => '儺', '��' => '儻', '��' => '儼', '��' => '儽', '��' => '儾', '��' => '兂', '��' => '兇', '��' => '兊', '��' => '兌', '��' => '兎', '��' => '兏', '��' => '児', '��' => '兒', '��' => '兓', '��' => '兗', '��' => '兘', '��' => '兙', '��' => '兛', '��' => '兝', '��' => '兞', '��' => '兟', '��' => '兠', '��' => '兡', '��' => '兣', '��' => '兤', '��' => '兦', '��' => '內', '��' => '兩', '��' => '兪', '��' => '兯', '��' => '兲', '��' => '兺', '��' => '兾', '��' => '兿', '��' => '冃', '��' => '冄', '��' => '円', '��' => '冇', '��' => '冊', '��' => '冋', '��' => '冎', '��' => '冏', '��' => '冐', '��' => '冑', '��' => '冓', '��' => '冔', '��' => '冘', '��' => '冚', '��' => '冝', '��' => '冞', '��' => '冟', '��' => '冡', '��' => '冣', '��' => '冦', '��' => '冧', '��' => '冨', '��' => '冩', '��' => '冪', '��' => '冭', '��' => '冮', '��' => '冴', '��' => '冸', '��' => '冹', '��' => '冺', '��' => '冾', '��' => '冿', '��' => '凁', '��' => '凂', '��' => '凃', '��' => '凅', '��' => '凈', '��' => '凊', '��' => '凍', '��' => '凎', '��' => '凐', '��' => '凒', '��' => '凓', '��' => '凔', '��' => '凕', '��' => '凖', '��' => '凗', '�@' => '凘', '�A' => '凙', '�B' => '凚', '�C' => '凜', '�D' => '凞', '�E' => '凟', '�F' => '凢', '�G' => '凣', '�H' => '凥', '�I' => '処', '�J' => '凧', '�K' => '凨', '�L' => '凩', '�M' => '凪', '�N' => '凬', '�O' => '凮', '�P' => '凱', '�Q' => '凲', '�R' => '凴', '�S' => '凷', '�T' => '凾', '�U' => '刄', '�V' => '刅', '�W' => '刉', '�X' => '刋', '�Y' => '刌', '�Z' => '刏', '�[' => '刐', '�\\' => '刓', '�]' => '刔', '�^' => '刕', '�_' => '刜', '�`' => '刞', '�a' => '刟', '�b' => '刡', '�c' => '刢', '�d' => '刣', '�e' => '別', '�f' => '刦', '�g' => '刧', '�h' => '刪', '�i' => '刬', '�j' => '刯', '�k' => '刱', '�l' => '刲', '�m' => '刴', '�n' => '刵', '�o' => '刼', '�p' => '刾', '�q' => '剄', '�r' => '剅', '�s' => '剆', '�t' => '則', '�u' => '剈', '�v' => '剉', '�w' => '剋', '�x' => '剎', '�y' => '剏', '�z' => '剒', '�{' => '剓', '�|' => '剕', '�}' => '剗', '�~' => '剘', '��' => '剙', '��' => '剚', '��' => '剛', '��' => '剝', '��' => '剟', '��' => '剠', '��' => '剢', '��' => '剣', '��' => '剤', '��' => '剦', '��' => '剨', '��' => '剫', '��' => '剬', '��' => '剭', '��' => '剮', '��' => '剰', '��' => '剱', '��' => '剳', '��' => '剴', '��' => '創', '��' => '剶', '��' => '剷', '��' => '剸', '��' => '剹', '��' => '剺', '��' => '剻', '��' => '剼', '��' => '剾', '��' => '劀', '��' => '劃', '��' => '劄', '��' => '劅', '��' => '劆', '��' => '劇', '��' => '劉', '��' => '劊', '��' => '劋', '��' => '劌', '��' => '劍', '��' => '劎', '��' => '劏', '��' => '劑', '��' => '劒', '��' => '劔', '��' => '劕', '��' => '劖', '��' => '劗', '��' => '劘', '��' => '劙', '��' => '劚', '��' => '劜', '��' => '劤', '��' => '劥', '��' => '劦', '��' => '劧', '��' => '劮', '��' => '劯', '��' => '劰', '��' => '労', '��' => '劵', '��' => '劶', '��' => '劷', '��' => '劸', '��' => '効', '��' => '劺', '��' => '劻', '��' => '劼', '��' => '劽', '��' => '勀', '��' => '勁', '��' => '勂', '��' => '勄', '��' => '勅', '��' => '勆', '��' => '勈', '��' => '勊', '��' => '勌', '��' => '勍', '��' => '勎', '��' => '勏', '��' => '勑', '��' => '勓', '��' => '勔', '��' => '動', '��' => '勗', '��' => '務', '��' => '勚', '��' => '勛', '��' => '勜', '��' => '勝', '��' => '勞', '��' => '勠', '��' => '勡', '��' => '勢', '��' => '勣', '��' => '勥', '��' => '勦', '��' => '勧', '��' => '勨', '��' => '勩', '��' => '勪', '��' => '勫', '��' => '勬', '��' => '勭', '��' => '勮', '��' => '勯', '��' => '勱', '��' => '勲', '��' => '勳', '��' => '勴', '��' => '勵', '��' => '勶', '��' => '勷', '��' => '勸', '��' => '勻', '��' => '勼', '��' => '勽', '��' => '匁', '��' => '匂', '��' => '匃', '��' => '匄', '��' => '匇', '��' => '匉', '��' => '匊', '��' => '匋', '��' => '匌', '��' => '匎', '�@' => '匑', '�A' => '匒', '�B' => '匓', '�C' => '匔', '�D' => '匘', '�E' => '匛', '�F' => '匜', '�G' => '匞', '�H' => '匟', '�I' => '匢', '�J' => '匤', '�K' => '匥', '�L' => '匧', '�M' => '匨', '�N' => '匩', '�O' => '匫', '�P' => '匬', '�Q' => '匭', '�R' => '匯', '�S' => '匰', '�T' => '匱', '�U' => '匲', '�V' => '匳', '�W' => '匴', '�X' => '匵', '�Y' => '匶', '�Z' => '匷', '�[' => '匸', '�\\' => '匼', '�]' => '匽', '�^' => '區', '�_' => '卂', '�`' => '卄', '�a' => '卆', '�b' => '卋', '�c' => '卌', '�d' => '卍', '�e' => '卐', '�f' => '協', '�g' => '単', '�h' => '卙', '�i' => '卛', '�j' => '卝', '�k' => '卥', '�l' => '卨', '�m' => '卪', '�n' => '卬', '�o' => '卭', '�p' => '卲', '�q' => '卶', '�r' => '卹', '�s' => '卻', '�t' => '卼', '�u' => '卽', '�v' => '卾', '�w' => '厀', '�x' => '厁', '�y' => '厃', '�z' => '厇', '�{' => '厈', '�|' => '厊', '�}' => '厎', '�~' => '厏', '��' => '厐', '��' => '厑', '��' => '厒', '��' => '厓', '��' => '厔', '��' => '厖', '��' => '厗', '��' => '厙', '��' => '厛', '��' => '厜', '��' => '厞', '��' => '厠', '��' => '厡', '��' => '厤', '��' => '厧', '��' => '厪', '��' => '厫', '��' => '厬', '��' => '厭', '��' => '厯', '��' => '厰', '��' => '厱', '��' => '厲', '��' => '厳', '��' => '厴', '��' => '厵', '��' => '厷', '��' => '厸', '��' => '厹', '��' => '厺', '��' => '厼', '��' => '厽', '��' => '厾', '��' => '叀', '��' => '參', '��' => '叄', '��' => '叅', '��' => '叆', '��' => '叇', '��' => '収', '��' => '叏', '��' => '叐', '��' => '叒', '��' => '叓', '��' => '叕', '��' => '叚', '��' => '叜', '��' => '叝', '��' => '叞', '��' => '叡', '��' => '叢', '��' => '叧', '��' => '叴', '��' => '叺', '��' => '叾', '��' => '叿', '��' => '吀', '��' => '吂', '��' => '吅', '��' => '吇', '��' => '吋', '��' => '吔', '��' => '吘', '��' => '吙', '��' => '吚', '��' => '吜', '��' => '吢', '��' => '吤', '��' => '吥', '��' => '吪', '��' => '吰', '��' => '吳', '��' => '吶', '��' => '吷', '��' => '吺', '��' => '吽', '��' => '吿', '��' => '呁', '��' => '呂', '��' => '呄', '��' => '呅', '��' => '呇', '��' => '呉', '��' => '呌', '��' => '呍', '��' => '呎', '��' => '呏', '��' => '呑', '��' => '呚', '��' => '呝', '��' => '呞', '��' => '呟', '��' => '呠', '��' => '呡', '��' => '呣', '��' => '呥', '��' => '呧', '��' => '呩', '��' => '呪', '��' => '呫', '��' => '呬', '��' => '呭', '��' => '呮', '��' => '呯', '��' => '呰', '��' => '呴', '��' => '呹', '��' => '呺', '��' => '呾', '��' => '呿', '��' => '咁', '��' => '咃', '��' => '咅', '��' => '咇', '��' => '咈', '��' => '咉', '��' => '咊', '��' => '咍', '��' => '咑', '��' => '咓', '��' => '咗', '��' => '咘', '��' => '咜', '��' => '咞', '��' => '咟', '��' => '咠', '��' => '咡', '�@' => '咢', '�A' => '咥', '�B' => '咮', '�C' => '咰', '�D' => '咲', '�E' => '咵', '�F' => '咶', '�G' => '咷', '�H' => '咹', '�I' => '咺', '�J' => '咼', '�K' => '咾', '�L' => '哃', '�M' => '哅', '�N' => '哊', '�O' => '哋', '�P' => '哖', '�Q' => '哘', '�R' => '哛', '�S' => '哠', '�T' => '員', '�U' => '哢', '�V' => '哣', '�W' => '哤', '�X' => '哫', '�Y' => '哬', '�Z' => '哯', '�[' => '哰', '�\\' => '哱', '�]' => '哴', '�^' => '哵', '�_' => '哶', '�`' => '哷', '�a' => '哸', '�b' => '哹', '�c' => '哻', '�d' => '哾', '�e' => '唀', '�f' => '唂', '�g' => '唃', '�h' => '唄', '�i' => '唅', '�j' => '唈', '�k' => '唊', '�l' => '唋', '�m' => '唌', '�n' => '唍', '�o' => '唎', '�p' => '唒', '�q' => '唓', '�r' => '唕', '�s' => '唖', '�t' => '唗', '�u' => '唘', '�v' => '唙', '�w' => '唚', '�x' => '唜', '�y' => '唝', '�z' => '唞', '�{' => '唟', '�|' => '唡', '�}' => '唥', '�~' => '唦', '��' => '唨', '��' => '唩', '��' => '唫', '��' => '唭', '��' => '唲', '��' => '唴', '��' => '唵', '��' => '唶', '��' => '唸', '��' => '唹', '��' => '唺', '��' => '唻', '��' => '唽', '��' => '啀', '��' => '啂', '��' => '啅', '��' => '啇', '��' => '啈', '��' => '啋', '��' => '啌', '��' => '啍', '��' => '啎', '��' => '問', '��' => '啑', '��' => '啒', '��' => '啓', '��' => '啔', '��' => '啗', '��' => '啘', '��' => '啙', '��' => '啚', '��' => '啛', '��' => '啝', '��' => '啞', '��' => '啟', '��' => '啠', '��' => '啢', '��' => '啣', '��' => '啨', '��' => '啩', '��' => '啫', '��' => '啯', '��' => '啰', '��' => '啱', '��' => '啲', '��' => '啳', '��' => '啴', '��' => '啹', '��' => '啺', '��' => '啽', '��' => '啿', '��' => '喅', '��' => '喆', '��' => '喌', '��' => '喍', '��' => '喎', '��' => '喐', '��' => '喒', '��' => '喓', '��' => '喕', '��' => '喖', '��' => '喗', '��' => '喚', '��' => '喛', '��' => '喞', '��' => '喠', '��' => '喡', '��' => '喢', '��' => '喣', '��' => '喤', '��' => '喥', '��' => '喦', '��' => '喨', '��' => '喩', '��' => '喪', '��' => '喫', '��' => '喬', '��' => '喭', '��' => '單', '��' => '喯', '��' => '喰', '��' => '喲', '��' => '喴', '��' => '営', '��' => '喸', '��' => '喺', '��' => '喼', '��' => '喿', '��' => '嗀', '��' => '嗁', '��' => '嗂', '��' => '嗃', '��' => '嗆', '��' => '嗇', '��' => '嗈', '��' => '嗊', '��' => '嗋', '��' => '嗎', '��' => '嗏', '��' => '嗐', '��' => '嗕', '��' => '嗗', '��' => '嗘', '��' => '嗙', '��' => '嗚', '��' => '嗛', '��' => '嗞', '��' => '嗠', '��' => '嗢', '��' => '嗧', '��' => '嗩', '��' => '嗭', '��' => '嗮', '��' => '嗰', '��' => '嗱', '��' => '嗴', '��' => '嗶', '��' => '嗸', '��' => '嗹', '��' => '嗺', '��' => '嗻', '��' => '嗼', '��' => '嗿', '��' => '嘂', '��' => '嘃', '��' => '嘄', '��' => '嘅', '�@' => '嘆', '�A' => '嘇', '�B' => '嘊', '�C' => '嘋', '�D' => '嘍', '�E' => '嘐', '�F' => '嘑', '�G' => '嘒', '�H' => '嘓', '�I' => '嘔', '�J' => '嘕', '�K' => '嘖', '�L' => '嘗', '�M' => '嘙', '�N' => '嘚', '�O' => '嘜', '�P' => '嘝', '�Q' => '嘠', '�R' => '嘡', '�S' => '嘢', '�T' => '嘥', '�U' => '嘦', '�V' => '嘨', '�W' => '嘩', '�X' => '嘪', '�Y' => '嘫', '�Z' => '嘮', '�[' => '嘯', '�\\' => '嘰', '�]' => '嘳', '�^' => '嘵', '�_' => '嘷', '�`' => '嘸', '�a' => '嘺', '�b' => '嘼', '�c' => '嘽', '�d' => '嘾', '�e' => '噀', '�f' => '噁', '�g' => '噂', '�h' => '噃', '�i' => '噄', '�j' => '噅', '�k' => '噆', '�l' => '噇', '�m' => '噈', '�n' => '噉', '�o' => '噊', '�p' => '噋', '�q' => '噏', '�r' => '噐', '�s' => '噑', '�t' => '噒', '�u' => '噓', '�v' => '噕', '�w' => '噖', '�x' => '噚', '�y' => '噛', '�z' => '噝', '�{' => '噞', '�|' => '噟', '�}' => '噠', '�~' => '噡', '��' => '噣', '��' => '噥', '��' => '噦', '��' => '噧', '��' => '噭', '��' => '噮', '��' => '噯', '��' => '噰', '��' => '噲', '��' => '噳', '��' => '噴', '��' => '噵', '��' => '噷', '��' => '噸', '��' => '噹', '��' => '噺', '��' => '噽', '��' => '噾', '��' => '噿', '��' => '嚀', '��' => '嚁', '��' => '嚂', '��' => '嚃', '��' => '嚄', '��' => '嚇', '��' => '嚈', '��' => '嚉', '��' => '嚊', '��' => '嚋', '��' => '嚌', '��' => '嚍', '��' => '嚐', '��' => '嚑', '��' => '嚒', '��' => '嚔', '��' => '嚕', '��' => '嚖', '��' => '嚗', '��' => '嚘', '��' => '嚙', '��' => '嚚', '��' => '嚛', '��' => '嚜', '��' => '嚝', '��' => '嚞', '��' => '嚟', '��' => '嚠', '��' => '嚡', '��' => '嚢', '��' => '嚤', '��' => '嚥', '��' => '嚦', '��' => '嚧', '��' => '嚨', '��' => '嚩', '��' => '嚪', '��' => '嚫', '��' => '嚬', '��' => '嚭', '��' => '嚮', '��' => '嚰', '��' => '嚱', '��' => '嚲', '��' => '嚳', '��' => '嚴', '��' => '嚵', '��' => '嚶', '��' => '嚸', '��' => '嚹', '��' => '嚺', '��' => '嚻', '��' => '嚽', '��' => '嚾', '��' => '嚿', '��' => '囀', '��' => '囁', '��' => '囂', '��' => '囃', '��' => '囄', '��' => '囅', '��' => '囆', '��' => '囇', '��' => '囈', '��' => '囉', '��' => '囋', '��' => '囌', '��' => '囍', '��' => '囎', '��' => '囏', '��' => '囐', '��' => '囑', '��' => '囒', '��' => '囓', '��' => '囕', '��' => '囖', '��' => '囘', '��' => '囙', '��' => '囜', '��' => '団', '��' => '囥', '��' => '囦', '��' => '囧', '��' => '囨', '��' => '囩', '��' => '囪', '��' => '囬', '��' => '囮', '��' => '囯', '��' => '囲', '��' => '図', '��' => '囶', '��' => '囷', '��' => '囸', '��' => '囻', '��' => '囼', '��' => '圀', '��' => '圁', '��' => '圂', '��' => '圅', '��' => '圇', '��' => '國', '��' => '圌', '��' => '圍', '��' => '圎', '��' => '圏', '��' => '圐', '��' => '圑', '�@' => '園', '�A' => '圓', '�B' => '圔', '�C' => '圕', '�D' => '圖', '�E' => '圗', '�F' => '團', '�G' => '圙', '�H' => '圚', '�I' => '圛', '�J' => '圝', '�K' => '圞', '�L' => '圠', '�M' => '圡', '�N' => '圢', '�O' => '圤', '�P' => '圥', '�Q' => '圦', '�R' => '圧', '�S' => '圫', '�T' => '圱', '�U' => '圲', '�V' => '圴', '�W' => '圵', '�X' => '圶', '�Y' => '圷', '�Z' => '圸', '�[' => '圼', '�\\' => '圽', '�]' => '圿', '�^' => '坁', '�_' => '坃', '�`' => '坄', '�a' => '坅', '�b' => '坆', '�c' => '坈', '�d' => '坉', '�e' => '坋', '�f' => '坒', '�g' => '坓', '�h' => '坔', '�i' => '坕', '�j' => '坖', '�k' => '坘', '�l' => '坙', '�m' => '坢', '�n' => '坣', '�o' => '坥', '�p' => '坧', '�q' => '坬', '�r' => '坮', '�s' => '坰', '�t' => '坱', '�u' => '坲', '�v' => '坴', '�w' => '坵', '�x' => '坸', '�y' => '坹', '�z' => '坺', '�{' => '坽', '�|' => '坾', '�}' => '坿', '�~' => '垀', '��' => '垁', '��' => '垇', '��' => '垈', '��' => '垉', '��' => '垊', '��' => '垍', '��' => '垎', '��' => '垏', '��' => '垐', '��' => '垑', '��' => '垔', '��' => '垕', '��' => '垖', '��' => '垗', '��' => '垘', '��' => '垙', '��' => '垚', '��' => '垜', '��' => '垝', '��' => '垞', '��' => '垟', '��' => '垥', '��' => '垨', '��' => '垪', '��' => '垬', '��' => '垯', '��' => '垰', '��' => '垱', '��' => '垳', '��' => '垵', '��' => '垶', '��' => '垷', '��' => '垹', '��' => '垺', '��' => '垻', '��' => '垼', '��' => '垽', '��' => '垾', '��' => '垿', '��' => '埀', '��' => '埁', '��' => '埄', '��' => '埅', '��' => '埆', '��' => '埇', '��' => '埈', '��' => '埉', '��' => '埊', '��' => '埌', '��' => '埍', '��' => '埐', '��' => '埑', '��' => '埓', '��' => '埖', '��' => '埗', '��' => '埛', '��' => '埜', '��' => '埞', '��' => '埡', '��' => '埢', '��' => '埣', '��' => '埥', '��' => '埦', '��' => '埧', '��' => '埨', '��' => '埩', '��' => '埪', '��' => '埫', '��' => '埬', '��' => '埮', '��' => '埰', '��' => '埱', '��' => '埲', '��' => '埳', '��' => '埵', '��' => '埶', '��' => '執', '��' => '埻', '��' => '埼', '��' => '埾', '��' => '埿', '��' => '堁', '��' => '堃', '��' => '堄', '��' => '堅', '��' => '堈', '��' => '堉', '��' => '堊', '��' => '堌', '��' => '堎', '��' => '堏', '��' => '堐', '��' => '堒', '��' => '堓', '��' => '堔', '��' => '堖', '��' => '堗', '��' => '堘', '��' => '堚', '��' => '堛', '��' => '堜', '��' => '堝', '��' => '堟', '��' => '堢', '��' => '堣', '��' => '堥', '��' => '堦', '��' => '堧', '��' => '堨', '��' => '堩', '��' => '堫', '��' => '堬', '��' => '堭', '��' => '堮', '��' => '堯', '��' => '報', '��' => '堲', '��' => '堳', '��' => '場', '��' => '堶', '��' => '堷', '��' => '堸', '��' => '堹', '��' => '堺', '��' => '堻', '��' => '堼', '��' => '堽', '�@' => '堾', '�A' => '堿', '�B' => '塀', '�C' => '塁', '�D' => '塂', '�E' => '塃', '�F' => '塅', '�G' => '塆', '�H' => '塇', '�I' => '塈', '�J' => '塉', '�K' => '塊', '�L' => '塋', '�M' => '塎', '�N' => '塏', '�O' => '塐', '�P' => '塒', '�Q' => '塓', '�R' => '塕', '�S' => '塖', '�T' => '塗', '�U' => '塙', '�V' => '塚', '�W' => '塛', '�X' => '塜', '�Y' => '塝', '�Z' => '塟', '�[' => '塠', '�\\' => '塡', '�]' => '塢', '�^' => '塣', '�_' => '塤', '�`' => '塦', '�a' => '塧', '�b' => '塨', '�c' => '塩', '�d' => '塪', '�e' => '塭', '�f' => '塮', '�g' => '塯', '�h' => '塰', '�i' => '塱', '�j' => '塲', '�k' => '塳', '�l' => '塴', '�m' => '塵', '�n' => '塶', '�o' => '塷', '�p' => '塸', '�q' => '塹', '�r' => '塺', '�s' => '塻', '�t' => '塼', '�u' => '塽', '�v' => '塿', '�w' => '墂', '�x' => '墄', '�y' => '墆', '�z' => '墇', '�{' => '墈', '�|' => '墊', '�}' => '墋', '�~' => '墌', '��' => '墍', '��' => '墎', '��' => '墏', '��' => '墐', '��' => '墑', '��' => '墔', '��' => '墕', '��' => '墖', '��' => '増', '��' => '墘', '��' => '墛', '��' => '墜', '��' => '墝', '��' => '墠', '��' => '墡', '��' => '墢', '��' => '墣', '��' => '墤', '��' => '墥', '��' => '墦', '��' => '墧', '��' => '墪', '��' => '墫', '��' => '墬', '��' => '墭', '��' => '墮', '��' => '墯', '��' => '墰', '��' => '墱', '��' => '墲', '��' => '墳', '��' => '墴', '��' => '墵', '��' => '墶', '��' => '墷', '��' => '墸', '��' => '墹', '��' => '墺', '��' => '墻', '��' => '墽', '��' => '墾', '��' => '墿', '��' => '壀', '��' => '壂', '��' => '壃', '��' => '壄', '��' => '壆', '��' => '壇', '��' => '壈', '��' => '壉', '��' => '壊', '��' => '壋', '��' => '壌', '��' => '壍', '��' => '壎', '��' => '壏', '��' => '壐', '��' => '壒', '��' => '壓', '��' => '壔', '��' => '壖', '��' => '壗', '��' => '壘', '��' => '壙', '��' => '壚', '��' => '壛', '��' => '壜', '��' => '壝', '��' => '壞', '��' => '壟', '��' => '壠', '��' => '壡', '��' => '壢', '��' => '壣', '��' => '壥', '��' => '壦', '��' => '壧', '��' => '壨', '��' => '壩', '��' => '壪', '��' => '壭', '��' => '壯', '��' => '壱', '��' => '売', '��' => '壴', '��' => '壵', '��' => '壷', '��' => '壸', '��' => '壺', '��' => '壻', '��' => '壼', '��' => '壽', '��' => '壾', '��' => '壿', '��' => '夀', '��' => '夁', '��' => '夃', '��' => '夅', '��' => '夆', '��' => '夈', '��' => '変', '��' => '夊', '��' => '夋', '��' => '夌', '��' => '夎', '��' => '夐', '��' => '夑', '��' => '夒', '��' => '夓', '��' => '夗', '��' => '夘', '��' => '夛', '��' => '夝', '��' => '夞', '��' => '夠', '��' => '夡', '��' => '夢', '��' => '夣', '��' => '夦', '��' => '夨', '��' => '夬', '��' => '夰', '��' => '夲', '��' => '夳', '��' => '夵', '��' => '夶', '��' => '夻', '�@' => '夽', '�A' => '夾', '�B' => '夿', '�C' => '奀', '�D' => '奃', '�E' => '奅', '�F' => '奆', '�G' => '奊', '�H' => '奌', '�I' => '奍', '�J' => '奐', '�K' => '奒', '�L' => '奓', '�M' => '奙', '�N' => '奛', '�O' => '奜', '�P' => '奝', '�Q' => '奞', '�R' => '奟', '�S' => '奡', '�T' => '奣', '�U' => '奤', '�V' => '奦', '�W' => '奧', '�X' => '奨', '�Y' => '奩', '�Z' => '奪', '�[' => '奫', '�\\' => '奬', '�]' => '奭', '�^' => '奮', '�_' => '奯', '�`' => '奰', '�a' => '奱', '�b' => '奲', '�c' => '奵', '�d' => '奷', '�e' => '奺', '�f' => '奻', '�g' => '奼', '�h' => '奾', '�i' => '奿', '�j' => '妀', '�k' => '妅', '�l' => '妉', '�m' => '妋', '�n' => '妌', '�o' => '妎', '�p' => '妏', '�q' => '妐', '�r' => '妑', '�s' => '妔', '�t' => '妕', '�u' => '妘', '�v' => '妚', '�w' => '妛', '�x' => '妜', '�y' => '妝', '�z' => '妟', '�{' => '妠', '�|' => '妡', '�}' => '妢', '�~' => '妦', '��' => '妧', '��' => '妬', '��' => '妭', '��' => '妰', '��' => '妱', '��' => '妳', '��' => '妴', '��' => '妵', '��' => '妶', '��' => '妷', '��' => '妸', '��' => '妺', '��' => '妼', '��' => '妽', '��' => '妿', '��' => '姀', '��' => '姁', '��' => '姂', '��' => '姃', '��' => '姄', '��' => '姅', '��' => '姇', '��' => '姈', '��' => '姉', '��' => '姌', '��' => '姍', '��' => '姎', '��' => '姏', '��' => '姕', '��' => '姖', '��' => '姙', '��' => '姛', '��' => '姞', '��' => '姟', '��' => '姠', '��' => '姡', '��' => '姢', '��' => '姤', '��' => '姦', '��' => '姧', '��' => '姩', '��' => '姪', '��' => '姫', '��' => '姭', '��' => '姮', '��' => '姯', '��' => '姰', '��' => '姱', '��' => '姲', '��' => '姳', '��' => '姴', '��' => '姵', '��' => '姶', '��' => '姷', '��' => '姸', '��' => '姺', '��' => '姼', '��' => '姽', '��' => '姾', '��' => '娀', '��' => '娂', '��' => '娊', '��' => '娋', '��' => '娍', '��' => '娎', '��' => '娏', '��' => '娐', '��' => '娒', '��' => '娔', '��' => '娕', '��' => '娖', '��' => '娗', '��' => '娙', '��' => '娚', '��' => '娛', '��' => '娝', '��' => '娞', '��' => '娡', '��' => '娢', '��' => '娤', '��' => '娦', '��' => '娧', '��' => '娨', '��' => '娪', '��' => '娫', '��' => '娬', '��' => '娭', '��' => '娮', '��' => '娯', '��' => '娰', '��' => '娳', '��' => '娵', '��' => '娷', '��' => '娸', '��' => '娹', '��' => '娺', '��' => '娻', '��' => '娽', '��' => '娾', '��' => '娿', '��' => '婁', '��' => '婂', '��' => '婃', '��' => '婄', '��' => '婅', '��' => '婇', '��' => '婈', '��' => '婋', '��' => '婌', '��' => '婍', '��' => '婎', '��' => '婏', '��' => '婐', '��' => '婑', '��' => '婒', '��' => '婓', '��' => '婔', '��' => '婖', '��' => '婗', '��' => '婘', '��' => '婙', '��' => '婛', '��' => '婜', '��' => '婝', '��' => '婞', '��' => '婟', '��' => '婠', '�@' => '婡', '�A' => '婣', '�B' => '婤', '�C' => '婥', '�D' => '婦', '�E' => '婨', '�F' => '婩', '�G' => '婫', '�H' => '婬', '�I' => '婭', '�J' => '婮', '�K' => '婯', '�L' => '婰', '�M' => '婱', '�N' => '婲', '�O' => '婳', '�P' => '婸', '�Q' => '婹', '�R' => '婻', '�S' => '婼', '�T' => '婽', '�U' => '婾', '�V' => '媀', '�W' => '媁', '�X' => '媂', '�Y' => '媃', '�Z' => '媄', '�[' => '媅', '�\\' => '媆', '�]' => '媇', '�^' => '媈', '�_' => '媉', '�`' => '媊', '�a' => '媋', '�b' => '媌', '�c' => '媍', '�d' => '媎', '�e' => '媏', '�f' => '媐', '�g' => '媑', '�h' => '媓', '�i' => '媔', '�j' => '媕', '�k' => '媖', '�l' => '媗', '�m' => '媘', '�n' => '媙', '�o' => '媜', '�p' => '媝', '�q' => '媞', '�r' => '媟', '�s' => '媠', '�t' => '媡', '�u' => '媢', '�v' => '媣', '�w' => '媤', '�x' => '媥', '�y' => '媦', '�z' => '媧', '�{' => '媨', '�|' => '媩', '�}' => '媫', '�~' => '媬', '��' => '媭', '��' => '媮', '��' => '媯', '��' => '媰', '��' => '媱', '��' => '媴', '��' => '媶', '��' => '媷', '��' => '媹', '��' => '媺', '��' => '媻', '��' => '媼', '��' => '媽', '��' => '媿', '��' => '嫀', '��' => '嫃', '��' => '嫄', '��' => '嫅', '��' => '嫆', '��' => '嫇', '��' => '嫈', '��' => '嫊', '��' => '嫋', '��' => '嫍', '��' => '嫎', '��' => '嫏', '��' => '嫐', '��' => '嫑', '��' => '嫓', '��' => '嫕', '��' => '嫗', '��' => '嫙', '��' => '嫚', '��' => '嫛', '��' => '嫝', '��' => '嫞', '��' => '嫟', '��' => '嫢', '��' => '嫤', '��' => '嫥', '��' => '嫧', '��' => '嫨', '��' => '嫪', '��' => '嫬', '��' => '嫭', '��' => '嫮', '��' => '嫯', '��' => '嫰', '��' => '嫲', '��' => '嫳', '��' => '嫴', '��' => '嫵', '��' => '嫶', '��' => '嫷', '��' => '嫸', '��' => '嫹', '��' => '嫺', '��' => '嫻', '��' => '嫼', '��' => '嫽', '��' => '嫾', '��' => '嫿', '��' => '嬀', '��' => '嬁', '��' => '嬂', '��' => '嬃', '��' => '嬄', '��' => '嬅', '��' => '嬆', '��' => '嬇', '��' => '嬈', '��' => '嬊', '��' => '嬋', '��' => '嬌', '��' => '嬍', '��' => '嬎', '��' => '嬏', '��' => '嬐', '��' => '嬑', '��' => '嬒', '��' => '嬓', '��' => '嬔', '��' => '嬕', '��' => '嬘', '��' => '嬙', '��' => '嬚', '��' => '嬛', '��' => '嬜', '��' => '嬝', '��' => '嬞', '��' => '嬟', '��' => '嬠', '��' => '嬡', '��' => '嬢', '��' => '嬣', '��' => '嬤', '��' => '嬥', '��' => '嬦', '��' => '嬧', '��' => '嬨', '��' => '嬩', '��' => '嬪', '��' => '嬫', '��' => '嬬', '��' => '嬭', '��' => '嬮', '��' => '嬯', '��' => '嬰', '��' => '嬱', '��' => '嬳', '��' => '嬵', '��' => '嬶', '��' => '嬸', '��' => '嬹', '��' => '嬺', '��' => '嬻', '��' => '嬼', '��' => '嬽', '��' => '嬾', '��' => '嬿', '��' => '孁', '��' => '孂', '��' => '孃', '��' => '孄', '��' => '孅', '��' => '孆', '��' => '孇', '�@' => '孈', '�A' => '孉', '�B' => '孊', '�C' => '孋', '�D' => '孌', '�E' => '孍', '�F' => '孎', '�G' => '孏', '�H' => '孒', '�I' => '孖', '�J' => '孞', '�K' => '孠', '�L' => '孡', '�M' => '孧', '�N' => '孨', '�O' => '孫', '�P' => '孭', '�Q' => '孮', '�R' => '孯', '�S' => '孲', '�T' => '孴', '�U' => '孶', '�V' => '孷', '�W' => '學', '�X' => '孹', '�Y' => '孻', '�Z' => '孼', '�[' => '孾', '�\\' => '孿', '�]' => '宂', '�^' => '宆', '�_' => '宊', '�`' => '宍', '�a' => '宎', '�b' => '宐', '�c' => '宑', '�d' => '宒', '�e' => '宔', '�f' => '宖', '�g' => '実', '�h' => '宧', '�i' => '宨', '�j' => '宩', '�k' => '宬', '�l' => '宭', '�m' => '宮', '�n' => '宯', '�o' => '宱', '�p' => '宲', '�q' => '宷', '�r' => '宺', '�s' => '宻', '�t' => '宼', '�u' => '寀', '�v' => '寁', '�w' => '寃', '�x' => '寈', '�y' => '寉', '�z' => '寊', '�{' => '寋', '�|' => '寍', '�}' => '寎', '�~' => '寏', '��' => '寑', '��' => '寔', '��' => '寕', '��' => '寖', '��' => '寗', '��' => '寘', '��' => '寙', '��' => '寚', '��' => '寛', '��' => '寜', '��' => '寠', '��' => '寢', '��' => '寣', '��' => '實', '��' => '寧', '��' => '審', '��' => '寪', '��' => '寫', '��' => '寬', '��' => '寭', '��' => '寯', '��' => '寱', '��' => '寲', '��' => '寳', '��' => '寴', '��' => '寵', '��' => '寶', '��' => '寷', '��' => '寽', '��' => '対', '��' => '尀', '��' => '専', '��' => '尃', '��' => '尅', '��' => '將', '��' => '專', '��' => '尋', '��' => '尌', '��' => '對', '��' => '導', '��' => '尐', '��' => '尒', '��' => '尓', '��' => '尗', '��' => '尙', '��' => '尛', '��' => '尞', '��' => '尟', '��' => '尠', '��' => '尡', '��' => '尣', '��' => '尦', '��' => '尨', '��' => '尩', '��' => '尪', '��' => '尫', '��' => '尭', '��' => '尮', '��' => '尯', '��' => '尰', '��' => '尲', '��' => '尳', '��' => '尵', '��' => '尶', '��' => '尷', '��' => '屃', '��' => '屄', '��' => '屆', '��' => '屇', '��' => '屌', '��' => '屍', '��' => '屒', '��' => '屓', '��' => '屔', '��' => '屖', '��' => '屗', '��' => '屘', '��' => '屚', '��' => '屛', '��' => '屜', '��' => '屝', '��' => '屟', '��' => '屢', '��' => '層', '��' => '屧', '��' => '屨', '��' => '屩', '��' => '屪', '��' => '屫', '��' => '屬', '��' => '屭', '��' => '屰', '��' => '屲', '��' => '屳', '��' => '屴', '��' => '屵', '��' => '屶', '��' => '屷', '��' => '屸', '��' => '屻', '��' => '屼', '��' => '屽', '��' => '屾', '��' => '岀', '��' => '岃', '��' => '岄', '��' => '岅', '��' => '岆', '��' => '岇', '��' => '岉', '��' => '岊', '��' => '岋', '��' => '岎', '��' => '岏', '��' => '岒', '��' => '岓', '��' => '岕', '��' => '岝', '��' => '岞', '��' => '岟', '��' => '岠', '��' => '岡', '��' => '岤', '��' => '岥', '��' => '岦', '��' => '岧', '��' => '岨', '�@' => '岪', '�A' => '岮', '�B' => '岯', '�C' => '岰', '�D' => '岲', '�E' => '岴', '�F' => '岶', '�G' => '岹', '�H' => '岺', '�I' => '岻', '�J' => '岼', '�K' => '岾', '�L' => '峀', '�M' => '峂', '�N' => '峃', '�O' => '峅', '�P' => '峆', '�Q' => '峇', '�R' => '峈', '�S' => '峉', '�T' => '峊', '�U' => '峌', '�V' => '峍', '�W' => '峎', '�X' => '峏', '�Y' => '峐', '�Z' => '峑', '�[' => '峓', '�\\' => '峔', '�]' => '峕', '�^' => '峖', '�_' => '峗', '�`' => '峘', '�a' => '峚', '�b' => '峛', '�c' => '峜', '�d' => '峝', '�e' => '峞', '�f' => '峟', '�g' => '峠', '�h' => '峢', '�i' => '峣', '�j' => '峧', '�k' => '峩', '�l' => '峫', '�m' => '峬', '�n' => '峮', '�o' => '峯', '�p' => '峱', '�q' => '峲', '�r' => '峳', '�s' => '峴', '�t' => '峵', '�u' => '島', '�v' => '峷', '�w' => '峸', '�x' => '峹', '�y' => '峺', '�z' => '峼', '�{' => '峽', '�|' => '峾', '�}' => '峿', '�~' => '崀', '��' => '崁', '��' => '崄', '��' => '崅', '��' => '崈', '��' => '崉', '��' => '崊', '��' => '崋', '��' => '崌', '��' => '崍', '��' => '崏', '��' => '崐', '��' => '崑', '��' => '崒', '��' => '崓', '��' => '崕', '��' => '崗', '��' => '崘', '��' => '崙', '��' => '崚', '��' => '崜', '��' => '崝', '��' => '崟', '��' => '崠', '��' => '崡', '��' => '崢', '��' => '崣', '��' => '崥', '��' => '崨', '��' => '崪', '��' => '崫', '��' => '崬', '��' => '崯', '��' => '崰', '��' => '崱', '��' => '崲', '��' => '崳', '��' => '崵', '��' => '崶', '��' => '崷', '��' => '崸', '��' => '崹', '��' => '崺', '��' => '崻', '��' => '崼', '��' => '崿', '��' => '嵀', '��' => '嵁', '��' => '嵂', '��' => '嵃', '��' => '嵄', '��' => '嵅', '��' => '嵆', '��' => '嵈', '��' => '嵉', '��' => '嵍', '��' => '嵎', '��' => '嵏', '��' => '嵐', '��' => '嵑', '��' => '嵒', '��' => '嵓', '��' => '嵔', '��' => '嵕', '��' => '嵖', '��' => '嵗', '��' => '嵙', '��' => '嵚', '��' => '嵜', '��' => '嵞', '��' => '嵟', '��' => '嵠', '��' => '嵡', '��' => '嵢', '��' => '嵣', '��' => '嵤', '��' => '嵥', '��' => '嵦', '��' => '嵧', '��' => '嵨', '��' => '嵪', '��' => '嵭', '��' => '嵮', '��' => '嵰', '��' => '嵱', '��' => '嵲', '��' => '嵳', '��' => '嵵', '��' => '嵶', '��' => '嵷', '��' => '嵸', '��' => '嵹', '��' => '嵺', '��' => '嵻', '��' => '嵼', '��' => '嵽', '��' => '嵾', '��' => '嵿', '��' => '嶀', '��' => '嶁', '��' => '嶃', '��' => '嶄', '��' => '嶅', '��' => '嶆', '��' => '嶇', '��' => '嶈', '��' => '嶉', '��' => '嶊', '��' => '嶋', '��' => '嶌', '��' => '嶍', '��' => '嶎', '��' => '嶏', '��' => '嶐', '��' => '嶑', '��' => '嶒', '��' => '嶓', '��' => '嶔', '��' => '嶕', '��' => '嶖', '��' => '嶗', '��' => '嶘', '��' => '嶚', '��' => '嶛', '��' => '嶜', '��' => '嶞', '��' => '嶟', '��' => '嶠', '�@' => '嶡', '�A' => '嶢', '�B' => '嶣', '�C' => '嶤', '�D' => '嶥', '�E' => '嶦', '�F' => '嶧', '�G' => '嶨', '�H' => '嶩', '�I' => '嶪', '�J' => '嶫', '�K' => '嶬', '�L' => '嶭', '�M' => '嶮', '�N' => '嶯', '�O' => '嶰', '�P' => '嶱', '�Q' => '嶲', '�R' => '嶳', '�S' => '嶴', '�T' => '嶵', '�U' => '嶶', '�V' => '嶸', '�W' => '嶹', '�X' => '嶺', '�Y' => '嶻', '�Z' => '嶼', '�[' => '嶽', '�\\' => '嶾', '�]' => '嶿', '�^' => '巀', '�_' => '巁', '�`' => '巂', '�a' => '巃', '�b' => '巄', '�c' => '巆', '�d' => '巇', '�e' => '巈', '�f' => '巉', '�g' => '巊', '�h' => '巋', '�i' => '巌', '�j' => '巎', '�k' => '巏', '�l' => '巐', '�m' => '巑', '�n' => '巒', '�o' => '巓', '�p' => '巔', '�q' => '巕', '�r' => '巖', '�s' => '巗', '�t' => '巘', '�u' => '巙', '�v' => '巚', '�w' => '巜', '�x' => '巟', '�y' => '巠', '�z' => '巣', '�{' => '巤', '�|' => '巪', '�}' => '巬', '�~' => '巭', '��' => '巰', '��' => '巵', '��' => '巶', '��' => '巸', '��' => '巹', '��' => '巺', '��' => '巻', '��' => '巼', '��' => '巿', '��' => '帀', '��' => '帄', '��' => '帇', '��' => '帉', '��' => '帊', '��' => '帋', '��' => '帍', '��' => '帎', '��' => '帒', '��' => '帓', '��' => '帗', '��' => '帞', '��' => '帟', '��' => '帠', '��' => '帡', '��' => '帢', '��' => '帣', '��' => '帤', '��' => '帥', '��' => '帨', '��' => '帩', '��' => '帪', '��' => '師', '��' => '帬', '��' => '帯', '��' => '帰', '��' => '帲', '��' => '帳', '��' => '帴', '��' => '帵', '��' => '帶', '��' => '帹', '��' => '帺', '��' => '帾', '��' => '帿', '��' => '幀', '��' => '幁', '��' => '幃', '��' => '幆', '��' => '幇', '��' => '幈', '��' => '幉', '��' => '幊', '��' => '幋', '��' => '幍', '��' => '幎', '��' => '幏', '��' => '幐', '��' => '幑', '��' => '幒', '��' => '幓', '��' => '幖', '��' => '幗', '��' => '幘', '��' => '幙', '��' => '幚', '��' => '幜', '��' => '幝', '��' => '幟', '��' => '幠', '��' => '幣', '��' => '幤', '��' => '幥', '��' => '幦', '��' => '幧', '��' => '幨', '��' => '幩', '��' => '幪', '��' => '幫', '��' => '幬', '��' => '幭', '��' => '幮', '��' => '幯', '��' => '幰', '��' => '幱', '��' => '幵', '��' => '幷', '��' => '幹', '��' => '幾', '��' => '庁', '��' => '庂', '��' => '広', '��' => '庅', '��' => '庈', '��' => '庉', '��' => '庌', '��' => '庍', '��' => '庎', '��' => '庒', '��' => '庘', '��' => '庛', '��' => '庝', '��' => '庡', '��' => '庢', '��' => '庣', '��' => '庤', '��' => '庨', '��' => '庩', '��' => '庪', '��' => '庫', '��' => '庬', '��' => '庮', '��' => '庯', '��' => '庰', '��' => '庱', '��' => '庲', '��' => '庴', '��' => '庺', '��' => '庻', '��' => '庼', '��' => '庽', '��' => '庿', '��' => '廀', '��' => '廁', '��' => '廂', '��' => '廃', '��' => '廄', '��' => '廅', '�@' => '廆', '�A' => '廇', '�B' => '廈', '�C' => '廋', '�D' => '廌', '�E' => '廍', '�F' => '廎', '�G' => '廏', '�H' => '廐', '�I' => '廔', '�J' => '廕', '�K' => '廗', '�L' => '廘', '�M' => '廙', '�N' => '廚', '�O' => '廜', '�P' => '廝', '�Q' => '廞', '�R' => '廟', '�S' => '廠', '�T' => '廡', '�U' => '廢', '�V' => '廣', '�W' => '廤', '�X' => '廥', '�Y' => '廦', '�Z' => '廧', '�[' => '廩', '�\\' => '廫', '�]' => '廬', '�^' => '廭', '�_' => '廮', '�`' => '廯', '�a' => '廰', '�b' => '廱', '�c' => '廲', '�d' => '廳', '�e' => '廵', '�f' => '廸', '�g' => '廹', '�h' => '廻', '�i' => '廼', '�j' => '廽', '�k' => '弅', '�l' => '弆', '�m' => '弇', '�n' => '弉', '�o' => '弌', '�p' => '弍', '�q' => '弎', '�r' => '弐', '�s' => '弒', '�t' => '弔', '�u' => '弖', '�v' => '弙', '�w' => '弚', '�x' => '弜', '�y' => '弝', '�z' => '弞', '�{' => '弡', '�|' => '弢', '�}' => '弣', '�~' => '弤', '��' => '弨', '��' => '弫', '��' => '弬', '��' => '弮', '��' => '弰', '��' => '弲', '��' => '弳', '��' => '弴', '��' => '張', '��' => '弶', '��' => '強', '��' => '弸', '��' => '弻', '��' => '弽', '��' => '弾', '��' => '弿', '��' => '彁', '��' => '彂', '��' => '彃', '��' => '彄', '��' => '彅', '��' => '彆', '��' => '彇', '��' => '彈', '��' => '彉', '��' => '彊', '��' => '彋', '��' => '彌', '��' => '彍', '��' => '彎', '��' => '彏', '��' => '彑', '��' => '彔', '��' => '彙', '��' => '彚', '��' => '彛', '��' => '彜', '��' => '彞', '��' => '彟', '��' => '彠', '��' => '彣', '��' => '彥', '��' => '彧', '��' => '彨', '��' => '彫', '��' => '彮', '��' => '彯', '��' => '彲', '��' => '彴', '��' => '彵', '��' => '彶', '��' => '彸', '��' => '彺', '��' => '彽', '��' => '彾', '��' => '彿', '��' => '徃', '��' => '徆', '��' => '徍', '��' => '徎', '��' => '徏', '��' => '徑', '��' => '従', '��' => '徔', '��' => '徖', '��' => '徚', '��' => '徛', '��' => '徝', '��' => '從', '��' => '徟', '��' => '徠', '��' => '徢', '��' => '徣', '��' => '徤', '��' => '徥', '��' => '徦', '��' => '徧', '��' => '復', '��' => '徫', '��' => '徬', '��' => '徯', '��' => '徰', '��' => '徱', '��' => '徲', '��' => '徳', '��' => '徴', '��' => '徶', '��' => '徸', '��' => '徹', '��' => '徺', '��' => '徻', '��' => '徾', '��' => '徿', '��' => '忀', '��' => '忁', '��' => '忂', '��' => '忇', '��' => '忈', '��' => '忊', '��' => '忋', '��' => '忎', '��' => '忓', '��' => '忔', '��' => '忕', '��' => '忚', '��' => '忛', '��' => '応', '��' => '忞', '��' => '忟', '��' => '忢', '��' => '忣', '��' => '忥', '��' => '忦', '��' => '忨', '��' => '忩', '��' => '忬', '��' => '忯', '��' => '忰', '��' => '忲', '��' => '忳', '��' => '忴', '��' => '忶', '��' => '忷', '��' => '忹', '��' => '忺', '��' => '忼', '��' => '怇', '�@' => '怈', '�A' => '怉', '�B' => '怋', '�C' => '怌', '�D' => '怐', '�E' => '怑', '�F' => '怓', '�G' => '怗', '�H' => '怘', '�I' => '怚', '�J' => '怞', '�K' => '怟', '�L' => '怢', '�M' => '怣', '�N' => '怤', '�O' => '怬', '�P' => '怭', '�Q' => '怮', '�R' => '怰', '�S' => '怱', '�T' => '怲', '�U' => '怳', '�V' => '怴', '�W' => '怶', '�X' => '怷', '�Y' => '怸', '�Z' => '怹', '�[' => '怺', '�\\' => '怽', '�]' => '怾', '�^' => '恀', '�_' => '恄', '�`' => '恅', '�a' => '恆', '�b' => '恇', '�c' => '恈', '�d' => '恉', '�e' => '恊', '�f' => '恌', '�g' => '恎', '�h' => '恏', '�i' => '恑', '�j' => '恓', '�k' => '恔', '�l' => '恖', '�m' => '恗', '�n' => '恘', '�o' => '恛', '�p' => '恜', '�q' => '恞', '�r' => '恟', '�s' => '恠', '�t' => '恡', '�u' => '恥', '�v' => '恦', '�w' => '恮', '�x' => '恱', '�y' => '恲', '�z' => '恴', '�{' => '恵', '�|' => '恷', '�}' => '恾', '�~' => '悀', '��' => '悁', '��' => '悂', '��' => '悅', '��' => '悆', '��' => '悇', '��' => '悈', '��' => '悊', '��' => '悋', '��' => '悎', '��' => '悏', '��' => '悐', '��' => '悑', '��' => '悓', '��' => '悕', '��' => '悗', '��' => '悘', '��' => '悙', '��' => '悜', '��' => '悞', '��' => '悡', '��' => '悢', '��' => '悤', '��' => '悥', '��' => '悧', '��' => '悩', '��' => '悪', '��' => '悮', '��' => '悰', '��' => '悳', '��' => '悵', '��' => '悶', '��' => '悷', '��' => '悹', '��' => '悺', '��' => '悽', '��' => '悾', '��' => '悿', '��' => '惀', '��' => '惁', '��' => '惂', '��' => '惃', '��' => '惄', '��' => '惇', '��' => '惈', '��' => '惉', '��' => '惌', '��' => '惍', '��' => '惎', '��' => '惏', '��' => '惐', '��' => '惒', '��' => '惓', '��' => '惔', '��' => '惖', '��' => '惗', '��' => '惙', '��' => '惛', '��' => '惞', '��' => '惡', '��' => '惢', '��' => '惣', '��' => '惤', '��' => '惥', '��' => '惪', '��' => '惱', '��' => '惲', '��' => '惵', '��' => '惷', '��' => '惸', '��' => '惻', '��' => '惼', '��' => '惽', '��' => '惾', '��' => '惿', '��' => '愂', '��' => '愃', '��' => '愄', '��' => '愅', '��' => '愇', '��' => '愊', '��' => '愋', '��' => '愌', '��' => '愐', '��' => '愑', '��' => '愒', '��' => '愓', '��' => '愔', '��' => '愖', '��' => '愗', '��' => '愘', '��' => '愙', '��' => '愛', '��' => '愜', '��' => '愝', '��' => '愞', '��' => '愡', '��' => '愢', '��' => '愥', '��' => '愨', '��' => '愩', '��' => '愪', '��' => '愬', '��' => '愭', '��' => '愮', '��' => '愯', '��' => '愰', '��' => '愱', '��' => '愲', '��' => '愳', '��' => '愴', '��' => '愵', '��' => '愶', '��' => '愷', '��' => '愸', '��' => '愹', '��' => '愺', '��' => '愻', '��' => '愼', '��' => '愽', '��' => '愾', '��' => '慀', '��' => '慁', '��' => '慂', '��' => '慃', '��' => '慄', '��' => '慅', '��' => '慆', '�@' => '慇', '�A' => '慉', '�B' => '態', '�C' => '慍', '�D' => '慏', '�E' => '慐', '�F' => '慒', '�G' => '慓', '�H' => '慔', '�I' => '慖', '�J' => '慗', '�K' => '慘', '�L' => '慙', '�M' => '慚', '�N' => '慛', '�O' => '慜', '�P' => '慞', '�Q' => '慟', '�R' => '慠', '�S' => '慡', '�T' => '慣', '�U' => '慤', '�V' => '慥', '�W' => '慦', '�X' => '慩', '�Y' => '慪', '�Z' => '慫', '�[' => '慬', '�\\' => '慭', '�]' => '慮', '�^' => '慯', '�_' => '慱', '�`' => '慲', '�a' => '慳', '�b' => '慴', '�c' => '慶', '�d' => '慸', '�e' => '慹', '�f' => '慺', '�g' => '慻', '�h' => '慼', '�i' => '慽', '�j' => '慾', '�k' => '慿', '�l' => '憀', '�m' => '憁', '�n' => '憂', '�o' => '憃', '�p' => '憄', '�q' => '憅', '�r' => '憆', '�s' => '憇', '�t' => '憈', '�u' => '憉', '�v' => '憊', '�w' => '憌', '�x' => '憍', '�y' => '憏', '�z' => '憐', '�{' => '憑', '�|' => '憒', '�}' => '憓', '�~' => '憕', '��' => '憖', '��' => '憗', '��' => '憘', '��' => '憙', '��' => '憚', '��' => '憛', '��' => '憜', '��' => '憞', '��' => '憟', '��' => '憠', '��' => '憡', '��' => '憢', '��' => '憣', '��' => '憤', '��' => '憥', '��' => '憦', '��' => '憪', '��' => '憫', '��' => '憭', '��' => '憮', '��' => '憯', '��' => '憰', '��' => '憱', '��' => '憲', '��' => '憳', '��' => '憴', '��' => '憵', '��' => '憶', '��' => '憸', '��' => '憹', '��' => '憺', '��' => '憻', '��' => '憼', '��' => '憽', '��' => '憿', '��' => '懀', '��' => '懁', '��' => '懃', '��' => '懄', '��' => '懅', '��' => '懆', '��' => '懇', '��' => '應', '��' => '懌', '��' => '懍', '��' => '懎', '��' => '懏', '��' => '懐', '��' => '懓', '��' => '懕', '��' => '懖', '��' => '懗', '��' => '懘', '��' => '懙', '��' => '懚', '��' => '懛', '��' => '懜', '��' => '懝', '��' => '懞', '��' => '懟', '��' => '懠', '��' => '懡', '��' => '懢', '��' => '懣', '��' => '懤', '��' => '懥', '��' => '懧', '��' => '懨', '��' => '懩', '��' => '懪', '��' => '懫', '��' => '懬', '��' => '懭', '��' => '懮', '��' => '懯', '��' => '懰', '��' => '懱', '��' => '懲', '��' => '懳', '��' => '懴', '��' => '懶', '��' => '懷', '��' => '懸', '��' => '懹', '��' => '懺', '��' => '懻', '��' => '懼', '��' => '懽', '��' => '懾', '��' => '戀', '��' => '戁', '��' => '戂', '��' => '戃', '��' => '戄', '��' => '戅', '��' => '戇', '��' => '戉', '��' => '戓', '��' => '戔', '��' => '戙', '��' => '戜', '��' => '戝', '��' => '戞', '��' => '戠', '��' => '戣', '��' => '戦', '��' => '戧', '��' => '戨', '��' => '戩', '��' => '戫', '��' => '戭', '��' => '戯', '��' => '戰', '��' => '戱', '��' => '戲', '��' => '戵', '��' => '戶', '��' => '戸', '��' => '戹', '��' => '戺', '��' => '戻', '��' => '戼', '��' => '扂', '��' => '扄', '��' => '扅', '��' => '扆', '��' => '扊', '�@' => '扏', '�A' => '扐', '�B' => '払', '�C' => '扖', '�D' => '扗', '�E' => '扙', '�F' => '扚', '�G' => '扜', '�H' => '扝', '�I' => '扞', '�J' => '扟', '�K' => '扠', '�L' => '扡', '�M' => '扢', '�N' => '扤', '�O' => '扥', '�P' => '扨', '�Q' => '扱', '�R' => '扲', '�S' => '扴', '�T' => '扵', '�U' => '扷', '�V' => '扸', '�W' => '扺', '�X' => '扻', '�Y' => '扽', '�Z' => '抁', '�[' => '抂', '�\\' => '抃', '�]' => '抅', '�^' => '抆', '�_' => '抇', '�`' => '抈', '�a' => '抋', '�b' => '抌', '�c' => '抍', '�d' => '抎', '�e' => '抏', '�f' => '抐', '�g' => '抔', '�h' => '抙', '�i' => '抜', '�j' => '抝', '�k' => '択', '�l' => '抣', '�m' => '抦', '�n' => '抧', '�o' => '抩', '�p' => '抪', '�q' => '抭', '�r' => '抮', '�s' => '抯', '�t' => '抰', '�u' => '抲', '�v' => '抳', '�w' => '抴', '�x' => '抶', '�y' => '抷', '�z' => '抸', '�{' => '抺', '�|' => '抾', '�}' => '拀', '�~' => '拁', '��' => '拃', '��' => '拋', '��' => '拏', '��' => '拑', '��' => '拕', '��' => '拝', '��' => '拞', '��' => '拠', '��' => '拡', '��' => '拤', '��' => '拪', '��' => '拫', '��' => '拰', '��' => '拲', '��' => '拵', '��' => '拸', '��' => '拹', '��' => '拺', '��' => '拻', '��' => '挀', '��' => '挃', '��' => '挄', '��' => '挅', '��' => '挆', '��' => '挊', '��' => '挋', '��' => '挌', '��' => '挍', '��' => '挏', '��' => '挐', '��' => '挒', '��' => '挓', '��' => '挔', '��' => '挕', '��' => '挗', '��' => '挘', '��' => '挙', '��' => '挜', '��' => '挦', '��' => '挧', '��' => '挩', '��' => '挬', '��' => '挭', '��' => '挮', '��' => '挰', '��' => '挱', '��' => '挳', '��' => '挴', '��' => '挵', '��' => '挶', '��' => '挷', '��' => '挸', '��' => '挻', '��' => '挼', '��' => '挾', '��' => '挿', '��' => '捀', '��' => '捁', '��' => '捄', '��' => '捇', '��' => '捈', '��' => '捊', '��' => '捑', '��' => '捒', '��' => '捓', '��' => '捔', '��' => '捖', '��' => '捗', '��' => '捘', '��' => '捙', '��' => '捚', '��' => '捛', '��' => '捜', '��' => '捝', '��' => '捠', '��' => '捤', '��' => '捥', '��' => '捦', '��' => '捨', '��' => '捪', '��' => '捫', '��' => '捬', '��' => '捯', '��' => '捰', '��' => '捲', '��' => '捳', '��' => '捴', '��' => '捵', '��' => '捸', '��' => '捹', '��' => '捼', '��' => '捽', '��' => '捾', '��' => '捿', '��' => '掁', '��' => '掃', '��' => '掄', '��' => '掅', '��' => '掆', '��' => '掋', '��' => '掍', '��' => '掑', '��' => '掓', '��' => '掔', '��' => '掕', '��' => '掗', '��' => '掙', '��' => '掚', '��' => '掛', '��' => '掜', '��' => '掝', '��' => '掞', '��' => '掟', '��' => '採', '��' => '掤', '��' => '掦', '��' => '掫', '��' => '掯', '��' => '掱', '��' => '掲', '��' => '掵', '��' => '掶', '��' => '掹', '��' => '掻', '��' => '掽', '��' => '掿', '��' => '揀', '�@' => '揁', '�A' => '揂', '�B' => '揃', '�C' => '揅', '�D' => '揇', '�E' => '揈', '�F' => '揊', '�G' => '揋', '�H' => '揌', '�I' => '揑', '�J' => '揓', '�K' => '揔', '�L' => '揕', '�M' => '揗', '�N' => '揘', '�O' => '揙', '�P' => '揚', '�Q' => '換', '�R' => '揜', '�S' => '揝', '�T' => '揟', '�U' => '揢', '�V' => '揤', '�W' => '揥', '�X' => '揦', '�Y' => '揧', '�Z' => '揨', '�[' => '揫', '�\\' => '揬', '�]' => '揮', '�^' => '揯', '�_' => '揰', '�`' => '揱', '�a' => '揳', '�b' => '揵', '�c' => '揷', '�d' => '揹', '�e' => '揺', '�f' => '揻', '�g' => '揼', '�h' => '揾', '�i' => '搃', '�j' => '搄', '�k' => '搆', '�l' => '搇', '�m' => '搈', '�n' => '搉', '�o' => '搊', '�p' => '損', '�q' => '搎', '�r' => '搑', '�s' => '搒', '�t' => '搕', '�u' => '搖', '�v' => '搗', '�w' => '搘', '�x' => '搙', '�y' => '搚', '�z' => '搝', '�{' => '搟', '�|' => '搢', '�}' => '搣', '�~' => '搤', '��' => '搥', '��' => '搧', '��' => '搨', '��' => '搩', '��' => '搫', '��' => '搮', '��' => '搯', '��' => '搰', '��' => '搱', '��' => '搲', '��' => '搳', '��' => '搵', '��' => '搶', '��' => '搷', '��' => '搸', '��' => '搹', '��' => '搻', '��' => '搼', '��' => '搾', '��' => '摀', '��' => '摂', '��' => '摃', '��' => '摉', '��' => '摋', '��' => '摌', '��' => '摍', '��' => '摎', '��' => '摏', '��' => '摐', '��' => '摑', '��' => '摓', '��' => '摕', '��' => '摖', '��' => '摗', '��' => '摙', '��' => '摚', '��' => '摛', '��' => '摜', '��' => '摝', '��' => '摟', '��' => '摠', '��' => '摡', '��' => '摢', '��' => '摣', '��' => '摤', '��' => '摥', '��' => '摦', '��' => '摨', '��' => '摪', '��' => '摫', '��' => '摬', '��' => '摮', '��' => '摯', '��' => '摰', '��' => '摱', '��' => '摲', '��' => '摳', '��' => '摴', '��' => '摵', '��' => '摶', '��' => '摷', '��' => '摻', '��' => '摼', '��' => '摽', '��' => '摾', '��' => '摿', '��' => '撀', '��' => '撁', '��' => '撃', '��' => '撆', '��' => '撈', '��' => '撉', '��' => '撊', '��' => '撋', '��' => '撌', '��' => '撍', '��' => '撎', '��' => '撏', '��' => '撐', '��' => '撓', '��' => '撔', '��' => '撗', '��' => '撘', '��' => '撚', '��' => '撛', '��' => '撜', '��' => '撝', '��' => '撟', '��' => '撠', '��' => '撡', '��' => '撢', '��' => '撣', '��' => '撥', '��' => '撦', '��' => '撧', '��' => '撨', '��' => '撪', '��' => '撫', '��' => '撯', '��' => '撱', '��' => '撲', '��' => '撳', '��' => '撴', '��' => '撶', '��' => '撹', '��' => '撻', '��' => '撽', '��' => '撾', '��' => '撿', '��' => '擁', '��' => '擃', '��' => '擄', '��' => '擆', '��' => '擇', '��' => '擈', '��' => '擉', '��' => '擊', '��' => '擋', '��' => '擌', '��' => '擏', '��' => '擑', '��' => '擓', '��' => '擔', '��' => '擕', '��' => '擖', '��' => '擙', '��' => '據', '�@' => '擛', '�A' => '擜', '�B' => '擝', '�C' => '擟', '�D' => '擠', '�E' => '擡', '�F' => '擣', '�G' => '擥', '�H' => '擧', '�I' => '擨', '�J' => '擩', '�K' => '擪', '�L' => '擫', '�M' => '擬', '�N' => '擭', '�O' => '擮', '�P' => '擯', '�Q' => '擰', '�R' => '擱', '�S' => '擲', '�T' => '擳', '�U' => '擴', '�V' => '擵', '�W' => '擶', '�X' => '擷', '�Y' => '擸', '�Z' => '擹', '�[' => '擺', '�\\' => '擻', '�]' => '擼', '�^' => '擽', '�_' => '擾', '�`' => '擿', '�a' => '攁', '�b' => '攂', '�c' => '攃', '�d' => '攄', '�e' => '攅', '�f' => '攆', '�g' => '攇', '�h' => '攈', '�i' => '攊', '�j' => '攋', '�k' => '攌', '�l' => '攍', '�m' => '攎', '�n' => '攏', '�o' => '攐', '�p' => '攑', '�q' => '攓', '�r' => '攔', '�s' => '攕', '�t' => '攖', '�u' => '攗', '�v' => '攙', '�w' => '攚', '�x' => '攛', '�y' => '攜', '�z' => '攝', '�{' => '攞', '�|' => '攟', '�}' => '攠', '�~' => '攡', '��' => '攢', '��' => '攣', '��' => '攤', '��' => '攦', '��' => '攧', '��' => '攨', '��' => '攩', '��' => '攪', '��' => '攬', '��' => '攭', '��' => '攰', '��' => '攱', '��' => '攲', '��' => '攳', '��' => '攷', '��' => '攺', '��' => '攼', '��' => '攽', '��' => '敀', '��' => '敁', '��' => '敂', '��' => '敃', '��' => '敄', '��' => '敆', '��' => '敇', '��' => '敊', '��' => '敋', '��' => '敍', '��' => '敎', '��' => '敐', '��' => '敒', '��' => '敓', '��' => '敔', '��' => '敗', '��' => '敘', '��' => '敚', '��' => '敜', '��' => '敟', '��' => '敠', '��' => '敡', '��' => '敤', '��' => '敥', '��' => '敧', '��' => '敨', '��' => '敩', '��' => '敪', '��' => '敭', '��' => '敮', '��' => '敯', '��' => '敱', '��' => '敳', '��' => '敵', '��' => '敶', '��' => '數', '��' => '敹', '��' => '敺', '��' => '敻', '��' => '敼', '��' => '敽', '��' => '敾', '��' => '敿', '��' => '斀', '��' => '斁', '��' => '斂', '��' => '斃', '��' => '斄', '��' => '斅', '��' => '斆', '��' => '斈', '��' => '斉', '��' => '斊', '��' => '斍', '��' => '斎', '��' => '斏', '��' => '斒', '��' => '斔', '��' => '斕', '��' => '斖', '��' => '斘', '��' => '斚', '��' => '斝', '��' => '斞', '��' => '斠', '��' => '斢', '��' => '斣', '��' => '斦', '��' => '斨', '��' => '斪', '��' => '斬', '��' => '斮', '��' => '斱', '��' => '斲', '��' => '斳', '��' => '斴', '��' => '斵', '��' => '斶', '��' => '斷', '��' => '斸', '��' => '斺', '��' => '斻', '��' => '斾', '��' => '斿', '��' => '旀', '��' => '旂', '��' => '旇', '��' => '旈', '��' => '旉', '��' => '旊', '��' => '旍', '��' => '旐', '��' => '旑', '��' => '旓', '��' => '旔', '��' => '旕', '��' => '旘', '��' => '旙', '��' => '旚', '��' => '旛', '��' => '旜', '��' => '旝', '��' => '旞', '��' => '旟', '��' => '旡', '��' => '旣', '��' => '旤', '��' => '旪', '��' => '旫', '�@' => '旲', '�A' => '旳', '�B' => '旴', '�C' => '旵', '�D' => '旸', '�E' => '旹', '�F' => '旻', '�G' => '旼', '�H' => '旽', '�I' => '旾', '�J' => '旿', '�K' => '昁', '�L' => '昄', '�M' => '昅', '�N' => '昇', '�O' => '昈', '�P' => '昉', '�Q' => '昋', '�R' => '昍', '�S' => '昐', '�T' => '昑', '�U' => '昒', '�V' => '昖', '�W' => '昗', '�X' => '昘', '�Y' => '昚', '�Z' => '昛', '�[' => '昜', '�\\' => '昞', '�]' => '昡', '�^' => '昢', '�_' => '昣', '�`' => '昤', '�a' => '昦', '�b' => '昩', '�c' => '昪', '�d' => '昫', '�e' => '昬', '�f' => '昮', '�g' => '昰', '�h' => '昲', '�i' => '昳', '�j' => '昷', '�k' => '昸', '�l' => '昹', '�m' => '昺', '�n' => '昻', '�o' => '昽', '�p' => '昿', '�q' => '晀', '�r' => '時', '�s' => '晄', '�t' => '晅', '�u' => '晆', '�v' => '晇', '�w' => '晈', '�x' => '晉', '�y' => '晊', '�z' => '晍', '�{' => '晎', '�|' => '晐', '�}' => '晑', '�~' => '晘', '��' => '晙', '��' => '晛', '��' => '晜', '��' => '晝', '��' => '晞', '��' => '晠', '��' => '晢', '��' => '晣', '��' => '晥', '��' => '晧', '��' => '晩', '��' => '晪', '��' => '晫', '��' => '晬', '��' => '晭', '��' => '晱', '��' => '晲', '��' => '晳', '��' => '晵', '��' => '晸', '��' => '晹', '��' => '晻', '��' => '晼', '��' => '晽', '��' => '晿', '��' => '暀', '��' => '暁', '��' => '暃', '��' => '暅', '��' => '暆', '��' => '暈', '��' => '暉', '��' => '暊', '��' => '暋', '��' => '暍', '��' => '暎', '��' => '暏', '��' => '暐', '��' => '暒', '��' => '暓', '��' => '暔', '��' => '暕', '��' => '暘', '��' => '暙', '��' => '暚', '��' => '暛', '��' => '暜', '��' => '暞', '��' => '暟', '��' => '暠', '��' => '暡', '��' => '暢', '��' => '暣', '��' => '暤', '��' => '暥', '��' => '暦', '��' => '暩', '��' => '暪', '��' => '暫', '��' => '暬', '��' => '暭', '��' => '暯', '��' => '暰', '��' => '暱', '��' => '暲', '��' => '暳', '��' => '暵', '��' => '暶', '��' => '暷', '��' => '暸', '��' => '暺', '��' => '暻', '��' => '暼', '��' => '暽', '��' => '暿', '��' => '曀', '��' => '曁', '��' => '曂', '��' => '曃', '��' => '曄', '��' => '曅', '��' => '曆', '��' => '曇', '��' => '曈', '��' => '曉', '��' => '曊', '��' => '曋', '��' => '曌', '��' => '曍', '��' => '曎', '��' => '曏', '��' => '曐', '��' => '曑', '��' => '曒', '��' => '曓', '��' => '曔', '��' => '曕', '��' => '曖', '��' => '曗', '��' => '曘', '��' => '曚', '��' => '曞', '��' => '曟', '��' => '曠', '��' => '曡', '��' => '曢', '��' => '曣', '��' => '曤', '��' => '曥', '��' => '曧', '��' => '曨', '��' => '曪', '��' => '曫', '��' => '曬', '��' => '曭', '��' => '曮', '��' => '曯', '��' => '曱', '��' => '曵', '��' => '曶', '��' => '書', '��' => '曺', '��' => '曻', '��' => '曽', '��' => '朁', '��' => '朂', '��' => '會', '�@' => '朄', '�A' => '朅', '�B' => '朆', '�C' => '朇', '�D' => '朌', '�E' => '朎', '�F' => '朏', '�G' => '朑', '�H' => '朒', '�I' => '朓', '�J' => '朖', '�K' => '朘', '�L' => '朙', '�M' => '朚', '�N' => '朜', '�O' => '朞', '�P' => '朠', '�Q' => '朡', '�R' => '朢', '�S' => '朣', '�T' => '朤', '�U' => '朥', '�V' => '朧', '�W' => '朩', '�X' => '朮', '�Y' => '朰', '�Z' => '朲', '�[' => '朳', '�\\' => '朶', '�]' => '朷', '�^' => '朸', '�_' => '朹', '�`' => '朻', '�a' => '朼', '�b' => '朾', '�c' => '朿', '�d' => '杁', '�e' => '杄', '�f' => '杅', '�g' => '杇', '�h' => '杊', '�i' => '杋', '�j' => '杍', '�k' => '杒', '�l' => '杔', '�m' => '杕', '�n' => '杗', '�o' => '杘', '�p' => '杙', '�q' => '杚', '�r' => '杛', '�s' => '杝', '�t' => '杢', '�u' => '杣', '�v' => '杤', '�w' => '杦', '�x' => '杧', '�y' => '杫', '�z' => '杬', '�{' => '杮', '�|' => '東', '�}' => '杴', '�~' => '杶', '��' => '杸', '��' => '杹', '��' => '杺', '��' => '杻', '��' => '杽', '��' => '枀', '��' => '枂', '��' => '枃', '��' => '枅', '��' => '枆', '��' => '枈', '��' => '枊', '��' => '枌', '��' => '枍', '��' => '枎', '��' => '枏', '��' => '枑', '��' => '枒', '��' => '枓', '��' => '枔', '��' => '枖', '��' => '枙', '��' => '枛', '��' => '枟', '��' => '枠', '��' => '枡', '��' => '枤', '��' => '枦', '��' => '枩', '��' => '枬', '��' => '枮', '��' => '枱', '��' => '枲', '��' => '枴', '��' => '枹', '��' => '枺', '��' => '枻', '��' => '枼', '��' => '枽', '��' => '枾', '��' => '枿', '��' => '柀', '��' => '柂', '��' => '柅', '��' => '柆', '��' => '柇', '��' => '柈', '��' => '柉', '��' => '柊', '��' => '柋', '��' => '柌', '��' => '柍', '��' => '柎', '��' => '柕', '��' => '柖', '��' => '柗', '��' => '柛', '��' => '柟', '��' => '柡', '��' => '柣', '��' => '柤', '��' => '柦', '��' => '柧', '��' => '柨', '��' => '柪', '��' => '柫', '��' => '柭', '��' => '柮', '��' => '柲', '��' => '柵', '��' => '柶', '��' => '柷', '��' => '柸', '��' => '柹', '��' => '柺', '��' => '査', '��' => '柼', '��' => '柾', '��' => '栁', '��' => '栂', '��' => '栃', '��' => '栄', '��' => '栆', '��' => '栍', '��' => '栐', '��' => '栒', '��' => '栔', '��' => '栕', '��' => '栘', '��' => '栙', '��' => '栚', '��' => '栛', '��' => '栜', '��' => '栞', '��' => '栟', '��' => '栠', '��' => '栢', '��' => '栣', '��' => '栤', '��' => '栥', '��' => '栦', '��' => '栧', '��' => '栨', '��' => '栫', '��' => '栬', '��' => '栭', '��' => '栮', '��' => '栯', '��' => '栰', '��' => '栱', '��' => '栴', '��' => '栵', '��' => '栶', '��' => '栺', '��' => '栻', '��' => '栿', '��' => '桇', '��' => '桋', '��' => '桍', '��' => '桏', '��' => '桒', '��' => '桖', '��' => '桗', '��' => '桘', '��' => '桙', '��' => '桚', '��' => '桛', '�@' => '桜', '�A' => '桝', '�B' => '桞', '�C' => '桟', '�D' => '桪', '�E' => '桬', '�F' => '桭', '�G' => '桮', '�H' => '桯', '�I' => '桰', '�J' => '桱', '�K' => '桲', '�L' => '桳', '�M' => '桵', '�N' => '桸', '�O' => '桹', '�P' => '桺', '�Q' => '桻', '�R' => '桼', '�S' => '桽', '�T' => '桾', '�U' => '桿', '�V' => '梀', '�W' => '梂', '�X' => '梄', '�Y' => '梇', '�Z' => '梈', '�[' => '梉', '�\\' => '梊', '�]' => '梋', '�^' => '梌', '�_' => '梍', '�`' => '梎', '�a' => '梐', '�b' => '梑', '�c' => '梒', '�d' => '梔', '�e' => '梕', '�f' => '梖', '�g' => '梘', '�h' => '梙', '�i' => '梚', '�j' => '梛', '�k' => '梜', '�l' => '條', '�m' => '梞', '�n' => '梟', '�o' => '梠', '�p' => '梡', '�q' => '梣', '�r' => '梤', '�s' => '梥', '�t' => '梩', '�u' => '梪', '�v' => '梫', '�w' => '梬', '�x' => '梮', '�y' => '梱', '�z' => '梲', '�{' => '梴', '�|' => '梶', '�}' => '梷', '�~' => '梸', '��' => '梹', '��' => '梺', '��' => '梻', '��' => '梼', '��' => '梽', '��' => '梾', '��' => '梿', '��' => '棁', '��' => '棃', '��' => '棄', '��' => '棅', '��' => '棆', '��' => '棇', '��' => '棈', '��' => '棊', '��' => '棌', '��' => '棎', '��' => '棏', '��' => '棐', '��' => '棑', '��' => '棓', '��' => '棔', '��' => '棖', '��' => '棗', '��' => '棙', '��' => '棛', '��' => '棜', '��' => '棝', '��' => '棞', '��' => '棟', '��' => '棡', '��' => '棢', '��' => '棤', '��' => '棥', '��' => '棦', '��' => '棧', '��' => '棨', '��' => '棩', '��' => '棪', '��' => '棫', '��' => '棬', '��' => '棭', '��' => '棯', '��' => '棲', '��' => '棳', '��' => '棴', '��' => '棶', '��' => '棷', '��' => '棸', '��' => '棻', '��' => '棽', '��' => '棾', '��' => '棿', '��' => '椀', '��' => '椂', '��' => '椃', '��' => '椄', '��' => '椆', '��' => '椇', '��' => '椈', '��' => '椉', '��' => '椊', '��' => '椌', '��' => '椏', '��' => '椑', '��' => '椓', '��' => '椔', '��' => '椕', '��' => '椖', '��' => '椗', '��' => '椘', '��' => '椙', '��' => '椚', '��' => '椛', '��' => '検', '��' => '椝', '��' => '椞', '��' => '椡', '��' => '椢', '��' => '椣', '��' => '椥', '��' => '椦', '��' => '椧', '��' => '椨', '��' => '椩', '��' => '椪', '��' => '椫', '��' => '椬', '��' => '椮', '��' => '椯', '��' => '椱', '��' => '椲', '��' => '椳', '��' => '椵', '��' => '椶', '��' => '椷', '��' => '椸', '��' => '椺', '��' => '椻', '��' => '椼', '��' => '椾', '��' => '楀', '��' => '楁', '��' => '楃', '��' => '楄', '��' => '楅', '��' => '楆', '��' => '楇', '��' => '楈', '��' => '楉', '��' => '楊', '��' => '楋', '��' => '楌', '��' => '楍', '��' => '楎', '��' => '楏', '��' => '楐', '��' => '楑', '��' => '楒', '��' => '楓', '��' => '楕', '��' => '楖', '��' => '楘', '��' => '楙', '��' => '楛', '��' => '楜', '��' => '楟', '�@' => '楡', '�A' => '楢', '�B' => '楤', '�C' => '楥', '�D' => '楧', '�E' => '楨', '�F' => '楩', '�G' => '楪', '�H' => '楬', '�I' => '業', '�J' => '楯', '�K' => '楰', '�L' => '楲', '�M' => '楳', '�N' => '楴', '�O' => '極', '�P' => '楶', '�Q' => '楺', '�R' => '楻', '�S' => '楽', '�T' => '楾', '�U' => '楿', '�V' => '榁', '�W' => '榃', '�X' => '榅', '�Y' => '榊', '�Z' => '榋', '�[' => '榌', '�\\' => '榎', '�]' => '榏', '�^' => '榐', '�_' => '榑', '�`' => '榒', '�a' => '榓', '�b' => '榖', '�c' => '榗', '�d' => '榙', '�e' => '榚', '�f' => '榝', '�g' => '榞', '�h' => '榟', '�i' => '榠', '�j' => '榡', '�k' => '榢', '�l' => '榣', '�m' => '榤', '�n' => '榥', '�o' => '榦', '�p' => '榩', '�q' => '榪', '�r' => '榬', '�s' => '榮', '�t' => '榯', '�u' => '榰', '�v' => '榲', '�w' => '榳', '�x' => '榵', '�y' => '榶', '�z' => '榸', '�{' => '榹', '�|' => '榺', '�}' => '榼', '�~' => '榽', '��' => '榾', '��' => '榿', '��' => '槀', '��' => '槂', '��' => '槃', '��' => '槄', '��' => '槅', '��' => '槆', '��' => '槇', '��' => '槈', '��' => '槉', '��' => '構', '��' => '槍', '��' => '槏', '��' => '槑', '��' => '槒', '��' => '槓', '��' => '槕', '��' => '槖', '��' => '槗', '��' => '様', '��' => '槙', '��' => '槚', '��' => '槜', '��' => '槝', '��' => '槞', '��' => '槡', '��' => '槢', '��' => '槣', '��' => '槤', '��' => '槥', '��' => '槦', '��' => '槧', '��' => '槨', '��' => '槩', '��' => '槪', '��' => '槫', '��' => '槬', '��' => '槮', '��' => '槯', '��' => '槰', '��' => '槱', '��' => '槳', '��' => '槴', '��' => '槵', '��' => '槶', '��' => '槷', '��' => '槸', '��' => '槹', '��' => '槺', '��' => '槻', '��' => '槼', '��' => '槾', '��' => '樀', '��' => '樁', '��' => '樂', '��' => '樃', '��' => '樄', '��' => '樅', '��' => '樆', '��' => '樇', '��' => '樈', '��' => '樉', '��' => '樋', '��' => '樌', '��' => '樍', '��' => '樎', '��' => '樏', '��' => '樐', '��' => '樑', '��' => '樒', '��' => '樓', '��' => '樔', '��' => '樕', '��' => '樖', '��' => '標', '��' => '樚', '��' => '樛', '��' => '樜', '��' => '樝', '��' => '樞', '��' => '樠', '��' => '樢', '��' => '樣', '��' => '樤', '��' => '樥', '��' => '樦', '��' => '樧', '��' => '権', '��' => '樫', '��' => '樬', '��' => '樭', '��' => '樮', '��' => '樰', '��' => '樲', '��' => '樳', '��' => '樴', '��' => '樶', '��' => '樷', '��' => '樸', '��' => '樹', '��' => '樺', '��' => '樻', '��' => '樼', '��' => '樿', '��' => '橀', '��' => '橁', '��' => '橂', '��' => '橃', '��' => '橅', '��' => '橆', '��' => '橈', '��' => '橉', '��' => '橊', '��' => '橋', '��' => '橌', '��' => '橍', '��' => '橎', '��' => '橏', '��' => '橑', '��' => '橒', '��' => '橓', '��' => '橔', '��' => '橕', '��' => '橖', '��' => '橗', '��' => '橚', '�@' => '橜', '�A' => '橝', '�B' => '橞', '�C' => '機', '�D' => '橠', '�E' => '橢', '�F' => '橣', '�G' => '橤', '�H' => '橦', '�I' => '橧', '�J' => '橨', '�K' => '橩', '�L' => '橪', '�M' => '橫', '�N' => '橬', '�O' => '橭', '�P' => '橮', '�Q' => '橯', '�R' => '橰', '�S' => '橲', '�T' => '橳', '�U' => '橴', '�V' => '橵', '�W' => '橶', '�X' => '橷', '�Y' => '橸', '�Z' => '橺', '�[' => '橻', '�\\' => '橽', '�]' => '橾', '�^' => '橿', '�_' => '檁', '�`' => '檂', '�a' => '檃', '�b' => '檅', '�c' => '檆', '�d' => '檇', '�e' => '檈', '�f' => '檉', '�g' => '檊', '�h' => '檋', '�i' => '檌', '�j' => '檍', '�k' => '檏', '�l' => '檒', '�m' => '檓', '�n' => '檔', '�o' => '檕', '�p' => '檖', '�q' => '檘', '�r' => '檙', '�s' => '檚', '�t' => '檛', '�u' => '檜', '�v' => '檝', '�w' => '檞', '�x' => '檟', '�y' => '檡', '�z' => '檢', '�{' => '檣', '�|' => '檤', '�}' => '檥', '�~' => '檦', '��' => '檧', '��' => '檨', '��' => '檪', '��' => '檭', '��' => '檮', '��' => '檯', '��' => '檰', '��' => '檱', '��' => '檲', '��' => '檳', '��' => '檴', '��' => '檵', '��' => '檶', '��' => '檷', '��' => '檸', '��' => '檹', '��' => '檺', '��' => '檻', '��' => '檼', '��' => '檽', '��' => '檾', '��' => '檿', '��' => '櫀', '��' => '櫁', '��' => '櫂', '��' => '櫃', '��' => '櫄', '��' => '櫅', '��' => '櫆', '��' => '櫇', '��' => '櫈', '��' => '櫉', '��' => '櫊', '��' => '櫋', '��' => '櫌', '��' => '櫍', '��' => '櫎', '��' => '櫏', '��' => '櫐', '��' => '櫑', '��' => '櫒', '��' => '櫓', '��' => '櫔', '��' => '櫕', '��' => '櫖', '��' => '櫗', '��' => '櫘', '��' => '櫙', '��' => '櫚', '��' => '櫛', '��' => '櫜', '��' => '櫝', '��' => '櫞', '��' => '櫟', '��' => '櫠', '��' => '櫡', '��' => '櫢', '��' => '櫣', '��' => '櫤', '��' => '櫥', '��' => '櫦', '��' => '櫧', '��' => '櫨', '��' => '櫩', '��' => '櫪', '��' => '櫫', '��' => '櫬', '��' => '櫭', '��' => '櫮', '��' => '櫯', '��' => '櫰', '��' => '櫱', '��' => '櫲', '��' => '櫳', '��' => '櫴', '��' => '櫵', '��' => '櫶', '��' => '櫷', '��' => '櫸', '��' => '櫹', '��' => '櫺', '��' => '櫻', '��' => '櫼', '��' => '櫽', '��' => '櫾', '��' => '櫿', '��' => '欀', '��' => '欁', '��' => '欂', '��' => '欃', '��' => '欄', '��' => '欅', '��' => '欆', '��' => '欇', '��' => '欈', '��' => '欉', '��' => '權', '��' => '欋', '��' => '欌', '��' => '欍', '��' => '欎', '��' => '欏', '��' => '欐', '��' => '欑', '��' => '欒', '��' => '欓', '��' => '欔', '��' => '欕', '��' => '欖', '��' => '欗', '��' => '欘', '��' => '欙', '��' => '欚', '��' => '欛', '��' => '欜', '��' => '欝', '��' => '欞', '��' => '欟', '��' => '欥', '��' => '欦', '��' => '欨', '��' => '欩', '��' => '欪', '��' => '欫', '��' => '欬', '��' => '欭', '��' => '欮', '�@' => '欯', '�A' => '欰', '�B' => '欱', '�C' => '欳', '�D' => '欴', '�E' => '欵', '�F' => '欶', '�G' => '欸', '�H' => '欻', '�I' => '欼', '�J' => '欽', '�K' => '欿', '�L' => '歀', '�M' => '歁', '�N' => '歂', '�O' => '歄', '�P' => '歅', '�Q' => '歈', '�R' => '歊', '�S' => '歋', '�T' => '歍', '�U' => '歎', '�V' => '歏', '�W' => '歐', '�X' => '歑', '�Y' => '歒', '�Z' => '歓', '�[' => '歔', '�\\' => '歕', '�]' => '歖', '�^' => '歗', '�_' => '歘', '�`' => '歚', '�a' => '歛', '�b' => '歜', '�c' => '歝', '�d' => '歞', '�e' => '歟', '�f' => '歠', '�g' => '歡', '�h' => '歨', '�i' => '歩', '�j' => '歫', '�k' => '歬', '�l' => '歭', '�m' => '歮', '�n' => '歯', '�o' => '歰', '�p' => '歱', '�q' => '歲', '�r' => '歳', '�s' => '歴', '�t' => '歵', '�u' => '歶', '�v' => '歷', '�w' => '歸', '�x' => '歺', '�y' => '歽', '�z' => '歾', '�{' => '歿', '�|' => '殀', '�}' => '殅', '�~' => '殈', '��' => '殌', '��' => '殎', '��' => '殏', '��' => '殐', '��' => '殑', '��' => '殔', '��' => '殕', '��' => '殗', '��' => '殘', '��' => '殙', '��' => '殜', '��' => '殝', '��' => '殞', '��' => '殟', '��' => '殠', '��' => '殢', '��' => '殣', '��' => '殤', '��' => '殥', '��' => '殦', '��' => '殧', '��' => '殨', '��' => '殩', '��' => '殫', '��' => '殬', '��' => '殭', '��' => '殮', '��' => '殯', '��' => '殰', '��' => '殱', '��' => '殲', '��' => '殶', '��' => '殸', '��' => '殹', '��' => '殺', '��' => '殻', '��' => '殼', '��' => '殽', '��' => '殾', '��' => '毀', '��' => '毃', '��' => '毄', '��' => '毆', '��' => '毇', '��' => '毈', '��' => '毉', '��' => '毊', '��' => '毌', '��' => '毎', '��' => '毐', '��' => '毑', '��' => '毘', '��' => '毚', '��' => '毜', '��' => '毝', '��' => '毞', '��' => '毟', '��' => '毠', '��' => '毢', '��' => '毣', '��' => '毤', '��' => '毥', '��' => '毦', '��' => '毧', '��' => '毨', '��' => '毩', '��' => '毬', '��' => '毭', '��' => '毮', '��' => '毰', '��' => '毱', '��' => '毲', '��' => '毴', '��' => '毶', '��' => '毷', '��' => '毸', '��' => '毺', '��' => '毻', '��' => '毼', '��' => '毾', '��' => '毿', '��' => '氀', '��' => '氁', '��' => '氂', '��' => '氃', '��' => '氄', '��' => '氈', '��' => '氉', '��' => '氊', '��' => '氋', '��' => '氌', '��' => '氎', '��' => '氒', '��' => '気', '��' => '氜', '��' => '氝', '��' => '氞', '��' => '氠', '��' => '氣', '��' => '氥', '��' => '氫', '��' => '氬', '��' => '氭', '��' => '氱', '��' => '氳', '��' => '氶', '��' => '氷', '��' => '氹', '��' => '氺', '��' => '氻', '��' => '氼', '��' => '氾', '��' => '氿', '��' => '汃', '��' => '汄', '��' => '汅', '��' => '汈', '��' => '汋', '��' => '汌', '��' => '汍', '��' => '汎', '��' => '汏', '��' => '汑', '��' => '汒', '��' => '汓', '��' => '汖', '��' => '汘', '�@' => '汙', '�A' => '汚', '�B' => '汢', '�C' => '汣', '�D' => '汥', '�E' => '汦', '�F' => '汧', '�G' => '汫', '�H' => '汬', '�I' => '汭', '�J' => '汮', '�K' => '汯', '�L' => '汱', '�M' => '汳', '�N' => '汵', '�O' => '汷', '�P' => '汸', '�Q' => '決', '�R' => '汻', '�S' => '汼', '�T' => '汿', '�U' => '沀', '�V' => '沄', '�W' => '沇', '�X' => '沊', '�Y' => '沋', '�Z' => '沍', '�[' => '沎', '�\\' => '沑', '�]' => '沒', '�^' => '沕', '�_' => '沖', '�`' => '沗', '�a' => '沘', '�b' => '沚', '�c' => '沜', '�d' => '沝', '�e' => '沞', '�f' => '沠', '�g' => '沢', '�h' => '沨', '�i' => '沬', '�j' => '沯', '�k' => '沰', '�l' => '沴', '�m' => '沵', '�n' => '沶', '�o' => '沷', '�p' => '沺', '�q' => '泀', '�r' => '況', '�s' => '泂', '�t' => '泃', '�u' => '泆', '�v' => '泇', '�w' => '泈', '�x' => '泋', '�y' => '泍', '�z' => '泎', '�{' => '泏', '�|' => '泑', '�}' => '泒', '�~' => '泘', '��' => '泙', '��' => '泚', '��' => '泜', '��' => '泝', '��' => '泟', '��' => '泤', '��' => '泦', '��' => '泧', '��' => '泩', '��' => '泬', '��' => '泭', '��' => '泲', '��' => '泴', '��' => '泹', '��' => '泿', '��' => '洀', '��' => '洂', '��' => '洃', '��' => '洅', '��' => '洆', '��' => '洈', '��' => '洉', '��' => '洊', '��' => '洍', '��' => '洏', '��' => '洐', '��' => '洑', '��' => '洓', '��' => '洔', '��' => '洕', '��' => '洖', '��' => '洘', '��' => '洜', '��' => '洝', '��' => '洟', '��' => '洠', '��' => '洡', '��' => '洢', '��' => '洣', '��' => '洤', '��' => '洦', '��' => '洨', '��' => '洩', '��' => '洬', '��' => '洭', '��' => '洯', '��' => '洰', '��' => '洴', '��' => '洶', '��' => '洷', '��' => '洸', '��' => '洺', '��' => '洿', '��' => '浀', '��' => '浂', '��' => '浄', '��' => '浉', '��' => '浌', '��' => '浐', '��' => '浕', '��' => '浖', '��' => '浗', '��' => '浘', '��' => '浛', '��' => '浝', '��' => '浟', '��' => '浡', '��' => '浢', '��' => '浤', '��' => '浥', '��' => '浧', '��' => '浨', '��' => '浫', '��' => '浬', '��' => '浭', '��' => '浰', '��' => '浱', '��' => '浲', '��' => '浳', '��' => '浵', '��' => '浶', '��' => '浹', '��' => '浺', '��' => '浻', '��' => '浽', '��' => '浾', '��' => '浿', '��' => '涀', '��' => '涁', '��' => '涃', '��' => '涄', '��' => '涆', '��' => '涇', '��' => '涊', '��' => '涋', '��' => '涍', '��' => '涏', '��' => '涐', '��' => '涒', '��' => '涖', '��' => '涗', '��' => '涘', '��' => '涙', '��' => '涚', '��' => '涜', '��' => '涢', '��' => '涥', '��' => '涬', '��' => '涭', '��' => '涰', '��' => '涱', '��' => '涳', '��' => '涴', '��' => '涶', '��' => '涷', '��' => '涹', '��' => '涺', '��' => '涻', '��' => '涼', '��' => '涽', '��' => '涾', '��' => '淁', '��' => '淂', '��' => '淃', '��' => '淈', '��' => '淉', '��' => '淊', '�@' => '淍', '�A' => '淎', '�B' => '淏', '�C' => '淐', '�D' => '淒', '�E' => '淓', '�F' => '淔', '�G' => '淕', '�H' => '淗', '�I' => '淚', '�J' => '淛', '�K' => '淜', '�L' => '淟', '�M' => '淢', '�N' => '淣', '�O' => '淥', '�P' => '淧', '�Q' => '淨', '�R' => '淩', '�S' => '淪', '�T' => '淭', '�U' => '淯', '�V' => '淰', '�W' => '淲', '�X' => '淴', '�Y' => '淵', '�Z' => '淶', '�[' => '淸', '�\\' => '淺', '�]' => '淽', '�^' => '淾', '�_' => '淿', '�`' => '渀', '�a' => '渁', '�b' => '渂', '�c' => '渃', '�d' => '渄', '�e' => '渆', '�f' => '渇', '�g' => '済', '�h' => '渉', '�i' => '渋', '�j' => '渏', '�k' => '渒', '�l' => '渓', '�m' => '渕', '�n' => '渘', '�o' => '渙', '�p' => '減', '�q' => '渜', '�r' => '渞', '�s' => '渟', '�t' => '渢', '�u' => '渦', '�v' => '渧', '�w' => '渨', '�x' => '渪', '�y' => '測', '�z' => '渮', '�{' => '渰', '�|' => '渱', '�}' => '渳', '�~' => '渵', '��' => '渶', '��' => '渷', '��' => '渹', '��' => '渻', '��' => '渼', '��' => '渽', '��' => '渾', '��' => '渿', '��' => '湀', '��' => '湁', '��' => '湂', '��' => '湅', '��' => '湆', '��' => '湇', '��' => '湈', '��' => '湉', '��' => '湊', '��' => '湋', '��' => '湌', '��' => '湏', '��' => '湐', '��' => '湑', '��' => '湒', '��' => '湕', '��' => '湗', '��' => '湙', '��' => '湚', '��' => '湜', '��' => '湝', '��' => '湞', '��' => '湠', '��' => '湡', '��' => '湢', '��' => '湣', '��' => '湤', '��' => '湥', '��' => '湦', '��' => '湧', '��' => '湨', '��' => '湩', '��' => '湪', '��' => '湬', '��' => '湭', '��' => '湯', '��' => '湰', '��' => '湱', '��' => '湲', '��' => '湳', '��' => '湴', '��' => '湵', '��' => '湶', '��' => '湷', '��' => '湸', '��' => '湹', '��' => '湺', '��' => '湻', '��' => '湼', '��' => '湽', '��' => '満', '��' => '溁', '��' => '溂', '��' => '溄', '��' => '溇', '��' => '溈', '��' => '溊', '��' => '溋', '��' => '溌', '��' => '溍', '��' => '溎', '��' => '溑', '��' => '溒', '��' => '溓', '��' => '溔', '��' => '溕', '��' => '準', '��' => '溗', '��' => '溙', '��' => '溚', '��' => '溛', '��' => '溝', '��' => '溞', '��' => '溠', '��' => '溡', '��' => '溣', '��' => '溤', '��' => '溦', '��' => '溨', '��' => '溩', '��' => '溫', '��' => '溬', '��' => '溭', '��' => '溮', '��' => '溰', '��' => '溳', '��' => '溵', '��' => '溸', '��' => '溹', '��' => '溼', '��' => '溾', '��' => '溿', '��' => '滀', '��' => '滃', '��' => '滄', '��' => '滅', '��' => '滆', '��' => '滈', '��' => '滉', '��' => '滊', '��' => '滌', '��' => '滍', '��' => '滎', '��' => '滐', '��' => '滒', '��' => '滖', '��' => '滘', '��' => '滙', '��' => '滛', '��' => '滜', '��' => '滝', '��' => '滣', '��' => '滧', '��' => '滪', '��' => '滫', '��' => '滬', '��' => '滭', '��' => '滮', '��' => '滯', '�@' => '滰', '�A' => '滱', '�B' => '滲', '�C' => '滳', '�D' => '滵', '�E' => '滶', '�F' => '滷', '�G' => '滸', '�H' => '滺', '�I' => '滻', '�J' => '滼', '�K' => '滽', '�L' => '滾', '�M' => '滿', '�N' => '漀', '�O' => '漁', '�P' => '漃', '�Q' => '漄', '�R' => '漅', '�S' => '漇', '�T' => '漈', '�U' => '漊', '�V' => '漋', '�W' => '漌', '�X' => '漍', '�Y' => '漎', '�Z' => '漐', '�[' => '漑', '�\\' => '漒', '�]' => '漖', '�^' => '漗', '�_' => '漘', '�`' => '漙', '�a' => '漚', '�b' => '漛', '�c' => '漜', '�d' => '漝', '�e' => '漞', '�f' => '漟', '�g' => '漡', '�h' => '漢', '�i' => '漣', '�j' => '漥', '�k' => '漦', '�l' => '漧', '�m' => '漨', '�n' => '漬', '�o' => '漮', '�p' => '漰', '�q' => '漲', '�r' => '漴', '�s' => '漵', '�t' => '漷', '�u' => '漸', '�v' => '漹', '�w' => '漺', '�x' => '漻', '�y' => '漼', '�z' => '漽', '�{' => '漿', '�|' => '潀', '�}' => '潁', '�~' => '潂', '��' => '潃', '��' => '潄', '��' => '潅', '��' => '潈', '��' => '潉', '��' => '潊', '��' => '潌', '��' => '潎', '��' => '潏', '��' => '潐', '��' => '潑', '��' => '潒', '��' => '潓', '��' => '潔', '��' => '潕', '��' => '潖', '��' => '潗', '��' => '潙', '��' => '潚', '��' => '潛', '��' => '潝', '��' => '潟', '��' => '潠', '��' => '潡', '��' => '潣', '��' => '潤', '��' => '潥', '��' => '潧', '��' => '潨', '��' => '潩', '��' => '潪', '��' => '潫', '��' => '潬', '��' => '潯', '��' => '潰', '��' => '潱', '��' => '潳', '��' => '潵', '��' => '潶', '��' => '潷', '��' => '潹', '��' => '潻', '��' => '潽', '��' => '潾', '��' => '潿', '��' => '澀', '��' => '澁', '��' => '澂', '��' => '澃', '��' => '澅', '��' => '澆', '��' => '澇', '��' => '澊', '��' => '澋', '��' => '澏', '��' => '澐', '��' => '澑', '��' => '澒', '��' => '澓', '��' => '澔', '��' => '澕', '��' => '澖', '��' => '澗', '��' => '澘', '��' => '澙', '��' => '澚', '��' => '澛', '��' => '澝', '��' => '澞', '��' => '澟', '��' => '澠', '��' => '澢', '��' => '澣', '��' => '澤', '��' => '澥', '��' => '澦', '��' => '澨', '��' => '澩', '��' => '澪', '��' => '澫', '��' => '澬', '��' => '澭', '��' => '澮', '��' => '澯', '��' => '澰', '��' => '澱', '��' => '澲', '��' => '澴', '��' => '澵', '��' => '澷', '��' => '澸', '��' => '澺', '��' => '澻', '��' => '澼', '��' => '澽', '��' => '澾', '��' => '澿', '��' => '濁', '��' => '濃', '��' => '濄', '��' => '濅', '��' => '濆', '��' => '濇', '��' => '濈', '��' => '濊', '��' => '濋', '��' => '濌', '��' => '濍', '��' => '濎', '��' => '濏', '��' => '濐', '��' => '濓', '��' => '濔', '��' => '濕', '��' => '濖', '��' => '濗', '��' => '濘', '��' => '濙', '��' => '濚', '��' => '濛', '��' => '濜', '��' => '濝', '��' => '濟', '��' => '濢', '��' => '濣', '��' => '濤', '��' => '濥', '�@' => '濦', '�A' => '濧', '�B' => '濨', '�C' => '濩', '�D' => '濪', '�E' => '濫', '�F' => '濬', '�G' => '濭', '�H' => '濰', '�I' => '濱', '�J' => '濲', '�K' => '濳', '�L' => '濴', '�M' => '濵', '�N' => '濶', '�O' => '濷', '�P' => '濸', '�Q' => '濹', '�R' => '濺', '�S' => '濻', '�T' => '濼', '�U' => '濽', '�V' => '濾', '�W' => '濿', '�X' => '瀀', '�Y' => '瀁', '�Z' => '瀂', '�[' => '瀃', '�\\' => '瀄', '�]' => '瀅', '�^' => '瀆', '�_' => '瀇', '�`' => '瀈', '�a' => '瀉', '�b' => '瀊', '�c' => '瀋', '�d' => '瀌', '�e' => '瀍', '�f' => '瀎', '�g' => '瀏', '�h' => '瀐', '�i' => '瀒', '�j' => '瀓', '�k' => '瀔', '�l' => '瀕', '�m' => '瀖', '�n' => '瀗', '�o' => '瀘', '�p' => '瀙', '�q' => '瀜', '�r' => '瀝', '�s' => '瀞', '�t' => '瀟', '�u' => '瀠', '�v' => '瀡', '�w' => '瀢', '�x' => '瀤', '�y' => '瀥', '�z' => '瀦', '�{' => '瀧', '�|' => '瀨', '�}' => '瀩', '�~' => '瀪', '��' => '瀫', '��' => '瀬', '��' => '瀭', '��' => '瀮', '��' => '瀯', '��' => '瀰', '��' => '瀱', '��' => '瀲', '��' => '瀳', '��' => '瀴', '��' => '瀶', '��' => '瀷', '��' => '瀸', '��' => '瀺', '��' => '瀻', '��' => '瀼', '��' => '瀽', '��' => '瀾', '��' => '瀿', '��' => '灀', '��' => '灁', '��' => '灂', '��' => '灃', '��' => '灄', '��' => '灅', '��' => '灆', '��' => '灇', '��' => '灈', '��' => '灉', '��' => '灊', '��' => '灋', '��' => '灍', '��' => '灎', '��' => '灐', '��' => '灑', '��' => '灒', '��' => '灓', '��' => '灔', '��' => '灕', '��' => '灖', '��' => '灗', '��' => '灘', '��' => '灙', '��' => '灚', '��' => '灛', '��' => '灜', '��' => '灝', '��' => '灟', '��' => '灠', '��' => '灡', '��' => '灢', '��' => '灣', '��' => '灤', '��' => '灥', '��' => '灦', '��' => '灧', '��' => '灨', '��' => '灩', '��' => '灪', '��' => '灮', '��' => '灱', '��' => '灲', '��' => '灳', '��' => '灴', '��' => '灷', '��' => '灹', '��' => '灺', '��' => '灻', '��' => '災', '��' => '炁', '��' => '炂', '��' => '炃', '��' => '炄', '��' => '炆', '��' => '炇', '��' => '炈', '��' => '炋', '��' => '炌', '��' => '炍', '��' => '炏', '��' => '炐', '��' => '炑', '��' => '炓', '��' => '炗', '��' => '炘', '��' => '炚', '��' => '炛', '��' => '炞', '��' => '炟', '��' => '炠', '��' => '炡', '��' => '炢', '��' => '炣', '��' => '炤', '��' => '炥', '��' => '炦', '��' => '炧', '��' => '炨', '��' => '炩', '��' => '炪', '��' => '炰', '��' => '炲', '��' => '炴', '��' => '炵', '��' => '炶', '��' => '為', '��' => '炾', '��' => '炿', '��' => '烄', '��' => '烅', '��' => '烆', '��' => '烇', '��' => '烉', '��' => '烋', '��' => '烌', '��' => '烍', '��' => '烎', '��' => '烏', '��' => '烐', '��' => '烑', '��' => '烒', '��' => '烓', '��' => '烔', '��' => '烕', '��' => '烖', '��' => '烗', '��' => '烚', '�@' => '烜', '�A' => '烝', '�B' => '烞', '�C' => '烠', '�D' => '烡', '�E' => '烢', '�F' => '烣', '�G' => '烥', '�H' => '烪', '�I' => '烮', '�J' => '烰', '�K' => '烱', '�L' => '烲', '�M' => '烳', '�N' => '烴', '�O' => '烵', '�P' => '烶', '�Q' => '烸', '�R' => '烺', '�S' => '烻', '�T' => '烼', '�U' => '烾', '�V' => '烿', '�W' => '焀', '�X' => '焁', '�Y' => '焂', '�Z' => '焃', '�[' => '焄', '�\\' => '焅', '�]' => '焆', '�^' => '焇', '�_' => '焈', '�`' => '焋', '�a' => '焌', '�b' => '焍', '�c' => '焎', '�d' => '焏', '�e' => '焑', '�f' => '焒', '�g' => '焔', '�h' => '焗', '�i' => '焛', '�j' => '焜', '�k' => '焝', '�l' => '焞', '�m' => '焟', '�n' => '焠', '�o' => '無', '�p' => '焢', '�q' => '焣', '�r' => '焤', '�s' => '焥', '�t' => '焧', '�u' => '焨', '�v' => '焩', '�w' => '焪', '�x' => '焫', '�y' => '焬', '�z' => '焭', '�{' => '焮', '�|' => '焲', '�}' => '焳', '�~' => '焴', '��' => '焵', '��' => '焷', '��' => '焸', '��' => '焹', '��' => '焺', '��' => '焻', '��' => '焼', '��' => '焽', '��' => '焾', '��' => '焿', '��' => '煀', '��' => '煁', '��' => '煂', '��' => '煃', '��' => '煄', '��' => '煆', '��' => '煇', '��' => '煈', '��' => '煉', '��' => '煋', '��' => '煍', '��' => '煏', '��' => '煐', '��' => '煑', '��' => '煒', '��' => '煓', '��' => '煔', '��' => '煕', '��' => '煖', '��' => '煗', '��' => '煘', '��' => '煙', '��' => '煚', '��' => '煛', '��' => '煝', '��' => '煟', '��' => '煠', '��' => '煡', '��' => '煢', '��' => '煣', '��' => '煥', '��' => '煩', '��' => '煪', '��' => '煫', '��' => '煬', '��' => '煭', '��' => '煯', '��' => '煰', '��' => '煱', '��' => '煴', '��' => '煵', '��' => '煶', '��' => '煷', '��' => '煹', '��' => '煻', '��' => '煼', '��' => '煾', '��' => '煿', '��' => '熀', '��' => '熁', '��' => '熂', '��' => '熃', '��' => '熅', '��' => '熆', '��' => '熇', '��' => '熈', '��' => '熉', '��' => '熋', '��' => '熌', '��' => '熍', '��' => '熎', '��' => '熐', '��' => '熑', '��' => '熒', '��' => '熓', '��' => '熕', '��' => '熖', '��' => '熗', '��' => '熚', '��' => '熛', '��' => '熜', '��' => '熝', '��' => '熞', '��' => '熡', '��' => '熢', '��' => '熣', '��' => '熤', '��' => '熥', '��' => '熦', '��' => '熧', '��' => '熩', '��' => '熪', '��' => '熫', '��' => '熭', '��' => '熮', '��' => '熯', '��' => '熰', '��' => '熱', '��' => '熲', '��' => '熴', '��' => '熶', '��' => '熷', '��' => '熸', '��' => '熺', '��' => '熻', '��' => '熼', '��' => '熽', '��' => '熾', '��' => '熿', '��' => '燀', '��' => '燁', '��' => '燂', '��' => '燄', '��' => '燅', '��' => '燆', '��' => '燇', '��' => '燈', '��' => '燉', '��' => '燊', '��' => '燋', '��' => '燌', '��' => '燍', '��' => '燏', '��' => '燐', '��' => '燑', '��' => '燒', '��' => '燓', '�@' => '燖', '�A' => '燗', '�B' => '燘', '�C' => '燙', '�D' => '燚', '�E' => '燛', '�F' => '燜', '�G' => '燝', '�H' => '燞', '�I' => '營', '�J' => '燡', '�K' => '燢', '�L' => '燣', '�M' => '燤', '�N' => '燦', '�O' => '燨', '�P' => '燩', '�Q' => '燪', '�R' => '燫', '�S' => '燬', '�T' => '燭', '�U' => '燯', '�V' => '燰', '�W' => '燱', '�X' => '燲', '�Y' => '燳', '�Z' => '燴', '�[' => '燵', '�\\' => '燶', '�]' => '燷', '�^' => '燸', '�_' => '燺', '�`' => '燻', '�a' => '燼', '�b' => '燽', '�c' => '燾', '�d' => '燿', '�e' => '爀', '�f' => '爁', '�g' => '爂', '�h' => '爃', '�i' => '爄', '�j' => '爅', '�k' => '爇', '�l' => '爈', '�m' => '爉', '�n' => '爊', '�o' => '爋', '�p' => '爌', '�q' => '爍', '�r' => '爎', '�s' => '爏', '�t' => '爐', '�u' => '爑', '�v' => '爒', '�w' => '爓', '�x' => '爔', '�y' => '爕', '�z' => '爖', '�{' => '爗', '�|' => '爘', '�}' => '爙', '�~' => '爚', '��' => '爛', '��' => '爜', '��' => '爞', '��' => '爟', '��' => '爠', '��' => '爡', '��' => '爢', '��' => '爣', '��' => '爤', '��' => '爥', '��' => '爦', '��' => '爧', '��' => '爩', '��' => '爫', '��' => '爭', '��' => '爮', '��' => '爯', '��' => '爲', '��' => '爳', '��' => '爴', '��' => '爺', '��' => '爼', '��' => '爾', '��' => '牀', '��' => '牁', '��' => '牂', '��' => '牃', '��' => '牄', '��' => '牅', '��' => '牆', '��' => '牉', '��' => '牊', '��' => '牋', '��' => '牎', '��' => '牏', '��' => '牐', '��' => '牑', '��' => '牓', '��' => '牔', '��' => '牕', '��' => '牗', '��' => '牘', '��' => '牚', '��' => '牜', '��' => '牞', '��' => '牠', '��' => '牣', '��' => '牤', '��' => '牥', '��' => '牨', '��' => '牪', '��' => '牫', '��' => '牬', '��' => '牭', '��' => '牰', '��' => '牱', '��' => '牳', '��' => '牴', '��' => '牶', '��' => '牷', '��' => '牸', '��' => '牻', '��' => '牼', '��' => '牽', '��' => '犂', '��' => '犃', '��' => '犅', '��' => '犆', '��' => '犇', '��' => '犈', '��' => '犉', '��' => '犌', '��' => '犎', '��' => '犐', '��' => '犑', '��' => '犓', '��' => '犔', '��' => '犕', '��' => '犖', '��' => '犗', '��' => '犘', '��' => '犙', '��' => '犚', '��' => '犛', '��' => '犜', '��' => '犝', '��' => '犞', '��' => '犠', '��' => '犡', '��' => '犢', '��' => '犣', '��' => '犤', '��' => '犥', '��' => '犦', '��' => '犧', '��' => '犨', '��' => '犩', '��' => '犪', '��' => '犫', '��' => '犮', '��' => '犱', '��' => '犲', '��' => '犳', '��' => '犵', '��' => '犺', '��' => '犻', '��' => '犼', '��' => '犽', '��' => '犾', '��' => '犿', '��' => '狀', '��' => '狅', '��' => '狆', '��' => '狇', '��' => '狉', '��' => '狊', '��' => '狋', '��' => '狌', '��' => '狏', '��' => '狑', '��' => '狓', '��' => '狔', '��' => '狕', '��' => '狖', '��' => '狘', '��' => '狚', '��' => '狛', '��' => ' ', '��' => '、', '��' => '。', '��' => '·', '��' => 'ˉ', '��' => 'ˇ', '��' => '¨', '��' => '〃', '��' => '々', '��' => '—', '��' => '~', '��' => '‖', '��' => '…', '��' => '‘', '��' => '’', '��' => '“', '��' => '”', '��' => '〔', '��' => '〕', '��' => '〈', '��' => '〉', '��' => '《', '��' => '》', '��' => '「', '��' => '」', '��' => '『', '��' => '』', '��' => '〖', '��' => '〗', '��' => '【', '��' => '】', '��' => '±', '��' => '×', '��' => '÷', '��' => '∶', '��' => '∧', '��' => '∨', '��' => '∑', '��' => '∏', '��' => '∪', '��' => '∩', '��' => '∈', '��' => '∷', '��' => '√', '��' => '⊥', '��' => '∥', '��' => '∠', '��' => '⌒', '��' => '⊙', '��' => '∫', '��' => '∮', '��' => '≡', '��' => '≌', '��' => '≈', '��' => '∽', '��' => '∝', '��' => '≠', '��' => '≮', '��' => '≯', '��' => '≤', '��' => '≥', '��' => '∞', '��' => '∵', '��' => '∴', '��' => '♂', '��' => '♀', '��' => '°', '��' => '′', '��' => '″', '��' => '℃', '��' => '$', '��' => '¤', '��' => '¢', '��' => '£', '��' => '‰', '��' => '§', '��' => '№', '��' => '☆', '��' => '★', '��' => '○', '��' => '●', '��' => '◎', '��' => '◇', '��' => '◆', '��' => '□', '��' => '■', '��' => '△', '��' => '▲', '��' => '※', '��' => '→', '��' => '←', '��' => '↑', '��' => '↓', '��' => '〓', '��' => 'ⅰ', '��' => 'ⅱ', '��' => 'ⅲ', '��' => 'ⅳ', '��' => 'ⅴ', '��' => 'ⅵ', '��' => 'ⅶ', '��' => 'ⅷ', '��' => 'ⅸ', '��' => 'ⅹ', '��' => '⒈', '��' => '⒉', '��' => '⒊', '��' => '⒋', '��' => '⒌', '��' => '⒍', '��' => '⒎', '��' => '⒏', '��' => '⒐', '��' => '⒑', '��' => '⒒', '��' => '⒓', '��' => '⒔', '��' => '⒕', '��' => '⒖', '��' => '⒗', '��' => '⒘', '��' => '⒙', '��' => '⒚', '��' => '⒛', '��' => '⑴', '��' => '⑵', '��' => '⑶', '��' => '⑷', '��' => '⑸', '��' => '⑹', '��' => '⑺', '��' => '⑻', '��' => '⑼', '��' => '⑽', '��' => '⑾', '��' => '⑿', '��' => '⒀', '��' => '⒁', '��' => '⒂', '��' => '⒃', '��' => '⒄', '��' => '⒅', '��' => '⒆', '��' => '⒇', '��' => '①', '��' => '②', '��' => '③', '��' => '④', '��' => '⑤', '��' => '⑥', '��' => '⑦', '��' => '⑧', '��' => '⑨', '��' => '⑩', '��' => '㈠', '��' => '㈡', '��' => '㈢', '��' => '㈣', '��' => '㈤', '��' => '㈥', '��' => '㈦', '��' => '㈧', '��' => '㈨', '��' => '㈩', '��' => 'Ⅰ', '��' => 'Ⅱ', '��' => 'Ⅲ', '��' => 'Ⅳ', '��' => 'Ⅴ', '��' => 'Ⅵ', '��' => 'Ⅶ', '��' => 'Ⅷ', '��' => 'Ⅸ', '��' => 'Ⅹ', '��' => 'Ⅺ', '��' => 'Ⅻ', '��' => '!', '��' => '"', '��' => '#', '��' => '¥', '��' => '%', '��' => '&', '��' => ''', '��' => '(', '��' => ')', '��' => '*', '��' => '+', '��' => ',', '��' => '-', '��' => '.', '��' => '/', '��' => '0', '��' => '1', '��' => '2', '��' => '3', '��' => '4', '��' => '5', '��' => '6', '��' => '7', '��' => '8', '��' => '9', '��' => ':', '��' => ';', '��' => '<', '��' => '=', '��' => '>', '��' => '?', '��' => '@', '��' => 'A', '��' => 'B', '��' => 'C', '��' => 'D', '��' => 'E', '��' => 'F', '��' => 'G', '��' => 'H', '��' => 'I', '��' => 'J', '��' => 'K', '��' => 'L', '��' => 'M', '��' => 'N', '��' => 'O', '��' => 'P', '��' => 'Q', '��' => 'R', '��' => 'S', '��' => 'T', '��' => 'U', '��' => 'V', '��' => 'W', '��' => 'X', '��' => 'Y', '��' => 'Z', '��' => '[', '��' => '\', '��' => ']', '��' => '^', '��' => '_', '��' => '`', '��' => 'a', '��' => 'b', '��' => 'c', '��' => 'd', '��' => 'e', '��' => 'f', '��' => 'g', '��' => 'h', '��' => 'i', '��' => 'j', '��' => 'k', '��' => 'l', '��' => 'm', '��' => 'n', '��' => 'o', '��' => 'p', '��' => 'q', '��' => 'r', '��' => 's', '��' => 't', '��' => 'u', '��' => 'v', '��' => 'w', '��' => 'x', '��' => 'y', '��' => 'z', '��' => '{', '��' => '|', '��' => '}', '��' => ' ̄', '��' => 'ぁ', '��' => 'あ', '��' => 'ぃ', '��' => 'い', '��' => 'ぅ', '��' => 'う', '��' => 'ぇ', '��' => 'え', '��' => 'ぉ', '��' => 'お', '��' => 'か', '��' => 'が', '��' => 'き', '��' => 'ぎ', '��' => 'く', '��' => 'ぐ', '��' => 'け', '��' => 'げ', '��' => 'こ', '��' => 'ご', '��' => 'さ', '��' => 'ざ', '��' => 'し', '��' => 'じ', '��' => 'す', '��' => 'ず', '��' => 'せ', '��' => 'ぜ', '��' => 'そ', '��' => 'ぞ', '��' => 'た', '��' => 'だ', '��' => 'ち', '��' => 'ぢ', '��' => 'っ', '��' => 'つ', '��' => 'づ', '��' => 'て', '��' => 'で', '��' => 'と', '��' => 'ど', '��' => 'な', '��' => 'に', '��' => 'ぬ', '��' => 'ね', '��' => 'の', '��' => 'は', '��' => 'ば', '��' => 'ぱ', '��' => 'ひ', '��' => 'び', '��' => 'ぴ', '��' => 'ふ', '��' => 'ぶ', '��' => 'ぷ', '��' => 'へ', '��' => 'べ', '��' => 'ぺ', '��' => 'ほ', '��' => 'ぼ', '��' => 'ぽ', '��' => 'ま', '��' => 'み', '��' => 'む', '��' => 'め', '��' => 'も', '��' => 'ゃ', '��' => 'や', '��' => 'ゅ', '��' => 'ゆ', '��' => 'ょ', '��' => 'よ', '��' => 'ら', '��' => 'り', '��' => 'る', '��' => 'れ', '��' => 'ろ', '��' => 'ゎ', '��' => 'わ', '��' => 'ゐ', '��' => 'ゑ', '��' => 'を', '��' => 'ん', '��' => 'ァ', '��' => 'ア', '��' => 'ィ', '��' => 'イ', '��' => 'ゥ', '��' => 'ウ', '��' => 'ェ', '��' => 'エ', '��' => 'ォ', '��' => 'オ', '��' => 'カ', '��' => 'ガ', '��' => 'キ', '��' => 'ギ', '��' => 'ク', '��' => 'グ', '��' => 'ケ', '��' => 'ゲ', '��' => 'コ', '��' => 'ゴ', '��' => 'サ', '��' => 'ザ', '��' => 'シ', '��' => 'ジ', '��' => 'ス', '��' => 'ズ', '��' => 'セ', '��' => 'ゼ', '��' => 'ソ', '��' => 'ゾ', '��' => 'タ', '��' => 'ダ', '��' => 'チ', '��' => 'ヂ', '��' => 'ッ', '��' => 'ツ', '��' => 'ヅ', '��' => 'テ', '��' => 'デ', '��' => 'ト', '��' => 'ド', '��' => 'ナ', '��' => 'ニ', '��' => 'ヌ', '��' => 'ネ', '��' => 'ノ', '��' => 'ハ', '��' => 'バ', '��' => 'パ', '��' => 'ヒ', '��' => 'ビ', '��' => 'ピ', '��' => 'フ', '��' => 'ブ', '��' => 'プ', '��' => 'ヘ', '��' => 'ベ', '��' => 'ペ', '��' => 'ホ', '��' => 'ボ', '��' => 'ポ', '��' => 'マ', '��' => 'ミ', '��' => 'ム', '��' => 'メ', '��' => 'モ', '��' => 'ャ', '��' => 'ヤ', '��' => 'ュ', '��' => 'ユ', '��' => 'ョ', '��' => 'ヨ', '��' => 'ラ', '��' => 'リ', '��' => 'ル', '��' => 'レ', '��' => 'ロ', '��' => 'ヮ', '��' => 'ワ', '��' => 'ヰ', '��' => 'ヱ', '��' => 'ヲ', '��' => 'ン', '��' => 'ヴ', '��' => 'ヵ', '��' => 'ヶ', '��' => 'Α', '��' => 'Β', '��' => 'Γ', '��' => 'Δ', '��' => 'Ε', '��' => 'Ζ', '��' => 'Η', '��' => 'Θ', '��' => 'Ι', '��' => 'Κ', '��' => 'Λ', '��' => 'Μ', '��' => 'Ν', '��' => 'Ξ', '��' => 'Ο', '��' => 'Π', '��' => 'Ρ', '��' => 'Σ', '��' => 'Τ', '��' => 'Υ', '��' => 'Φ', '��' => 'Χ', '��' => 'Ψ', '��' => 'Ω', '��' => 'α', '��' => 'β', '��' => 'γ', '��' => 'δ', '��' => 'ε', '��' => 'ζ', '��' => 'η', '��' => 'θ', '��' => 'ι', '��' => 'κ', '��' => 'λ', '��' => 'μ', '��' => 'ν', '��' => 'ξ', '��' => 'ο', '��' => 'π', '��' => 'ρ', '��' => 'σ', '��' => 'τ', '��' => 'υ', '��' => 'φ', '��' => 'χ', '��' => 'ψ', '��' => 'ω', '��' => '︵', '��' => '︶', '��' => '︹', '��' => '︺', '��' => '︿', '��' => '﹀', '��' => '︽', '��' => '︾', '��' => '﹁', '��' => '﹂', '��' => '﹃', '��' => '﹄', '��' => '︻', '��' => '︼', '��' => '︷', '��' => '︸', '��' => '︱', '��' => '︳', '��' => '︴', '��' => 'А', '��' => 'Б', '��' => 'В', '��' => 'Г', '��' => 'Д', '��' => 'Е', '��' => 'Ё', '��' => 'Ж', '��' => 'З', '��' => 'И', '��' => 'Й', '��' => 'К', '��' => 'Л', '��' => 'М', '��' => 'Н', '��' => 'О', '��' => 'П', '��' => 'Р', '��' => 'С', '��' => 'Т', '��' => 'У', '��' => 'Ф', '��' => 'Х', '��' => 'Ц', '��' => 'Ч', '��' => 'Ш', '��' => 'Щ', '��' => 'Ъ', '��' => 'Ы', '��' => 'Ь', '��' => 'Э', '��' => 'Ю', '��' => 'Я', '��' => 'а', '��' => 'б', '��' => 'в', '��' => 'г', '��' => 'д', '��' => 'е', '��' => 'ё', '��' => 'ж', '��' => 'з', '��' => 'и', '��' => 'й', '��' => 'к', '��' => 'л', '��' => 'м', '��' => 'н', '��' => 'о', '��' => 'п', '��' => 'р', '��' => 'с', '��' => 'т', '��' => 'у', '��' => 'ф', '��' => 'х', '��' => 'ц', '��' => 'ч', '��' => 'ш', '��' => 'щ', '��' => 'ъ', '��' => 'ы', '��' => 'ь', '��' => 'э', '��' => 'ю', '��' => 'я', '�@' => 'ˊ', '�A' => 'ˋ', '�B' => '˙', '�C' => '–', '�D' => '―', '�E' => '‥', '�F' => '‵', '�G' => '℅', '�H' => '℉', '�I' => '↖', '�J' => '↗', '�K' => '↘', '�L' => '↙', '�M' => '∕', '�N' => '∟', '�O' => '∣', '�P' => '≒', '�Q' => '≦', '�R' => '≧', '�S' => '⊿', '�T' => '═', '�U' => '║', '�V' => '╒', '�W' => '╓', '�X' => '╔', '�Y' => '╕', '�Z' => '╖', '�[' => '╗', '�\\' => '╘', '�]' => '╙', '�^' => '╚', '�_' => '╛', '�`' => '╜', '�a' => '╝', '�b' => '╞', '�c' => '╟', '�d' => '╠', '�e' => '╡', '�f' => '╢', '�g' => '╣', '�h' => '╤', '�i' => '╥', '�j' => '╦', '�k' => '╧', '�l' => '╨', '�m' => '╩', '�n' => '╪', '�o' => '╫', '�p' => '╬', '�q' => '╭', '�r' => '╮', '�s' => '╯', '�t' => '╰', '�u' => '╱', '�v' => '╲', '�w' => '╳', '�x' => '▁', '�y' => '▂', '�z' => '▃', '�{' => '▄', '�|' => '▅', '�}' => '▆', '�~' => '▇', '��' => '█', '��' => '▉', '��' => '▊', '��' => '▋', '��' => '▌', '��' => '▍', '��' => '▎', '��' => '▏', '��' => '▓', '��' => '▔', '��' => '▕', '��' => '▼', '��' => '▽', '��' => '◢', '��' => '◣', '��' => '◤', '��' => '◥', '��' => '☉', '��' => '⊕', '��' => '〒', '��' => '〝', '��' => '〞', '��' => 'ā', '��' => 'á', '��' => 'ǎ', '��' => 'à', '��' => 'ē', '��' => 'é', '��' => 'ě', '��' => 'è', '��' => 'ī', '��' => 'í', '��' => 'ǐ', '��' => 'ì', '��' => 'ō', '��' => 'ó', '��' => 'ǒ', '��' => 'ò', '��' => 'ū', '��' => 'ú', '��' => 'ǔ', '��' => 'ù', '��' => 'ǖ', '��' => 'ǘ', '��' => 'ǚ', '��' => 'ǜ', '��' => 'ü', '��' => 'ê', '��' => 'ɑ', '��' => 'ń', '��' => 'ň', '��' => 'ɡ', '��' => 'ㄅ', '��' => 'ㄆ', '��' => 'ㄇ', '��' => 'ㄈ', '��' => 'ㄉ', '��' => 'ㄊ', '��' => 'ㄋ', '��' => 'ㄌ', '��' => 'ㄍ', '��' => 'ㄎ', '��' => 'ㄏ', '��' => 'ㄐ', '��' => 'ㄑ', '��' => 'ㄒ', '��' => 'ㄓ', '��' => 'ㄔ', '��' => 'ㄕ', '��' => 'ㄖ', '��' => 'ㄗ', '��' => 'ㄘ', '��' => 'ㄙ', '��' => 'ㄚ', '��' => 'ㄛ', '��' => 'ㄜ', '��' => 'ㄝ', '��' => 'ㄞ', '��' => 'ㄟ', '��' => 'ㄠ', '��' => 'ㄡ', '��' => 'ㄢ', '��' => 'ㄣ', '��' => 'ㄤ', '��' => 'ㄥ', '��' => 'ㄦ', '��' => 'ㄧ', '��' => 'ㄨ', '��' => 'ㄩ', '�@' => '〡', '�A' => '〢', '�B' => '〣', '�C' => '〤', '�D' => '〥', '�E' => '〦', '�F' => '〧', '�G' => '〨', '�H' => '〩', '�I' => '㊣', '�J' => '㎎', '�K' => '㎏', '�L' => '㎜', '�M' => '㎝', '�N' => '㎞', '�O' => '㎡', '�P' => '㏄', '�Q' => '㏎', '�R' => '㏑', '�S' => '㏒', '�T' => '㏕', '�U' => '︰', '�V' => '¬', '�W' => '¦', '�Y' => '℡', '�Z' => '㈱', '�\\' => '‐', '�`' => 'ー', '�a' => '゛', '�b' => '゜', '�c' => 'ヽ', '�d' => 'ヾ', '�e' => '〆', '�f' => 'ゝ', '�g' => 'ゞ', '�h' => '﹉', '�i' => '﹊', '�j' => '﹋', '�k' => '﹌', '�l' => '﹍', '�m' => '﹎', '�n' => '﹏', '�o' => '﹐', '�p' => '﹑', '�q' => '﹒', '�r' => '﹔', '�s' => '﹕', '�t' => '﹖', '�u' => '﹗', '�v' => '﹙', '�w' => '﹚', '�x' => '﹛', '�y' => '﹜', '�z' => '﹝', '�{' => '﹞', '�|' => '﹟', '�}' => '﹠', '�~' => '﹡', '��' => '﹢', '��' => '﹣', '��' => '﹤', '��' => '﹥', '��' => '﹦', '��' => '﹨', '��' => '﹩', '��' => '﹪', '��' => '﹫', '��' => '〇', '��' => '─', '��' => '━', '��' => '│', '��' => '┃', '��' => '┄', '��' => '┅', '��' => '┆', '��' => '┇', '��' => '┈', '��' => '┉', '��' => '┊', '��' => '┋', '��' => '┌', '��' => '┍', '��' => '┎', '��' => '┏', '��' => '┐', '��' => '┑', '��' => '┒', '��' => '┓', '��' => '└', '��' => '┕', '��' => '┖', '��' => '┗', '��' => '┘', '��' => '┙', '��' => '┚', '��' => '┛', '��' => '├', '��' => '┝', '��' => '┞', '��' => '┟', '��' => '┠', '��' => '┡', '��' => '┢', '��' => '┣', '��' => '┤', '��' => '┥', '��' => '┦', '��' => '┧', '��' => '┨', '��' => '┩', '��' => '┪', '��' => '┫', '��' => '┬', '��' => '┭', '��' => '┮', '��' => '┯', '��' => '┰', '��' => '┱', '��' => '┲', '��' => '┳', '��' => '┴', '��' => '┵', '��' => '┶', '��' => '┷', '��' => '┸', '��' => '┹', '��' => '┺', '��' => '┻', '��' => '┼', '��' => '┽', '��' => '┾', '��' => '┿', '��' => '╀', '��' => '╁', '��' => '╂', '��' => '╃', '��' => '╄', '��' => '╅', '��' => '╆', '��' => '╇', '��' => '╈', '��' => '╉', '��' => '╊', '��' => '╋', '�@' => '狜', '�A' => '狝', '�B' => '狟', '�C' => '狢', '�D' => '狣', '�E' => '狤', '�F' => '狥', '�G' => '狦', '�H' => '狧', '�I' => '狪', '�J' => '狫', '�K' => '狵', '�L' => '狶', '�M' => '狹', '�N' => '狽', '�O' => '狾', '�P' => '狿', '�Q' => '猀', '�R' => '猂', '�S' => '猄', '�T' => '猅', '�U' => '猆', '�V' => '猇', '�W' => '猈', '�X' => '猉', '�Y' => '猋', '�Z' => '猌', '�[' => '猍', '�\\' => '猏', '�]' => '猐', '�^' => '猑', '�_' => '猒', '�`' => '猔', '�a' => '猘', '�b' => '猙', '�c' => '猚', '�d' => '猟', '�e' => '猠', '�f' => '猣', '�g' => '猤', '�h' => '猦', '�i' => '猧', '�j' => '猨', '�k' => '猭', '�l' => '猯', '�m' => '猰', '�n' => '猲', '�o' => '猳', '�p' => '猵', '�q' => '猶', '�r' => '猺', '�s' => '猻', '�t' => '猼', '�u' => '猽', '�v' => '獀', '�w' => '獁', '�x' => '獂', '�y' => '獃', '�z' => '獄', '�{' => '獅', '�|' => '獆', '�}' => '獇', '�~' => '獈', '��' => '獉', '��' => '獊', '��' => '獋', '��' => '獌', '��' => '獎', '��' => '獏', '��' => '獑', '��' => '獓', '��' => '獔', '��' => '獕', '��' => '獖', '��' => '獘', '��' => '獙', '��' => '獚', '��' => '獛', '��' => '獜', '��' => '獝', '��' => '獞', '��' => '獟', '��' => '獡', '��' => '獢', '��' => '獣', '��' => '獤', '��' => '獥', '��' => '獦', '��' => '獧', '��' => '獨', '��' => '獩', '��' => '獪', '��' => '獫', '��' => '獮', '��' => '獰', '��' => '獱', '�@' => '獲', '�A' => '獳', '�B' => '獴', '�C' => '獵', '�D' => '獶', '�E' => '獷', '�F' => '獸', '�G' => '獹', '�H' => '獺', '�I' => '獻', '�J' => '獼', '�K' => '獽', '�L' => '獿', '�M' => '玀', '�N' => '玁', '�O' => '玂', '�P' => '玃', '�Q' => '玅', '�R' => '玆', '�S' => '玈', '�T' => '玊', '�U' => '玌', '�V' => '玍', '�W' => '玏', '�X' => '玐', '�Y' => '玒', '�Z' => '玓', '�[' => '玔', '�\\' => '玕', '�]' => '玗', '�^' => '玘', '�_' => '玙', '�`' => '玚', '�a' => '玜', '�b' => '玝', '�c' => '玞', '�d' => '玠', '�e' => '玡', '�f' => '玣', '�g' => '玤', '�h' => '玥', '�i' => '玦', '�j' => '玧', '�k' => '玨', '�l' => '玪', '�m' => '玬', '�n' => '玭', '�o' => '玱', '�p' => '玴', '�q' => '玵', '�r' => '玶', '�s' => '玸', '�t' => '玹', '�u' => '玼', '�v' => '玽', '�w' => '玾', '�x' => '玿', '�y' => '珁', '�z' => '珃', '�{' => '珄', '�|' => '珅', '�}' => '珆', '�~' => '珇', '��' => '珋', '��' => '珌', '��' => '珎', '��' => '珒', '��' => '珓', '��' => '珔', '��' => '珕', '��' => '珖', '��' => '珗', '��' => '珘', '��' => '珚', '��' => '珛', '��' => '珜', '��' => '珝', '��' => '珟', '��' => '珡', '��' => '珢', '��' => '珣', '��' => '珤', '��' => '珦', '��' => '珨', '��' => '珪', '��' => '珫', '��' => '珬', '��' => '珮', '��' => '珯', '��' => '珰', '��' => '珱', '��' => '珳', '��' => '珴', '��' => '珵', '��' => '珶', '��' => '珷', '�@' => '珸', '�A' => '珹', '�B' => '珺', '�C' => '珻', '�D' => '珼', '�E' => '珽', '�F' => '現', '�G' => '珿', '�H' => '琀', '�I' => '琁', '�J' => '琂', '�K' => '琄', '�L' => '琇', '�M' => '琈', '�N' => '琋', '�O' => '琌', '�P' => '琍', '�Q' => '琎', '�R' => '琑', '�S' => '琒', '�T' => '琓', '�U' => '琔', '�V' => '琕', '�W' => '琖', '�X' => '琗', '�Y' => '琘', '�Z' => '琙', '�[' => '琜', '�\\' => '琝', '�]' => '琞', '�^' => '琟', '�_' => '琠', '�`' => '琡', '�a' => '琣', '�b' => '琤', '�c' => '琧', '�d' => '琩', '�e' => '琫', '�f' => '琭', '�g' => '琯', '�h' => '琱', '�i' => '琲', '�j' => '琷', '�k' => '琸', '�l' => '琹', '�m' => '琺', '�n' => '琻', '�o' => '琽', '�p' => '琾', '�q' => '琿', '�r' => '瑀', '�s' => '瑂', '�t' => '瑃', '�u' => '瑄', '�v' => '瑅', '�w' => '瑆', '�x' => '瑇', '�y' => '瑈', '�z' => '瑉', '�{' => '瑊', '�|' => '瑋', '�}' => '瑌', '�~' => '瑍', '��' => '瑎', '��' => '瑏', '��' => '瑐', '��' => '瑑', '��' => '瑒', '��' => '瑓', '��' => '瑔', '��' => '瑖', '��' => '瑘', '��' => '瑝', '��' => '瑠', '��' => '瑡', '��' => '瑢', '��' => '瑣', '��' => '瑤', '��' => '瑥', '��' => '瑦', '��' => '瑧', '��' => '瑨', '��' => '瑩', '��' => '瑪', '��' => '瑫', '��' => '瑬', '��' => '瑮', '��' => '瑯', '��' => '瑱', '��' => '瑲', '��' => '瑳', '��' => '瑴', '��' => '瑵', '��' => '瑸', '��' => '瑹', '��' => '瑺', '�@' => '瑻', '�A' => '瑼', '�B' => '瑽', '�C' => '瑿', '�D' => '璂', '�E' => '璄', '�F' => '璅', '�G' => '璆', '�H' => '璈', '�I' => '璉', '�J' => '璊', '�K' => '璌', '�L' => '璍', '�M' => '璏', '�N' => '璑', '�O' => '璒', '�P' => '璓', '�Q' => '璔', '�R' => '璕', '�S' => '璖', '�T' => '璗', '�U' => '璘', '�V' => '璙', '�W' => '璚', '�X' => '璛', '�Y' => '璝', '�Z' => '璟', '�[' => '璠', '�\\' => '璡', '�]' => '璢', '�^' => '璣', '�_' => '璤', '�`' => '璥', '�a' => '璦', '�b' => '璪', '�c' => '璫', '�d' => '璬', '�e' => '璭', '�f' => '璮', '�g' => '璯', '�h' => '環', '�i' => '璱', '�j' => '璲', '�k' => '璳', '�l' => '璴', '�m' => '璵', '�n' => '璶', '�o' => '璷', '�p' => '璸', '�q' => '璹', '�r' => '璻', '�s' => '璼', '�t' => '璽', '�u' => '璾', '�v' => '璿', '�w' => '瓀', '�x' => '瓁', '�y' => '瓂', '�z' => '瓃', '�{' => '瓄', '�|' => '瓅', '�}' => '瓆', '�~' => '瓇', '��' => '瓈', '��' => '瓉', '��' => '瓊', '��' => '瓋', '��' => '瓌', '��' => '瓍', '��' => '瓎', '��' => '瓏', '��' => '瓐', '��' => '瓑', '��' => '瓓', '��' => '瓔', '��' => '瓕', '��' => '瓖', '��' => '瓗', '��' => '瓘', '��' => '瓙', '��' => '瓚', '��' => '瓛', '��' => '瓝', '��' => '瓟', '��' => '瓡', '��' => '瓥', '��' => '瓧', '��' => '瓨', '��' => '瓩', '��' => '瓪', '��' => '瓫', '��' => '瓬', '��' => '瓭', '��' => '瓰', '��' => '瓱', '��' => '瓲', '�@' => '瓳', '�A' => '瓵', '�B' => '瓸', '�C' => '瓹', '�D' => '瓺', '�E' => '瓻', '�F' => '瓼', '�G' => '瓽', '�H' => '瓾', '�I' => '甀', '�J' => '甁', '�K' => '甂', '�L' => '甃', '�M' => '甅', '�N' => '甆', '�O' => '甇', '�P' => '甈', '�Q' => '甉', '�R' => '甊', '�S' => '甋', '�T' => '甌', '�U' => '甎', '�V' => '甐', '�W' => '甒', '�X' => '甔', '�Y' => '甕', '�Z' => '甖', '�[' => '甗', '�\\' => '甛', '�]' => '甝', '�^' => '甞', '�_' => '甠', '�`' => '甡', '�a' => '產', '�b' => '産', '�c' => '甤', '�d' => '甦', '�e' => '甧', '�f' => '甪', '�g' => '甮', '�h' => '甴', '�i' => '甶', '�j' => '甹', '�k' => '甼', '�l' => '甽', '�m' => '甿', '�n' => '畁', '�o' => '畂', '�p' => '畃', '�q' => '畄', '�r' => '畆', '�s' => '畇', '�t' => '畉', '�u' => '畊', '�v' => '畍', '�w' => '畐', '�x' => '畑', '�y' => '畒', '�z' => '畓', '�{' => '畕', '�|' => '畖', '�}' => '畗', '�~' => '畘', '��' => '畝', '��' => '畞', '��' => '畟', '��' => '畠', '��' => '畡', '��' => '畢', '��' => '畣', '��' => '畤', '��' => '畧', '��' => '畨', '��' => '畩', '��' => '畫', '��' => '畬', '��' => '畭', '��' => '畮', '��' => '畯', '��' => '異', '��' => '畱', '��' => '畳', '��' => '畵', '��' => '當', '��' => '畷', '��' => '畺', '��' => '畻', '��' => '畼', '��' => '畽', '��' => '畾', '��' => '疀', '��' => '疁', '��' => '疂', '��' => '疄', '��' => '疅', '��' => '疇', '�@' => '疈', '�A' => '疉', '�B' => '疊', '�C' => '疌', '�D' => '疍', '�E' => '疎', '�F' => '疐', '�G' => '疓', '�H' => '疕', '�I' => '疘', '�J' => '疛', '�K' => '疜', '�L' => '疞', '�M' => '疢', '�N' => '疦', '�O' => '疧', '�P' => '疨', '�Q' => '疩', '�R' => '疪', '�S' => '疭', '�T' => '疶', '�U' => '疷', '�V' => '疺', '�W' => '疻', '�X' => '疿', '�Y' => '痀', '�Z' => '痁', '�[' => '痆', '�\\' => '痋', '�]' => '痌', '�^' => '痎', '�_' => '痏', '�`' => '痐', '�a' => '痑', '�b' => '痓', '�c' => '痗', '�d' => '痙', '�e' => '痚', '�f' => '痜', '�g' => '痝', '�h' => '痟', '�i' => '痠', '�j' => '痡', '�k' => '痥', '�l' => '痩', '�m' => '痬', '�n' => '痭', '�o' => '痮', '�p' => '痯', '�q' => '痲', '�r' => '痳', '�s' => '痵', '�t' => '痶', '�u' => '痷', '�v' => '痸', '�w' => '痺', '�x' => '痻', '�y' => '痽', '�z' => '痾', '�{' => '瘂', '�|' => '瘄', '�}' => '瘆', '�~' => '瘇', '��' => '瘈', '��' => '瘉', '��' => '瘋', '��' => '瘍', '��' => '瘎', '��' => '瘏', '��' => '瘑', '��' => '瘒', '��' => '瘓', '��' => '瘔', '��' => '瘖', '��' => '瘚', '��' => '瘜', '��' => '瘝', '��' => '瘞', '��' => '瘡', '��' => '瘣', '��' => '瘧', '��' => '瘨', '��' => '瘬', '��' => '瘮', '��' => '瘯', '��' => '瘱', '��' => '瘲', '��' => '瘶', '��' => '瘷', '��' => '瘹', '��' => '瘺', '��' => '瘻', '��' => '瘽', '��' => '癁', '��' => '療', '��' => '癄', '�@' => '癅', '�A' => '癆', '�B' => '癇', '�C' => '癈', '�D' => '癉', '�E' => '癊', '�F' => '癋', '�G' => '癎', '�H' => '癏', '�I' => '癐', '�J' => '癑', '�K' => '癒', '�L' => '癓', '�M' => '癕', '�N' => '癗', '�O' => '癘', '�P' => '癙', '�Q' => '癚', '�R' => '癛', '�S' => '癝', '�T' => '癟', '�U' => '癠', '�V' => '癡', '�W' => '癢', '�X' => '癤', '�Y' => '癥', '�Z' => '癦', '�[' => '癧', '�\\' => '癨', '�]' => '癩', '�^' => '癪', '�_' => '癬', '�`' => '癭', '�a' => '癮', '�b' => '癰', '�c' => '癱', '�d' => '癲', '�e' => '癳', '�f' => '癴', '�g' => '癵', '�h' => '癶', '�i' => '癷', '�j' => '癹', '�k' => '発', '�l' => '發', '�m' => '癿', '�n' => '皀', '�o' => '皁', '�p' => '皃', '�q' => '皅', '�r' => '皉', '�s' => '皊', '�t' => '皌', '�u' => '皍', '�v' => '皏', '�w' => '皐', '�x' => '皒', '�y' => '皔', '�z' => '皕', '�{' => '皗', '�|' => '皘', '�}' => '皚', '�~' => '皛', '��' => '皜', '��' => '皝', '��' => '皞', '��' => '皟', '��' => '皠', '��' => '皡', '��' => '皢', '��' => '皣', '��' => '皥', '��' => '皦', '��' => '皧', '��' => '皨', '��' => '皩', '��' => '皪', '��' => '皫', '��' => '皬', '��' => '皭', '��' => '皯', '��' => '皰', '��' => '皳', '��' => '皵', '��' => '皶', '��' => '皷', '��' => '皸', '��' => '皹', '��' => '皺', '��' => '皻', '��' => '皼', '��' => '皽', '��' => '皾', '��' => '盀', '��' => '盁', '��' => '盃', '��' => '啊', '��' => '阿', '��' => '埃', '��' => '挨', '��' => '哎', '��' => '唉', '��' => '哀', '��' => '皑', '��' => '癌', '��' => '蔼', '��' => '矮', '��' => '艾', '��' => '碍', '��' => '爱', '��' => '隘', '��' => '鞍', '��' => '氨', '��' => '安', '��' => '俺', '��' => '按', '��' => '暗', '��' => '岸', '��' => '胺', '��' => '案', '��' => '肮', '��' => '昂', '��' => '盎', '��' => '凹', '��' => '敖', '��' => '熬', '��' => '翱', '��' => '袄', '��' => '傲', '��' => '奥', '��' => '懊', '��' => '澳', '��' => '芭', '��' => '捌', '��' => '扒', '��' => '叭', '��' => '吧', '��' => '笆', '��' => '八', '��' => '疤', '��' => '巴', '��' => '拔', '��' => '跋', '��' => '靶', '��' => '把', '��' => '耙', '��' => '坝', '��' => '霸', '��' => '罢', '��' => '爸', '��' => '白', '��' => '柏', '��' => '百', '��' => '摆', '��' => '佰', '��' => '败', '��' => '拜', '��' => '稗', '��' => '斑', '��' => '班', '��' => '搬', '��' => '扳', '��' => '般', '��' => '颁', '��' => '板', '��' => '版', '��' => '扮', '��' => '拌', '��' => '伴', '��' => '瓣', '��' => '半', '��' => '办', '��' => '绊', '��' => '邦', '��' => '帮', '��' => '梆', '��' => '榜', '��' => '膀', '��' => '绑', '��' => '棒', '��' => '磅', '��' => '蚌', '��' => '镑', '��' => '傍', '��' => '谤', '��' => '苞', '��' => '胞', '��' => '包', '��' => '褒', '��' => '剥', '�@' => '盄', '�A' => '盇', '�B' => '盉', '�C' => '盋', '�D' => '盌', '�E' => '盓', '�F' => '盕', '�G' => '盙', '�H' => '盚', '�I' => '盜', '�J' => '盝', '�K' => '盞', '�L' => '盠', '�M' => '盡', '�N' => '盢', '�O' => '監', '�P' => '盤', '�Q' => '盦', '�R' => '盧', '�S' => '盨', '�T' => '盩', '�U' => '盪', '�V' => '盫', '�W' => '盬', '�X' => '盭', '�Y' => '盰', '�Z' => '盳', '�[' => '盵', '�\\' => '盶', '�]' => '盷', '�^' => '盺', '�_' => '盻', '�`' => '盽', '�a' => '盿', '�b' => '眀', '�c' => '眂', '�d' => '眃', '�e' => '眅', '�f' => '眆', '�g' => '眊', '�h' => '県', '�i' => '眎', '�j' => '眏', '�k' => '眐', '�l' => '眑', '�m' => '眒', '�n' => '眓', '�o' => '眔', '�p' => '眕', '�q' => '眖', '�r' => '眗', '�s' => '眘', '�t' => '眛', '�u' => '眜', '�v' => '眝', '�w' => '眞', '�x' => '眡', '�y' => '眣', '�z' => '眤', '�{' => '眥', '�|' => '眧', '�}' => '眪', '�~' => '眫', '��' => '眬', '��' => '眮', '��' => '眰', '��' => '眱', '��' => '眲', '��' => '眳', '��' => '眴', '��' => '眹', '��' => '眻', '��' => '眽', '��' => '眾', '��' => '眿', '��' => '睂', '��' => '睄', '��' => '睅', '��' => '睆', '��' => '睈', '��' => '睉', '��' => '睊', '��' => '睋', '��' => '睌', '��' => '睍', '��' => '睎', '��' => '睏', '��' => '睒', '��' => '睓', '��' => '睔', '��' => '睕', '��' => '睖', '��' => '睗', '��' => '睘', '��' => '睙', '��' => '睜', '��' => '薄', '��' => '雹', '��' => '保', '��' => '堡', '��' => '饱', '��' => '宝', '��' => '抱', '��' => '报', '��' => '暴', '��' => '豹', '��' => '鲍', '��' => '爆', '��' => '杯', '��' => '碑', '��' => '悲', '��' => '卑', '��' => '北', '��' => '辈', '��' => '背', '��' => '贝', '��' => '钡', '��' => '倍', '��' => '狈', '��' => '备', '��' => '惫', '��' => '焙', '��' => '被', '��' => '奔', '��' => '苯', '��' => '本', '��' => '笨', '��' => '崩', '��' => '绷', '��' => '甭', '��' => '泵', '��' => '蹦', '��' => '迸', '��' => '逼', '��' => '鼻', '��' => '比', '��' => '鄙', '��' => '笔', '��' => '彼', '��' => '碧', '��' => '蓖', '��' => '蔽', '��' => '毕', '��' => '毙', '��' => '毖', '��' => '币', '��' => '庇', '��' => '痹', '��' => '闭', '��' => '敝', '��' => '弊', '��' => '必', '��' => '辟', '��' => '壁', '��' => '臂', '��' => '避', '��' => '陛', '��' => '鞭', '��' => '边', '��' => '编', '��' => '贬', '��' => '扁', '��' => '便', '��' => '变', '��' => '卞', '��' => '辨', '��' => '辩', '��' => '辫', '��' => '遍', '��' => '标', '��' => '彪', '��' => '膘', '��' => '表', '��' => '鳖', '��' => '憋', '��' => '别', '��' => '瘪', '��' => '彬', '��' => '斌', '��' => '濒', '��' => '滨', '��' => '宾', '��' => '摈', '��' => '兵', '��' => '冰', '��' => '柄', '��' => '丙', '��' => '秉', '��' => '饼', '��' => '炳', '�@' => '睝', '�A' => '睞', '�B' => '睟', '�C' => '睠', '�D' => '睤', '�E' => '睧', '�F' => '睩', '�G' => '睪', '�H' => '睭', '�I' => '睮', '�J' => '睯', '�K' => '睰', '�L' => '睱', '�M' => '睲', '�N' => '睳', '�O' => '睴', '�P' => '睵', '�Q' => '睶', '�R' => '睷', '�S' => '睸', '�T' => '睺', '�U' => '睻', '�V' => '睼', '�W' => '瞁', '�X' => '瞂', '�Y' => '瞃', '�Z' => '瞆', '�[' => '瞇', '�\\' => '瞈', '�]' => '瞉', '�^' => '瞊', '�_' => '瞋', '�`' => '瞏', '�a' => '瞐', '�b' => '瞓', '�c' => '瞔', '�d' => '瞕', '�e' => '瞖', '�f' => '瞗', '�g' => '瞘', '�h' => '瞙', '�i' => '瞚', '�j' => '瞛', '�k' => '瞜', '�l' => '瞝', '�m' => '瞞', '�n' => '瞡', '�o' => '瞣', '�p' => '瞤', '�q' => '瞦', '�r' => '瞨', '�s' => '瞫', '�t' => '瞭', '�u' => '瞮', '�v' => '瞯', '�w' => '瞱', '�x' => '瞲', '�y' => '瞴', '�z' => '瞶', '�{' => '瞷', '�|' => '瞸', '�}' => '瞹', '�~' => '瞺', '��' => '瞼', '��' => '瞾', '��' => '矀', '��' => '矁', '��' => '矂', '��' => '矃', '��' => '矄', '��' => '矅', '��' => '矆', '��' => '矇', '��' => '矈', '��' => '矉', '��' => '矊', '��' => '矋', '��' => '矌', '��' => '矎', '��' => '矏', '��' => '矐', '��' => '矑', '��' => '矒', '��' => '矓', '��' => '矔', '��' => '矕', '��' => '矖', '��' => '矘', '��' => '矙', '��' => '矚', '��' => '矝', '��' => '矞', '��' => '矟', '��' => '矠', '��' => '矡', '��' => '矤', '��' => '病', '��' => '并', '��' => '玻', '��' => '菠', '��' => '播', '��' => '拨', '��' => '钵', '��' => '波', '��' => '博', '��' => '勃', '��' => '搏', '��' => '铂', '��' => '箔', '��' => '伯', '��' => '帛', '��' => '舶', '��' => '脖', '��' => '膊', '��' => '渤', '��' => '泊', '��' => '驳', '��' => '捕', '��' => '卜', '��' => '哺', '��' => '补', '��' => '埠', '��' => '不', '��' => '布', '��' => '步', '��' => '簿', '��' => '部', '��' => '怖', '��' => '擦', '��' => '猜', '��' => '裁', '��' => '材', '��' => '才', '��' => '财', '��' => '睬', '��' => '踩', '��' => '采', '��' => '彩', '��' => '菜', '��' => '蔡', '��' => '餐', '��' => '参', '��' => '蚕', '��' => '残', '��' => '惭', '��' => '惨', '��' => '灿', '��' => '苍', '��' => '舱', '��' => '仓', '��' => '沧', '��' => '藏', '��' => '操', '��' => '糙', '��' => '槽', '��' => '曹', '��' => '草', '��' => '厕', '��' => '策', '��' => '侧', '��' => '册', '��' => '测', '��' => '层', '��' => '蹭', '��' => '插', '��' => '叉', '��' => '茬', '��' => '茶', '��' => '查', '��' => '碴', '��' => '搽', '��' => '察', '��' => '岔', '��' => '差', '��' => '诧', '��' => '拆', '��' => '柴', '��' => '豺', '��' => '搀', '��' => '掺', '��' => '蝉', '��' => '馋', '��' => '谗', '��' => '缠', '��' => '铲', '��' => '产', '��' => '阐', '��' => '颤', '��' => '昌', '��' => '猖', '�@' => '矦', '�A' => '矨', '�B' => '矪', '�C' => '矯', '�D' => '矰', '�E' => '矱', '�F' => '矲', '�G' => '矴', '�H' => '矵', '�I' => '矷', '�J' => '矹', '�K' => '矺', '�L' => '矻', '�M' => '矼', '�N' => '砃', '�O' => '砄', '�P' => '砅', '�Q' => '砆', '�R' => '砇', '�S' => '砈', '�T' => '砊', '�U' => '砋', '�V' => '砎', '�W' => '砏', '�X' => '砐', '�Y' => '砓', '�Z' => '砕', '�[' => '砙', '�\\' => '砛', '�]' => '砞', '�^' => '砠', '�_' => '砡', '�`' => '砢', '�a' => '砤', '�b' => '砨', '�c' => '砪', '�d' => '砫', '�e' => '砮', '�f' => '砯', '�g' => '砱', '�h' => '砲', '�i' => '砳', '�j' => '砵', '�k' => '砶', '�l' => '砽', '�m' => '砿', '�n' => '硁', '�o' => '硂', '�p' => '硃', '�q' => '硄', '�r' => '硆', '�s' => '硈', '�t' => '硉', '�u' => '硊', '�v' => '硋', '�w' => '硍', '�x' => '硏', '�y' => '硑', '�z' => '硓', '�{' => '硔', '�|' => '硘', '�}' => '硙', '�~' => '硚', '��' => '硛', '��' => '硜', '��' => '硞', '��' => '硟', '��' => '硠', '��' => '硡', '��' => '硢', '��' => '硣', '��' => '硤', '��' => '硥', '��' => '硦', '��' => '硧', '��' => '硨', '��' => '硩', '��' => '硯', '��' => '硰', '��' => '硱', '��' => '硲', '��' => '硳', '��' => '硴', '��' => '硵', '��' => '硶', '��' => '硸', '��' => '硹', '��' => '硺', '��' => '硻', '��' => '硽', '��' => '硾', '��' => '硿', '��' => '碀', '��' => '碁', '��' => '碂', '��' => '碃', '��' => '场', '��' => '尝', '��' => '常', '��' => '长', '��' => '偿', '��' => '肠', '��' => '厂', '��' => '敞', '��' => '畅', '��' => '唱', '��' => '倡', '��' => '超', '��' => '抄', '��' => '钞', '��' => '朝', '��' => '嘲', '��' => '潮', '��' => '巢', '��' => '吵', '��' => '炒', '��' => '车', '��' => '扯', '��' => '撤', '��' => '掣', '��' => '彻', '��' => '澈', '��' => '郴', '��' => '臣', '��' => '辰', '��' => '尘', '��' => '晨', '��' => '忱', '��' => '沉', '��' => '陈', '��' => '趁', '��' => '衬', '��' => '撑', '��' => '称', '��' => '城', '��' => '橙', '��' => '成', '��' => '呈', '��' => '乘', '��' => '程', '��' => '惩', '��' => '澄', '��' => '诚', '��' => '承', '��' => '逞', '��' => '骋', '��' => '秤', '��' => '吃', '��' => '痴', '��' => '持', '��' => '匙', '��' => '池', '��' => '迟', '��' => '弛', '��' => '驰', '��' => '耻', '��' => '齿', '��' => '侈', '��' => '尺', '��' => '赤', '��' => '翅', '��' => '斥', '��' => '炽', '��' => '充', '��' => '冲', '��' => '虫', '��' => '崇', '��' => '宠', '��' => '抽', '��' => '酬', '��' => '畴', '��' => '踌', '��' => '稠', '��' => '愁', '��' => '筹', '��' => '仇', '��' => '绸', '��' => '瞅', '��' => '丑', '��' => '臭', '��' => '初', '��' => '出', '��' => '橱', '��' => '厨', '��' => '躇', '��' => '锄', '��' => '雏', '��' => '滁', '��' => '除', '��' => '楚', '�@' => '碄', '�A' => '碅', '�B' => '碆', '�C' => '碈', '�D' => '碊', '�E' => '碋', '�F' => '碏', '�G' => '碐', '�H' => '碒', '�I' => '碔', '�J' => '碕', '�K' => '碖', '�L' => '碙', '�M' => '碝', '�N' => '碞', '�O' => '碠', '�P' => '碢', '�Q' => '碤', '�R' => '碦', '�S' => '碨', '�T' => '碩', '�U' => '碪', '�V' => '碫', '�W' => '碬', '�X' => '碭', '�Y' => '碮', '�Z' => '碯', '�[' => '碵', '�\\' => '碶', '�]' => '碷', '�^' => '碸', '�_' => '確', '�`' => '碻', '�a' => '碼', '�b' => '碽', '�c' => '碿', '�d' => '磀', '�e' => '磂', '�f' => '磃', '�g' => '磄', '�h' => '磆', '�i' => '磇', '�j' => '磈', '�k' => '磌', '�l' => '磍', '�m' => '磎', '�n' => '磏', '�o' => '磑', '�p' => '磒', '�q' => '磓', '�r' => '磖', '�s' => '磗', '�t' => '磘', '�u' => '磚', '�v' => '磛', '�w' => '磜', '�x' => '磝', '�y' => '磞', '�z' => '磟', '�{' => '磠', '�|' => '磡', '�}' => '磢', '�~' => '磣', '��' => '磤', '��' => '磥', '��' => '磦', '��' => '磧', '��' => '磩', '��' => '磪', '��' => '磫', '��' => '磭', '��' => '磮', '��' => '磯', '��' => '磰', '��' => '磱', '��' => '磳', '��' => '磵', '��' => '磶', '��' => '磸', '��' => '磹', '��' => '磻', '��' => '磼', '��' => '磽', '��' => '磾', '��' => '磿', '��' => '礀', '��' => '礂', '��' => '礃', '��' => '礄', '��' => '礆', '��' => '礇', '��' => '礈', '��' => '礉', '��' => '礊', '��' => '礋', '��' => '礌', '��' => '础', '��' => '储', '��' => '矗', '��' => '搐', '��' => '触', '��' => '处', '��' => '揣', '��' => '川', '��' => '穿', '��' => '椽', '��' => '传', '��' => '船', '��' => '喘', '��' => '串', '��' => '疮', '��' => '窗', '��' => '幢', '��' => '床', '��' => '闯', '��' => '创', '��' => '吹', '��' => '炊', '��' => '捶', '��' => '锤', '��' => '垂', '��' => '春', '��' => '椿', '��' => '醇', '��' => '唇', '��' => '淳', '��' => '纯', '��' => '蠢', '��' => '戳', '��' => '绰', '��' => '疵', '��' => '茨', '��' => '磁', '��' => '雌', '��' => '辞', '��' => '慈', '��' => '瓷', '��' => '词', '��' => '此', '��' => '刺', '��' => '赐', '��' => '次', '��' => '聪', '��' => '葱', '��' => '囱', '��' => '匆', '��' => '从', '��' => '丛', '��' => '凑', '��' => '粗', '��' => '醋', '��' => '簇', '��' => '促', '��' => '蹿', '��' => '篡', '��' => '窜', '��' => '摧', '��' => '崔', '��' => '催', '��' => '脆', '��' => '瘁', '��' => '粹', '��' => '淬', '��' => '翠', '��' => '村', '��' => '存', '��' => '寸', '��' => '磋', '��' => '撮', '��' => '搓', '��' => '措', '��' => '挫', '��' => '错', '��' => '搭', '��' => '达', '��' => '答', '��' => '瘩', '��' => '打', '��' => '大', '��' => '呆', '��' => '歹', '��' => '傣', '��' => '戴', '��' => '带', '��' => '殆', '��' => '代', '��' => '贷', '��' => '袋', '��' => '待', '��' => '逮', '�@' => '礍', '�A' => '礎', '�B' => '礏', '�C' => '礐', '�D' => '礑', '�E' => '礒', '�F' => '礔', '�G' => '礕', '�H' => '礖', '�I' => '礗', '�J' => '礘', '�K' => '礙', '�L' => '礚', '�M' => '礛', '�N' => '礜', '�O' => '礝', '�P' => '礟', '�Q' => '礠', '�R' => '礡', '�S' => '礢', '�T' => '礣', '�U' => '礥', '�V' => '礦', '�W' => '礧', '�X' => '礨', '�Y' => '礩', '�Z' => '礪', '�[' => '礫', '�\\' => '礬', '�]' => '礭', '�^' => '礮', '�_' => '礯', '�`' => '礰', '�a' => '礱', '�b' => '礲', '�c' => '礳', '�d' => '礵', '�e' => '礶', '�f' => '礷', '�g' => '礸', '�h' => '礹', '�i' => '礽', '�j' => '礿', '�k' => '祂', '�l' => '祃', '�m' => '祄', '�n' => '祅', '�o' => '祇', '�p' => '祊', '�q' => '祋', '�r' => '祌', '�s' => '祍', '�t' => '祎', '�u' => '祏', '�v' => '祐', '�w' => '祑', '�x' => '祒', '�y' => '祔', '�z' => '祕', '�{' => '祘', '�|' => '祙', '�}' => '祡', '�~' => '祣', '��' => '祤', '��' => '祦', '��' => '祩', '��' => '祪', '��' => '祫', '��' => '祬', '��' => '祮', '��' => '祰', '��' => '祱', '��' => '祲', '��' => '祳', '��' => '祴', '��' => '祵', '��' => '祶', '��' => '祹', '��' => '祻', '��' => '祼', '��' => '祽', '��' => '祾', '��' => '祿', '��' => '禂', '��' => '禃', '��' => '禆', '��' => '禇', '��' => '禈', '��' => '禉', '��' => '禋', '��' => '禌', '��' => '禍', '��' => '禎', '��' => '禐', '��' => '禑', '��' => '禒', '��' => '怠', '��' => '耽', '��' => '担', '��' => '丹', '��' => '单', '��' => '郸', '��' => '掸', '��' => '胆', '��' => '旦', '��' => '氮', '��' => '但', '��' => '惮', '��' => '淡', '��' => '诞', '��' => '弹', '��' => '蛋', '��' => '当', '��' => '挡', '��' => '党', '��' => '荡', '��' => '档', '��' => '刀', '��' => '捣', '��' => '蹈', '��' => '倒', '��' => '岛', '��' => '祷', '��' => '导', '��' => '到', '��' => '稻', '��' => '悼', '��' => '道', '��' => '盗', '��' => '德', '��' => '得', '��' => '的', '��' => '蹬', '��' => '灯', '��' => '登', '��' => '等', '��' => '瞪', '��' => '凳', '��' => '邓', '��' => '堤', '��' => '低', '��' => '滴', '��' => '迪', '��' => '敌', '��' => '笛', '��' => '狄', '��' => '涤', '��' => '翟', '��' => '嫡', '��' => '抵', '��' => '底', '��' => '地', '��' => '蒂', '��' => '第', '��' => '帝', '��' => '弟', '��' => '递', '��' => '缔', '��' => '颠', '��' => '掂', '��' => '滇', '��' => '碘', '��' => '点', '��' => '典', '��' => '靛', '��' => '垫', '��' => '电', '��' => '佃', '��' => '甸', '��' => '店', '��' => '惦', '��' => '奠', '��' => '淀', '��' => '殿', '��' => '碉', '��' => '叼', '��' => '雕', '��' => '凋', '��' => '刁', '��' => '掉', '��' => '吊', '��' => '钓', '��' => '调', '��' => '跌', '��' => '爹', '��' => '碟', '��' => '蝶', '��' => '迭', '��' => '谍', '��' => '叠', '�@' => '禓', '�A' => '禔', '�B' => '禕', '�C' => '禖', '�D' => '禗', '�E' => '禘', '�F' => '禙', '�G' => '禛', '�H' => '禜', '�I' => '禝', '�J' => '禞', '�K' => '禟', '�L' => '禠', '�M' => '禡', '�N' => '禢', '�O' => '禣', '�P' => '禤', '�Q' => '禥', '�R' => '禦', '�S' => '禨', '�T' => '禩', '�U' => '禪', '�V' => '禫', '�W' => '禬', '�X' => '禭', '�Y' => '禮', '�Z' => '禯', '�[' => '禰', '�\\' => '禱', '�]' => '禲', '�^' => '禴', '�_' => '禵', '�`' => '禶', '�a' => '禷', '�b' => '禸', '�c' => '禼', '�d' => '禿', '�e' => '秂', '�f' => '秄', '�g' => '秅', '�h' => '秇', '�i' => '秈', '�j' => '秊', '�k' => '秌', '�l' => '秎', '�m' => '秏', '�n' => '秐', '�o' => '秓', '�p' => '秔', '�q' => '秖', '�r' => '秗', '�s' => '秙', '�t' => '秚', '�u' => '秛', '�v' => '秜', '�w' => '秝', '�x' => '秞', '�y' => '秠', '�z' => '秡', '�{' => '秢', '�|' => '秥', '�}' => '秨', '�~' => '秪', '��' => '秬', '��' => '秮', '��' => '秱', '��' => '秲', '��' => '秳', '��' => '秴', '��' => '秵', '��' => '秶', '��' => '秷', '��' => '秹', '��' => '秺', '��' => '秼', '��' => '秾', '��' => '秿', '��' => '稁', '��' => '稄', '��' => '稅', '��' => '稇', '��' => '稈', '��' => '稉', '��' => '稊', '��' => '稌', '��' => '稏', '��' => '稐', '��' => '稑', '��' => '稒', '��' => '稓', '��' => '稕', '��' => '稖', '��' => '稘', '��' => '稙', '��' => '稛', '��' => '稜', '��' => '丁', '��' => '盯', '��' => '叮', '��' => '钉', '��' => '顶', '��' => '鼎', '��' => '锭', '��' => '定', '��' => '订', '��' => '丢', '��' => '东', '��' => '冬', '��' => '董', '��' => '懂', '��' => '动', '��' => '栋', '��' => '侗', '��' => '恫', '��' => '冻', '��' => '洞', '��' => '兜', '��' => '抖', '��' => '斗', '��' => '陡', '��' => '豆', '��' => '逗', '��' => '痘', '��' => '都', '��' => '督', '��' => '毒', '��' => '犊', '��' => '独', '��' => '读', '��' => '堵', '��' => '睹', '��' => '赌', '��' => '杜', '��' => '镀', '��' => '肚', '��' => '度', '��' => '渡', '��' => '妒', '��' => '端', '��' => '短', '��' => '锻', '��' => '段', '��' => '断', '��' => '缎', '��' => '堆', '��' => '兑', '��' => '队', '��' => '对', '��' => '墩', '��' => '吨', '��' => '蹲', '��' => '敦', '��' => '顿', '��' => '囤', '��' => '钝', '��' => '盾', '��' => '遁', '��' => '掇', '��' => '哆', '��' => '多', '��' => '夺', '��' => '垛', '��' => '躲', '��' => '朵', '��' => '跺', '��' => '舵', '��' => '剁', '��' => '惰', '��' => '堕', '��' => '蛾', '��' => '峨', '��' => '鹅', '��' => '俄', '��' => '额', '��' => '讹', '��' => '娥', '��' => '恶', '��' => '厄', '��' => '扼', '��' => '遏', '��' => '鄂', '��' => '饿', '��' => '恩', '��' => '而', '��' => '儿', '��' => '耳', '��' => '尔', '��' => '饵', '��' => '洱', '��' => '二', '�@' => '稝', '�A' => '稟', '�B' => '稡', '�C' => '稢', '�D' => '稤', '�E' => '稥', '�F' => '稦', '�G' => '稧', '�H' => '稨', '�I' => '稩', '�J' => '稪', '�K' => '稫', '�L' => '稬', '�M' => '稭', '�N' => '種', '�O' => '稯', '�P' => '稰', '�Q' => '稱', '�R' => '稲', '�S' => '稴', '�T' => '稵', '�U' => '稶', '�V' => '稸', '�W' => '稺', '�X' => '稾', '�Y' => '穀', '�Z' => '穁', '�[' => '穂', '�\\' => '穃', '�]' => '穄', '�^' => '穅', '�_' => '穇', '�`' => '穈', '�a' => '穉', '�b' => '穊', '�c' => '穋', '�d' => '穌', '�e' => '積', '�f' => '穎', '�g' => '穏', '�h' => '穐', '�i' => '穒', '�j' => '穓', '�k' => '穔', '�l' => '穕', '�m' => '穖', '�n' => '穘', '�o' => '穙', '�p' => '穚', '�q' => '穛', '�r' => '穜', '�s' => '穝', '�t' => '穞', '�u' => '穟', '�v' => '穠', '�w' => '穡', '�x' => '穢', '�y' => '穣', '�z' => '穤', '�{' => '穥', '�|' => '穦', '�}' => '穧', '�~' => '穨', '��' => '穩', '��' => '穪', '��' => '穫', '��' => '穬', '��' => '穭', '��' => '穮', '��' => '穯', '��' => '穱', '��' => '穲', '��' => '穳', '��' => '穵', '��' => '穻', '��' => '穼', '��' => '穽', '��' => '穾', '��' => '窂', '��' => '窅', '��' => '窇', '��' => '窉', '��' => '窊', '��' => '窋', '��' => '窌', '��' => '窎', '��' => '窏', '��' => '窐', '��' => '窓', '��' => '窔', '��' => '窙', '��' => '窚', '��' => '窛', '��' => '窞', '��' => '窡', '��' => '窢', '��' => '贰', '��' => '发', '��' => '罚', '��' => '筏', '��' => '伐', '��' => '乏', '��' => '阀', '��' => '法', '��' => '珐', '��' => '藩', '��' => '帆', '��' => '番', '��' => '翻', '��' => '樊', '��' => '矾', '��' => '钒', '��' => '繁', '��' => '凡', '��' => '烦', '��' => '反', '��' => '返', '��' => '范', '��' => '贩', '��' => '犯', '��' => '饭', '��' => '泛', '��' => '坊', '��' => '芳', '��' => '方', '��' => '肪', '��' => '房', '��' => '防', '��' => '妨', '��' => '仿', '��' => '访', '��' => '纺', '��' => '放', '��' => '菲', '��' => '非', '��' => '啡', '��' => '飞', '��' => '肥', '��' => '匪', '��' => '诽', '��' => '吠', '��' => '肺', '��' => '废', '��' => '沸', '��' => '费', '��' => '芬', '��' => '酚', '��' => '吩', '��' => '氛', '��' => '分', '��' => '纷', '��' => '坟', '��' => '焚', '��' => '汾', '��' => '粉', '��' => '奋', '��' => '份', '��' => '忿', '��' => '愤', '��' => '粪', '��' => '丰', '��' => '封', '��' => '枫', '��' => '蜂', '��' => '峰', '��' => '锋', '��' => '风', '��' => '疯', '��' => '烽', '��' => '逢', '��' => '冯', '��' => '缝', '��' => '讽', '��' => '奉', '��' => '凤', '��' => '佛', '��' => '否', '��' => '夫', '��' => '敷', '��' => '肤', '��' => '孵', '��' => '扶', '��' => '拂', '��' => '辐', '��' => '幅', '��' => '氟', '��' => '符', '��' => '伏', '��' => '俘', '��' => '服', '�@' => '窣', '�A' => '窤', '�B' => '窧', '�C' => '窩', '�D' => '窪', '�E' => '窫', '�F' => '窮', '�G' => '窯', '�H' => '窰', '�I' => '窱', '�J' => '窲', '�K' => '窴', '�L' => '窵', '�M' => '窶', '�N' => '窷', '�O' => '窸', '�P' => '窹', '�Q' => '窺', '�R' => '窻', '�S' => '窼', '�T' => '窽', '�U' => '窾', '�V' => '竀', '�W' => '竁', '�X' => '竂', '�Y' => '竃', '�Z' => '竄', '�[' => '竅', '�\\' => '竆', '�]' => '竇', '�^' => '竈', '�_' => '竉', '�`' => '竊', '�a' => '竌', '�b' => '竍', '�c' => '竎', '�d' => '竏', '�e' => '竐', '�f' => '竑', '�g' => '竒', '�h' => '竓', '�i' => '竔', '�j' => '竕', '�k' => '竗', '�l' => '竘', '�m' => '竚', '�n' => '竛', '�o' => '竜', '�p' => '竝', '�q' => '竡', '�r' => '竢', '�s' => '竤', '�t' => '竧', '�u' => '竨', '�v' => '竩', '�w' => '竪', '�x' => '竫', '�y' => '竬', '�z' => '竮', '�{' => '竰', '�|' => '竱', '�}' => '竲', '�~' => '竳', '��' => '竴', '��' => '竵', '��' => '競', '��' => '竷', '��' => '竸', '��' => '竻', '��' => '竼', '��' => '竾', '��' => '笀', '��' => '笁', '��' => '笂', '��' => '笅', '��' => '笇', '��' => '笉', '��' => '笌', '��' => '笍', '��' => '笎', '��' => '笐', '��' => '笒', '��' => '笓', '��' => '笖', '��' => '笗', '��' => '笘', '��' => '笚', '��' => '笜', '��' => '笝', '��' => '笟', '��' => '笡', '��' => '笢', '��' => '笣', '��' => '笧', '��' => '笩', '��' => '笭', '��' => '浮', '��' => '涪', '��' => '福', '��' => '袱', '��' => '弗', '��' => '甫', '��' => '抚', '��' => '辅', '��' => '俯', '��' => '釜', '��' => '斧', '��' => '脯', '��' => '腑', '��' => '府', '��' => '腐', '��' => '赴', '��' => '副', '��' => '覆', '��' => '赋', '��' => '复', '��' => '傅', '��' => '付', '��' => '阜', '��' => '父', '��' => '腹', '��' => '负', '��' => '富', '��' => '讣', '��' => '附', '��' => '妇', '��' => '缚', '��' => '咐', '��' => '噶', '��' => '嘎', '��' => '该', '��' => '改', '��' => '概', '��' => '钙', '��' => '盖', '��' => '溉', '��' => '干', '��' => '甘', '��' => '杆', '��' => '柑', '��' => '竿', '��' => '肝', '��' => '赶', '��' => '感', '��' => '秆', '��' => '敢', '��' => '赣', '��' => '冈', '��' => '刚', '��' => '钢', '��' => '缸', '��' => '肛', '��' => '纲', '��' => '岗', '��' => '港', '��' => '杠', '��' => '篙', '��' => '皋', '��' => '高', '��' => '膏', '��' => '羔', '��' => '糕', '��' => '搞', '��' => '镐', '��' => '稿', '��' => '告', '��' => '哥', '��' => '歌', '��' => '搁', '��' => '戈', '��' => '鸽', '��' => '胳', '��' => '疙', '��' => '割', '��' => '革', '��' => '葛', '��' => '格', '��' => '蛤', '��' => '阁', '��' => '隔', '��' => '铬', '��' => '个', '��' => '各', '��' => '给', '��' => '根', '��' => '跟', '��' => '耕', '��' => '更', '��' => '庚', '��' => '羹', '�@' => '笯', '�A' => '笰', '�B' => '笲', '�C' => '笴', '�D' => '笵', '�E' => '笶', '�F' => '笷', '�G' => '笹', '�H' => '笻', '�I' => '笽', '�J' => '笿', '�K' => '筀', '�L' => '筁', '�M' => '筂', '�N' => '筃', '�O' => '筄', '�P' => '筆', '�Q' => '筈', '�R' => '筊', '�S' => '筍', '�T' => '筎', '�U' => '筓', '�V' => '筕', '�W' => '筗', '�X' => '筙', '�Y' => '筜', '�Z' => '筞', '�[' => '筟', '�\\' => '筡', '�]' => '筣', '�^' => '筤', '�_' => '筥', '�`' => '筦', '�a' => '筧', '�b' => '筨', '�c' => '筩', '�d' => '筪', '�e' => '筫', '�f' => '筬', '�g' => '筭', '�h' => '筯', '�i' => '筰', '�j' => '筳', '�k' => '筴', '�l' => '筶', '�m' => '筸', '�n' => '筺', '�o' => '筼', '�p' => '筽', '�q' => '筿', '�r' => '箁', '�s' => '箂', '�t' => '箃', '�u' => '箄', '�v' => '箆', '�w' => '箇', '�x' => '箈', '�y' => '箉', '�z' => '箊', '�{' => '箋', '�|' => '箌', '�}' => '箎', '�~' => '箏', '��' => '箑', '��' => '箒', '��' => '箓', '��' => '箖', '��' => '箘', '��' => '箙', '��' => '箚', '��' => '箛', '��' => '箞', '��' => '箟', '��' => '箠', '��' => '箣', '��' => '箤', '��' => '箥', '��' => '箮', '��' => '箯', '��' => '箰', '��' => '箲', '��' => '箳', '��' => '箵', '��' => '箶', '��' => '箷', '��' => '箹', '��' => '箺', '��' => '箻', '��' => '箼', '��' => '箽', '��' => '箾', '��' => '箿', '��' => '節', '��' => '篂', '��' => '篃', '��' => '範', '��' => '埂', '��' => '耿', '��' => '梗', '��' => '工', '��' => '攻', '��' => '功', '��' => '恭', '��' => '龚', '��' => '供', '��' => '躬', '��' => '公', '��' => '宫', '��' => '弓', '��' => '巩', '��' => '汞', '��' => '拱', '��' => '贡', '��' => '共', '��' => '钩', '��' => '勾', '��' => '沟', '��' => '苟', '��' => '狗', '��' => '垢', '��' => '构', '��' => '购', '��' => '够', '��' => '辜', '��' => '菇', '��' => '咕', '��' => '箍', '��' => '估', '��' => '沽', '��' => '孤', '��' => '姑', '��' => '鼓', '��' => '古', '��' => '蛊', '��' => '骨', '��' => '谷', '��' => '股', '��' => '故', '��' => '顾', '��' => '固', '��' => '雇', '��' => '刮', '��' => '瓜', '��' => '剐', '��' => '寡', '��' => '挂', '��' => '褂', '��' => '乖', '��' => '拐', '��' => '怪', '��' => '棺', '��' => '关', '��' => '官', '��' => '冠', '��' => '观', '��' => '管', '��' => '馆', '��' => '罐', '��' => '惯', '��' => '灌', '��' => '贯', '��' => '光', '��' => '广', '��' => '逛', '��' => '瑰', '��' => '规', '��' => '圭', '��' => '硅', '��' => '归', '��' => '龟', '��' => '闺', '��' => '轨', '��' => '鬼', '��' => '诡', '��' => '癸', '��' => '桂', '��' => '柜', '��' => '跪', '��' => '贵', '��' => '刽', '��' => '辊', '��' => '滚', '��' => '棍', '��' => '锅', '��' => '郭', '��' => '国', '��' => '果', '��' => '裹', '��' => '过', '��' => '哈', '�@' => '篅', '�A' => '篈', '�B' => '築', '�C' => '篊', '�D' => '篋', '�E' => '篍', '�F' => '篎', '�G' => '篏', '�H' => '篐', '�I' => '篒', '�J' => '篔', '�K' => '篕', '�L' => '篖', '�M' => '篗', '�N' => '篘', '�O' => '篛', '�P' => '篜', '�Q' => '篞', '�R' => '篟', '�S' => '篠', '�T' => '篢', '�U' => '篣', '�V' => '篤', '�W' => '篧', '�X' => '篨', '�Y' => '篩', '�Z' => '篫', '�[' => '篬', '�\\' => '篭', '�]' => '篯', '�^' => '篰', '�_' => '篲', '�`' => '篳', '�a' => '篴', '�b' => '篵', '�c' => '篶', '�d' => '篸', '�e' => '篹', '�f' => '篺', '�g' => '篻', '�h' => '篽', '�i' => '篿', '�j' => '簀', '�k' => '簁', '�l' => '簂', '�m' => '簃', '�n' => '簄', '�o' => '簅', '�p' => '簆', '�q' => '簈', '�r' => '簉', '�s' => '簊', '�t' => '簍', '�u' => '簎', '�v' => '簐', '�w' => '簑', '�x' => '簒', '�y' => '簓', '�z' => '簔', '�{' => '簕', '�|' => '簗', '�}' => '簘', '�~' => '簙', '��' => '簚', '��' => '簛', '��' => '簜', '��' => '簝', '��' => '簞', '��' => '簠', '��' => '簡', '��' => '簢', '��' => '簣', '��' => '簤', '��' => '簥', '��' => '簨', '��' => '簩', '��' => '簫', '��' => '簬', '��' => '簭', '��' => '簮', '��' => '簯', '��' => '簰', '��' => '簱', '��' => '簲', '��' => '簳', '��' => '簴', '��' => '簵', '��' => '簶', '��' => '簷', '��' => '簹', '��' => '簺', '��' => '簻', '��' => '簼', '��' => '簽', '��' => '簾', '��' => '籂', '��' => '骸', '��' => '孩', '��' => '海', '��' => '氦', '��' => '亥', '��' => '害', '��' => '骇', '��' => '酣', '��' => '憨', '��' => '邯', '��' => '韩', '��' => '含', '��' => '涵', '��' => '寒', '��' => '函', '��' => '喊', '��' => '罕', '��' => '翰', '��' => '撼', '��' => '捍', '��' => '旱', '��' => '憾', '��' => '悍', '��' => '焊', '��' => '汗', '��' => '汉', '��' => '夯', '��' => '杭', '��' => '航', '��' => '壕', '��' => '嚎', '��' => '豪', '��' => '毫', '��' => '郝', '��' => '好', '��' => '耗', '��' => '号', '��' => '浩', '��' => '呵', '��' => '喝', '��' => '荷', '��' => '菏', '��' => '核', '��' => '禾', '��' => '和', '��' => '何', '��' => '合', '��' => '盒', '��' => '貉', '��' => '阂', '��' => '河', '��' => '涸', '��' => '赫', '��' => '褐', '��' => '鹤', '��' => '贺', '��' => '嘿', '��' => '黑', '��' => '痕', '��' => '很', '��' => '狠', '��' => '恨', '��' => '哼', '��' => '亨', '��' => '横', '��' => '衡', '��' => '恒', '��' => '轰', '��' => '哄', '��' => '烘', '��' => '虹', '��' => '鸿', '��' => '洪', '��' => '宏', '��' => '弘', '��' => '红', '��' => '喉', '��' => '侯', '��' => '猴', '��' => '吼', '��' => '厚', '��' => '候', '��' => '后', '��' => '呼', '��' => '乎', '��' => '忽', '��' => '瑚', '��' => '壶', '��' => '葫', '��' => '胡', '��' => '蝴', '��' => '狐', '��' => '糊', '��' => '湖', '�@' => '籃', '�A' => '籄', '�B' => '籅', '�C' => '籆', '�D' => '籇', '�E' => '籈', '�F' => '籉', '�G' => '籊', '�H' => '籋', '�I' => '籌', '�J' => '籎', '�K' => '籏', '�L' => '籐', '�M' => '籑', '�N' => '籒', '�O' => '籓', '�P' => '籔', '�Q' => '籕', '�R' => '籖', '�S' => '籗', '�T' => '籘', '�U' => '籙', '�V' => '籚', '�W' => '籛', '�X' => '籜', '�Y' => '籝', '�Z' => '籞', '�[' => '籟', '�\\' => '籠', '�]' => '籡', '�^' => '籢', '�_' => '籣', '�`' => '籤', '�a' => '籥', '�b' => '籦', '�c' => '籧', '�d' => '籨', '�e' => '籩', '�f' => '籪', '�g' => '籫', '�h' => '籬', '�i' => '籭', '�j' => '籮', '�k' => '籯', '�l' => '籰', '�m' => '籱', '�n' => '籲', '�o' => '籵', '�p' => '籶', '�q' => '籷', '�r' => '籸', '�s' => '籹', '�t' => '籺', '�u' => '籾', '�v' => '籿', '�w' => '粀', '�x' => '粁', '�y' => '粂', '�z' => '粃', '�{' => '粄', '�|' => '粅', '�}' => '粆', '�~' => '粇', '��' => '粈', '��' => '粊', '��' => '粋', '��' => '粌', '��' => '粍', '��' => '粎', '��' => '粏', '��' => '粐', '��' => '粓', '��' => '粔', '��' => '粖', '��' => '粙', '��' => '粚', '��' => '粛', '��' => '粠', '��' => '粡', '��' => '粣', '��' => '粦', '��' => '粧', '��' => '粨', '��' => '粩', '��' => '粫', '��' => '粬', '��' => '粭', '��' => '粯', '��' => '粰', '��' => '粴', '��' => '粵', '��' => '粶', '��' => '粷', '��' => '粸', '��' => '粺', '��' => '粻', '��' => '弧', '��' => '虎', '��' => '唬', '��' => '护', '��' => '互', '��' => '沪', '��' => '户', '��' => '花', '��' => '哗', '��' => '华', '��' => '猾', '��' => '滑', '��' => '画', '��' => '划', '��' => '化', '��' => '话', '��' => '槐', '��' => '徊', '��' => '怀', '��' => '淮', '��' => '坏', '��' => '欢', '��' => '环', '��' => '桓', '��' => '还', '��' => '缓', '��' => '换', '��' => '患', '��' => '唤', '��' => '痪', '��' => '豢', '��' => '焕', '��' => '涣', '��' => '宦', '��' => '幻', '��' => '荒', '��' => '慌', '��' => '黄', '��' => '磺', '��' => '蝗', '��' => '簧', '��' => '皇', '��' => '凰', '��' => '惶', '��' => '煌', '��' => '晃', '��' => '幌', '��' => '恍', '��' => '谎', '��' => '灰', '��' => '挥', '��' => '辉', '��' => '徽', '��' => '恢', '��' => '蛔', '��' => '回', '��' => '毁', '��' => '悔', '��' => '慧', '��' => '卉', '��' => '惠', '��' => '晦', '��' => '贿', '��' => '秽', '��' => '会', '��' => '烩', '��' => '汇', '��' => '讳', '��' => '诲', '��' => '绘', '��' => '荤', '��' => '昏', '��' => '婚', '��' => '魂', '��' => '浑', '��' => '混', '��' => '豁', '��' => '活', '��' => '伙', '��' => '火', '��' => '获', '��' => '或', '��' => '惑', '��' => '霍', '��' => '货', '��' => '祸', '��' => '击', '��' => '圾', '��' => '基', '��' => '机', '��' => '畸', '��' => '稽', '��' => '积', '��' => '箕', '�@' => '粿', '�A' => '糀', '�B' => '糂', '�C' => '糃', '�D' => '糄', '�E' => '糆', '�F' => '糉', '�G' => '糋', '�H' => '糎', '�I' => '糏', '�J' => '糐', '�K' => '糑', '�L' => '糒', '�M' => '糓', '�N' => '糔', '�O' => '糘', '�P' => '糚', '�Q' => '糛', '�R' => '糝', '�S' => '糞', '�T' => '糡', '�U' => '糢', '�V' => '糣', '�W' => '糤', '�X' => '糥', '�Y' => '糦', '�Z' => '糧', '�[' => '糩', '�\\' => '糪', '�]' => '糫', '�^' => '糬', '�_' => '糭', '�`' => '糮', '�a' => '糰', '�b' => '糱', '�c' => '糲', '�d' => '糳', '�e' => '糴', '�f' => '糵', '�g' => '糶', '�h' => '糷', '�i' => '糹', '�j' => '糺', '�k' => '糼', '�l' => '糽', '�m' => '糾', '�n' => '糿', '�o' => '紀', '�p' => '紁', '�q' => '紂', '�r' => '紃', '�s' => '約', '�t' => '紅', '�u' => '紆', '�v' => '紇', '�w' => '紈', '�x' => '紉', '�y' => '紋', '�z' => '紌', '�{' => '納', '�|' => '紎', '�}' => '紏', '�~' => '紐', '��' => '紑', '��' => '紒', '��' => '紓', '��' => '純', '��' => '紕', '��' => '紖', '��' => '紗', '��' => '紘', '��' => '紙', '��' => '級', '��' => '紛', '��' => '紜', '��' => '紝', '��' => '紞', '��' => '紟', '��' => '紡', '��' => '紣', '��' => '紤', '��' => '紥', '��' => '紦', '��' => '紨', '��' => '紩', '��' => '紪', '��' => '紬', '��' => '紭', '��' => '紮', '��' => '細', '��' => '紱', '��' => '紲', '��' => '紳', '��' => '紴', '��' => '紵', '��' => '紶', '��' => '肌', '��' => '饥', '��' => '迹', '��' => '激', '��' => '讥', '��' => '鸡', '��' => '姬', '��' => '绩', '��' => '缉', '��' => '吉', '��' => '极', '��' => '棘', '��' => '辑', '��' => '籍', '��' => '集', '��' => '及', '��' => '急', '��' => '疾', '��' => '汲', '��' => '即', '��' => '嫉', '��' => '级', '��' => '挤', '��' => '几', '��' => '脊', '��' => '己', '��' => '蓟', '��' => '技', '��' => '冀', '��' => '季', '��' => '伎', '��' => '祭', '��' => '剂', '��' => '悸', '��' => '济', '��' => '寄', '��' => '寂', '��' => '计', '��' => '记', '��' => '既', '��' => '忌', '��' => '际', '��' => '妓', '��' => '继', '��' => '纪', '��' => '嘉', '��' => '枷', '��' => '夹', '��' => '佳', '��' => '家', '��' => '加', '��' => '荚', '��' => '颊', '��' => '贾', '��' => '甲', '��' => '钾', '��' => '假', '��' => '稼', '��' => '价', '��' => '架', '��' => '驾', '��' => '嫁', '��' => '歼', '��' => '监', '��' => '坚', '��' => '尖', '��' => '笺', '��' => '间', '��' => '煎', '��' => '兼', '��' => '肩', '��' => '艰', '��' => '奸', '��' => '缄', '��' => '茧', '��' => '检', '��' => '柬', '��' => '碱', '��' => '硷', '��' => '拣', '��' => '捡', '��' => '简', '��' => '俭', '��' => '剪', '��' => '减', '��' => '荐', '��' => '槛', '��' => '鉴', '��' => '践', '��' => '贱', '��' => '见', '��' => '键', '��' => '箭', '��' => '件', '�@' => '紷', '�A' => '紸', '�B' => '紹', '�C' => '紺', '�D' => '紻', '�E' => '紼', '�F' => '紽', '�G' => '紾', '�H' => '紿', '�I' => '絀', '�J' => '絁', '�K' => '終', '�L' => '絃', '�M' => '組', '�N' => '絅', '�O' => '絆', '�P' => '絇', '�Q' => '絈', '�R' => '絉', '�S' => '絊', '�T' => '絋', '�U' => '経', '�V' => '絍', '�W' => '絎', '�X' => '絏', '�Y' => '結', '�Z' => '絑', '�[' => '絒', '�\\' => '絓', '�]' => '絔', '�^' => '絕', '�_' => '絖', '�`' => '絗', '�a' => '絘', '�b' => '絙', '�c' => '絚', '�d' => '絛', '�e' => '絜', '�f' => '絝', '�g' => '絞', '�h' => '絟', '�i' => '絠', '�j' => '絡', '�k' => '絢', '�l' => '絣', '�m' => '絤', '�n' => '絥', '�o' => '給', '�p' => '絧', '�q' => '絨', '�r' => '絩', '�s' => '絪', '�t' => '絫', '�u' => '絬', '�v' => '絭', '�w' => '絯', '�x' => '絰', '�y' => '統', '�z' => '絲', '�{' => '絳', '�|' => '絴', '�}' => '絵', '�~' => '絶', '��' => '絸', '��' => '絹', '��' => '絺', '��' => '絻', '��' => '絼', '��' => '絽', '��' => '絾', '��' => '絿', '��' => '綀', '��' => '綁', '��' => '綂', '��' => '綃', '��' => '綄', '��' => '綅', '��' => '綆', '��' => '綇', '��' => '綈', '��' => '綉', '��' => '綊', '��' => '綋', '��' => '綌', '��' => '綍', '��' => '綎', '��' => '綏', '��' => '綐', '��' => '綑', '��' => '綒', '��' => '經', '��' => '綔', '��' => '綕', '��' => '綖', '��' => '綗', '��' => '綘', '��' => '健', '��' => '舰', '��' => '剑', '��' => '饯', '��' => '渐', '��' => '溅', '��' => '涧', '��' => '建', '��' => '僵', '��' => '姜', '��' => '将', '��' => '浆', '��' => '江', '��' => '疆', '��' => '蒋', '��' => '桨', '��' => '奖', '��' => '讲', '��' => '匠', '��' => '酱', '��' => '降', '��' => '蕉', '��' => '椒', '��' => '礁', '��' => '焦', '��' => '胶', '��' => '交', '��' => '郊', '��' => '浇', '��' => '骄', '��' => '娇', '��' => '嚼', '��' => '搅', '��' => '铰', '��' => '矫', '��' => '侥', '��' => '脚', '��' => '狡', '��' => '角', '��' => '饺', '��' => '缴', '��' => '绞', '��' => '剿', '��' => '教', '��' => '酵', '��' => '轿', '��' => '较', '��' => '叫', '��' => '窖', '��' => '揭', '��' => '接', '��' => '皆', '��' => '秸', '��' => '街', '��' => '阶', '��' => '截', '��' => '劫', '��' => '节', '��' => '桔', '��' => '杰', '��' => '捷', '��' => '睫', '��' => '竭', '��' => '洁', '��' => '结', '��' => '解', '��' => '姐', '��' => '戒', '��' => '藉', '��' => '芥', '��' => '界', '��' => '借', '��' => '介', '��' => '疥', '��' => '诫', '��' => '届', '��' => '巾', '��' => '筋', '��' => '斤', '��' => '金', '��' => '今', '��' => '津', '��' => '襟', '��' => '紧', '��' => '锦', '��' => '仅', '��' => '谨', '��' => '进', '��' => '靳', '��' => '晋', '��' => '禁', '��' => '近', '��' => '烬', '��' => '浸', '�@' => '継', '�A' => '続', '�B' => '綛', '�C' => '綜', '�D' => '綝', '�E' => '綞', '�F' => '綟', '�G' => '綠', '�H' => '綡', '�I' => '綢', '�J' => '綣', '�K' => '綤', '�L' => '綥', '�M' => '綧', '�N' => '綨', '�O' => '綩', '�P' => '綪', '�Q' => '綫', '�R' => '綬', '�S' => '維', '�T' => '綯', '�U' => '綰', '�V' => '綱', '�W' => '網', '�X' => '綳', '�Y' => '綴', '�Z' => '綵', '�[' => '綶', '�\\' => '綷', '�]' => '綸', '�^' => '綹', '�_' => '綺', '�`' => '綻', '�a' => '綼', '�b' => '綽', '�c' => '綾', '�d' => '綿', '�e' => '緀', '�f' => '緁', '�g' => '緂', '�h' => '緃', '�i' => '緄', '�j' => '緅', '�k' => '緆', '�l' => '緇', '�m' => '緈', '�n' => '緉', '�o' => '緊', '�p' => '緋', '�q' => '緌', '�r' => '緍', '�s' => '緎', '�t' => '総', '�u' => '緐', '�v' => '緑', '�w' => '緒', '�x' => '緓', '�y' => '緔', '�z' => '緕', '�{' => '緖', '�|' => '緗', '�}' => '緘', '�~' => '緙', '��' => '線', '��' => '緛', '��' => '緜', '��' => '緝', '��' => '緞', '��' => '緟', '��' => '締', '��' => '緡', '��' => '緢', '��' => '緣', '��' => '緤', '��' => '緥', '��' => '緦', '��' => '緧', '��' => '編', '��' => '緩', '��' => '緪', '��' => '緫', '��' => '緬', '��' => '緭', '��' => '緮', '��' => '緯', '��' => '緰', '��' => '緱', '��' => '緲', '��' => '緳', '��' => '練', '��' => '緵', '��' => '緶', '��' => '緷', '��' => '緸', '��' => '緹', '��' => '緺', '��' => '尽', '��' => '劲', '��' => '荆', '��' => '兢', '��' => '茎', '��' => '睛', '��' => '晶', '��' => '鲸', '��' => '京', '��' => '惊', '��' => '精', '��' => '粳', '��' => '经', '��' => '井', '��' => '警', '��' => '景', '��' => '颈', '��' => '静', '��' => '境', '��' => '敬', '��' => '镜', '��' => '径', '��' => '痉', '��' => '靖', '��' => '竟', '��' => '竞', '��' => '净', '��' => '炯', '��' => '窘', '��' => '揪', '��' => '究', '��' => '纠', '��' => '玖', '��' => '韭', '��' => '久', '��' => '灸', '��' => '九', '��' => '酒', '��' => '厩', '��' => '救', '��' => '旧', '��' => '臼', '��' => '舅', '��' => '咎', '��' => '就', '��' => '疚', '��' => '鞠', '��' => '拘', '��' => '狙', '��' => '疽', '��' => '居', '��' => '驹', '��' => '菊', '��' => '局', '��' => '咀', '��' => '矩', '��' => '举', '��' => '沮', '��' => '聚', '��' => '拒', '��' => '据', '��' => '巨', '��' => '具', '��' => '距', '��' => '踞', '��' => '锯', '��' => '俱', '��' => '句', '��' => '惧', '��' => '炬', '��' => '剧', '��' => '捐', '��' => '鹃', '��' => '娟', '��' => '倦', '��' => '眷', '��' => '卷', '��' => '绢', '��' => '撅', '��' => '攫', '��' => '抉', '��' => '掘', '��' => '倔', '��' => '爵', '��' => '觉', '��' => '决', '��' => '诀', '��' => '绝', '��' => '均', '��' => '菌', '��' => '钧', '��' => '军', '��' => '君', '��' => '峻', '�@' => '緻', '�A' => '緼', '�B' => '緽', '�C' => '緾', '�D' => '緿', '�E' => '縀', '�F' => '縁', '�G' => '縂', '�H' => '縃', '�I' => '縄', '�J' => '縅', '�K' => '縆', '�L' => '縇', '�M' => '縈', '�N' => '縉', '�O' => '縊', '�P' => '縋', '�Q' => '縌', '�R' => '縍', '�S' => '縎', '�T' => '縏', '�U' => '縐', '�V' => '縑', '�W' => '縒', '�X' => '縓', '�Y' => '縔', '�Z' => '縕', '�[' => '縖', '�\\' => '縗', '�]' => '縘', '�^' => '縙', '�_' => '縚', '�`' => '縛', '�a' => '縜', '�b' => '縝', '�c' => '縞', '�d' => '縟', '�e' => '縠', '�f' => '縡', '�g' => '縢', '�h' => '縣', '�i' => '縤', '�j' => '縥', '�k' => '縦', '�l' => '縧', '�m' => '縨', '�n' => '縩', '�o' => '縪', '�p' => '縫', '�q' => '縬', '�r' => '縭', '�s' => '縮', '�t' => '縯', '�u' => '縰', '�v' => '縱', '�w' => '縲', '�x' => '縳', '�y' => '縴', '�z' => '縵', '�{' => '縶', '�|' => '縷', '�}' => '縸', '�~' => '縹', '��' => '縺', '��' => '縼', '��' => '總', '��' => '績', '��' => '縿', '��' => '繀', '��' => '繂', '��' => '繃', '��' => '繄', '��' => '繅', '��' => '繆', '��' => '繈', '��' => '繉', '��' => '繊', '��' => '繋', '��' => '繌', '��' => '繍', '��' => '繎', '��' => '繏', '��' => '繐', '��' => '繑', '��' => '繒', '��' => '繓', '��' => '織', '��' => '繕', '��' => '繖', '��' => '繗', '��' => '繘', '��' => '繙', '��' => '繚', '��' => '繛', '��' => '繜', '��' => '繝', '��' => '俊', '��' => '竣', '��' => '浚', '��' => '郡', '��' => '骏', '��' => '喀', '��' => '咖', '��' => '卡', '��' => '咯', '��' => '开', '��' => '揩', '��' => '楷', '��' => '凯', '��' => '慨', '��' => '刊', '��' => '堪', '��' => '勘', '��' => '坎', '��' => '砍', '��' => '看', '��' => '康', '��' => '慷', '��' => '糠', '��' => '扛', '��' => '抗', '��' => '亢', '��' => '炕', '��' => '考', '��' => '拷', '��' => '烤', '��' => '靠', '��' => '坷', '��' => '苛', '��' => '柯', '��' => '棵', '��' => '磕', '��' => '颗', '��' => '科', '��' => '壳', '��' => '咳', '��' => '可', '��' => '渴', '��' => '克', '��' => '刻', '��' => '客', '��' => '课', '��' => '肯', '��' => '啃', '��' => '垦', '��' => '恳', '��' => '坑', '��' => '吭', '��' => '空', '��' => '恐', '��' => '孔', '��' => '控', '��' => '抠', '��' => '口', '��' => '扣', '��' => '寇', '��' => '枯', '��' => '哭', '��' => '窟', '��' => '苦', '��' => '酷', '��' => '库', '��' => '裤', '��' => '夸', '��' => '垮', '��' => '挎', '��' => '跨', '��' => '胯', '��' => '块', '��' => '筷', '��' => '侩', '��' => '快', '��' => '宽', '��' => '款', '��' => '匡', '��' => '筐', '��' => '狂', '��' => '框', '��' => '矿', '��' => '眶', '��' => '旷', '��' => '况', '��' => '亏', '��' => '盔', '��' => '岿', '��' => '窥', '��' => '葵', '��' => '奎', '��' => '魁', '��' => '傀', '�@' => '繞', '�A' => '繟', '�B' => '繠', '�C' => '繡', '�D' => '繢', '�E' => '繣', '�F' => '繤', '�G' => '繥', '�H' => '繦', '�I' => '繧', '�J' => '繨', '�K' => '繩', '�L' => '繪', '�M' => '繫', '�N' => '繬', '�O' => '繭', '�P' => '繮', '�Q' => '繯', '�R' => '繰', '�S' => '繱', '�T' => '繲', '�U' => '繳', '�V' => '繴', '�W' => '繵', '�X' => '繶', '�Y' => '繷', '�Z' => '繸', '�[' => '繹', '�\\' => '繺', '�]' => '繻', '�^' => '繼', '�_' => '繽', '�`' => '繾', '�a' => '繿', '�b' => '纀', '�c' => '纁', '�d' => '纃', '�e' => '纄', '�f' => '纅', '�g' => '纆', '�h' => '纇', '�i' => '纈', '�j' => '纉', '�k' => '纊', '�l' => '纋', '�m' => '續', '�n' => '纍', '�o' => '纎', '�p' => '纏', '�q' => '纐', '�r' => '纑', '�s' => '纒', '�t' => '纓', '�u' => '纔', '�v' => '纕', '�w' => '纖', '�x' => '纗', '�y' => '纘', '�z' => '纙', '�{' => '纚', '�|' => '纜', '�}' => '纝', '�~' => '纞', '��' => '纮', '��' => '纴', '��' => '纻', '��' => '纼', '��' => '绖', '��' => '绤', '��' => '绬', '��' => '绹', '��' => '缊', '��' => '缐', '��' => '缞', '��' => '缷', '��' => '缹', '��' => '缻', '��' => '缼', '��' => '缽', '��' => '缾', '��' => '缿', '��' => '罀', '��' => '罁', '��' => '罃', '��' => '罆', '��' => '罇', '��' => '罈', '��' => '罉', '��' => '罊', '��' => '罋', '��' => '罌', '��' => '罍', '��' => '罎', '��' => '罏', '��' => '罒', '��' => '罓', '��' => '馈', '��' => '愧', '��' => '溃', '��' => '坤', '��' => '昆', '��' => '捆', '��' => '困', '��' => '括', '��' => '扩', '��' => '廓', '��' => '阔', '��' => '垃', '��' => '拉', '��' => '喇', '��' => '蜡', '��' => '腊', '��' => '辣', '��' => '啦', '��' => '莱', '��' => '来', '��' => '赖', '��' => '蓝', '��' => '婪', '��' => '栏', '��' => '拦', '��' => '篮', '��' => '阑', '��' => '兰', '��' => '澜', '��' => '谰', '��' => '揽', '��' => '览', '��' => '懒', '��' => '缆', '��' => '烂', '��' => '滥', '��' => '琅', '��' => '榔', '��' => '狼', '��' => '廊', '��' => '郎', '��' => '朗', '��' => '浪', '��' => '捞', '��' => '劳', '��' => '牢', '��' => '老', '��' => '佬', '��' => '姥', '��' => '酪', '��' => '烙', '��' => '涝', '��' => '勒', '��' => '乐', '��' => '雷', '��' => '镭', '��' => '蕾', '��' => '磊', '��' => '累', '��' => '儡', '��' => '垒', '��' => '擂', '��' => '肋', '��' => '类', '��' => '泪', '��' => '棱', '��' => '楞', '��' => '冷', '��' => '厘', '��' => '梨', '��' => '犁', '��' => '黎', '��' => '篱', '��' => '狸', '��' => '离', '��' => '漓', '��' => '理', '��' => '李', '��' => '里', '��' => '鲤', '��' => '礼', '��' => '莉', '��' => '荔', '��' => '吏', '��' => '栗', '��' => '丽', '��' => '厉', '��' => '励', '��' => '砾', '��' => '历', '��' => '利', '��' => '傈', '��' => '例', '��' => '俐', '�@' => '罖', '�A' => '罙', '�B' => '罛', '�C' => '罜', '�D' => '罝', '�E' => '罞', '�F' => '罠', '�G' => '罣', '�H' => '罤', '�I' => '罥', '�J' => '罦', '�K' => '罧', '�L' => '罫', '�M' => '罬', '�N' => '罭', '�O' => '罯', '�P' => '罰', '�Q' => '罳', '�R' => '罵', '�S' => '罶', '�T' => '罷', '�U' => '罸', '�V' => '罺', '�W' => '罻', '�X' => '罼', '�Y' => '罽', '�Z' => '罿', '�[' => '羀', '�\\' => '羂', '�]' => '羃', '�^' => '羄', '�_' => '羅', '�`' => '羆', '�a' => '羇', '�b' => '羈', '�c' => '羉', '�d' => '羋', '�e' => '羍', '�f' => '羏', '�g' => '羐', '�h' => '羑', '�i' => '羒', '�j' => '羓', '�k' => '羕', '�l' => '羖', '�m' => '羗', '�n' => '羘', '�o' => '羙', '�p' => '羛', '�q' => '羜', '�r' => '羠', '�s' => '羢', '�t' => '羣', '�u' => '羥', '�v' => '羦', '�w' => '羨', '�x' => '義', '�y' => '羪', '�z' => '羫', '�{' => '羬', '�|' => '羭', '�}' => '羮', '�~' => '羱', '��' => '羳', '��' => '羴', '��' => '羵', '��' => '羶', '��' => '羷', '��' => '羺', '��' => '羻', '��' => '羾', '��' => '翀', '��' => '翂', '��' => '翃', '��' => '翄', '��' => '翆', '��' => '翇', '��' => '翈', '��' => '翉', '��' => '翋', '��' => '翍', '��' => '翏', '��' => '翐', '��' => '翑', '��' => '習', '��' => '翓', '��' => '翖', '��' => '翗', '��' => '翙', '��' => '翚', '��' => '翛', '��' => '翜', '��' => '翝', '��' => '翞', '��' => '翢', '��' => '翣', '��' => '痢', '��' => '立', '��' => '粒', '��' => '沥', '��' => '隶', '��' => '力', '��' => '璃', '��' => '哩', '��' => '俩', '��' => '联', '��' => '莲', '��' => '连', '��' => '镰', '��' => '廉', '��' => '怜', '��' => '涟', '��' => '帘', '��' => '敛', '��' => '脸', '��' => '链', '��' => '恋', '��' => '炼', '��' => '练', '��' => '粮', '��' => '凉', '��' => '梁', '��' => '粱', '��' => '良', '��' => '两', '��' => '辆', '��' => '量', '��' => '晾', '��' => '亮', '��' => '谅', '��' => '撩', '��' => '聊', '��' => '僚', '��' => '疗', '��' => '燎', '��' => '寥', '��' => '辽', '��' => '潦', '��' => '了', '��' => '撂', '��' => '镣', '��' => '廖', '��' => '料', '��' => '列', '��' => '裂', '��' => '烈', '��' => '劣', '��' => '猎', '��' => '琳', '��' => '林', '��' => '磷', '��' => '霖', '��' => '临', '��' => '邻', '��' => '鳞', '��' => '淋', '��' => '凛', '��' => '赁', '��' => '吝', '��' => '拎', '��' => '玲', '��' => '菱', '��' => '零', '��' => '龄', '��' => '铃', '��' => '伶', '��' => '羚', '��' => '凌', '��' => '灵', '��' => '陵', '��' => '岭', '��' => '领', '��' => '另', '��' => '令', '��' => '溜', '��' => '琉', '��' => '榴', '��' => '硫', '��' => '馏', '��' => '留', '��' => '刘', '��' => '瘤', '��' => '流', '��' => '柳', '��' => '六', '��' => '龙', '��' => '聋', '��' => '咙', '��' => '笼', '��' => '窿', '�@' => '翤', '�A' => '翧', '�B' => '翨', '�C' => '翪', '�D' => '翫', '�E' => '翬', '�F' => '翭', '�G' => '翯', '�H' => '翲', '�I' => '翴', '�J' => '翵', '�K' => '翶', '�L' => '翷', '�M' => '翸', '�N' => '翹', '�O' => '翺', '�P' => '翽', '�Q' => '翾', '�R' => '翿', '�S' => '耂', '�T' => '耇', '�U' => '耈', '�V' => '耉', '�W' => '耊', '�X' => '耎', '�Y' => '耏', '�Z' => '耑', '�[' => '耓', '�\\' => '耚', '�]' => '耛', '�^' => '耝', '�_' => '耞', '�`' => '耟', '�a' => '耡', '�b' => '耣', '�c' => '耤', '�d' => '耫', '�e' => '耬', '�f' => '耭', '�g' => '耮', '�h' => '耯', '�i' => '耰', '�j' => '耲', '�k' => '耴', '�l' => '耹', '�m' => '耺', '�n' => '耼', '�o' => '耾', '�p' => '聀', '�q' => '聁', '�r' => '聄', '�s' => '聅', '�t' => '聇', '�u' => '聈', '�v' => '聉', '�w' => '聎', '�x' => '聏', '�y' => '聐', '�z' => '聑', '�{' => '聓', '�|' => '聕', '�}' => '聖', '�~' => '聗', '' => '聙', '' => '聛', '' => '聜', '' => '聝', '' => '聞', ' ' => '聟', '' => '聠', '' => '聡', '' => '聢', '' => '聣', '' => '聤', '' => '聥', '' => '聦', '' => '聧', '' => '聨', '' => '聫', '' => '聬', '' => '聭', '' => '聮', '' => '聯', '' => '聰', '' => '聲', '' => '聳', '' => '聴', '' => '聵', '' => '聶', '' => '職', '' => '聸', '' => '聹', '' => '聺', '' => '聻', '' => '聼', ' ' => '聽', '¡' => '隆', '¢' => '垄', '£' => '拢', '¤' => '陇', '¥' => '楼', '¦' => '娄', '§' => '搂', '¨' => '篓', '©' => '漏', 'ª' => '陋', '«' => '芦', '¬' => '卢', '' => '颅', '®' => '庐', '¯' => '炉', '°' => '掳', '±' => '卤', '²' => '虏', '³' => '鲁', '´' => '麓', 'µ' => '碌', '¶' => '露', '·' => '路', '¸' => '赂', '¹' => '鹿', 'º' => '潞', '»' => '禄', '¼' => '录', '½' => '陆', '¾' => '戮', '¿' => '驴', '�' => '吕', '�' => '铝', '��' => '侣', '��' => '旅', '��' => '履', '��' => '屡', '��' => '缕', '��' => '虑', '��' => '氯', '��' => '律', '��' => '率', '��' => '滤', '��' => '绿', '��' => '峦', '��' => '挛', '��' => '孪', '��' => '滦', '��' => '卵', '��' => '乱', '��' => '掠', '��' => '略', '��' => '抡', '��' => '轮', '��' => '伦', '��' => '仑', '��' => '沦', '��' => '纶', '��' => '论', '��' => '萝', '��' => '螺', '��' => '罗', '��' => '逻', '��' => '锣', '��' => '箩', '��' => '骡', '��' => '裸', '��' => '落', '��' => '洛', '��' => '骆', '��' => '络', '��' => '妈', '��' => '麻', '��' => '玛', '��' => '码', '��' => '蚂', '��' => '马', '��' => '骂', '��' => '嘛', '��' => '吗', '��' => '埋', '��' => '买', '��' => '麦', '��' => '卖', '�' => '迈', '�' => '脉', '�' => '瞒', '�' => '馒', '�' => '蛮', '�' => '满', '�' => '蔓', '�' => '曼', '�' => '慢', '�' => '漫', '�@' => '聾', '�A' => '肁', '�B' => '肂', '�C' => '肅', '�D' => '肈', '�E' => '肊', '�F' => '肍', '�G' => '肎', '�H' => '肏', '�I' => '肐', '�J' => '肑', '�K' => '肒', '�L' => '肔', '�M' => '肕', '�N' => '肗', '�O' => '肙', '�P' => '肞', '�Q' => '肣', '�R' => '肦', '�S' => '肧', '�T' => '肨', '�U' => '肬', '�V' => '肰', '�W' => '肳', '�X' => '肵', '�Y' => '肶', '�Z' => '肸', '�[' => '肹', '�\\' => '肻', '�]' => '胅', '�^' => '胇', '�_' => '胈', '�`' => '胉', '�a' => '胊', '�b' => '胋', '�c' => '胏', '�d' => '胐', '�e' => '胑', '�f' => '胒', '�g' => '胓', '�h' => '胔', '�i' => '胕', '�j' => '胘', '�k' => '胟', '�l' => '胠', '�m' => '胢', '�n' => '胣', '�o' => '胦', '�p' => '胮', '�q' => '胵', '�r' => '胷', '�s' => '胹', '�t' => '胻', '�u' => '胾', '�v' => '胿', '�w' => '脀', '�x' => '脁', '�y' => '脃', '�z' => '脄', '�{' => '脅', '�|' => '脇', '�}' => '脈', '�~' => '脋', 'À' => '脌', 'Á' => '脕', 'Â' => '脗', 'Ã' => '脙', 'Ä' => '脛', 'Å' => '脜', 'Æ' => '脝', 'Ç' => '脟', 'È' => '脠', 'É' => '脡', 'Ê' => '脢', 'Ë' => '脣', 'Ì' => '脤', 'Í' => '脥', 'Î' => '脦', 'Ï' => '脧', 'Ð' => '脨', 'Ñ' => '脩', 'Ò' => '脪', 'Ó' => '脫', 'Ô' => '脭', 'Õ' => '脮', 'Ö' => '脰', '×' => '脳', 'Ø' => '脴', 'Ù' => '脵', 'Ú' => '脷', 'Û' => '脹', 'Ü' => '脺', 'Ý' => '脻', 'Þ' => '脼', 'ß' => '脽', 'à' => '脿', 'á' => '谩', 'â' => '芒', 'ã' => '茫', 'ä' => '盲', 'å' => '氓', 'æ' => '忙', 'ç' => '莽', 'è' => '猫', 'é' => '茅', 'ê' => '锚', 'ë' => '毛', 'ì' => '矛', 'í' => '铆', 'î' => '卯', 'ï' => '茂', 'ð' => '冒', 'ñ' => '帽', 'ò' => '貌', 'ó' => '贸', 'ô' => '么', 'õ' => '玫', 'ö' => '枚', '÷' => '梅', 'ø' => '酶', 'ù' => '霉', 'ú' => '煤', 'û' => '没', 'ü' => '眉', 'ý' => '媒', 'þ' => '镁', 'ÿ' => '每', '�' => '美', '�' => '昧', '��' => '寐', '��' => '妹', '��' => '媚', '��' => '门', '��' => '闷', '��' => '们', '��' => '萌', '��' => '蒙', '��' => '檬', '��' => '盟', '��' => '锰', '��' => '猛', '��' => '梦', '��' => '孟', '��' => '眯', '��' => '醚', '��' => '靡', '��' => '糜', '��' => '迷', '��' => '谜', '��' => '弥', '��' => '米', '��' => '秘', '��' => '觅', '��' => '泌', '��' => '蜜', '��' => '密', '��' => '幂', '��' => '棉', '��' => '眠', '��' => '绵', '��' => '冕', '��' => '免', '��' => '勉', '��' => '娩', '��' => '缅', '��' => '面', '��' => '苗', '��' => '描', '��' => '瞄', '��' => '藐', '��' => '秒', '��' => '渺', '��' => '庙', '��' => '妙', '��' => '蔑', '��' => '灭', '��' => '民', '��' => '抿', '��' => '皿', '��' => '敏', '�' => '悯', '�' => '闽', '�' => '明', '�' => '螟', '�' => '鸣', '�' => '铭', '�' => '名', '�' => '命', '�' => '谬', '�' => '摸', '�@' => '腀', '�A' => '腁', '�B' => '腂', '�C' => '腃', '�D' => '腄', '�E' => '腅', '�F' => '腇', '�G' => '腉', '�H' => '腍', '�I' => '腎', '�J' => '腏', '�K' => '腒', '�L' => '腖', '�M' => '腗', '�N' => '腘', '�O' => '腛', '�P' => '腜', '�Q' => '腝', '�R' => '腞', '�S' => '腟', '�T' => '腡', '�U' => '腢', '�V' => '腣', '�W' => '腤', '�X' => '腦', '�Y' => '腨', '�Z' => '腪', '�[' => '腫', '�\\' => '腬', '�]' => '腯', '�^' => '腲', '�_' => '腳', '�`' => '腵', '�a' => '腶', '�b' => '腷', '�c' => '腸', '�d' => '膁', '�e' => '膃', '�f' => '膄', '�g' => '膅', '�h' => '膆', '�i' => '膇', '�j' => '膉', '�k' => '膋', '�l' => '膌', '�m' => '膍', '�n' => '膎', '�o' => '膐', '�p' => '膒', '�q' => '膓', '�r' => '膔', '�s' => '膕', '�t' => '膖', '�u' => '膗', '�v' => '膙', '�w' => '膚', '�x' => '膞', '�y' => '膟', '�z' => '膠', '�{' => '膡', '�|' => '膢', '�}' => '膤', '�~' => '膥', 'Ā' => '膧', 'ā' => '膩', 'Ă' => '膫', 'ă' => '膬', 'Ą' => '膭', 'ą' => '膮', 'Ć' => '膯', 'ć' => '膰', 'Ĉ' => '膱', 'ĉ' => '膲', 'Ċ' => '膴', 'ċ' => '膵', 'Č' => '膶', 'č' => '膷', 'Ď' => '膸', 'ď' => '膹', 'Đ' => '膼', 'đ' => '膽', 'Ē' => '膾', 'ē' => '膿', 'Ĕ' => '臄', 'ĕ' => '臅', 'Ė' => '臇', 'ė' => '臈', 'Ę' => '臉', 'ę' => '臋', 'Ě' => '臍', 'ě' => '臎', 'Ĝ' => '臏', 'ĝ' => '臐', 'Ğ' => '臑', 'ğ' => '臒', 'Ġ' => '臓', 'ġ' => '摹', 'Ģ' => '蘑', 'ģ' => '模', 'Ĥ' => '膜', 'ĥ' => '磨', 'Ħ' => '摩', 'ħ' => '魔', 'Ĩ' => '抹', 'ĩ' => '末', 'Ī' => '莫', 'ī' => '墨', 'Ĭ' => '默', 'ĭ' => '沫', 'Į' => '漠', 'į' => '寞', 'İ' => '陌', 'ı' => '谋', 'IJ' => '牟', 'ij' => '某', 'Ĵ' => '拇', 'ĵ' => '牡', 'Ķ' => '亩', 'ķ' => '姆', 'ĸ' => '母', 'Ĺ' => '墓', 'ĺ' => '暮', 'Ļ' => '幕', 'ļ' => '募', 'Ľ' => '慕', 'ľ' => '木', 'Ŀ' => '目', '�' => '睦', '�' => '牧', '��' => '穆', '��' => '拿', '��' => '哪', '��' => '呐', '��' => '钠', '��' => '那', '��' => '娜', '��' => '纳', '��' => '氖', '��' => '乃', '��' => '奶', '��' => '耐', '��' => '奈', '��' => '南', '��' => '男', '��' => '难', '��' => '囊', '��' => '挠', '��' => '脑', '��' => '恼', '��' => '闹', '��' => '淖', '��' => '呢', '��' => '馁', '��' => '内', '��' => '嫩', '��' => '能', '��' => '妮', '��' => '霓', '��' => '倪', '��' => '泥', '��' => '尼', '��' => '拟', '��' => '你', '��' => '匿', '��' => '腻', '��' => '逆', '��' => '溺', '��' => '蔫', '��' => '拈', '��' => '年', '��' => '碾', '��' => '撵', '��' => '捻', '��' => '念', '��' => '娘', '��' => '酿', '��' => '鸟', '��' => '尿', '��' => '捏', '��' => '聂', '�' => '孽', '�' => '啮', '�' => '镊', '�' => '镍', '�' => '涅', '�' => '您', '�' => '柠', '�' => '狞', '�' => '凝', '�' => '宁', '�@' => '臔', '�A' => '臕', '�B' => '臖', '�C' => '臗', '�D' => '臘', '�E' => '臙', '�F' => '臚', '�G' => '臛', '�H' => '臜', '�I' => '臝', '�J' => '臞', '�K' => '臟', '�L' => '臠', '�M' => '臡', '�N' => '臢', '�O' => '臤', '�P' => '臥', '�Q' => '臦', '�R' => '臨', '�S' => '臩', '�T' => '臫', '�U' => '臮', '�V' => '臯', '�W' => '臰', '�X' => '臱', '�Y' => '臲', '�Z' => '臵', '�[' => '臶', '�\\' => '臷', '�]' => '臸', '�^' => '臹', '�_' => '臺', '�`' => '臽', '�a' => '臿', '�b' => '舃', '�c' => '與', '�d' => '興', '�e' => '舉', '�f' => '舊', '�g' => '舋', '�h' => '舎', '�i' => '舏', '�j' => '舑', '�k' => '舓', '�l' => '舕', '�m' => '舖', '�n' => '舗', '�o' => '舘', '�p' => '舙', '�q' => '舚', '�r' => '舝', '�s' => '舠', '�t' => '舤', '�u' => '舥', '�v' => '舦', '�w' => '舧', '�x' => '舩', '�y' => '舮', '�z' => '舲', '�{' => '舺', '�|' => '舼', '�}' => '舽', '�~' => '舿', 'ŀ' => '艀', 'Ł' => '艁', 'ł' => '艂', 'Ń' => '艃', 'ń' => '艅', 'Ņ' => '艆', 'ņ' => '艈', 'Ň' => '艊', 'ň' => '艌', 'ʼn' => '艍', 'Ŋ' => '艎', 'ŋ' => '艐', 'Ō' => '艑', 'ō' => '艒', 'Ŏ' => '艓', 'ŏ' => '艔', 'Ő' => '艕', 'ő' => '艖', 'Œ' => '艗', 'œ' => '艙', 'Ŕ' => '艛', 'ŕ' => '艜', 'Ŗ' => '艝', 'ŗ' => '艞', 'Ř' => '艠', 'ř' => '艡', 'Ś' => '艢', 'ś' => '艣', 'Ŝ' => '艤', 'ŝ' => '艥', 'Ş' => '艦', 'ş' => '艧', 'Š' => '艩', 'š' => '拧', 'Ţ' => '泞', 'ţ' => '牛', 'Ť' => '扭', 'ť' => '钮', 'Ŧ' => '纽', 'ŧ' => '脓', 'Ũ' => '浓', 'ũ' => '农', 'Ū' => '弄', 'ū' => '奴', 'Ŭ' => '努', 'ŭ' => '怒', 'Ů' => '女', 'ů' => '暖', 'Ű' => '虐', 'ű' => '疟', 'Ų' => '挪', 'ų' => '懦', 'Ŵ' => '糯', 'ŵ' => '诺', 'Ŷ' => '哦', 'ŷ' => '欧', 'Ÿ' => '鸥', 'Ź' => '殴', 'ź' => '藕', 'Ż' => '呕', 'ż' => '偶', 'Ž' => '沤', 'ž' => '啪', 'ſ' => '趴', '�' => '爬', '�' => '帕', '��' => '怕', '��' => '琶', '��' => '拍', '��' => '排', '��' => '牌', '��' => '徘', '��' => '湃', '��' => '派', '��' => '攀', '��' => '潘', '��' => '盘', '��' => '磐', '��' => '盼', '��' => '畔', '��' => '判', '��' => '叛', '��' => '乓', '��' => '庞', '��' => '旁', '��' => '耪', '��' => '胖', '��' => '抛', '��' => '咆', '��' => '刨', '��' => '炮', '��' => '袍', '��' => '跑', '��' => '泡', '��' => '呸', '��' => '胚', '��' => '培', '��' => '裴', '��' => '赔', '��' => '陪', '��' => '配', '��' => '佩', '��' => '沛', '��' => '喷', '��' => '盆', '��' => '砰', '��' => '抨', '��' => '烹', '��' => '澎', '��' => '彭', '��' => '蓬', '��' => '棚', '��' => '硼', '��' => '篷', '��' => '膨', '��' => '朋', '��' => '鹏', '�' => '捧', '�' => '碰', '�' => '坯', '�' => '砒', '�' => '霹', '�' => '批', '�' => '披', '�' => '劈', '�' => '琵', '�' => '毗', '�@' => '艪', '�A' => '艫', '�B' => '艬', '�C' => '艭', '�D' => '艱', '�E' => '艵', '�F' => '艶', '�G' => '艷', '�H' => '艸', '�I' => '艻', '�J' => '艼', '�K' => '芀', '�L' => '芁', '�M' => '芃', '�N' => '芅', '�O' => '芆', '�P' => '芇', '�Q' => '芉', '�R' => '芌', '�S' => '芐', '�T' => '芓', '�U' => '芔', '�V' => '芕', '�W' => '芖', '�X' => '芚', '�Y' => '芛', '�Z' => '芞', '�[' => '芠', '�\\' => '芢', '�]' => '芣', '�^' => '芧', '�_' => '芲', '�`' => '芵', '�a' => '芶', '�b' => '芺', '�c' => '芻', '�d' => '芼', '�e' => '芿', '�f' => '苀', '�g' => '苂', '�h' => '苃', '�i' => '苅', '�j' => '苆', '�k' => '苉', '�l' => '苐', '�m' => '苖', '�n' => '苙', '�o' => '苚', '�p' => '苝', '�q' => '苢', '�r' => '苧', '�s' => '苨', '�t' => '苩', '�u' => '苪', '�v' => '苬', '�w' => '苭', '�x' => '苮', '�y' => '苰', '�z' => '苲', '�{' => '苳', '�|' => '苵', '�}' => '苶', '�~' => '苸', 'ƀ' => '苺', 'Ɓ' => '苼', 'Ƃ' => '苽', 'ƃ' => '苾', 'Ƅ' => '苿', 'ƅ' => '茀', 'Ɔ' => '茊', 'Ƈ' => '茋', 'ƈ' => '茍', 'Ɖ' => '茐', 'Ɗ' => '茒', 'Ƌ' => '茓', 'ƌ' => '茖', 'ƍ' => '茘', 'Ǝ' => '茙', 'Ə' => '茝', 'Ɛ' => '茞', 'Ƒ' => '茟', 'ƒ' => '茠', 'Ɠ' => '茡', 'Ɣ' => '茢', 'ƕ' => '茣', 'Ɩ' => '茤', 'Ɨ' => '茥', 'Ƙ' => '茦', 'ƙ' => '茩', 'ƚ' => '茪', 'ƛ' => '茮', 'Ɯ' => '茰', 'Ɲ' => '茲', 'ƞ' => '茷', 'Ɵ' => '茻', 'Ơ' => '茽', 'ơ' => '啤', 'Ƣ' => '脾', 'ƣ' => '疲', 'Ƥ' => '皮', 'ƥ' => '匹', 'Ʀ' => '痞', 'Ƨ' => '僻', 'ƨ' => '屁', 'Ʃ' => '譬', 'ƪ' => '篇', 'ƫ' => '偏', 'Ƭ' => '片', 'ƭ' => '骗', 'Ʈ' => '飘', 'Ư' => '漂', 'ư' => '瓢', 'Ʊ' => '票', 'Ʋ' => '撇', 'Ƴ' => '瞥', 'ƴ' => '拼', 'Ƶ' => '频', 'ƶ' => '贫', 'Ʒ' => '品', 'Ƹ' => '聘', 'ƹ' => '乒', 'ƺ' => '坪', 'ƻ' => '苹', 'Ƽ' => '萍', 'ƽ' => '平', 'ƾ' => '凭', 'ƿ' => '瓶', '�' => '评', '�' => '屏', '��' => '坡', '��' => '泼', '��' => '颇', '��' => '婆', '��' => '破', '��' => '魄', '��' => '迫', '��' => '粕', '��' => '剖', '��' => '扑', '��' => '铺', '��' => '仆', '��' => '莆', '��' => '葡', '��' => '菩', '��' => '蒲', '��' => '埔', '��' => '朴', '��' => '圃', '��' => '普', '��' => '浦', '��' => '谱', '��' => '曝', '��' => '瀑', '��' => '期', '��' => '欺', '��' => '栖', '��' => '戚', '��' => '妻', '��' => '七', '��' => '凄', '��' => '漆', '��' => '柒', '��' => '沏', '��' => '其', '��' => '棋', '��' => '奇', '��' => '歧', '��' => '畦', '��' => '崎', '��' => '脐', '��' => '齐', '��' => '旗', '��' => '祈', '��' => '祁', '��' => '骑', '��' => '起', '��' => '岂', '��' => '乞', '��' => '企', '��' => '启', '�' => '契', '�' => '砌', '�' => '器', '�' => '气', '�' => '迄', '�' => '弃', '�' => '汽', '�' => '泣', '�' => '讫', '�' => '掐', '�@' => '茾', '�A' => '茿', '�B' => '荁', '�C' => '荂', '�D' => '荄', '�E' => '荅', '�F' => '荈', '�G' => '荊', '�H' => '荋', '�I' => '荌', '�J' => '荍', '�K' => '荎', '�L' => '荓', '�M' => '荕', '�N' => '荖', '�O' => '荗', '�P' => '荘', '�Q' => '荙', '�R' => '荝', '�S' => '荢', '�T' => '荰', '�U' => '荱', '�V' => '荲', '�W' => '荳', '�X' => '荴', '�Y' => '荵', '�Z' => '荶', '�[' => '荹', '�\\' => '荺', '�]' => '荾', '�^' => '荿', '�_' => '莀', '�`' => '莁', '�a' => '莂', '�b' => '莃', '�c' => '莄', '�d' => '莇', '�e' => '莈', '�f' => '莊', '�g' => '莋', '�h' => '莌', '�i' => '莍', '�j' => '莏', '�k' => '莐', '�l' => '莑', '�m' => '莔', '�n' => '莕', '�o' => '莖', '�p' => '莗', '�q' => '莙', '�r' => '莚', '�s' => '莝', '�t' => '莟', '�u' => '莡', '�v' => '莢', '�w' => '莣', '�x' => '莤', '�y' => '莥', '�z' => '莦', '�{' => '莧', '�|' => '莬', '�}' => '莭', '�~' => '莮', 'ǀ' => '莯', 'ǁ' => '莵', 'ǂ' => '莻', 'ǃ' => '莾', 'DŽ' => '莿', 'Dž' => '菂', 'dž' => '菃', 'LJ' => '菄', 'Lj' => '菆', 'lj' => '菈', 'NJ' => '菉', 'Nj' => '菋', 'nj' => '菍', 'Ǎ' => '菎', 'ǎ' => '菐', 'Ǐ' => '菑', 'ǐ' => '菒', 'Ǒ' => '菓', 'ǒ' => '菕', 'Ǔ' => '菗', 'ǔ' => '菙', 'Ǖ' => '菚', 'ǖ' => '菛', 'Ǘ' => '菞', 'ǘ' => '菢', 'Ǚ' => '菣', 'ǚ' => '菤', 'Ǜ' => '菦', 'ǜ' => '菧', 'ǝ' => '菨', 'Ǟ' => '菫', 'ǟ' => '菬', 'Ǡ' => '菭', 'ǡ' => '恰', 'Ǣ' => '洽', 'ǣ' => '牵', 'Ǥ' => '扦', 'ǥ' => '钎', 'Ǧ' => '铅', 'ǧ' => '千', 'Ǩ' => '迁', 'ǩ' => '签', 'Ǫ' => '仟', 'ǫ' => '谦', 'Ǭ' => '乾', 'ǭ' => '黔', 'Ǯ' => '钱', 'ǯ' => '钳', 'ǰ' => '前', 'DZ' => '潜', 'Dz' => '遣', 'dz' => '浅', 'Ǵ' => '谴', 'ǵ' => '堑', 'Ƕ' => '嵌', 'Ƿ' => '欠', 'Ǹ' => '歉', 'ǹ' => '枪', 'Ǻ' => '呛', 'ǻ' => '腔', 'Ǽ' => '羌', 'ǽ' => '墙', 'Ǿ' => '蔷', 'ǿ' => '强', '�' => '抢', '�' => '橇', '��' => '锹', '��' => '敲', '��' => '悄', '��' => '桥', '��' => '瞧', '��' => '乔', '��' => '侨', '��' => '巧', '��' => '鞘', '��' => '撬', '��' => '翘', '��' => '峭', '��' => '俏', '��' => '窍', '��' => '切', '��' => '茄', '��' => '且', '��' => '怯', '��' => '窃', '��' => '钦', '��' => '侵', '��' => '亲', '��' => '秦', '��' => '琴', '��' => '勤', '��' => '芹', '��' => '擒', '��' => '禽', '��' => '寝', '��' => '沁', '��' => '青', '��' => '轻', '��' => '氢', '��' => '倾', '��' => '卿', '��' => '清', '��' => '擎', '��' => '晴', '��' => '氰', '��' => '情', '��' => '顷', '��' => '请', '��' => '庆', '��' => '琼', '��' => '穷', '��' => '秋', '��' => '丘', '��' => '邱', '��' => '球', '��' => '求', '��' => '囚', '�' => '酋', '�' => '泅', '�' => '趋', '�' => '区', '�' => '蛆', '�' => '曲', '�' => '躯', '�' => '屈', '�' => '驱', '�' => '渠', '�@' => '菮', '�A' => '華', '�B' => '菳', '�C' => '菴', '�D' => '菵', '�E' => '菶', '�F' => '菷', '�G' => '菺', '�H' => '菻', '�I' => '菼', '�J' => '菾', '�K' => '菿', '�L' => '萀', '�M' => '萂', '�N' => '萅', '�O' => '萇', '�P' => '萈', '�Q' => '萉', '�R' => '萊', '�S' => '萐', '�T' => '萒', '�U' => '萓', '�V' => '萔', '�W' => '萕', '�X' => '萖', '�Y' => '萗', '�Z' => '萙', '�[' => '萚', '�\\' => '萛', '�]' => '萞', '�^' => '萟', '�_' => '萠', '�`' => '萡', '�a' => '萢', '�b' => '萣', '�c' => '萩', '�d' => '萪', '�e' => '萫', '�f' => '萬', '�g' => '萭', '�h' => '萮', '�i' => '萯', '�j' => '萰', '�k' => '萲', '�l' => '萳', '�m' => '萴', '�n' => '萵', '�o' => '萶', '�p' => '萷', '�q' => '萹', '�r' => '萺', '�s' => '萻', '�t' => '萾', '�u' => '萿', '�v' => '葀', '�w' => '葁', '�x' => '葂', '�y' => '葃', '�z' => '葄', '�{' => '葅', '�|' => '葇', '�}' => '葈', '�~' => '葉', 'Ȁ' => '葊', 'ȁ' => '葋', 'Ȃ' => '葌', 'ȃ' => '葍', 'Ȅ' => '葎', 'ȅ' => '葏', 'Ȇ' => '葐', 'ȇ' => '葒', 'Ȉ' => '葓', 'ȉ' => '葔', 'Ȋ' => '葕', 'ȋ' => '葖', 'Ȍ' => '葘', 'ȍ' => '葝', 'Ȏ' => '葞', 'ȏ' => '葟', 'Ȑ' => '葠', 'ȑ' => '葢', 'Ȓ' => '葤', 'ȓ' => '葥', 'Ȕ' => '葦', 'ȕ' => '葧', 'Ȗ' => '葨', 'ȗ' => '葪', 'Ș' => '葮', 'ș' => '葯', 'Ț' => '葰', 'ț' => '葲', 'Ȝ' => '葴', 'ȝ' => '葷', 'Ȟ' => '葹', 'ȟ' => '葻', 'Ƞ' => '葼', 'ȡ' => '取', 'Ȣ' => '娶', 'ȣ' => '龋', 'Ȥ' => '趣', 'ȥ' => '去', 'Ȧ' => '圈', 'ȧ' => '颧', 'Ȩ' => '权', 'ȩ' => '醛', 'Ȫ' => '泉', 'ȫ' => '全', 'Ȭ' => '痊', 'ȭ' => '拳', 'Ȯ' => '犬', 'ȯ' => '券', 'Ȱ' => '劝', 'ȱ' => '缺', 'Ȳ' => '炔', 'ȳ' => '瘸', 'ȴ' => '却', 'ȵ' => '鹊', 'ȶ' => '榷', 'ȷ' => '确', 'ȸ' => '雀', 'ȹ' => '裙', 'Ⱥ' => '群', 'Ȼ' => '然', 'ȼ' => '燃', 'Ƚ' => '冉', 'Ⱦ' => '染', 'ȿ' => '瓤', '�' => '壤', '�' => '攘', '��' => '嚷', '��' => '让', '��' => '饶', '��' => '扰', '��' => '绕', '��' => '惹', '��' => '热', '��' => '壬', '��' => '仁', '��' => '人', '��' => '忍', '��' => '韧', '��' => '任', '��' => '认', '��' => '刃', '��' => '妊', '��' => '纫', '��' => '扔', '��' => '仍', '��' => '日', '��' => '戎', '��' => '茸', '��' => '蓉', '��' => '荣', '��' => '融', '��' => '熔', '��' => '溶', '��' => '容', '��' => '绒', '��' => '冗', '��' => '揉', '��' => '柔', '��' => '肉', '��' => '茹', '��' => '蠕', '��' => '儒', '��' => '孺', '��' => '如', '��' => '辱', '��' => '乳', '��' => '汝', '��' => '入', '��' => '褥', '��' => '软', '��' => '阮', '��' => '蕊', '��' => '瑞', '��' => '锐', '��' => '闰', '��' => '润', '��' => '若', '�' => '弱', '�' => '撒', '�' => '洒', '�' => '萨', '�' => '腮', '�' => '鳃', '�' => '塞', '�' => '赛', '�' => '三', '�' => '叁', '�@' => '葽', '�A' => '葾', '�B' => '葿', '�C' => '蒀', '�D' => '蒁', '�E' => '蒃', '�F' => '蒄', '�G' => '蒅', '�H' => '蒆', '�I' => '蒊', '�J' => '蒍', '�K' => '蒏', '�L' => '蒐', '�M' => '蒑', '�N' => '蒒', '�O' => '蒓', '�P' => '蒔', '�Q' => '蒕', '�R' => '蒖', '�S' => '蒘', '�T' => '蒚', '�U' => '蒛', '�V' => '蒝', '�W' => '蒞', '�X' => '蒟', '�Y' => '蒠', '�Z' => '蒢', '�[' => '蒣', '�\\' => '蒤', '�]' => '蒥', '�^' => '蒦', '�_' => '蒧', '�`' => '蒨', '�a' => '蒩', '�b' => '蒪', '�c' => '蒫', '�d' => '蒬', '�e' => '蒭', '�f' => '蒮', '�g' => '蒰', '�h' => '蒱', '�i' => '蒳', '�j' => '蒵', '�k' => '蒶', '�l' => '蒷', '�m' => '蒻', '�n' => '蒼', '�o' => '蒾', '�p' => '蓀', '�q' => '蓂', '�r' => '蓃', '�s' => '蓅', '�t' => '蓆', '�u' => '蓇', '�v' => '蓈', '�w' => '蓋', '�x' => '蓌', '�y' => '蓎', '�z' => '蓏', '�{' => '蓒', '�|' => '蓔', '�}' => '蓕', '�~' => '蓗', 'ɀ' => '蓘', 'Ɂ' => '蓙', 'ɂ' => '蓚', 'Ƀ' => '蓛', 'Ʉ' => '蓜', 'Ʌ' => '蓞', 'Ɇ' => '蓡', 'ɇ' => '蓢', 'Ɉ' => '蓤', 'ɉ' => '蓧', 'Ɋ' => '蓨', 'ɋ' => '蓩', 'Ɍ' => '蓪', 'ɍ' => '蓫', 'Ɏ' => '蓭', 'ɏ' => '蓮', 'ɐ' => '蓯', 'ɑ' => '蓱', 'ɒ' => '蓲', 'ɓ' => '蓳', 'ɔ' => '蓴', 'ɕ' => '蓵', 'ɖ' => '蓶', 'ɗ' => '蓷', 'ɘ' => '蓸', 'ə' => '蓹', 'ɚ' => '蓺', 'ɛ' => '蓻', 'ɜ' => '蓽', 'ɝ' => '蓾', 'ɞ' => '蔀', 'ɟ' => '蔁', 'ɠ' => '蔂', 'ɡ' => '伞', 'ɢ' => '散', 'ɣ' => '桑', 'ɤ' => '嗓', 'ɥ' => '丧', 'ɦ' => '搔', 'ɧ' => '骚', 'ɨ' => '扫', 'ɩ' => '嫂', 'ɪ' => '瑟', 'ɫ' => '色', 'ɬ' => '涩', 'ɭ' => '森', 'ɮ' => '僧', 'ɯ' => '莎', 'ɰ' => '砂', 'ɱ' => '杀', 'ɲ' => '刹', 'ɳ' => '沙', 'ɴ' => '纱', 'ɵ' => '傻', 'ɶ' => '啥', 'ɷ' => '煞', 'ɸ' => '筛', 'ɹ' => '晒', 'ɺ' => '珊', 'ɻ' => '苫', 'ɼ' => '杉', 'ɽ' => '山', 'ɾ' => '删', 'ɿ' => '煽', '�' => '衫', '�' => '闪', '��' => '陕', '��' => '擅', '��' => '赡', '��' => '膳', '��' => '善', '��' => '汕', '��' => '扇', '��' => '缮', '��' => '墒', '��' => '伤', '��' => '商', '��' => '赏', '��' => '晌', '��' => '上', '��' => '尚', '��' => '裳', '��' => '梢', '��' => '捎', '��' => '稍', '��' => '烧', '��' => '芍', '��' => '勺', '��' => '韶', '��' => '少', '��' => '哨', '��' => '邵', '��' => '绍', '��' => '奢', '��' => '赊', '��' => '蛇', '��' => '舌', '��' => '舍', '��' => '赦', '��' => '摄', '��' => '射', '��' => '慑', '��' => '涉', '��' => '社', '��' => '设', '��' => '砷', '��' => '申', '��' => '呻', '��' => '伸', '��' => '身', '��' => '深', '��' => '娠', '��' => '绅', '��' => '神', '��' => '沈', '��' => '审', '��' => '婶', '�' => '甚', '�' => '肾', '�' => '慎', '�' => '渗', '�' => '声', '�' => '生', '�' => '甥', '�' => '牲', '�' => '升', '�' => '绳', '�@' => '蔃', '�A' => '蔄', '�B' => '蔅', '�C' => '蔆', '�D' => '蔇', '�E' => '蔈', '�F' => '蔉', '�G' => '蔊', '�H' => '蔋', '�I' => '蔍', '�J' => '蔎', '�K' => '蔏', '�L' => '蔐', '�M' => '蔒', '�N' => '蔔', '�O' => '蔕', '�P' => '蔖', '�Q' => '蔘', '�R' => '蔙', '�S' => '蔛', '�T' => '蔜', '�U' => '蔝', '�V' => '蔞', '�W' => '蔠', '�X' => '蔢', '�Y' => '蔣', '�Z' => '蔤', '�[' => '蔥', '�\\' => '蔦', '�]' => '蔧', '�^' => '蔨', '�_' => '蔩', '�`' => '蔪', '�a' => '蔭', '�b' => '蔮', '�c' => '蔯', '�d' => '蔰', '�e' => '蔱', '�f' => '蔲', '�g' => '蔳', '�h' => '蔴', '�i' => '蔵', '�j' => '蔶', '�k' => '蔾', '�l' => '蔿', '�m' => '蕀', '�n' => '蕁', '�o' => '蕂', '�p' => '蕄', '�q' => '蕅', '�r' => '蕆', '�s' => '蕇', '�t' => '蕋', '�u' => '蕌', '�v' => '蕍', '�w' => '蕎', '�x' => '蕏', '�y' => '蕐', '�z' => '蕑', '�{' => '蕒', '�|' => '蕓', '�}' => '蕔', '�~' => '蕕', 'ʀ' => '蕗', 'ʁ' => '蕘', 'ʂ' => '蕚', 'ʃ' => '蕛', 'ʄ' => '蕜', 'ʅ' => '蕝', 'ʆ' => '蕟', 'ʇ' => '蕠', 'ʈ' => '蕡', 'ʉ' => '蕢', 'ʊ' => '蕣', 'ʋ' => '蕥', 'ʌ' => '蕦', 'ʍ' => '蕧', 'ʎ' => '蕩', 'ʏ' => '蕪', 'ʐ' => '蕫', 'ʑ' => '蕬', 'ʒ' => '蕭', 'ʓ' => '蕮', 'ʔ' => '蕯', 'ʕ' => '蕰', 'ʖ' => '蕱', 'ʗ' => '蕳', 'ʘ' => '蕵', 'ʙ' => '蕶', 'ʚ' => '蕷', 'ʛ' => '蕸', 'ʜ' => '蕼', 'ʝ' => '蕽', 'ʞ' => '蕿', 'ʟ' => '薀', 'ʠ' => '薁', 'ʡ' => '省', 'ʢ' => '盛', 'ʣ' => '剩', 'ʤ' => '胜', 'ʥ' => '圣', 'ʦ' => '师', 'ʧ' => '失', 'ʨ' => '狮', 'ʩ' => '施', 'ʪ' => '湿', 'ʫ' => '诗', 'ʬ' => '尸', 'ʭ' => '虱', 'ʮ' => '十', 'ʯ' => '石', 'ʰ' => '拾', 'ʱ' => '时', 'ʲ' => '什', 'ʳ' => '食', 'ʴ' => '蚀', 'ʵ' => '实', 'ʶ' => '识', 'ʷ' => '史', 'ʸ' => '矢', 'ʹ' => '使', 'ʺ' => '屎', 'ʻ' => '驶', 'ʼ' => '始', 'ʽ' => '式', 'ʾ' => '示', 'ʿ' => '士', '�' => '世', '�' => '柿', '��' => '事', '��' => '拭', '��' => '誓', '��' => '逝', '��' => '势', '��' => '是', '��' => '嗜', '��' => '噬', '��' => '适', '��' => '仕', '��' => '侍', '��' => '释', '��' => '饰', '��' => '氏', '��' => '市', '��' => '恃', '��' => '室', '��' => '视', '��' => '试', '��' => '收', '��' => '手', '��' => '首', '��' => '守', '��' => '寿', '��' => '授', '��' => '售', '��' => '受', '��' => '瘦', '��' => '兽', '��' => '蔬', '��' => '枢', '��' => '梳', '��' => '殊', '��' => '抒', '��' => '输', '��' => '叔', '��' => '舒', '��' => '淑', '��' => '疏', '��' => '书', '��' => '赎', '��' => '孰', '��' => '熟', '��' => '薯', '��' => '暑', '��' => '曙', '��' => '署', '��' => '蜀', '��' => '黍', '��' => '鼠', '��' => '属', '�' => '术', '�' => '述', '�' => '树', '�' => '束', '�' => '戍', '�' => '竖', '�' => '墅', '�' => '庶', '�' => '数', '�' => '漱', '�@' => '薂', '�A' => '薃', '�B' => '薆', '�C' => '薈', '�D' => '薉', '�E' => '薊', '�F' => '薋', '�G' => '薌', '�H' => '薍', '�I' => '薎', '�J' => '薐', '�K' => '薑', '�L' => '薒', '�M' => '薓', '�N' => '薔', '�O' => '薕', '�P' => '薖', '�Q' => '薗', '�R' => '薘', '�S' => '薙', '�T' => '薚', '�U' => '薝', '�V' => '薞', '�W' => '薟', '�X' => '薠', '�Y' => '薡', '�Z' => '薢', '�[' => '薣', '�\\' => '薥', '�]' => '薦', '�^' => '薧', '�_' => '薩', '�`' => '薫', '�a' => '薬', '�b' => '薭', '�c' => '薱', '�d' => '薲', '�e' => '薳', '�f' => '薴', '�g' => '薵', '�h' => '薶', '�i' => '薸', '�j' => '薺', '�k' => '薻', '�l' => '薼', '�m' => '薽', '�n' => '薾', '�o' => '薿', '�p' => '藀', '�q' => '藂', '�r' => '藃', '�s' => '藄', '�t' => '藅', '�u' => '藆', '�v' => '藇', '�w' => '藈', '�x' => '藊', '�y' => '藋', '�z' => '藌', '�{' => '藍', '�|' => '藎', '�}' => '藑', '�~' => '藒', 'ˀ' => '藔', 'ˁ' => '藖', '˂' => '藗', '˃' => '藘', '˄' => '藙', '˅' => '藚', 'ˆ' => '藛', 'ˇ' => '藝', 'ˈ' => '藞', 'ˉ' => '藟', 'ˊ' => '藠', 'ˋ' => '藡', 'ˌ' => '藢', 'ˍ' => '藣', 'ˎ' => '藥', 'ˏ' => '藦', 'ː' => '藧', 'ˑ' => '藨', '˒' => '藪', '˓' => '藫', '˔' => '藬', '˕' => '藭', '˖' => '藮', '˗' => '藯', '˘' => '藰', '˙' => '藱', '˚' => '藲', '˛' => '藳', '˜' => '藴', '˝' => '藵', '˞' => '藶', '˟' => '藷', 'ˠ' => '藸', 'ˡ' => '恕', 'ˢ' => '刷', 'ˣ' => '耍', 'ˤ' => '摔', '˥' => '衰', '˦' => '甩', '˧' => '帅', '˨' => '栓', '˩' => '拴', '˪' => '霜', '˫' => '双', 'ˬ' => '爽', '˭' => '谁', 'ˮ' => '水', '˯' => '睡', '˰' => '税', '˱' => '吮', '˲' => '瞬', '˳' => '顺', '˴' => '舜', '˵' => '说', '˶' => '硕', '˷' => '朔', '˸' => '烁', '˹' => '斯', '˺' => '撕', '˻' => '嘶', '˼' => '思', '˽' => '私', '˾' => '司', '˿' => '丝', '�' => '死', '�' => '肆', '��' => '寺', '��' => '嗣', '��' => '四', '��' => '伺', '��' => '似', '��' => '饲', '��' => '巳', '��' => '松', '��' => '耸', '��' => '怂', '��' => '颂', '��' => '送', '��' => '宋', '��' => '讼', '��' => '诵', '��' => '搜', '��' => '艘', '��' => '擞', '��' => '嗽', '��' => '苏', '��' => '酥', '��' => '俗', '��' => '素', '��' => '速', '��' => '粟', '��' => '僳', '��' => '塑', '��' => '溯', '��' => '宿', '��' => '诉', '��' => '肃', '��' => '酸', '��' => '蒜', '��' => '算', '��' => '虽', '��' => '隋', '��' => '随', '��' => '绥', '��' => '髓', '��' => '碎', '��' => '岁', '��' => '穗', '��' => '遂', '��' => '隧', '��' => '祟', '��' => '孙', '��' => '损', '��' => '笋', '��' => '蓑', '��' => '梭', '��' => '唆', '�' => '缩', '�' => '琐', '�' => '索', '�' => '锁', '�' => '所', '�' => '塌', '�' => '他', '�' => '它', '�' => '她', '�' => '塔', '�@' => '藹', '�A' => '藺', '�B' => '藼', '�C' => '藽', '�D' => '藾', '�E' => '蘀', '�F' => '蘁', '�G' => '蘂', '�H' => '蘃', '�I' => '蘄', '�J' => '蘆', '�K' => '蘇', '�L' => '蘈', '�M' => '蘉', '�N' => '蘊', '�O' => '蘋', '�P' => '蘌', '�Q' => '蘍', '�R' => '蘎', '�S' => '蘏', '�T' => '蘐', '�U' => '蘒', '�V' => '蘓', '�W' => '蘔', '�X' => '蘕', '�Y' => '蘗', '�Z' => '蘘', '�[' => '蘙', '�\\' => '蘚', '�]' => '蘛', '�^' => '蘜', '�_' => '蘝', '�`' => '蘞', '�a' => '蘟', '�b' => '蘠', '�c' => '蘡', '�d' => '蘢', '�e' => '蘣', '�f' => '蘤', '�g' => '蘥', '�h' => '蘦', '�i' => '蘨', '�j' => '蘪', '�k' => '蘫', '�l' => '蘬', '�m' => '蘭', '�n' => '蘮', '�o' => '蘯', '�p' => '蘰', '�q' => '蘱', '�r' => '蘲', '�s' => '蘳', '�t' => '蘴', '�u' => '蘵', '�v' => '蘶', '�w' => '蘷', '�x' => '蘹', '�y' => '蘺', '�z' => '蘻', '�{' => '蘽', '�|' => '蘾', '�}' => '蘿', '�~' => '虀', '̀' => '虁', '́' => '虂', '̂' => '虃', '̃' => '虄', '̄' => '虅', '̅' => '虆', '̆' => '虇', '̇' => '虈', '̈' => '虉', '̉' => '虊', '̊' => '虋', '̋' => '虌', '̌' => '虒', '̍' => '虓', '̎' => '處', '̏' => '虖', '̐' => '虗', '̑' => '虘', '̒' => '虙', '̓' => '虛', '̔' => '虜', '̕' => '虝', '̖' => '號', '̗' => '虠', '̘' => '虡', '̙' => '虣', '̚' => '虤', '̛' => '虥', '̜' => '虦', '̝' => '虧', '̞' => '虨', '̟' => '虩', '̠' => '虪', '̡' => '獭', '̢' => '挞', '̣' => '蹋', '̤' => '踏', '̥' => '胎', '̦' => '苔', '̧' => '抬', '̨' => '台', '̩' => '泰', '̪' => '酞', '̫' => '太', '̬' => '态', '̭' => '汰', '̮' => '坍', '̯' => '摊', '̰' => '贪', '̱' => '瘫', '̲' => '滩', '̳' => '坛', '̴' => '檀', '̵' => '痰', '̶' => '潭', '̷' => '谭', '̸' => '谈', '̹' => '坦', '̺' => '毯', '̻' => '袒', '̼' => '碳', '̽' => '探', '̾' => '叹', '̿' => '炭', '�' => '汤', '�' => '塘', '��' => '搪', '��' => '堂', '��' => '棠', '��' => '膛', '��' => '唐', '��' => '糖', '��' => '倘', '��' => '躺', '��' => '淌', '��' => '趟', '��' => '烫', '��' => '掏', '��' => '涛', '��' => '滔', '��' => '绦', '��' => '萄', '��' => '桃', '��' => '逃', '��' => '淘', '��' => '陶', '��' => '讨', '��' => '套', '��' => '特', '��' => '藤', '��' => '腾', '��' => '疼', '��' => '誊', '��' => '梯', '��' => '剔', '��' => '踢', '��' => '锑', '��' => '提', '��' => '题', '��' => '蹄', '��' => '啼', '��' => '体', '��' => '替', '��' => '嚏', '��' => '惕', '��' => '涕', '��' => '剃', '��' => '屉', '��' => '天', '��' => '添', '��' => '填', '��' => '田', '��' => '甜', '��' => '恬', '��' => '舔', '��' => '腆', '��' => '挑', '�' => '条', '�' => '迢', '�' => '眺', '�' => '跳', '�' => '贴', '�' => '铁', '�' => '帖', '�' => '厅', '�' => '听', '�' => '烃', '�@' => '虭', '�A' => '虯', '�B' => '虰', '�C' => '虲', '�D' => '虳', '�E' => '虴', '�F' => '虵', '�G' => '虶', '�H' => '虷', '�I' => '虸', '�J' => '蚃', '�K' => '蚄', '�L' => '蚅', '�M' => '蚆', '�N' => '蚇', '�O' => '蚈', '�P' => '蚉', '�Q' => '蚎', '�R' => '蚏', '�S' => '蚐', '�T' => '蚑', '�U' => '蚒', '�V' => '蚔', '�W' => '蚖', '�X' => '蚗', '�Y' => '蚘', '�Z' => '蚙', '�[' => '蚚', '�\\' => '蚛', '�]' => '蚞', '�^' => '蚟', '�_' => '蚠', '�`' => '蚡', '�a' => '蚢', '�b' => '蚥', '�c' => '蚦', '�d' => '蚫', '�e' => '蚭', '�f' => '蚮', '�g' => '蚲', '�h' => '蚳', '�i' => '蚷', '�j' => '蚸', '�k' => '蚹', '�l' => '蚻', '�m' => '蚼', '�n' => '蚽', '�o' => '蚾', '�p' => '蚿', '�q' => '蛁', '�r' => '蛂', '�s' => '蛃', '�t' => '蛅', '�u' => '蛈', '�v' => '蛌', '�w' => '蛍', '�x' => '蛒', '�y' => '蛓', '�z' => '蛕', '�{' => '蛖', '�|' => '蛗', '�}' => '蛚', '�~' => '蛜', '̀' => '蛝', '́' => '蛠', '͂' => '蛡', '̓' => '蛢', '̈́' => '蛣', 'ͅ' => '蛥', '͆' => '蛦', '͇' => '蛧', '͈' => '蛨', '͉' => '蛪', '͊' => '蛫', '͋' => '蛬', '͌' => '蛯', '͍' => '蛵', '͎' => '蛶', '͏' => '蛷', '͐' => '蛺', '͑' => '蛻', '͒' => '蛼', '͓' => '蛽', '͔' => '蛿', '͕' => '蜁', '͖' => '蜄', '͗' => '蜅', '͘' => '蜆', '͙' => '蜋', '͚' => '蜌', '͛' => '蜎', '͜' => '蜏', '͝' => '蜐', '͞' => '蜑', '͟' => '蜔', '͠' => '蜖', '͡' => '汀', '͢' => '廷', 'ͣ' => '停', 'ͤ' => '亭', 'ͥ' => '庭', 'ͦ' => '挺', 'ͧ' => '艇', 'ͨ' => '通', 'ͩ' => '桐', 'ͪ' => '酮', 'ͫ' => '瞳', 'ͬ' => '同', 'ͭ' => '铜', 'ͮ' => '彤', 'ͯ' => '童', 'Ͱ' => '桶', 'ͱ' => '捅', 'Ͳ' => '筒', 'ͳ' => '统', 'ʹ' => '痛', '͵' => '偷', 'Ͷ' => '投', 'ͷ' => '头', '' => '透', '' => '凸', 'ͺ' => '秃', 'ͻ' => '突', 'ͼ' => '图', 'ͽ' => '徒', ';' => '途', 'Ϳ' => '涂', '�' => '屠', '�' => '土', '��' => '吐', '��' => '兔', '��' => '湍', '��' => '团', '��' => '推', '��' => '颓', '��' => '腿', '��' => '蜕', '��' => '褪', '��' => '退', '��' => '吞', '��' => '屯', '��' => '臀', '��' => '拖', '��' => '托', '��' => '脱', '��' => '鸵', '��' => '陀', '��' => '驮', '��' => '驼', '��' => '椭', '��' => '妥', '��' => '拓', '��' => '唾', '��' => '挖', '��' => '哇', '��' => '蛙', '��' => '洼', '��' => '娃', '��' => '瓦', '��' => '袜', '��' => '歪', '��' => '外', '��' => '豌', '��' => '弯', '��' => '湾', '��' => '玩', '��' => '顽', '��' => '丸', '��' => '烷', '��' => '完', '��' => '碗', '��' => '挽', '��' => '晚', '��' => '皖', '��' => '惋', '��' => '宛', '��' => '婉', '��' => '万', '��' => '腕', '��' => '汪', '�' => '王', '�' => '亡', '�' => '枉', '�' => '网', '�' => '往', '�' => '旺', '�' => '望', '�' => '忘', '�' => '妄', '�' => '威', '�@' => '蜙', '�A' => '蜛', '�B' => '蜝', '�C' => '蜟', '�D' => '蜠', '�E' => '蜤', '�F' => '蜦', '�G' => '蜧', '�H' => '蜨', '�I' => '蜪', '�J' => '蜫', '�K' => '蜬', '�L' => '蜭', '�M' => '蜯', '�N' => '蜰', '�O' => '蜲', '�P' => '蜳', '�Q' => '蜵', '�R' => '蜶', '�S' => '蜸', '�T' => '蜹', '�U' => '蜺', '�V' => '蜼', '�W' => '蜽', '�X' => '蝀', '�Y' => '蝁', '�Z' => '蝂', '�[' => '蝃', '�\\' => '蝄', '�]' => '蝅', '�^' => '蝆', '�_' => '蝊', '�`' => '蝋', '�a' => '蝍', '�b' => '蝏', '�c' => '蝐', '�d' => '蝑', '�e' => '蝒', '�f' => '蝔', '�g' => '蝕', '�h' => '蝖', '�i' => '蝘', '�j' => '蝚', '�k' => '蝛', '�l' => '蝜', '�m' => '蝝', '�n' => '蝞', '�o' => '蝟', '�p' => '蝡', '�q' => '蝢', '�r' => '蝦', '�s' => '蝧', '�t' => '蝨', '�u' => '蝩', '�v' => '蝪', '�w' => '蝫', '�x' => '蝬', '�y' => '蝭', '�z' => '蝯', '�{' => '蝱', '�|' => '蝲', '�}' => '蝳', '�~' => '蝵', '' => '蝷', '' => '蝸', '' => '蝹', '' => '蝺', '΄' => '蝿', '΅' => '螀', 'Ά' => '螁', '·' => '螄', 'Έ' => '螆', 'Ή' => '螇', 'Ί' => '螉', '' => '螊', 'Ό' => '螌', '' => '螎', 'Ύ' => '螏', 'Ώ' => '螐', 'ΐ' => '螑', 'Α' => '螒', 'Β' => '螔', 'Γ' => '螕', 'Δ' => '螖', 'Ε' => '螘', 'Ζ' => '螙', 'Η' => '螚', 'Θ' => '螛', 'Ι' => '螜', 'Κ' => '螝', 'Λ' => '螞', 'Μ' => '螠', 'Ν' => '螡', 'Ξ' => '螢', 'Ο' => '螣', 'Π' => '螤', 'Ρ' => '巍', '' => '微', 'Σ' => '危', 'Τ' => '韦', 'Υ' => '违', 'Φ' => '桅', 'Χ' => '围', 'Ψ' => '唯', 'Ω' => '惟', 'Ϊ' => '为', 'Ϋ' => '潍', 'ά' => '维', 'έ' => '苇', 'ή' => '萎', 'ί' => '委', 'ΰ' => '伟', 'α' => '伪', 'β' => '尾', 'γ' => '纬', 'δ' => '未', 'ε' => '蔚', 'ζ' => '味', 'η' => '畏', 'θ' => '胃', 'ι' => '喂', 'κ' => '魏', 'λ' => '位', 'μ' => '渭', 'ν' => '谓', 'ξ' => '尉', 'ο' => '慰', '�' => '卫', '�' => '瘟', '��' => '温', '��' => '蚊', '��' => '文', '��' => '闻', '��' => '纹', '��' => '吻', '��' => '稳', '��' => '紊', '��' => '问', '��' => '嗡', '��' => '翁', '��' => '瓮', '��' => '挝', '��' => '蜗', '��' => '涡', '��' => '窝', '��' => '我', '��' => '斡', '��' => '卧', '��' => '握', '��' => '沃', '��' => '巫', '��' => '呜', '��' => '钨', '��' => '乌', '��' => '污', '��' => '诬', '��' => '屋', '��' => '无', '��' => '芜', '��' => '梧', '��' => '吾', '��' => '吴', '��' => '毋', '��' => '武', '��' => '五', '��' => '捂', '��' => '午', '��' => '舞', '��' => '伍', '��' => '侮', '��' => '坞', '��' => '戊', '��' => '雾', '��' => '晤', '��' => '物', '��' => '勿', '��' => '务', '��' => '悟', '��' => '误', '��' => '昔', '�' => '熙', '�' => '析', '�' => '西', '�' => '硒', '�' => '矽', '�' => '晰', '�' => '嘻', '�' => '吸', '�' => '锡', '�' => '牺', '�@' => '螥', '�A' => '螦', '�B' => '螧', '�C' => '螩', '�D' => '螪', '�E' => '螮', '�F' => '螰', '�G' => '螱', '�H' => '螲', '�I' => '螴', '�J' => '螶', '�K' => '螷', '�L' => '螸', '�M' => '螹', '�N' => '螻', '�O' => '螼', '�P' => '螾', '�Q' => '螿', '�R' => '蟁', '�S' => '蟂', '�T' => '蟃', '�U' => '蟄', '�V' => '蟅', '�W' => '蟇', '�X' => '蟈', '�Y' => '蟉', '�Z' => '蟌', '�[' => '蟍', '�\\' => '蟎', '�]' => '蟏', '�^' => '蟐', '�_' => '蟔', '�`' => '蟕', '�a' => '蟖', '�b' => '蟗', '�c' => '蟘', '�d' => '蟙', '�e' => '蟚', '�f' => '蟜', '�g' => '蟝', '�h' => '蟞', '�i' => '蟟', '�j' => '蟡', '�k' => '蟢', '�l' => '蟣', '�m' => '蟤', '�n' => '蟦', '�o' => '蟧', '�p' => '蟨', '�q' => '蟩', '�r' => '蟫', '�s' => '蟬', '�t' => '蟭', '�u' => '蟯', '�v' => '蟰', '�w' => '蟱', '�x' => '蟲', '�y' => '蟳', '�z' => '蟴', '�{' => '蟵', '�|' => '蟶', '�}' => '蟷', '�~' => '蟸', 'π' => '蟺', 'ρ' => '蟻', 'ς' => '蟼', 'σ' => '蟽', 'τ' => '蟿', 'υ' => '蠀', 'φ' => '蠁', 'χ' => '蠂', 'ψ' => '蠄', 'ω' => '蠅', 'ϊ' => '蠆', 'ϋ' => '蠇', 'ό' => '蠈', 'ύ' => '蠉', 'ώ' => '蠋', 'Ϗ' => '蠌', 'ϐ' => '蠍', 'ϑ' => '蠎', 'ϒ' => '蠏', 'ϓ' => '蠐', 'ϔ' => '蠑', 'ϕ' => '蠒', 'ϖ' => '蠔', 'ϗ' => '蠗', 'Ϙ' => '蠘', 'ϙ' => '蠙', 'Ϛ' => '蠚', 'ϛ' => '蠜', 'Ϝ' => '蠝', 'ϝ' => '蠞', 'Ϟ' => '蠟', 'ϟ' => '蠠', 'Ϡ' => '蠣', 'ϡ' => '稀', 'Ϣ' => '息', 'ϣ' => '希', 'Ϥ' => '悉', 'ϥ' => '膝', 'Ϧ' => '夕', 'ϧ' => '惜', 'Ϩ' => '熄', 'ϩ' => '烯', 'Ϫ' => '溪', 'ϫ' => '汐', 'Ϭ' => '犀', 'ϭ' => '檄', 'Ϯ' => '袭', 'ϯ' => '席', 'ϰ' => '习', 'ϱ' => '媳', 'ϲ' => '喜', 'ϳ' => '铣', 'ϴ' => '洗', 'ϵ' => '系', '϶' => '隙', 'Ϸ' => '戏', 'ϸ' => '细', 'Ϲ' => '瞎', 'Ϻ' => '虾', 'ϻ' => '匣', 'ϼ' => '霞', 'Ͻ' => '辖', 'Ͼ' => '暇', 'Ͽ' => '峡', '�' => '侠', '�' => '狭', '��' => '下', '��' => '厦', '��' => '夏', '��' => '吓', '��' => '掀', '��' => '锨', '��' => '先', '��' => '仙', '��' => '鲜', '��' => '纤', '��' => '咸', '��' => '贤', '��' => '衔', '��' => '舷', '��' => '闲', '��' => '涎', '��' => '弦', '��' => '嫌', '��' => '显', '��' => '险', '��' => '现', '��' => '献', '��' => '县', '��' => '腺', '��' => '馅', '��' => '羡', '��' => '宪', '��' => '陷', '��' => '限', '��' => '线', '��' => '相', '��' => '厢', '��' => '镶', '��' => '香', '��' => '箱', '��' => '襄', '��' => '湘', '��' => '乡', '��' => '翔', '��' => '祥', '��' => '详', '��' => '想', '��' => '响', '��' => '享', '��' => '项', '��' => '巷', '��' => '橡', '��' => '像', '��' => '向', '��' => '象', '��' => '萧', '�' => '硝', '�' => '霄', '�' => '削', '�' => '哮', '�' => '嚣', '�' => '销', '�' => '消', '�' => '宵', '�' => '淆', '�' => '晓', '�@' => '蠤', '�A' => '蠥', '�B' => '蠦', '�C' => '蠧', '�D' => '蠨', '�E' => '蠩', '�F' => '蠪', '�G' => '蠫', '�H' => '蠬', '�I' => '蠭', '�J' => '蠮', '�K' => '蠯', '�L' => '蠰', '�M' => '蠱', '�N' => '蠳', '�O' => '蠴', '�P' => '蠵', '�Q' => '蠶', '�R' => '蠷', '�S' => '蠸', '�T' => '蠺', '�U' => '蠻', '�V' => '蠽', '�W' => '蠾', '�X' => '蠿', '�Y' => '衁', '�Z' => '衂', '�[' => '衃', '�\\' => '衆', '�]' => '衇', '�^' => '衈', '�_' => '衉', '�`' => '衊', '�a' => '衋', '�b' => '衎', '�c' => '衏', '�d' => '衐', '�e' => '衑', '�f' => '衒', '�g' => '術', '�h' => '衕', '�i' => '衖', '�j' => '衘', '�k' => '衚', '�l' => '衛', '�m' => '衜', '�n' => '衝', '�o' => '衞', '�p' => '衟', '�q' => '衠', '�r' => '衦', '�s' => '衧', '�t' => '衪', '�u' => '衭', '�v' => '衯', '�w' => '衱', '�x' => '衳', '�y' => '衴', '�z' => '衵', '�{' => '衶', '�|' => '衸', '�}' => '衹', '�~' => '衺', 'Ѐ' => '衻', 'Ё' => '衼', 'Ђ' => '袀', 'Ѓ' => '袃', 'Є' => '袆', 'Ѕ' => '袇', 'І' => '袉', 'Ї' => '袊', 'Ј' => '袌', 'Љ' => '袎', 'Њ' => '袏', 'Ћ' => '袐', 'Ќ' => '袑', 'Ѝ' => '袓', 'Ў' => '袔', 'Џ' => '袕', 'А' => '袗', 'Б' => '袘', 'В' => '袙', 'Г' => '袚', 'Д' => '袛', 'Е' => '袝', 'Ж' => '袞', 'З' => '袟', 'И' => '袠', 'Й' => '袡', 'К' => '袣', 'Л' => '袥', 'М' => '袦', 'Н' => '袧', 'О' => '袨', 'П' => '袩', 'Р' => '袪', 'С' => '小', 'Т' => '孝', 'У' => '校', 'Ф' => '肖', 'Х' => '啸', 'Ц' => '笑', 'Ч' => '效', 'Ш' => '楔', 'Щ' => '些', 'Ъ' => '歇', 'Ы' => '蝎', 'Ь' => '鞋', 'Э' => '协', 'Ю' => '挟', 'Я' => '携', 'а' => '邪', 'б' => '斜', 'в' => '胁', 'г' => '谐', 'д' => '写', 'е' => '械', 'ж' => '卸', 'з' => '蟹', 'и' => '懈', 'й' => '泄', 'к' => '泻', 'л' => '谢', 'м' => '屑', 'н' => '薪', 'о' => '芯', 'п' => '锌', '�' => '欣', '�' => '辛', '��' => '新', '��' => '忻', '��' => '心', '��' => '信', '��' => '衅', '��' => '星', '��' => '腥', '��' => '猩', '��' => '惺', '��' => '兴', '��' => '刑', '��' => '型', '��' => '形', '��' => '邢', '��' => '行', '��' => '醒', '��' => '幸', '��' => '杏', '��' => '性', '��' => '姓', '��' => '兄', '��' => '凶', '��' => '胸', '��' => '匈', '��' => '汹', '��' => '雄', '��' => '熊', '��' => '休', '��' => '修', '��' => '羞', '��' => '朽', '��' => '嗅', '��' => '锈', '��' => '秀', '��' => '袖', '��' => '绣', '��' => '墟', '��' => '戌', '��' => '需', '��' => '虚', '��' => '嘘', '��' => '须', '��' => '徐', '��' => '许', '��' => '蓄', '��' => '酗', '��' => '叙', '��' => '旭', '��' => '序', '��' => '畜', '��' => '恤', '�' => '絮', '�' => '婿', '�' => '绪', '�' => '续', '�' => '轩', '�' => '喧', '�' => '宣', '�' => '悬', '�' => '旋', '�' => '玄', '�@' => '袬', '�A' => '袮', '�B' => '袯', '�C' => '袰', '�D' => '袲', '�E' => '袳', '�F' => '袴', '�G' => '袵', '�H' => '袶', '�I' => '袸', '�J' => '袹', '�K' => '袺', '�L' => '袻', '�M' => '袽', '�N' => '袾', '�O' => '袿', '�P' => '裀', '�Q' => '裃', '�R' => '裄', '�S' => '裇', '�T' => '裈', '�U' => '裊', '�V' => '裋', '�W' => '裌', '�X' => '裍', '�Y' => '裏', '�Z' => '裐', '�[' => '裑', '�\\' => '裓', '�]' => '裖', '�^' => '裗', '�_' => '裚', '�`' => '裛', '�a' => '補', '�b' => '裝', '�c' => '裞', '�d' => '裠', '�e' => '裡', '�f' => '裦', '�g' => '裧', '�h' => '裩', '�i' => '裪', '�j' => '裫', '�k' => '裬', '�l' => '裭', '�m' => '裮', '�n' => '裯', '�o' => '裲', '�p' => '裵', '�q' => '裶', '�r' => '裷', '�s' => '裺', '�t' => '裻', '�u' => '製', '�v' => '裿', '�w' => '褀', '�x' => '褁', '�y' => '褃', '�z' => '褄', '�{' => '褅', '�|' => '褆', '�}' => '複', '�~' => '褈', 'р' => '褉', 'с' => '褋', 'т' => '褌', 'у' => '褍', 'ф' => '褎', 'х' => '褏', 'ц' => '褑', 'ч' => '褔', 'ш' => '褕', 'щ' => '褖', 'ъ' => '褗', 'ы' => '褘', 'ь' => '褜', 'э' => '褝', 'ю' => '褞', 'я' => '褟', 'ѐ' => '褠', 'ё' => '褢', 'ђ' => '褣', 'ѓ' => '褤', 'є' => '褦', 'ѕ' => '褧', 'і' => '褨', 'ї' => '褩', 'ј' => '褬', 'љ' => '褭', 'њ' => '褮', 'ћ' => '褯', 'ќ' => '褱', 'ѝ' => '褲', 'ў' => '褳', 'џ' => '褵', 'Ѡ' => '褷', 'ѡ' => '选', 'Ѣ' => '癣', 'ѣ' => '眩', 'Ѥ' => '绚', 'ѥ' => '靴', 'Ѧ' => '薛', 'ѧ' => '学', 'Ѩ' => '穴', 'ѩ' => '雪', 'Ѫ' => '血', 'ѫ' => '勋', 'Ѭ' => '熏', 'ѭ' => '循', 'Ѯ' => '旬', 'ѯ' => '询', 'Ѱ' => '寻', 'ѱ' => '驯', 'Ѳ' => '巡', 'ѳ' => '殉', 'Ѵ' => '汛', 'ѵ' => '训', 'Ѷ' => '讯', 'ѷ' => '逊', 'Ѹ' => '迅', 'ѹ' => '压', 'Ѻ' => '押', 'ѻ' => '鸦', 'Ѽ' => '鸭', 'ѽ' => '呀', 'Ѿ' => '丫', 'ѿ' => '芽', '�' => '牙', '�' => '蚜', '��' => '崖', '��' => '衙', '��' => '涯', '��' => '雅', '��' => '哑', '��' => '亚', '��' => '讶', '��' => '焉', '��' => '咽', '��' => '阉', '��' => '烟', '��' => '淹', '��' => '盐', '��' => '严', '��' => '研', '��' => '蜒', '��' => '岩', '��' => '延', '��' => '言', '��' => '颜', '��' => '阎', '��' => '炎', '��' => '沿', '��' => '奄', '��' => '掩', '��' => '眼', '��' => '衍', '��' => '演', '��' => '艳', '��' => '堰', '��' => '燕', '��' => '厌', '��' => '砚', '��' => '雁', '��' => '唁', '��' => '彦', '��' => '焰', '��' => '宴', '��' => '谚', '��' => '验', '��' => '殃', '��' => '央', '��' => '鸯', '��' => '秧', '��' => '杨', '��' => '扬', '��' => '佯', '��' => '疡', '��' => '羊', '��' => '洋', '��' => '阳', '�' => '氧', '�' => '仰', '�' => '痒', '�' => '养', '�' => '样', '�' => '漾', '�' => '邀', '�' => '腰', '�' => '妖', '�' => '瑶', '�@' => '褸', '�A' => '褹', '�B' => '褺', '�C' => '褻', '�D' => '褼', '�E' => '褽', '�F' => '褾', '�G' => '褿', '�H' => '襀', '�I' => '襂', '�J' => '襃', '�K' => '襅', '�L' => '襆', '�M' => '襇', '�N' => '襈', '�O' => '襉', '�P' => '襊', '�Q' => '襋', '�R' => '襌', '�S' => '襍', '�T' => '襎', '�U' => '襏', '�V' => '襐', '�W' => '襑', '�X' => '襒', '�Y' => '襓', '�Z' => '襔', '�[' => '襕', '�\\' => '襖', '�]' => '襗', '�^' => '襘', '�_' => '襙', '�`' => '襚', '�a' => '襛', '�b' => '襜', '�c' => '襝', '�d' => '襠', '�e' => '襡', '�f' => '襢', '�g' => '襣', '�h' => '襤', '�i' => '襥', '�j' => '襧', '�k' => '襨', '�l' => '襩', '�m' => '襪', '�n' => '襫', '�o' => '襬', '�p' => '襭', '�q' => '襮', '�r' => '襯', '�s' => '襰', '�t' => '襱', '�u' => '襲', '�v' => '襳', '�w' => '襴', '�x' => '襵', '�y' => '襶', '�z' => '襷', '�{' => '襸', '�|' => '襹', '�}' => '襺', '�~' => '襼', 'Ҁ' => '襽', 'ҁ' => '襾', '҂' => '覀', '҃' => '覂', '҄' => '覄', '҅' => '覅', '҆' => '覇', '҇' => '覈', '҈' => '覉', '҉' => '覊', 'Ҋ' => '見', 'ҋ' => '覌', 'Ҍ' => '覍', 'ҍ' => '覎', 'Ҏ' => '規', 'ҏ' => '覐', 'Ґ' => '覑', 'ґ' => '覒', 'Ғ' => '覓', 'ғ' => '覔', 'Ҕ' => '覕', 'ҕ' => '視', 'Җ' => '覗', 'җ' => '覘', 'Ҙ' => '覙', 'ҙ' => '覚', 'Қ' => '覛', 'қ' => '覜', 'Ҝ' => '覝', 'ҝ' => '覞', 'Ҟ' => '覟', 'ҟ' => '覠', 'Ҡ' => '覡', 'ҡ' => '摇', 'Ң' => '尧', 'ң' => '遥', 'Ҥ' => '窑', 'ҥ' => '谣', 'Ҧ' => '姚', 'ҧ' => '咬', 'Ҩ' => '舀', 'ҩ' => '药', 'Ҫ' => '要', 'ҫ' => '耀', 'Ҭ' => '椰', 'ҭ' => '噎', 'Ү' => '耶', 'ү' => '爷', 'Ұ' => '野', 'ұ' => '冶', 'Ҳ' => '也', 'ҳ' => '页', 'Ҵ' => '掖', 'ҵ' => '业', 'Ҷ' => '叶', 'ҷ' => '曳', 'Ҹ' => '腋', 'ҹ' => '夜', 'Һ' => '液', 'һ' => '一', 'Ҽ' => '壹', 'ҽ' => '医', 'Ҿ' => '揖', 'ҿ' => '铱', '�' => '依', '�' => '伊', '��' => '衣', '��' => '颐', '��' => '夷', '��' => '遗', '��' => '移', '��' => '仪', '��' => '胰', '��' => '疑', '��' => '沂', '��' => '宜', '��' => '姨', '��' => '彝', '��' => '椅', '��' => '蚁', '��' => '倚', '��' => '已', '��' => '乙', '��' => '矣', '��' => '以', '��' => '艺', '��' => '抑', '��' => '易', '��' => '邑', '��' => '屹', '��' => '亿', '��' => '役', '��' => '臆', '��' => '逸', '��' => '肄', '��' => '疫', '��' => '亦', '��' => '裔', '��' => '意', '��' => '毅', '��' => '忆', '��' => '义', '��' => '益', '��' => '溢', '��' => '诣', '��' => '议', '��' => '谊', '��' => '译', '��' => '异', '��' => '翼', '��' => '翌', '��' => '绎', '��' => '茵', '��' => '荫', '��' => '因', '��' => '殷', '��' => '音', '�' => '阴', '�' => '姻', '�' => '吟', '�' => '银', '�' => '淫', '�' => '寅', '�' => '饮', '�' => '尹', '�' => '引', '�' => '隐', '�@' => '覢', '�A' => '覣', '�B' => '覤', '�C' => '覥', '�D' => '覦', '�E' => '覧', '�F' => '覨', '�G' => '覩', '�H' => '親', '�I' => '覫', '�J' => '覬', '�K' => '覭', '�L' => '覮', '�M' => '覯', '�N' => '覰', '�O' => '覱', '�P' => '覲', '�Q' => '観', '�R' => '覴', '�S' => '覵', '�T' => '覶', '�U' => '覷', '�V' => '覸', '�W' => '覹', '�X' => '覺', '�Y' => '覻', '�Z' => '覼', '�[' => '覽', '�\\' => '覾', '�]' => '覿', '�^' => '觀', '�_' => '觃', '�`' => '觍', '�a' => '觓', '�b' => '觔', '�c' => '觕', '�d' => '觗', '�e' => '觘', '�f' => '觙', '�g' => '觛', '�h' => '觝', '�i' => '觟', '�j' => '觠', '�k' => '觡', '�l' => '觢', '�m' => '觤', '�n' => '觧', '�o' => '觨', '�p' => '觩', '�q' => '觪', '�r' => '觬', '�s' => '觭', '�t' => '觮', '�u' => '觰', '�v' => '觱', '�w' => '觲', '�x' => '觴', '�y' => '觵', '�z' => '觶', '�{' => '觷', '�|' => '觸', '�}' => '觹', '�~' => '觺', 'Ӏ' => '觻', 'Ӂ' => '觼', 'ӂ' => '觽', 'Ӄ' => '觾', 'ӄ' => '觿', 'Ӆ' => '訁', 'ӆ' => '訂', 'Ӈ' => '訃', 'ӈ' => '訄', 'Ӊ' => '訅', 'ӊ' => '訆', 'Ӌ' => '計', 'ӌ' => '訉', 'Ӎ' => '訊', 'ӎ' => '訋', 'ӏ' => '訌', 'Ӑ' => '訍', 'ӑ' => '討', 'Ӓ' => '訏', 'ӓ' => '訐', 'Ӕ' => '訑', 'ӕ' => '訒', 'Ӗ' => '訓', 'ӗ' => '訔', 'Ә' => '訕', 'ә' => '訖', 'Ӛ' => '託', 'ӛ' => '記', 'Ӝ' => '訙', 'ӝ' => '訚', 'Ӟ' => '訛', 'ӟ' => '訜', 'Ӡ' => '訝', 'ӡ' => '印', 'Ӣ' => '英', 'ӣ' => '樱', 'Ӥ' => '婴', 'ӥ' => '鹰', 'Ӧ' => '应', 'ӧ' => '缨', 'Ө' => '莹', 'ө' => '萤', 'Ӫ' => '营', 'ӫ' => '荧', 'Ӭ' => '蝇', 'ӭ' => '迎', 'Ӯ' => '赢', 'ӯ' => '盈', 'Ӱ' => '影', 'ӱ' => '颖', 'Ӳ' => '硬', 'ӳ' => '映', 'Ӵ' => '哟', 'ӵ' => '拥', 'Ӷ' => '佣', 'ӷ' => '臃', 'Ӹ' => '痈', 'ӹ' => '庸', 'Ӻ' => '雍', 'ӻ' => '踊', 'Ӽ' => '蛹', 'ӽ' => '咏', 'Ӿ' => '泳', 'ӿ' => '涌', '�' => '永', '�' => '恿', '��' => '勇', '��' => '用', '��' => '幽', '��' => '优', '��' => '悠', '��' => '忧', '��' => '尤', '��' => '由', '��' => '邮', '��' => '铀', '��' => '犹', '��' => '油', '��' => '游', '��' => '酉', '��' => '有', '��' => '友', '��' => '右', '��' => '佑', '��' => '釉', '��' => '诱', '��' => '又', '��' => '幼', '��' => '迂', '��' => '淤', '��' => '于', '��' => '盂', '��' => '榆', '��' => '虞', '��' => '愚', '��' => '舆', '��' => '余', '��' => '俞', '��' => '逾', '��' => '鱼', '��' => '愉', '��' => '渝', '��' => '渔', '��' => '隅', '��' => '予', '��' => '娱', '��' => '雨', '��' => '与', '��' => '屿', '��' => '禹', '��' => '宇', '��' => '语', '��' => '羽', '��' => '玉', '��' => '域', '��' => '芋', '��' => '郁', '�' => '吁', '�' => '遇', '�' => '喻', '�' => '峪', '�' => '御', '�' => '愈', '�' => '欲', '�' => '狱', '�' => '育', '�' => '誉', '�@' => '訞', '�A' => '訟', '�B' => '訠', '�C' => '訡', '�D' => '訢', '�E' => '訣', '�F' => '訤', '�G' => '訥', '�H' => '訦', '�I' => '訧', '�J' => '訨', '�K' => '訩', '�L' => '訪', '�M' => '訫', '�N' => '訬', '�O' => '設', '�P' => '訮', '�Q' => '訯', '�R' => '訰', '�S' => '許', '�T' => '訲', '�U' => '訳', '�V' => '訴', '�W' => '訵', '�X' => '訶', '�Y' => '訷', '�Z' => '訸', '�[' => '訹', '�\\' => '診', '�]' => '註', '�^' => '証', '�_' => '訽', '�`' => '訿', '�a' => '詀', '�b' => '詁', '�c' => '詂', '�d' => '詃', '�e' => '詄', '�f' => '詅', '�g' => '詆', '�h' => '詇', '�i' => '詉', '�j' => '詊', '�k' => '詋', '�l' => '詌', '�m' => '詍', '�n' => '詎', '�o' => '詏', '�p' => '詐', '�q' => '詑', '�r' => '詒', '�s' => '詓', '�t' => '詔', '�u' => '評', '�v' => '詖', '�w' => '詗', '�x' => '詘', '�y' => '詙', '�z' => '詚', '�{' => '詛', '�|' => '詜', '�}' => '詝', '�~' => '詞', 'Ԁ' => '詟', 'ԁ' => '詠', 'Ԃ' => '詡', 'ԃ' => '詢', 'Ԅ' => '詣', 'ԅ' => '詤', 'Ԇ' => '詥', 'ԇ' => '試', 'Ԉ' => '詧', 'ԉ' => '詨', 'Ԋ' => '詩', 'ԋ' => '詪', 'Ԍ' => '詫', 'ԍ' => '詬', 'Ԏ' => '詭', 'ԏ' => '詮', 'Ԑ' => '詯', 'ԑ' => '詰', 'Ԓ' => '話', 'ԓ' => '該', 'Ԕ' => '詳', 'ԕ' => '詴', 'Ԗ' => '詵', 'ԗ' => '詶', 'Ԙ' => '詷', 'ԙ' => '詸', 'Ԛ' => '詺', 'ԛ' => '詻', 'Ԝ' => '詼', 'ԝ' => '詽', 'Ԟ' => '詾', 'ԟ' => '詿', 'Ԡ' => '誀', 'ԡ' => '浴', 'Ԣ' => '寓', 'ԣ' => '裕', 'Ԥ' => '预', 'ԥ' => '豫', 'Ԧ' => '驭', 'ԧ' => '鸳', 'Ԩ' => '渊', 'ԩ' => '冤', 'Ԫ' => '元', 'ԫ' => '垣', 'Ԭ' => '袁', 'ԭ' => '原', 'Ԯ' => '援', 'ԯ' => '辕', '' => '园', 'Ա' => '员', 'Բ' => '圆', 'Գ' => '猿', 'Դ' => '源', 'Ե' => '缘', 'Զ' => '远', 'Է' => '苑', 'Ը' => '愿', 'Թ' => '怨', 'Ժ' => '院', 'Ի' => '曰', 'Լ' => '约', 'Խ' => '越', 'Ծ' => '跃', 'Կ' => '钥', '�' => '岳', '�' => '粤', '��' => '月', '��' => '悦', '��' => '阅', '��' => '耘', '��' => '云', '��' => '郧', '��' => '匀', '��' => '陨', '��' => '允', '��' => '运', '��' => '蕴', '��' => '酝', '��' => '晕', '��' => '韵', '��' => '孕', '��' => '匝', '��' => '砸', '��' => '杂', '��' => '栽', '��' => '哉', '��' => '灾', '��' => '宰', '��' => '载', '��' => '再', '��' => '在', '��' => '咱', '��' => '攒', '��' => '暂', '��' => '赞', '��' => '赃', '��' => '脏', '��' => '葬', '��' => '遭', '��' => '糟', '��' => '凿', '��' => '藻', '��' => '枣', '��' => '早', '��' => '澡', '��' => '蚤', '��' => '躁', '��' => '噪', '��' => '造', '��' => '皂', '��' => '灶', '��' => '燥', '��' => '责', '��' => '择', '��' => '则', '��' => '泽', '��' => '贼', '�' => '怎', '�' => '增', '�' => '憎', '�' => '曾', '�' => '赠', '�' => '扎', '�' => '喳', '�' => '渣', '�' => '札', '�' => '轧', '�@' => '誁', '�A' => '誂', '�B' => '誃', '�C' => '誄', '�D' => '誅', '�E' => '誆', '�F' => '誇', '�G' => '誈', '�H' => '誋', '�I' => '誌', '�J' => '認', '�K' => '誎', '�L' => '誏', '�M' => '誐', '�N' => '誑', '�O' => '誒', '�P' => '誔', '�Q' => '誕', '�R' => '誖', '�S' => '誗', '�T' => '誘', '�U' => '誙', '�V' => '誚', '�W' => '誛', '�X' => '誜', '�Y' => '誝', '�Z' => '語', '�[' => '誟', '�\\' => '誠', '�]' => '誡', '�^' => '誢', '�_' => '誣', '�`' => '誤', '�a' => '誥', '�b' => '誦', '�c' => '誧', '�d' => '誨', '�e' => '誩', '�f' => '說', '�g' => '誫', '�h' => '説', '�i' => '読', '�j' => '誮', '�k' => '誯', '�l' => '誰', '�m' => '誱', '�n' => '課', '�o' => '誳', '�p' => '誴', '�q' => '誵', '�r' => '誶', '�s' => '誷', '�t' => '誸', '�u' => '誹', '�v' => '誺', '�w' => '誻', '�x' => '誼', '�y' => '誽', '�z' => '誾', '�{' => '調', '�|' => '諀', '�}' => '諁', '�~' => '諂', 'Հ' => '諃', 'Ձ' => '諄', 'Ղ' => '諅', 'Ճ' => '諆', 'Մ' => '談', 'Յ' => '諈', 'Ն' => '諉', 'Շ' => '諊', 'Ո' => '請', 'Չ' => '諌', 'Պ' => '諍', 'Ջ' => '諎', 'Ռ' => '諏', 'Ս' => '諐', 'Վ' => '諑', 'Տ' => '諒', 'Ր' => '諓', 'Ց' => '諔', 'Ւ' => '諕', 'Փ' => '論', 'Ք' => '諗', 'Օ' => '諘', 'Ֆ' => '諙', '' => '諚', '' => '諛', 'ՙ' => '諜', '՚' => '諝', '՛' => '諞', '՜' => '諟', '՝' => '諠', '՞' => '諡', '՟' => '諢', 'ՠ' => '諣', 'ա' => '铡', 'բ' => '闸', 'գ' => '眨', 'դ' => '栅', 'ե' => '榨', 'զ' => '咋', 'է' => '乍', 'ը' => '炸', 'թ' => '诈', 'ժ' => '摘', 'ի' => '斋', 'լ' => '宅', 'խ' => '窄', 'ծ' => '债', 'կ' => '寨', 'հ' => '瞻', 'ձ' => '毡', 'ղ' => '詹', 'ճ' => '粘', 'մ' => '沾', 'յ' => '盏', 'ն' => '斩', 'շ' => '辗', 'ո' => '崭', 'չ' => '展', 'պ' => '蘸', 'ջ' => '栈', 'ռ' => '占', 'ս' => '战', 'վ' => '站', 'տ' => '湛', '�' => '绽', '�' => '樟', '��' => '章', '��' => '彰', '��' => '漳', '��' => '张', '��' => '掌', '��' => '涨', '��' => '杖', '��' => '丈', '��' => '帐', '��' => '账', '��' => '仗', '��' => '胀', '��' => '瘴', '��' => '障', '��' => '招', '��' => '昭', '��' => '找', '��' => '沼', '��' => '赵', '��' => '照', '��' => '罩', '��' => '兆', '��' => '肇', '��' => '召', '��' => '遮', '��' => '折', '��' => '哲', '��' => '蛰', '��' => '辙', '��' => '者', '��' => '锗', '��' => '蔗', '��' => '这', '��' => '浙', '��' => '珍', '��' => '斟', '��' => '真', '��' => '甄', '��' => '砧', '��' => '臻', '��' => '贞', '��' => '针', '��' => '侦', '��' => '枕', '��' => '疹', '��' => '诊', '��' => '震', '��' => '振', '��' => '镇', '��' => '阵', '��' => '蒸', '�' => '挣', '�' => '睁', '�' => '征', '�' => '狰', '�' => '争', '�' => '怔', '�' => '整', '�' => '拯', '�' => '正', '�' => '政', '�@' => '諤', '�A' => '諥', '�B' => '諦', '�C' => '諧', '�D' => '諨', '�E' => '諩', '�F' => '諪', '�G' => '諫', '�H' => '諬', '�I' => '諭', '�J' => '諮', '�K' => '諯', '�L' => '諰', '�M' => '諱', '�N' => '諲', '�O' => '諳', '�P' => '諴', '�Q' => '諵', '�R' => '諶', '�S' => '諷', '�T' => '諸', '�U' => '諹', '�V' => '諺', '�W' => '諻', '�X' => '諼', '�Y' => '諽', '�Z' => '諾', '�[' => '諿', '�\\' => '謀', '�]' => '謁', '�^' => '謂', '�_' => '謃', '�`' => '謄', '�a' => '謅', '�b' => '謆', '�c' => '謈', '�d' => '謉', '�e' => '謊', '�f' => '謋', '�g' => '謌', '�h' => '謍', '�i' => '謎', '�j' => '謏', '�k' => '謐', '�l' => '謑', '�m' => '謒', '�n' => '謓', '�o' => '謔', '�p' => '謕', '�q' => '謖', '�r' => '謗', '�s' => '謘', '�t' => '謙', '�u' => '謚', '�v' => '講', '�w' => '謜', '�x' => '謝', '�y' => '謞', '�z' => '謟', '�{' => '謠', '�|' => '謡', '�}' => '謢', '�~' => '謣', 'ր' => '謤', 'ց' => '謥', 'ւ' => '謧', 'փ' => '謨', 'ք' => '謩', 'օ' => '謪', 'ֆ' => '謫', 'և' => '謬', 'ֈ' => '謭', '։' => '謮', '֊' => '謯', '' => '謰', '' => '謱', '֍' => '謲', '֎' => '謳', '֏' => '謴', '' => '謵', '֑' => '謶', '֒' => '謷', '֓' => '謸', '֔' => '謹', '֕' => '謺', '֖' => '謻', '֗' => '謼', '֘' => '謽', '֙' => '謾', '֚' => '謿', '֛' => '譀', '֜' => '譁', '֝' => '譂', '֞' => '譃', '֟' => '譄', '֠' => '譅', '֡' => '帧', '֢' => '症', '֣' => '郑', '֤' => '证', '֥' => '芝', '֦' => '枝', '֧' => '支', '֨' => '吱', '֩' => '蜘', '֪' => '知', '֫' => '肢', '֬' => '脂', '֭' => '汁', '֮' => '之', '֯' => '织', 'ְ' => '职', 'ֱ' => '直', 'ֲ' => '植', 'ֳ' => '殖', 'ִ' => '执', 'ֵ' => '值', 'ֶ' => '侄', 'ַ' => '址', 'ָ' => '指', 'ֹ' => '止', 'ֺ' => '趾', 'ֻ' => '只', 'ּ' => '旨', 'ֽ' => '纸', '־' => '志', 'ֿ' => '挚', '�' => '掷', '�' => '至', '��' => '致', '��' => '置', '��' => '帜', '��' => '峙', '��' => '制', '��' => '智', '��' => '秩', '��' => '稚', '��' => '质', '��' => '炙', '��' => '痔', '��' => '滞', '��' => '治', '��' => '窒', '��' => '中', '��' => '盅', '��' => '忠', '��' => '钟', '��' => '衷', '��' => '终', '��' => '种', '��' => '肿', '��' => '重', '��' => '仲', '��' => '众', '��' => '舟', '��' => '周', '��' => '州', '��' => '洲', '��' => '诌', '��' => '粥', '��' => '轴', '��' => '肘', '��' => '帚', '��' => '咒', '��' => '皱', '��' => '宙', '��' => '昼', '��' => '骤', '��' => '珠', '��' => '株', '��' => '蛛', '��' => '朱', '��' => '猪', '��' => '诸', '��' => '诛', '��' => '逐', '��' => '竹', '��' => '烛', '��' => '煮', '��' => '拄', '�' => '瞩', '�' => '嘱', '�' => '主', '�' => '著', '�' => '柱', '�' => '助', '�' => '蛀', '�' => '贮', '�' => '铸', '�' => '筑', '�@' => '譆', '�A' => '譇', '�B' => '譈', '�C' => '證', '�D' => '譊', '�E' => '譋', '�F' => '譌', '�G' => '譍', '�H' => '譎', '�I' => '譏', '�J' => '譐', '�K' => '譑', '�L' => '譒', '�M' => '譓', '�N' => '譔', '�O' => '譕', '�P' => '譖', '�Q' => '譗', '�R' => '識', '�S' => '譙', '�T' => '譚', '�U' => '譛', '�V' => '譜', '�W' => '譝', '�X' => '譞', '�Y' => '譟', '�Z' => '譠', '�[' => '譡', '�\\' => '譢', '�]' => '譣', '�^' => '譤', '�_' => '譥', '�`' => '譧', '�a' => '譨', '�b' => '譩', '�c' => '譪', '�d' => '譫', '�e' => '譭', '�f' => '譮', '�g' => '譯', '�h' => '議', '�i' => '譱', '�j' => '譲', '�k' => '譳', '�l' => '譴', '�m' => '譵', '�n' => '譶', '�o' => '護', '�p' => '譸', '�q' => '譹', '�r' => '譺', '�s' => '譻', '�t' => '譼', '�u' => '譽', '�v' => '譾', '�w' => '譿', '�x' => '讀', '�y' => '讁', '�z' => '讂', '�{' => '讃', '�|' => '讄', '�}' => '讅', '�~' => '讆', '׀' => '讇', 'ׁ' => '讈', 'ׂ' => '讉', '׃' => '變', 'ׄ' => '讋', 'ׅ' => '讌', '׆' => '讍', 'ׇ' => '讎', '' => '讏', '' => '讐', '' => '讑', '' => '讒', '' => '讓', '' => '讔', '' => '讕', '' => '讖', 'א' => '讗', 'ב' => '讘', 'ג' => '讙', 'ד' => '讚', 'ה' => '讛', 'ו' => '讜', 'ז' => '讝', 'ח' => '讞', 'ט' => '讟', 'י' => '讬', 'ך' => '讱', 'כ' => '讻', 'ל' => '诇', 'ם' => '诐', 'מ' => '诪', 'ן' => '谉', 'נ' => '谞', 'ס' => '住', 'ע' => '注', 'ף' => '祝', 'פ' => '驻', 'ץ' => '抓', 'צ' => '爪', 'ק' => '拽', 'ר' => '专', 'ש' => '砖', 'ת' => '转', '' => '撰', '' => '赚', '' => '篆', '' => '桩', 'ׯ' => '庄', 'װ' => '装', 'ױ' => '妆', 'ײ' => '撞', '׳' => '壮', '״' => '状', '' => '椎', '' => '锥', '' => '追', '' => '赘', '' => '坠', '' => '缀', '' => '谆', '' => '准', '' => '捉', '' => '拙', '' => '卓', '�' => '桌', '�' => '琢', '��' => '茁', '��' => '酌', '��' => '啄', '��' => '着', '��' => '灼', '��' => '浊', '��' => '兹', '��' => '咨', '��' => '资', '��' => '姿', '��' => '滋', '��' => '淄', '��' => '孜', '��' => '紫', '��' => '仔', '��' => '籽', '��' => '滓', '��' => '子', '��' => '自', '��' => '渍', '��' => '字', '��' => '鬃', '��' => '棕', '��' => '踪', '��' => '宗', '��' => '综', '��' => '总', '��' => '纵', '��' => '邹', '��' => '走', '��' => '奏', '��' => '揍', '��' => '租', '��' => '足', '��' => '卒', '��' => '族', '��' => '祖', '��' => '诅', '��' => '阻', '��' => '组', '��' => '钻', '��' => '纂', '��' => '嘴', '��' => '醉', '��' => '最', '��' => '罪', '��' => '尊', '��' => '遵', '��' => '昨', '��' => '左', '��' => '佐', '�' => '柞', '�' => '做', '�' => '作', '�' => '坐', '�' => '座', '�@' => '谸', '�A' => '谹', '�B' => '谺', '�C' => '谻', '�D' => '谼', '�E' => '谽', '�F' => '谾', '�G' => '谿', '�H' => '豀', '�I' => '豂', '�J' => '豃', '�K' => '豄', '�L' => '豅', '�M' => '豈', '�N' => '豊', '�O' => '豋', '�P' => '豍', '�Q' => '豎', '�R' => '豏', '�S' => '豐', '�T' => '豑', '�U' => '豒', '�V' => '豓', '�W' => '豔', '�X' => '豖', '�Y' => '豗', '�Z' => '豘', '�[' => '豙', '�\\' => '豛', '�]' => '豜', '�^' => '豝', '�_' => '豞', '�`' => '豟', '�a' => '豠', '�b' => '豣', '�c' => '豤', '�d' => '豥', '�e' => '豦', '�f' => '豧', '�g' => '豨', '�h' => '豩', '�i' => '豬', '�j' => '豭', '�k' => '豮', '�l' => '豯', '�m' => '豰', '�n' => '豱', '�o' => '豲', '�p' => '豴', '�q' => '豵', '�r' => '豶', '�s' => '豷', '�t' => '豻', '�u' => '豼', '�v' => '豽', '�w' => '豾', '�x' => '豿', '�y' => '貀', '�z' => '貁', '�{' => '貃', '�|' => '貄', '�}' => '貆', '�~' => '貇', '' => '貈', '' => '貋', '' => '貍', '' => '貎', '' => '貏', '' => '貐', '؆' => '貑', '؇' => '貒', '؈' => '貓', '؉' => '貕', '؊' => '貖', '؋' => '貗', '،' => '貙', '؍' => '貚', '؎' => '貛', '؏' => '貜', 'ؐ' => '貝', 'ؑ' => '貞', 'ؒ' => '貟', 'ؓ' => '負', 'ؔ' => '財', 'ؕ' => '貢', 'ؖ' => '貣', 'ؗ' => '貤', 'ؘ' => '貥', 'ؙ' => '貦', 'ؚ' => '貧', '؛' => '貨', '' => '販', '؝' => '貪', '؞' => '貫', '؟' => '責', 'ؠ' => '貭', 'ء' => '亍', 'آ' => '丌', 'أ' => '兀', 'ؤ' => '丐', 'إ' => '廿', 'ئ' => '卅', 'ا' => '丕', 'ب' => '亘', 'ة' => '丞', 'ت' => '鬲', 'ث' => '孬', 'ج' => '噩', 'ح' => '丨', 'خ' => '禺', 'د' => '丿', 'ذ' => '匕', 'ر' => '乇', 'ز' => '夭', 'س' => '爻', 'ش' => '卮', 'ص' => '氐', 'ض' => '囟', 'ط' => '胤', 'ظ' => '馗', 'ع' => '毓', 'غ' => '睾', 'ػ' => '鼗', 'ؼ' => '丶', 'ؽ' => '亟', 'ؾ' => '鼐', 'ؿ' => '乜', '�' => '乩', '�' => '亓', '��' => '芈', '��' => '孛', '��' => '啬', '��' => '嘏', '��' => '仄', '��' => '厍', '��' => '厝', '��' => '厣', '��' => '厥', '��' => '厮', '��' => '靥', '��' => '赝', '��' => '匚', '��' => '叵', '��' => '匦', '��' => '匮', '��' => '匾', '��' => '赜', '��' => '卦', '��' => '卣', '��' => '刂', '��' => '刈', '��' => '刎', '��' => '刭', '��' => '刳', '��' => '刿', '��' => '剀', '��' => '剌', '��' => '剞', '��' => '剡', '��' => '剜', '��' => '蒯', '��' => '剽', '��' => '劂', '��' => '劁', '��' => '劐', '��' => '劓', '��' => '冂', '��' => '罔', '��' => '亻', '��' => '仃', '��' => '仉', '��' => '仂', '��' => '仨', '��' => '仡', '��' => '仫', '��' => '仞', '��' => '伛', '��' => '仳', '��' => '伢', '��' => '佤', '�' => '仵', '�' => '伥', '�' => '伧', '�' => '伉', '�' => '伫', '�' => '佞', '�' => '佧', '�' => '攸', '�' => '佚', '�' => '佝', '�@' => '貮', '�A' => '貯', '�B' => '貰', '�C' => '貱', '�D' => '貲', '�E' => '貳', '�F' => '貴', '�G' => '貵', '�H' => '貶', '�I' => '買', '�J' => '貸', '�K' => '貹', '�L' => '貺', '�M' => '費', '�N' => '貼', '�O' => '貽', '�P' => '貾', '�Q' => '貿', '�R' => '賀', '�S' => '賁', '�T' => '賂', '�U' => '賃', '�V' => '賄', '�W' => '賅', '�X' => '賆', '�Y' => '資', '�Z' => '賈', '�[' => '賉', '�\\' => '賊', '�]' => '賋', '�^' => '賌', '�_' => '賍', '�`' => '賎', '�a' => '賏', '�b' => '賐', '�c' => '賑', '�d' => '賒', '�e' => '賓', '�f' => '賔', '�g' => '賕', '�h' => '賖', '�i' => '賗', '�j' => '賘', '�k' => '賙', '�l' => '賚', '�m' => '賛', '�n' => '賜', '�o' => '賝', '�p' => '賞', '�q' => '賟', '�r' => '賠', '�s' => '賡', '�t' => '賢', '�u' => '賣', '�v' => '賤', '�w' => '賥', '�x' => '賦', '�y' => '賧', '�z' => '賨', '�{' => '賩', '�|' => '質', '�}' => '賫', '�~' => '賬', 'ـ' => '賭', 'ف' => '賮', 'ق' => '賯', 'ك' => '賰', 'ل' => '賱', 'م' => '賲', 'ن' => '賳', 'ه' => '賴', 'و' => '賵', 'ى' => '賶', 'ي' => '賷', 'ً' => '賸', 'ٌ' => '賹', 'ٍ' => '賺', 'َ' => '賻', 'ُ' => '購', 'ِ' => '賽', 'ّ' => '賾', 'ْ' => '賿', 'ٓ' => '贀', 'ٔ' => '贁', 'ٕ' => '贂', 'ٖ' => '贃', 'ٗ' => '贄', '٘' => '贅', 'ٙ' => '贆', 'ٚ' => '贇', 'ٛ' => '贈', 'ٜ' => '贉', 'ٝ' => '贊', 'ٞ' => '贋', 'ٟ' => '贌', '٠' => '贍', '١' => '佟', '٢' => '佗', '٣' => '伲', '٤' => '伽', '٥' => '佶', '٦' => '佴', '٧' => '侑', '٨' => '侉', '٩' => '侃', '٪' => '侏', '٫' => '佾', '٬' => '佻', '٭' => '侪', 'ٮ' => '佼', 'ٯ' => '侬', 'ٰ' => '侔', 'ٱ' => '俦', 'ٲ' => '俨', 'ٳ' => '俪', 'ٴ' => '俅', 'ٵ' => '俚', 'ٶ' => '俣', 'ٷ' => '俜', 'ٸ' => '俑', 'ٹ' => '俟', 'ٺ' => '俸', 'ٻ' => '倩', 'ټ' => '偌', 'ٽ' => '俳', 'پ' => '倬', 'ٿ' => '倏', '�' => '倮', '�' => '倭', '��' => '俾', '��' => '倜', '��' => '倌', '��' => '倥', '��' => '倨', '��' => '偾', '��' => '偃', '��' => '偕', '��' => '偈', '��' => '偎', '��' => '偬', '��' => '偻', '��' => '傥', '��' => '傧', '��' => '傩', '��' => '傺', '��' => '僖', '��' => '儆', '��' => '僭', '��' => '僬', '��' => '僦', '��' => '僮', '��' => '儇', '��' => '儋', '��' => '仝', '��' => '氽', '��' => '佘', '��' => '佥', '��' => '俎', '��' => '龠', '��' => '汆', '��' => '籴', '��' => '兮', '��' => '巽', '��' => '黉', '��' => '馘', '��' => '冁', '��' => '夔', '��' => '勹', '��' => '匍', '��' => '訇', '��' => '匐', '��' => '凫', '��' => '夙', '��' => '兕', '��' => '亠', '��' => '兖', '��' => '亳', '��' => '衮', '��' => '袤', '��' => '亵', '�' => '脔', '�' => '裒', '�' => '禀', '�' => '嬴', '�' => '蠃', '�' => '羸', '�' => '冫', '�' => '冱', '�' => '冽', '�' => '冼', '�@' => '贎', '�A' => '贏', '�B' => '贐', '�C' => '贑', '�D' => '贒', '�E' => '贓', '�F' => '贔', '�G' => '贕', '�H' => '贖', '�I' => '贗', '�J' => '贘', '�K' => '贙', '�L' => '贚', '�M' => '贛', '�N' => '贜', '�O' => '贠', '�P' => '赑', '�Q' => '赒', '�R' => '赗', '�S' => '赟', '�T' => '赥', '�U' => '赨', '�V' => '赩', '�W' => '赪', '�X' => '赬', '�Y' => '赮', '�Z' => '赯', '�[' => '赱', '�\\' => '赲', '�]' => '赸', '�^' => '赹', '�_' => '赺', '�`' => '赻', '�a' => '赼', '�b' => '赽', '�c' => '赾', '�d' => '赿', '�e' => '趀', '�f' => '趂', '�g' => '趃', '�h' => '趆', '�i' => '趇', '�j' => '趈', '�k' => '趉', '�l' => '趌', '�m' => '趍', '�n' => '趎', '�o' => '趏', '�p' => '趐', '�q' => '趒', '�r' => '趓', '�s' => '趕', '�t' => '趖', '�u' => '趗', '�v' => '趘', '�w' => '趙', '�x' => '趚', '�y' => '趛', '�z' => '趜', '�{' => '趝', '�|' => '趞', '�}' => '趠', '�~' => '趡', 'ڀ' => '趢', 'ځ' => '趤', 'ڂ' => '趥', 'ڃ' => '趦', 'ڄ' => '趧', 'څ' => '趨', 'چ' => '趩', 'ڇ' => '趪', 'ڈ' => '趫', 'ډ' => '趬', 'ڊ' => '趭', 'ڋ' => '趮', 'ڌ' => '趯', 'ڍ' => '趰', 'ڎ' => '趲', 'ڏ' => '趶', 'ڐ' => '趷', 'ڑ' => '趹', 'ڒ' => '趻', 'ړ' => '趽', 'ڔ' => '跀', 'ڕ' => '跁', 'ږ' => '跂', 'ڗ' => '跅', 'ژ' => '跇', 'ڙ' => '跈', 'ښ' => '跉', 'ڛ' => '跊', 'ڜ' => '跍', 'ڝ' => '跐', 'ڞ' => '跒', 'ڟ' => '跓', 'ڠ' => '跔', 'ڡ' => '凇', 'ڢ' => '冖', 'ڣ' => '冢', 'ڤ' => '冥', 'ڥ' => '讠', 'ڦ' => '讦', 'ڧ' => '讧', 'ڨ' => '讪', 'ک' => '讴', 'ڪ' => '讵', 'ګ' => '讷', 'ڬ' => '诂', 'ڭ' => '诃', 'ڮ' => '诋', 'گ' => '诏', 'ڰ' => '诎', 'ڱ' => '诒', 'ڲ' => '诓', 'ڳ' => '诔', 'ڴ' => '诖', 'ڵ' => '诘', 'ڶ' => '诙', 'ڷ' => '诜', 'ڸ' => '诟', 'ڹ' => '诠', 'ں' => '诤', 'ڻ' => '诨', 'ڼ' => '诩', 'ڽ' => '诮', 'ھ' => '诰', 'ڿ' => '诳', '�' => '诶', '�' => '诹', '��' => '诼', '��' => '诿', '��' => '谀', '��' => '谂', '��' => '谄', '��' => '谇', '��' => '谌', '��' => '谏', '��' => '谑', '��' => '谒', '��' => '谔', '��' => '谕', '��' => '谖', '��' => '谙', '��' => '谛', '��' => '谘', '��' => '谝', '��' => '谟', '��' => '谠', '��' => '谡', '��' => '谥', '��' => '谧', '��' => '谪', '��' => '谫', '��' => '谮', '��' => '谯', '��' => '谲', '��' => '谳', '��' => '谵', '��' => '谶', '��' => '卩', '��' => '卺', '��' => '阝', '��' => '阢', '��' => '阡', '��' => '阱', '��' => '阪', '��' => '阽', '��' => '阼', '��' => '陂', '��' => '陉', '��' => '陔', '��' => '陟', '��' => '陧', '��' => '陬', '��' => '陲', '��' => '陴', '��' => '隈', '��' => '隍', '��' => '隗', '��' => '隰', '�' => '邗', '�' => '邛', '�' => '邝', '�' => '邙', '�' => '邬', '�' => '邡', '�' => '邴', '�' => '邳', '�' => '邶', '�' => '邺', '�@' => '跕', '�A' => '跘', '�B' => '跙', '�C' => '跜', '�D' => '跠', '�E' => '跡', '�F' => '跢', '�G' => '跥', '�H' => '跦', '�I' => '跧', '�J' => '跩', '�K' => '跭', '�L' => '跮', '�M' => '跰', '�N' => '跱', '�O' => '跲', '�P' => '跴', '�Q' => '跶', '�R' => '跼', '�S' => '跾', '�T' => '跿', '�U' => '踀', '�V' => '踁', '�W' => '踂', '�X' => '踃', '�Y' => '踄', '�Z' => '踆', '�[' => '踇', '�\\' => '踈', '�]' => '踋', '�^' => '踍', '�_' => '踎', '�`' => '踐', '�a' => '踑', '�b' => '踒', '�c' => '踓', '�d' => '踕', '�e' => '踖', '�f' => '踗', '�g' => '踘', '�h' => '踙', '�i' => '踚', '�j' => '踛', '�k' => '踜', '�l' => '踠', '�m' => '踡', '�n' => '踤', '�o' => '踥', '�p' => '踦', '�q' => '踧', '�r' => '踨', '�s' => '踫', '�t' => '踭', '�u' => '踰', '�v' => '踲', '�w' => '踳', '�x' => '踴', '�y' => '踶', '�z' => '踷', '�{' => '踸', '�|' => '踻', '�}' => '踼', '�~' => '踾', 'ۀ' => '踿', 'ہ' => '蹃', 'ۂ' => '蹅', 'ۃ' => '蹆', 'ۄ' => '蹌', 'ۅ' => '蹍', 'ۆ' => '蹎', 'ۇ' => '蹏', 'ۈ' => '蹐', 'ۉ' => '蹓', 'ۊ' => '蹔', 'ۋ' => '蹕', 'ی' => '蹖', 'ۍ' => '蹗', 'ێ' => '蹘', 'ۏ' => '蹚', 'ې' => '蹛', 'ۑ' => '蹜', 'ے' => '蹝', 'ۓ' => '蹞', '۔' => '蹟', 'ە' => '蹠', 'ۖ' => '蹡', 'ۗ' => '蹢', 'ۘ' => '蹣', 'ۙ' => '蹤', 'ۚ' => '蹥', 'ۛ' => '蹧', 'ۜ' => '蹨', '' => '蹪', '۞' => '蹫', '۟' => '蹮', '۠' => '蹱', 'ۡ' => '邸', 'ۢ' => '邰', 'ۣ' => '郏', 'ۤ' => '郅', 'ۥ' => '邾', 'ۦ' => '郐', 'ۧ' => '郄', 'ۨ' => '郇', '۩' => '郓', '۪' => '郦', '۫' => '郢', '۬' => '郜', 'ۭ' => '郗', 'ۮ' => '郛', 'ۯ' => '郫', '۰' => '郯', '۱' => '郾', '۲' => '鄄', '۳' => '鄢', '۴' => '鄞', '۵' => '鄣', '۶' => '鄱', '۷' => '鄯', '۸' => '鄹', '۹' => '酃', 'ۺ' => '酆', 'ۻ' => '刍', 'ۼ' => '奂', '۽' => '劢', '۾' => '劬', 'ۿ' => '劭', '�' => '劾', '�' => '哿', '��' => '勐', '��' => '勖', '��' => '勰', '��' => '叟', '��' => '燮', '��' => '矍', '��' => '廴', '��' => '凵', '��' => '凼', '��' => '鬯', '��' => '厶', '��' => '弁', '��' => '畚', '��' => '巯', '��' => '坌', '��' => '垩', '��' => '垡', '��' => '塾', '��' => '墼', '��' => '壅', '��' => '壑', '��' => '圩', '��' => '圬', '��' => '圪', '��' => '圳', '��' => '圹', '��' => '圮', '��' => '圯', '��' => '坜', '��' => '圻', '��' => '坂', '��' => '坩', '��' => '垅', '��' => '坫', '��' => '垆', '��' => '坼', '��' => '坻', '��' => '坨', '��' => '坭', '��' => '坶', '��' => '坳', '��' => '垭', '��' => '垤', '��' => '垌', '��' => '垲', '��' => '埏', '��' => '垧', '��' => '垴', '��' => '垓', '��' => '垠', '��' => '埕', '�' => '埘', '�' => '埚', '�' => '埙', '�' => '埒', '�' => '垸', '�' => '埴', '�' => '埯', '�' => '埸', '�' => '埤', '�' => '埝', '�@' => '蹳', '�A' => '蹵', '�B' => '蹷', '�C' => '蹸', '�D' => '蹹', '�E' => '蹺', '�F' => '蹻', '�G' => '蹽', '�H' => '蹾', '�I' => '躀', '�J' => '躂', '�K' => '躃', '�L' => '躄', '�M' => '躆', '�N' => '躈', '�O' => '躉', '�P' => '躊', '�Q' => '躋', '�R' => '躌', '�S' => '躍', '�T' => '躎', '�U' => '躑', '�V' => '躒', '�W' => '躓', '�X' => '躕', '�Y' => '躖', '�Z' => '躗', '�[' => '躘', '�\\' => '躙', '�]' => '躚', '�^' => '躛', '�_' => '躝', '�`' => '躟', '�a' => '躠', '�b' => '躡', '�c' => '躢', '�d' => '躣', '�e' => '躤', '�f' => '躥', '�g' => '躦', '�h' => '躧', '�i' => '躨', '�j' => '躩', '�k' => '躪', '�l' => '躭', '�m' => '躮', '�n' => '躰', '�o' => '躱', '�p' => '躳', '�q' => '躴', '�r' => '躵', '�s' => '躶', '�t' => '躷', '�u' => '躸', '�v' => '躹', '�w' => '躻', '�x' => '躼', '�y' => '躽', '�z' => '躾', '�{' => '躿', '�|' => '軀', '�}' => '軁', '�~' => '軂', '܀' => '軃', '܁' => '軄', '܂' => '軅', '܃' => '軆', '܄' => '軇', '܅' => '軈', '܆' => '軉', '܇' => '車', '܈' => '軋', '܉' => '軌', '܊' => '軍', '܋' => '軏', '܌' => '軐', '܍' => '軑', '' => '軒', '' => '軓', 'ܐ' => '軔', 'ܑ' => '軕', 'ܒ' => '軖', 'ܓ' => '軗', 'ܔ' => '軘', 'ܕ' => '軙', 'ܖ' => '軚', 'ܗ' => '軛', 'ܘ' => '軜', 'ܙ' => '軝', 'ܚ' => '軞', 'ܛ' => '軟', 'ܜ' => '軠', 'ܝ' => '軡', 'ܞ' => '転', 'ܟ' => '軣', 'ܠ' => '軤', 'ܡ' => '堋', 'ܢ' => '堍', 'ܣ' => '埽', 'ܤ' => '埭', 'ܥ' => '堀', 'ܦ' => '堞', 'ܧ' => '堙', 'ܨ' => '塄', 'ܩ' => '堠', 'ܪ' => '塥', 'ܫ' => '塬', 'ܬ' => '墁', 'ܭ' => '墉', 'ܮ' => '墚', 'ܯ' => '墀', 'ܰ' => '馨', 'ܱ' => '鼙', 'ܲ' => '懿', 'ܳ' => '艹', 'ܴ' => '艽', 'ܵ' => '艿', 'ܶ' => '芏', 'ܷ' => '芊', 'ܸ' => '芨', 'ܹ' => '芄', 'ܺ' => '芎', 'ܻ' => '芑', 'ܼ' => '芗', 'ܽ' => '芙', 'ܾ' => '芫', 'ܿ' => '芸', '�' => '芾', '�' => '芰', '��' => '苈', '��' => '苊', '��' => '苣', '��' => '芘', '��' => '芷', '��' => '芮', '��' => '苋', '��' => '苌', '��' => '苁', '��' => '芩', '��' => '芴', '��' => '芡', '��' => '芪', '��' => '芟', '��' => '苄', '��' => '苎', '��' => '芤', '��' => '苡', '��' => '茉', '��' => '苷', '��' => '苤', '��' => '茏', '��' => '茇', '��' => '苜', '��' => '苴', '��' => '苒', '��' => '苘', '��' => '茌', '��' => '苻', '��' => '苓', '��' => '茑', '��' => '茚', '��' => '茆', '��' => '茔', '��' => '茕', '��' => '苠', '��' => '苕', '��' => '茜', '��' => '荑', '��' => '荛', '��' => '荜', '��' => '茈', '��' => '莒', '��' => '茼', '��' => '茴', '��' => '茱', '��' => '莛', '��' => '荞', '��' => '茯', '��' => '荏', '��' => '荇', '�' => '荃', '�' => '荟', '�' => '荀', '�' => '茗', '�' => '荠', '�' => '茭', '�' => '茺', '�' => '茳', '�' => '荦', '�' => '荥', '�@' => '軥', '�A' => '軦', '�B' => '軧', '�C' => '軨', '�D' => '軩', '�E' => '軪', '�F' => '軫', '�G' => '軬', '�H' => '軭', '�I' => '軮', '�J' => '軯', '�K' => '軰', '�L' => '軱', '�M' => '軲', '�N' => '軳', '�O' => '軴', '�P' => '軵', '�Q' => '軶', '�R' => '軷', '�S' => '軸', '�T' => '軹', '�U' => '軺', '�V' => '軻', '�W' => '軼', '�X' => '軽', '�Y' => '軾', '�Z' => '軿', '�[' => '輀', '�\\' => '輁', '�]' => '輂', '�^' => '較', '�_' => '輄', '�`' => '輅', '�a' => '輆', '�b' => '輇', '�c' => '輈', '�d' => '載', '�e' => '輊', '�f' => '輋', '�g' => '輌', '�h' => '輍', '�i' => '輎', '�j' => '輏', '�k' => '輐', '�l' => '輑', '�m' => '輒', '�n' => '輓', '�o' => '輔', '�p' => '輕', '�q' => '輖', '�r' => '輗', '�s' => '輘', '�t' => '輙', '�u' => '輚', '�v' => '輛', '�w' => '輜', '�x' => '輝', '�y' => '輞', '�z' => '輟', '�{' => '輠', '�|' => '輡', '�}' => '輢', '�~' => '輣', '݀' => '輤', '݁' => '輥', '݂' => '輦', '݃' => '輧', '݄' => '輨', '݅' => '輩', '݆' => '輪', '݇' => '輫', '݈' => '輬', '݉' => '輭', '݊' => '輮', '' => '輯', '' => '輰', 'ݍ' => '輱', 'ݎ' => '輲', 'ݏ' => '輳', 'ݐ' => '輴', 'ݑ' => '輵', 'ݒ' => '輶', 'ݓ' => '輷', 'ݔ' => '輸', 'ݕ' => '輹', 'ݖ' => '輺', 'ݗ' => '輻', 'ݘ' => '輼', 'ݙ' => '輽', 'ݚ' => '輾', 'ݛ' => '輿', 'ݜ' => '轀', 'ݝ' => '轁', 'ݞ' => '轂', 'ݟ' => '轃', 'ݠ' => '轄', 'ݡ' => '荨', 'ݢ' => '茛', 'ݣ' => '荩', 'ݤ' => '荬', 'ݥ' => '荪', 'ݦ' => '荭', 'ݧ' => '荮', 'ݨ' => '莰', 'ݩ' => '荸', 'ݪ' => '莳', 'ݫ' => '莴', 'ݬ' => '莠', 'ݭ' => '莪', 'ݮ' => '莓', 'ݯ' => '莜', 'ݰ' => '莅', 'ݱ' => '荼', 'ݲ' => '莶', 'ݳ' => '莩', 'ݴ' => '荽', 'ݵ' => '莸', 'ݶ' => '荻', 'ݷ' => '莘', 'ݸ' => '莞', 'ݹ' => '莨', 'ݺ' => '莺', 'ݻ' => '莼', 'ݼ' => '菁', 'ݽ' => '萁', 'ݾ' => '菥', 'ݿ' => '菘', '�' => '堇', '�' => '萘', '��' => '萋', '��' => '菝', '��' => '菽', '��' => '菖', '��' => '萜', '��' => '萸', '��' => '萑', '��' => '萆', '��' => '菔', '��' => '菟', '��' => '萏', '��' => '萃', '��' => '菸', '��' => '菹', '��' => '菪', '��' => '菅', '��' => '菀', '��' => '萦', '��' => '菰', '��' => '菡', '��' => '葜', '��' => '葑', '��' => '葚', '��' => '葙', '��' => '葳', '��' => '蒇', '��' => '蒈', '��' => '葺', '��' => '蒉', '��' => '葸', '��' => '萼', '��' => '葆', '��' => '葩', '��' => '葶', '��' => '蒌', '��' => '蒎', '��' => '萱', '��' => '葭', '��' => '蓁', '��' => '蓍', '��' => '蓐', '��' => '蓦', '��' => '蒽', '��' => '蓓', '��' => '蓊', '��' => '蒿', '��' => '蒺', '��' => '蓠', '��' => '蒡', '��' => '蒹', '��' => '蒴', '�' => '蒗', '�' => '蓥', '�' => '蓣', '�' => '蔌', '�' => '甍', '�' => '蔸', '�' => '蓰', '�' => '蔹', '�' => '蔟', '�' => '蔺', '�@' => '轅', '�A' => '轆', '�B' => '轇', '�C' => '轈', '�D' => '轉', '�E' => '轊', '�F' => '轋', '�G' => '轌', '�H' => '轍', '�I' => '轎', '�J' => '轏', '�K' => '轐', '�L' => '轑', '�M' => '轒', '�N' => '轓', '�O' => '轔', '�P' => '轕', '�Q' => '轖', '�R' => '轗', '�S' => '轘', '�T' => '轙', '�U' => '轚', '�V' => '轛', '�W' => '轜', '�X' => '轝', '�Y' => '轞', '�Z' => '轟', '�[' => '轠', '�\\' => '轡', '�]' => '轢', '�^' => '轣', '�_' => '轤', '�`' => '轥', '�a' => '轪', '�b' => '辀', '�c' => '辌', '�d' => '辒', '�e' => '辝', '�f' => '辠', '�g' => '辡', '�h' => '辢', '�i' => '辤', '�j' => '辥', '�k' => '辦', '�l' => '辧', '�m' => '辪', '�n' => '辬', '�o' => '辭', '�p' => '辮', '�q' => '辯', '�r' => '農', '�s' => '辳', '�t' => '辴', '�u' => '辵', '�v' => '辷', '�w' => '辸', '�x' => '辺', '�y' => '辻', '�z' => '込', '�{' => '辿', '�|' => '迀', '�}' => '迃', '�~' => '迆', 'ހ' => '迉', 'ށ' => '迊', 'ނ' => '迋', 'ރ' => '迌', 'ބ' => '迍', 'ޅ' => '迏', 'ކ' => '迒', 'އ' => '迖', 'ވ' => '迗', 'މ' => '迚', 'ފ' => '迠', 'ދ' => '迡', 'ތ' => '迣', 'ލ' => '迧', 'ގ' => '迬', 'ޏ' => '迯', 'ސ' => '迱', 'ޑ' => '迲', 'ޒ' => '迴', 'ޓ' => '迵', 'ޔ' => '迶', 'ޕ' => '迺', 'ޖ' => '迻', 'ޗ' => '迼', 'ޘ' => '迾', 'ޙ' => '迿', 'ޚ' => '逇', 'ޛ' => '逈', 'ޜ' => '逌', 'ޝ' => '逎', 'ޞ' => '逓', 'ޟ' => '逕', 'ޠ' => '逘', 'ޡ' => '蕖', 'ޢ' => '蔻', 'ޣ' => '蓿', 'ޤ' => '蓼', 'ޥ' => '蕙', 'ަ' => '蕈', 'ާ' => '蕨', 'ި' => '蕤', 'ީ' => '蕞', 'ު' => '蕺', 'ޫ' => '瞢', 'ެ' => '蕃', 'ޭ' => '蕲', 'ޮ' => '蕻', 'ޯ' => '薤', 'ް' => '薨', 'ޱ' => '薇', '' => '薏', '' => '蕹', '' => '薮', '' => '薜', '' => '薅', '' => '薹', '' => '薷', '' => '薰', '' => '藓', '' => '藁', '' => '藜', '' => '藿', '' => '蘧', '' => '蘅', '�' => '蘩', '�' => '蘖', '��' => '蘼', '��' => '廾', '��' => '弈', '��' => '夼', '��' => '奁', '��' => '耷', '��' => '奕', '��' => '奚', '��' => '奘', '��' => '匏', '��' => '尢', '��' => '尥', '��' => '尬', '��' => '尴', '��' => '扌', '��' => '扪', '��' => '抟', '��' => '抻', '��' => '拊', '��' => '拚', '��' => '拗', '��' => '拮', '��' => '挢', '��' => '拶', '��' => '挹', '��' => '捋', '��' => '捃', '��' => '掭', '��' => '揶', '��' => '捱', '��' => '捺', '��' => '掎', '��' => '掴', '��' => '捭', '��' => '掬', '��' => '掊', '��' => '捩', '��' => '掮', '��' => '掼', '��' => '揲', '��' => '揸', '��' => '揠', '��' => '揿', '��' => '揄', '��' => '揞', '��' => '揎', '��' => '摒', '��' => '揆', '��' => '掾', '��' => '摅', '��' => '摁', '�' => '搋', '�' => '搛', '�' => '搠', '�' => '搌', '�' => '搦', '�' => '搡', '�' => '摞', '�' => '撄', '�' => '摭', '�' => '撖', '�@' => '這', '�A' => '逜', '�B' => '連', '�C' => '逤', '�D' => '逥', '�E' => '逧', '�F' => '逨', '�G' => '逩', '�H' => '逪', '�I' => '逫', '�J' => '逬', '�K' => '逰', '�L' => '週', '�M' => '進', '�N' => '逳', '�O' => '逴', '�P' => '逷', '�Q' => '逹', '�R' => '逺', '�S' => '逽', '�T' => '逿', '�U' => '遀', '�V' => '遃', '�W' => '遅', '�X' => '遆', '�Y' => '遈', '�Z' => '遉', '�[' => '遊', '�\\' => '運', '�]' => '遌', '�^' => '過', '�_' => '達', '�`' => '違', '�a' => '遖', '�b' => '遙', '�c' => '遚', '�d' => '遜', '�e' => '遝', '�f' => '遞', '�g' => '遟', '�h' => '遠', '�i' => '遡', '�j' => '遤', '�k' => '遦', '�l' => '遧', '�m' => '適', '�n' => '遪', '�o' => '遫', '�p' => '遬', '�q' => '遯', '�r' => '遰', '�s' => '遱', '�t' => '遲', '�u' => '遳', '�v' => '遶', '�w' => '遷', '�x' => '選', '�y' => '遹', '�z' => '遺', '�{' => '遻', '�|' => '遼', '�}' => '遾', '�~' => '邁', '߀' => '還', '߁' => '邅', '߂' => '邆', '߃' => '邇', '߄' => '邉', '߅' => '邊', '߆' => '邌', '߇' => '邍', '߈' => '邎', '߉' => '邏', 'ߊ' => '邐', 'ߋ' => '邒', 'ߌ' => '邔', 'ߍ' => '邖', 'ߎ' => '邘', 'ߏ' => '邚', 'ߐ' => '邜', 'ߑ' => '邞', 'ߒ' => '邟', 'ߓ' => '邠', 'ߔ' => '邤', 'ߕ' => '邥', 'ߖ' => '邧', 'ߗ' => '邨', 'ߘ' => '邩', 'ߙ' => '邫', 'ߚ' => '邭', 'ߛ' => '邲', 'ߜ' => '邷', 'ߝ' => '邼', 'ߞ' => '邽', 'ߟ' => '邿', 'ߠ' => '郀', 'ߡ' => '摺', 'ߢ' => '撷', 'ߣ' => '撸', 'ߤ' => '撙', 'ߥ' => '撺', 'ߦ' => '擀', 'ߧ' => '擐', 'ߨ' => '擗', 'ߩ' => '擤', 'ߪ' => '擢', '߫' => '攉', '߬' => '攥', '߭' => '攮', '߮' => '弋', '߯' => '忒', '߰' => '甙', '߱' => '弑', '߲' => '卟', '߳' => '叱', 'ߴ' => '叽', 'ߵ' => '叩', '߶' => '叨', '߷' => '叻', '߸' => '吒', '߹' => '吖', 'ߺ' => '吆', '' => '呋', '' => '呒', '߽' => '呓', '߾' => '呔', '߿' => '呖', '�' => '呃', '�' => '吡', '��' => '呗', '��' => '呙', '��' => '吣', '��' => '吲', '��' => '咂', '��' => '咔', '��' => '呷', '��' => '呱', '��' => '呤', '��' => '咚', '��' => '咛', '��' => '咄', '��' => '呶', '��' => '呦', '��' => '咝', '��' => '哐', '��' => '咭', '��' => '哂', '��' => '咴', '��' => '哒', '��' => '咧', '��' => '咦', '��' => '哓', '��' => '哔', '��' => '呲', '��' => '咣', '��' => '哕', '��' => '咻', '��' => '咿', '��' => '哌', '��' => '哙', '��' => '哚', '��' => '哜', '��' => '咩', '��' => '咪', '��' => '咤', '��' => '哝', '��' => '哏', '��' => '哞', '��' => '唛', '��' => '哧', '��' => '唠', '��' => '哽', '��' => '唔', '��' => '哳', '��' => '唢', '��' => '唣', '��' => '唏', '��' => '唑', '��' => '唧', '��' => '唪', '�' => '啧', '�' => '喏', '�' => '喵', '�' => '啉', '�' => '啭', '�' => '啁', '�' => '啕', '�' => '唿', '�' => '啐', '�' => '唼', '�@' => '郂', '�A' => '郃', '�B' => '郆', '�C' => '郈', '�D' => '郉', '�E' => '郋', '�F' => '郌', '�G' => '郍', '�H' => '郒', '�I' => '郔', '�J' => '郕', '�K' => '郖', '�L' => '郘', '�M' => '郙', '�N' => '郚', '�O' => '郞', '�P' => '郟', '�Q' => '郠', '�R' => '郣', '�S' => '郤', '�T' => '郥', '�U' => '郩', '�V' => '郪', '�W' => '郬', '�X' => '郮', '�Y' => '郰', '�Z' => '郱', '�[' => '郲', '�\\' => '郳', '�]' => '郵', '�^' => '郶', '�_' => '郷', '�`' => '郹', '�a' => '郺', '�b' => '郻', '�c' => '郼', '�d' => '郿', '�e' => '鄀', '�f' => '鄁', '�g' => '鄃', '�h' => '鄅', '�i' => '鄆', '�j' => '鄇', '�k' => '鄈', '�l' => '鄉', '�m' => '鄊', '�n' => '鄋', '�o' => '鄌', '�p' => '鄍', '�q' => '鄎', '�r' => '鄏', '�s' => '鄐', '�t' => '鄑', '�u' => '鄒', '�v' => '鄓', '�w' => '鄔', '�x' => '鄕', '�y' => '鄖', '�z' => '鄗', '�{' => '鄘', '�|' => '鄚', '�}' => '鄛', '�~' => '鄜', '�' => '鄝', '�' => '鄟', '�' => '鄠', '�' => '鄡', '�' => '鄤', '�' => '鄥', '�' => '鄦', '�' => '鄧', '�' => '鄨', '�' => '鄩', '�' => '鄪', '�' => '鄫', '�' => '鄬', '�' => '鄭', '�' => '鄮', '�' => '鄰', '�' => '鄲', '�' => '鄳', '�' => '鄴', '�' => '鄵', '�' => '鄶', '�' => '鄷', '�' => '鄸', '�' => '鄺', '�' => '鄻', '�' => '鄼', '�' => '鄽', '�' => '鄾', '�' => '鄿', '�' => '酀', '�' => '酁', '�' => '酂', '�' => '酄', '�' => '唷', '�' => '啖', '�' => '啵', '�' => '啶', '�' => '啷', '�' => '唳', '�' => '唰', '�' => '啜', '�' => '喋', '�' => '嗒', '�' => '喃', '�' => '喱', '�' => '喹', '�' => '喈', '�' => '喁', '�' => '喟', '�' => '啾', '�' => '嗖', '�' => '喑', '�' => '啻', '�' => '嗟', '�' => '喽', '�' => '喾', '�' => '喔', '�' => '喙', '�' => '嗪', '�' => '嗷', '�' => '嗉', '�' => '嘟', '�' => '嗑', '�' => '嗫', '�' => '嗬', '�' => '嗔', '��' => '嗦', '��' => '嗝', '��' => '嗄', '��' => '嗯', '��' => '嗥', '��' => '嗲', '��' => '嗳', '��' => '嗌', '��' => '嗍', '��' => '嗨', '��' => '嗵', '��' => '嗤', '��' => '辔', '��' => '嘞', '��' => '嘈', '��' => '嘌', '��' => '嘁', '��' => '嘤', '��' => '嘣', '��' => '嗾', '��' => '嘀', '��' => '嘧', '��' => '嘭', '��' => '噘', '��' => '嘹', '��' => '噗', '��' => '嘬', '��' => '噍', '��' => '噢', '��' => '噙', '��' => '噜', '��' => '噌', '��' => '噔', '��' => '嚆', '��' => '噤', '��' => '噱', '��' => '噫', '��' => '噻', '��' => '噼', '��' => '嚅', '��' => '嚓', '��' => '嚯', '��' => '囔', '��' => '囗', '��' => '囝', '��' => '囡', '��' => '囵', '��' => '囫', '��' => '囹', '��' => '囿', '��' => '圄', '�' => '圊', '�' => '圉', '�' => '圜', '�' => '帏', '�' => '帙', '�' => '帔', '�' => '帑', '�' => '帱', '�' => '帻', '�' => '帼', '�@' => '酅', '�A' => '酇', '�B' => '酈', '�C' => '酑', '�D' => '酓', '�E' => '酔', '�F' => '酕', '�G' => '酖', '�H' => '酘', '�I' => '酙', '�J' => '酛', '�K' => '酜', '�L' => '酟', '�M' => '酠', '�N' => '酦', '�O' => '酧', '�P' => '酨', '�Q' => '酫', '�R' => '酭', '�S' => '酳', '�T' => '酺', '�U' => '酻', '�V' => '酼', '�W' => '醀', '�X' => '醁', '�Y' => '醂', '�Z' => '醃', '�[' => '醄', '�\\' => '醆', '�]' => '醈', '�^' => '醊', '�_' => '醎', '�`' => '醏', '�a' => '醓', '�b' => '醔', '�c' => '醕', '�d' => '醖', '�e' => '醗', '�f' => '醘', '�g' => '醙', '�h' => '醜', '�i' => '醝', '�j' => '醞', '�k' => '醟', '�l' => '醠', '�m' => '醡', '�n' => '醤', '�o' => '醥', '�p' => '醦', '�q' => '醧', '�r' => '醨', '�s' => '醩', '�t' => '醫', '�u' => '醬', '�v' => '醰', '�w' => '醱', '�x' => '醲', '�y' => '醳', '�z' => '醶', '�{' => '醷', '�|' => '醸', '�}' => '醹', '�~' => '醻', '�' => '醼', '�' => '醽', '�' => '醾', '�' => '醿', '�' => '釀', '�' => '釁', '�' => '釂', '�' => '釃', '�' => '釄', '�' => '釅', '�' => '釆', '�' => '釈', '�' => '釋', '�' => '釐', '�' => '釒', '�' => '釓', '�' => '釔', '�' => '釕', '�' => '釖', '�' => '釗', '�' => '釘', '�' => '釙', '�' => '釚', '�' => '釛', '�' => '針', '�' => '釞', '�' => '釟', '�' => '釠', '�' => '釡', '�' => '釢', '�' => '釣', '�' => '釤', '�' => '釥', '�' => '帷', '�' => '幄', '�' => '幔', '�' => '幛', '�' => '幞', '�' => '幡', '�' => '岌', '�' => '屺', '�' => '岍', '�' => '岐', '�' => '岖', '�' => '岈', '�' => '岘', '�' => '岙', '�' => '岑', '�' => '岚', '�' => '岜', '�' => '岵', '�' => '岢', '�' => '岽', '�' => '岬', '�' => '岫', '�' => '岱', '�' => '岣', '�' => '峁', '�' => '岷', '�' => '峄', '�' => '峒', '�' => '峤', '�' => '峋', '�' => '峥', '�' => '崂', '�' => '崃', '��' => '崧', '��' => '崦', '��' => '崮', '��' => '崤', '��' => '崞', '��' => '崆', '��' => '崛', '��' => '嵘', '��' => '崾', '��' => '崴', '��' => '崽', '��' => '嵬', '��' => '嵛', '��' => '嵯', '��' => '嵝', '��' => '嵫', '��' => '嵋', '��' => '嵊', '��' => '嵩', '��' => '嵴', '��' => '嶂', '��' => '嶙', '��' => '嶝', '��' => '豳', '��' => '嶷', '��' => '巅', '��' => '彳', '��' => '彷', '��' => '徂', '��' => '徇', '��' => '徉', '��' => '後', '��' => '徕', '��' => '徙', '��' => '徜', '��' => '徨', '��' => '徭', '��' => '徵', '��' => '徼', '��' => '衢', '��' => '彡', '��' => '犭', '��' => '犰', '��' => '犴', '��' => '犷', '��' => '犸', '��' => '狃', '��' => '狁', '��' => '狎', '��' => '狍', '��' => '狒', '�' => '狨', '�' => '狯', '�' => '狩', '�' => '狲', '�' => '狴', '�' => '狷', '�' => '猁', '�' => '狳', '�' => '猃', '�' => '狺', '�@' => '釦', '�A' => '釧', '�B' => '釨', '�C' => '釩', '�D' => '釪', '�E' => '釫', '�F' => '釬', '�G' => '釭', '�H' => '釮', '�I' => '釯', '�J' => '釰', '�K' => '釱', '�L' => '釲', '�M' => '釳', '�N' => '釴', '�O' => '釵', '�P' => '釶', '�Q' => '釷', '�R' => '釸', '�S' => '釹', '�T' => '釺', '�U' => '釻', '�V' => '釼', '�W' => '釽', '�X' => '釾', '�Y' => '釿', '�Z' => '鈀', '�[' => '鈁', '�\\' => '鈂', '�]' => '鈃', '�^' => '鈄', '�_' => '鈅', '�`' => '鈆', '�a' => '鈇', '�b' => '鈈', '�c' => '鈉', '�d' => '鈊', '�e' => '鈋', '�f' => '鈌', '�g' => '鈍', '�h' => '鈎', '�i' => '鈏', '�j' => '鈐', '�k' => '鈑', '�l' => '鈒', '�m' => '鈓', '�n' => '鈔', '�o' => '鈕', '�p' => '鈖', '�q' => '鈗', '�r' => '鈘', '�s' => '鈙', '�t' => '鈚', '�u' => '鈛', '�v' => '鈜', '�w' => '鈝', '�x' => '鈞', '�y' => '鈟', '�z' => '鈠', '�{' => '鈡', '�|' => '鈢', '�}' => '鈣', '�~' => '鈤', '�' => '鈥', '�' => '鈦', '�' => '鈧', '�' => '鈨', '�' => '鈩', '�' => '鈪', '�' => '鈫', '�' => '鈬', '�' => '鈭', '�' => '鈮', '�' => '鈯', '�' => '鈰', '�' => '鈱', '�' => '鈲', '�' => '鈳', '�' => '鈴', '�' => '鈵', '�' => '鈶', '�' => '鈷', '�' => '鈸', '�' => '鈹', '�' => '鈺', '�' => '鈻', '�' => '鈼', '�' => '鈽', '�' => '鈾', '�' => '鈿', '�' => '鉀', '�' => '鉁', '�' => '鉂', '�' => '鉃', '�' => '鉄', '�' => '鉅', '�' => '狻', '�' => '猗', '�' => '猓', '�' => '猡', '�' => '猊', '�' => '猞', '�' => '猝', '�' => '猕', '�' => '猢', '�' => '猹', '�' => '猥', '�' => '猬', '�' => '猸', '�' => '猱', '�' => '獐', '�' => '獍', '�' => '獗', '�' => '獠', '�' => '獬', '�' => '獯', '�' => '獾', '�' => '舛', '�' => '夥', '�' => '飧', '�' => '夤', '�' => '夂', '�' => '饣', '�' => '饧', '�' => '饨', '�' => '饩', '�' => '饪', '�' => '饫', '�' => '饬', '��' => '饴', '��' => '饷', '��' => '饽', '��' => '馀', '��' => '馄', '��' => '馇', '��' => '馊', '��' => '馍', '��' => '馐', '��' => '馑', '��' => '馓', '��' => '馔', '��' => '馕', '��' => '庀', '��' => '庑', '��' => '庋', '��' => '庖', '��' => '庥', '��' => '庠', '��' => '庹', '��' => '庵', '��' => '庾', '��' => '庳', '��' => '赓', '��' => '廒', '��' => '廑', '��' => '廛', '��' => '廨', '��' => '廪', '��' => '膺', '��' => '忄', '��' => '忉', '��' => '忖', '��' => '忏', '��' => '怃', '��' => '忮', '��' => '怄', '��' => '忡', '��' => '忤', '��' => '忾', '��' => '怅', '��' => '怆', '��' => '忪', '��' => '忭', '��' => '忸', '��' => '怙', '��' => '怵', '��' => '怦', '��' => '怛', '��' => '怏', '��' => '怍', '�' => '怩', '�' => '怫', '�' => '怊', '�' => '怿', '�' => '怡', '�' => '恸', '�' => '恹', '�' => '恻', '�' => '恺', '�' => '恂', '�@' => '鉆', '�A' => '鉇', '�B' => '鉈', '�C' => '鉉', '�D' => '鉊', '�E' => '鉋', '�F' => '鉌', '�G' => '鉍', '�H' => '鉎', '�I' => '鉏', '�J' => '鉐', '�K' => '鉑', '�L' => '鉒', '�M' => '鉓', '�N' => '鉔', '�O' => '鉕', '�P' => '鉖', '�Q' => '鉗', '�R' => '鉘', '�S' => '鉙', '�T' => '鉚', '�U' => '鉛', '�V' => '鉜', '�W' => '鉝', '�X' => '鉞', '�Y' => '鉟', '�Z' => '鉠', '�[' => '鉡', '�\\' => '鉢', '�]' => '鉣', '�^' => '鉤', '�_' => '鉥', '�`' => '鉦', '�a' => '鉧', '�b' => '鉨', '�c' => '鉩', '�d' => '鉪', '�e' => '鉫', '�f' => '鉬', '�g' => '鉭', '�h' => '鉮', '�i' => '鉯', '�j' => '鉰', '�k' => '鉱', '�l' => '鉲', '�m' => '鉳', '�n' => '鉵', '�o' => '鉶', '�p' => '鉷', '�q' => '鉸', '�r' => '鉹', '�s' => '鉺', '�t' => '鉻', '�u' => '鉼', '�v' => '鉽', '�w' => '鉾', '�x' => '鉿', '�y' => '銀', '�z' => '銁', '�{' => '銂', '�|' => '銃', '�}' => '銄', '�~' => '銅', '�' => '銆', '�' => '銇', '�' => '銈', '�' => '銉', '�' => '銊', '�' => '銋', '�' => '銌', '�' => '銍', '�' => '銏', '�' => '銐', '�' => '銑', '�' => '銒', '�' => '銓', '�' => '銔', '�' => '銕', '�' => '銖', '�' => '銗', '�' => '銘', '�' => '銙', '�' => '銚', '�' => '銛', '�' => '銜', '�' => '銝', '�' => '銞', '�' => '銟', '�' => '銠', '�' => '銡', '�' => '銢', '�' => '銣', '�' => '銤', '�' => '銥', '�' => '銦', '�' => '銧', '�' => '恪', '�' => '恽', '�' => '悖', '�' => '悚', '�' => '悭', '�' => '悝', '�' => '悃', '�' => '悒', '�' => '悌', '�' => '悛', '�' => '惬', '�' => '悻', '�' => '悱', '�' => '惝', '�' => '惘', '�' => '惆', '�' => '惚', '�' => '悴', '�' => '愠', '�' => '愦', '�' => '愕', '�' => '愣', '�' => '惴', '�' => '愀', '�' => '愎', '�' => '愫', '�' => '慊', '�' => '慵', '�' => '憬', '�' => '憔', '�' => '憧', '�' => '憷', '�' => '懔', '��' => '懵', '��' => '忝', '��' => '隳', '��' => '闩', '��' => '闫', '��' => '闱', '��' => '闳', '��' => '闵', '��' => '闶', '��' => '闼', '��' => '闾', '��' => '阃', '��' => '阄', '��' => '阆', '��' => '阈', '��' => '阊', '��' => '阋', '��' => '阌', '��' => '阍', '��' => '阏', '��' => '阒', '��' => '阕', '��' => '阖', '��' => '阗', '��' => '阙', '��' => '阚', '��' => '丬', '��' => '爿', '��' => '戕', '��' => '氵', '��' => '汔', '��' => '汜', '��' => '汊', '��' => '沣', '��' => '沅', '��' => '沐', '��' => '沔', '��' => '沌', '��' => '汨', '��' => '汩', '��' => '汴', '��' => '汶', '��' => '沆', '��' => '沩', '��' => '泐', '��' => '泔', '��' => '沭', '��' => '泷', '��' => '泸', '��' => '泱', '��' => '泗', '�' => '沲', '�' => '泠', '�' => '泖', '�' => '泺', '�' => '泫', '�' => '泮', '�' => '沱', '�' => '泓', '�' => '泯', '�' => '泾', '�@' => '銨', '�A' => '銩', '�B' => '銪', '�C' => '銫', '�D' => '銬', '�E' => '銭', '�F' => '銯', '�G' => '銰', '�H' => '銱', '�I' => '銲', '�J' => '銳', '�K' => '銴', '�L' => '銵', '�M' => '銶', '�N' => '銷', '�O' => '銸', '�P' => '銹', '�Q' => '銺', '�R' => '銻', '�S' => '銼', '�T' => '銽', '�U' => '銾', '�V' => '銿', '�W' => '鋀', '�X' => '鋁', '�Y' => '鋂', '�Z' => '鋃', '�[' => '鋄', '�\\' => '鋅', '�]' => '鋆', '�^' => '鋇', '�_' => '鋉', '�`' => '鋊', '�a' => '鋋', '�b' => '鋌', '�c' => '鋍', '�d' => '鋎', '�e' => '鋏', '�f' => '鋐', '�g' => '鋑', '�h' => '鋒', '�i' => '鋓', '�j' => '鋔', '�k' => '鋕', '�l' => '鋖', '�m' => '鋗', '�n' => '鋘', '�o' => '鋙', '�p' => '鋚', '�q' => '鋛', '�r' => '鋜', '�s' => '鋝', '�t' => '鋞', '�u' => '鋟', '�v' => '鋠', '�w' => '鋡', '�x' => '鋢', '�y' => '鋣', '�z' => '鋤', '�{' => '鋥', '�|' => '鋦', '�}' => '鋧', '�~' => '鋨', '�' => '鋩', '�' => '鋪', '�' => '鋫', '�' => '鋬', '�' => '鋭', '�' => '鋮', '�' => '鋯', '�' => '鋰', '�' => '鋱', '�' => '鋲', '�' => '鋳', '�' => '鋴', '�' => '鋵', '�' => '鋶', '�' => '鋷', '�' => '鋸', '�' => '鋹', '�' => '鋺', '�' => '鋻', '�' => '鋼', '�' => '鋽', '�' => '鋾', '�' => '鋿', '�' => '錀', '�' => '錁', '�' => '錂', '�' => '錃', '�' => '錄', '�' => '錅', '�' => '錆', '�' => '錇', '�' => '錈', '�' => '錉', '�' => '洹', '�' => '洧', '�' => '洌', '�' => '浃', '�' => '浈', '�' => '洇', '�' => '洄', '�' => '洙', '�' => '洎', '�' => '洫', '�' => '浍', '�' => '洮', '�' => '洵', '�' => '洚', '�' => '浏', '�' => '浒', '�' => '浔', '�' => '洳', '�' => '涑', '�' => '浯', '�' => '涞', '�' => '涠', '�' => '浞', '�' => '涓', '�' => '涔', '�' => '浜', '�' => '浠', '�' => '浼', '�' => '浣', '�' => '渚', '�' => '淇', '�' => '淅', '�' => '淞', '��' => '渎', '��' => '涿', '��' => '淠', '��' => '渑', '��' => '淦', '��' => '淝', '��' => '淙', '��' => '渖', '��' => '涫', '��' => '渌', '��' => '涮', '��' => '渫', '��' => '湮', '��' => '湎', '��' => '湫', '��' => '溲', '��' => '湟', '��' => '溆', '��' => '湓', '��' => '湔', '��' => '渲', '��' => '渥', '��' => '湄', '��' => '滟', '��' => '溱', '��' => '溘', '��' => '滠', '��' => '漭', '��' => '滢', '��' => '溥', '��' => '溧', '��' => '溽', '��' => '溻', '��' => '溷', '��' => '滗', '��' => '溴', '��' => '滏', '��' => '溏', '��' => '滂', '��' => '溟', '��' => '潢', '��' => '潆', '��' => '潇', '��' => '漤', '��' => '漕', '��' => '滹', '��' => '漯', '��' => '漶', '��' => '潋', '��' => '潴', '��' => '漪', '�' => '漉', '�' => '漩', '�' => '澉', '�' => '澍', '�' => '澌', '�' => '潸', '�' => '潲', '�' => '潼', '�' => '潺', '�' => '濑', '�@' => '錊', '�A' => '錋', '�B' => '錌', '�C' => '錍', '�D' => '錎', '�E' => '錏', '�F' => '錐', '�G' => '錑', '�H' => '錒', '�I' => '錓', '�J' => '錔', '�K' => '錕', '�L' => '錖', '�M' => '錗', '�N' => '錘', '�O' => '錙', '�P' => '錚', '�Q' => '錛', '�R' => '錜', '�S' => '錝', '�T' => '錞', '�U' => '錟', '�V' => '錠', '�W' => '錡', '�X' => '錢', '�Y' => '錣', '�Z' => '錤', '�[' => '錥', '�\\' => '錦', '�]' => '錧', '�^' => '錨', '�_' => '錩', '�`' => '錪', '�a' => '錫', '�b' => '錬', '�c' => '錭', '�d' => '錮', '�e' => '錯', '�f' => '錰', '�g' => '錱', '�h' => '録', '�i' => '錳', '�j' => '錴', '�k' => '錵', '�l' => '錶', '�m' => '錷', '�n' => '錸', '�o' => '錹', '�p' => '錺', '�q' => '錻', '�r' => '錼', '�s' => '錽', '�t' => '錿', '�u' => '鍀', '�v' => '鍁', '�w' => '鍂', '�x' => '鍃', '�y' => '鍄', '�z' => '鍅', '�{' => '鍆', '�|' => '鍇', '�}' => '鍈', '�~' => '鍉', '�' => '鍊', '�' => '鍋', '�' => '鍌', '�' => '鍍', '�' => '鍎', '�' => '鍏', '�' => '鍐', '�' => '鍑', '�' => '鍒', '�' => '鍓', '�' => '鍔', '�' => '鍕', '�' => '鍖', '�' => '鍗', '�' => '鍘', '�' => '鍙', '�' => '鍚', '�' => '鍛', '�' => '鍜', '�' => '鍝', '�' => '鍞', '�' => '鍟', '�' => '鍠', '�' => '鍡', '�' => '鍢', '�' => '鍣', '�' => '鍤', '�' => '鍥', '�' => '鍦', '�' => '鍧', '�' => '鍨', '�' => '鍩', '�' => '鍫', '�' => '濉', '�' => '澧', '�' => '澹', '�' => '澶', '�' => '濂', '�' => '濡', '�' => '濮', '�' => '濞', '�' => '濠', '�' => '濯', '�' => '瀚', '�' => '瀣', '�' => '瀛', '�' => '瀹', '�' => '瀵', '�' => '灏', '�' => '灞', '�' => '宀', '�' => '宄', '�' => '宕', '�' => '宓', '�' => '宥', '�' => '宸', '�' => '甯', '�' => '骞', '�' => '搴', '�' => '寤', '�' => '寮', '�' => '褰', '�' => '寰', '�' => '蹇', '�' => '謇', '�' => '辶', '��' => '迓', '��' => '迕', '��' => '迥', '��' => '迮', '��' => '迤', '��' => '迩', '��' => '迦', '��' => '迳', '��' => '迨', '��' => '逅', '��' => '逄', '��' => '逋', '��' => '逦', '��' => '逑', '��' => '逍', '��' => '逖', '��' => '逡', '��' => '逵', '��' => '逶', '��' => '逭', '��' => '逯', '��' => '遄', '��' => '遑', '��' => '遒', '��' => '遐', '��' => '遨', '��' => '遘', '��' => '遢', '��' => '遛', '��' => '暹', '��' => '遴', '��' => '遽', '��' => '邂', '��' => '邈', '��' => '邃', '��' => '邋', '��' => '彐', '��' => '彗', '��' => '彖', '��' => '彘', '��' => '尻', '��' => '咫', '��' => '屐', '��' => '屙', '��' => '孱', '��' => '屣', '��' => '屦', '��' => '羼', '��' => '弪', '��' => '弩', '��' => '弭', '�' => '艴', '�' => '弼', '�' => '鬻', '�' => '屮', '�' => '妁', '�' => '妃', '�' => '妍', '�' => '妩', '�' => '妪', '�' => '妣', '�@' => '鍬', '�A' => '鍭', '�B' => '鍮', '�C' => '鍯', '�D' => '鍰', '�E' => '鍱', '�F' => '鍲', '�G' => '鍳', '�H' => '鍴', '�I' => '鍵', '�J' => '鍶', '�K' => '鍷', '�L' => '鍸', '�M' => '鍹', '�N' => '鍺', '�O' => '鍻', '�P' => '鍼', '�Q' => '鍽', '�R' => '鍾', '�S' => '鍿', '�T' => '鎀', '�U' => '鎁', '�V' => '鎂', '�W' => '鎃', '�X' => '鎄', '�Y' => '鎅', '�Z' => '鎆', '�[' => '鎇', '�\\' => '鎈', '�]' => '鎉', '�^' => '鎊', '�_' => '鎋', '�`' => '鎌', '�a' => '鎍', '�b' => '鎎', '�c' => '鎐', '�d' => '鎑', '�e' => '鎒', '�f' => '鎓', '�g' => '鎔', '�h' => '鎕', '�i' => '鎖', '�j' => '鎗', '�k' => '鎘', '�l' => '鎙', '�m' => '鎚', '�n' => '鎛', '�o' => '鎜', '�p' => '鎝', '�q' => '鎞', '�r' => '鎟', '�s' => '鎠', '�t' => '鎡', '�u' => '鎢', '�v' => '鎣', '�w' => '鎤', '�x' => '鎥', '�y' => '鎦', '�z' => '鎧', '�{' => '鎨', '�|' => '鎩', '�}' => '鎪', '�~' => '鎫', '�' => '鎬', '�' => '鎭', '�' => '鎮', '�' => '鎯', '�' => '鎰', '�' => '鎱', '�' => '鎲', '�' => '鎳', '�' => '鎴', '�' => '鎵', '�' => '鎶', '�' => '鎷', '�' => '鎸', '�' => '鎹', '�' => '鎺', '�' => '鎻', '�' => '鎼', '�' => '鎽', '�' => '鎾', '�' => '鎿', '�' => '鏀', '�' => '鏁', '�' => '鏂', '�' => '鏃', '�' => '鏄', '�' => '鏅', '�' => '鏆', '�' => '鏇', '�' => '鏈', '�' => '鏉', '�' => '鏋', '�' => '鏌', '�' => '鏍', '�' => '妗', '�' => '姊', '�' => '妫', '�' => '妞', '�' => '妤', '�' => '姒', '�' => '妲', '�' => '妯', '�' => '姗', '�' => '妾', '�' => '娅', '�' => '娆', '�' => '姝', '�' => '娈', '�' => '姣', '�' => '姘', '�' => '姹', '�' => '娌', '�' => '娉', '�' => '娲', '�' => '娴', '�' => '娑', '�' => '娣', '�' => '娓', '�' => '婀', '�' => '婧', '�' => '婊', '�' => '婕', '�' => '娼', '�' => '婢', '�' => '婵', '�' => '胬', '�' => '媪', '��' => '媛', '��' => '婷', '��' => '婺', '��' => '媾', '��' => '嫫', '��' => '媲', '��' => '嫒', '��' => '嫔', '��' => '媸', '��' => '嫠', '��' => '嫣', '��' => '嫱', '��' => '嫖', '��' => '嫦', '��' => '嫘', '��' => '嫜', '��' => '嬉', '��' => '嬗', '��' => '嬖', '��' => '嬲', '��' => '嬷', '��' => '孀', '��' => '尕', '��' => '尜', '��' => '孚', '��' => '孥', '��' => '孳', '��' => '孑', '��' => '孓', '��' => '孢', '��' => '驵', '��' => '驷', '��' => '驸', '��' => '驺', '��' => '驿', '��' => '驽', '��' => '骀', '��' => '骁', '��' => '骅', '��' => '骈', '��' => '骊', '��' => '骐', '��' => '骒', '��' => '骓', '��' => '骖', '��' => '骘', '��' => '骛', '��' => '骜', '��' => '骝', '��' => '骟', '��' => '骠', '�' => '骢', '�' => '骣', '�' => '骥', '�' => '骧', '�' => '纟', '�' => '纡', '�' => '纣', '�' => '纥', '�' => '纨', '�' => '纩', '�@' => '鏎', '�A' => '鏏', '�B' => '鏐', '�C' => '鏑', '�D' => '鏒', '�E' => '鏓', '�F' => '鏔', '�G' => '鏕', '�H' => '鏗', '�I' => '鏘', '�J' => '鏙', '�K' => '鏚', '�L' => '鏛', '�M' => '鏜', '�N' => '鏝', '�O' => '鏞', '�P' => '鏟', '�Q' => '鏠', '�R' => '鏡', '�S' => '鏢', '�T' => '鏣', '�U' => '鏤', '�V' => '鏥', '�W' => '鏦', '�X' => '鏧', '�Y' => '鏨', '�Z' => '鏩', '�[' => '鏪', '�\\' => '鏫', '�]' => '鏬', '�^' => '鏭', '�_' => '鏮', '�`' => '鏯', '�a' => '鏰', '�b' => '鏱', '�c' => '鏲', '�d' => '鏳', '�e' => '鏴', '�f' => '鏵', '�g' => '鏶', '�h' => '鏷', '�i' => '鏸', '�j' => '鏹', '�k' => '鏺', '�l' => '鏻', '�m' => '鏼', '�n' => '鏽', '�o' => '鏾', '�p' => '鏿', '�q' => '鐀', '�r' => '鐁', '�s' => '鐂', '�t' => '鐃', '�u' => '鐄', '�v' => '鐅', '�w' => '鐆', '�x' => '鐇', '�y' => '鐈', '�z' => '鐉', '�{' => '鐊', '�|' => '鐋', '�}' => '鐌', '�~' => '鐍', '�' => '鐎', '�' => '鐏', '�' => '鐐', '�' => '鐑', '�' => '鐒', '�' => '鐓', '�' => '鐔', '�' => '鐕', '�' => '鐖', '�' => '鐗', '�' => '鐘', '�' => '鐙', '�' => '鐚', '�' => '鐛', '�' => '鐜', '�' => '鐝', '�' => '鐞', '�' => '鐟', '�' => '鐠', '�' => '鐡', '�' => '鐢', '�' => '鐣', '�' => '鐤', '�' => '鐥', '�' => '鐦', '�' => '鐧', '�' => '鐨', '�' => '鐩', '�' => '鐪', '�' => '鐫', '�' => '鐬', '�' => '鐭', '�' => '鐮', '�' => '纭', '�' => '纰', '�' => '纾', '�' => '绀', '�' => '绁', '�' => '绂', '�' => '绉', '�' => '绋', '�' => '绌', '�' => '绐', '�' => '绔', '�' => '绗', '�' => '绛', '�' => '绠', '�' => '绡', '�' => '绨', '�' => '绫', '�' => '绮', '�' => '绯', '�' => '绱', '�' => '绲', '�' => '缍', '�' => '绶', '�' => '绺', '�' => '绻', '�' => '绾', '�' => '缁', '�' => '缂', '�' => '缃', '�' => '缇', '�' => '缈', '�' => '缋', '�' => '缌', '��' => '缏', '��' => '缑', '��' => '缒', '��' => '缗', '��' => '缙', '��' => '缜', '��' => '缛', '��' => '缟', '��' => '缡', '��' => '缢', '��' => '缣', '��' => '缤', '��' => '缥', '��' => '缦', '��' => '缧', '��' => '缪', '��' => '缫', '��' => '缬', '��' => '缭', '��' => '缯', '��' => '缰', '��' => '缱', '��' => '缲', '��' => '缳', '��' => '缵', '��' => '幺', '��' => '畿', '��' => '巛', '��' => '甾', '��' => '邕', '��' => '玎', '��' => '玑', '��' => '玮', '��' => '玢', '��' => '玟', '��' => '珏', '��' => '珂', '��' => '珑', '��' => '玷', '��' => '玳', '��' => '珀', '��' => '珉', '��' => '珈', '��' => '珥', '��' => '珙', '��' => '顼', '��' => '琊', '��' => '珩', '��' => '珧', '��' => '珞', '��' => '玺', '�' => '珲', '�' => '琏', '�' => '琪', '�' => '瑛', '�' => '琦', '�' => '琥', '�' => '琨', '�' => '琰', '�' => '琮', '�' => '琬', '�@' => '鐯', '�A' => '鐰', '�B' => '鐱', '�C' => '鐲', '�D' => '鐳', '�E' => '鐴', '�F' => '鐵', '�G' => '鐶', '�H' => '鐷', '�I' => '鐸', '�J' => '鐹', '�K' => '鐺', '�L' => '鐻', '�M' => '鐼', '�N' => '鐽', '�O' => '鐿', '�P' => '鑀', '�Q' => '鑁', '�R' => '鑂', '�S' => '鑃', '�T' => '鑄', '�U' => '鑅', '�V' => '鑆', '�W' => '鑇', '�X' => '鑈', '�Y' => '鑉', '�Z' => '鑊', '�[' => '鑋', '�\\' => '鑌', '�]' => '鑍', '�^' => '鑎', '�_' => '鑏', '�`' => '鑐', '�a' => '鑑', '�b' => '鑒', '�c' => '鑓', '�d' => '鑔', '�e' => '鑕', '�f' => '鑖', '�g' => '鑗', '�h' => '鑘', '�i' => '鑙', '�j' => '鑚', '�k' => '鑛', '�l' => '鑜', '�m' => '鑝', '�n' => '鑞', '�o' => '鑟', '�p' => '鑠', '�q' => '鑡', '�r' => '鑢', '�s' => '鑣', '�t' => '鑤', '�u' => '鑥', '�v' => '鑦', '�w' => '鑧', '�x' => '鑨', '�y' => '鑩', '�z' => '鑪', '�{' => '鑬', '�|' => '鑭', '�}' => '鑮', '�~' => '鑯', '�' => '鑰', '�' => '鑱', '�' => '鑲', '�' => '鑳', '�' => '鑴', '�' => '鑵', '�' => '鑶', '�' => '鑷', '�' => '鑸', '�' => '鑹', '�' => '鑺', '�' => '鑻', '�' => '鑼', '�' => '鑽', '�' => '鑾', '�' => '鑿', '�' => '钀', '�' => '钁', '�' => '钂', '�' => '钃', '�' => '钄', '�' => '钑', '�' => '钖', '�' => '钘', '�' => '铇', '�' => '铏', '�' => '铓', '�' => '铔', '�' => '铚', '�' => '铦', '�' => '铻', '�' => '锜', '�' => '锠', '�' => '琛', '�' => '琚', '�' => '瑁', '�' => '瑜', '�' => '瑗', '�' => '瑕', '�' => '瑙', '�' => '瑷', '�' => '瑭', '�' => '瑾', '�' => '璜', '�' => '璎', '�' => '璀', '�' => '璁', '�' => '璇', '�' => '璋', '�' => '璞', '�' => '璨', '�' => '璩', '�' => '璐', '�' => '璧', '�' => '瓒', '�' => '璺', '�' => '韪', '�' => '韫', '�' => '韬', '�' => '杌', '�' => '杓', '�' => '杞', '�' => '杈', '�' => '杩', '�' => '枥', '�' => '枇', '��' => '杪', '��' => '杳', '��' => '枘', '��' => '枧', '��' => '杵', '��' => '枨', '��' => '枞', '��' => '枭', '��' => '枋', '��' => '杷', '��' => '杼', '��' => '柰', '��' => '栉', '��' => '柘', '��' => '栊', '��' => '柩', '��' => '枰', '��' => '栌', '��' => '柙', '��' => '枵', '��' => '柚', '��' => '枳', '��' => '柝', '��' => '栀', '��' => '柃', '��' => '枸', '��' => '柢', '��' => '栎', '��' => '柁', '��' => '柽', '��' => '栲', '��' => '栳', '��' => '桠', '��' => '桡', '��' => '桎', '��' => '桢', '��' => '桄', '��' => '桤', '��' => '梃', '��' => '栝', '��' => '桕', '��' => '桦', '��' => '桁', '��' => '桧', '��' => '桀', '��' => '栾', '��' => '桊', '��' => '桉', '��' => '栩', '��' => '梵', '��' => '梏', '�' => '桴', '�' => '桷', '�' => '梓', '�' => '桫', '�' => '棂', '�' => '楮', '�' => '棼', '�' => '椟', '�' => '椠', '�' => '棹', '�@' => '锧', '�A' => '锳', '�B' => '锽', '�C' => '镃', '�D' => '镈', '�E' => '镋', '�F' => '镕', '�G' => '镚', '�H' => '镠', '�I' => '镮', '�J' => '镴', '�K' => '镵', '�L' => '長', '�M' => '镸', '�N' => '镹', '�O' => '镺', '�P' => '镻', '�Q' => '镼', '�R' => '镽', '�S' => '镾', '�T' => '門', '�U' => '閁', '�V' => '閂', '�W' => '閃', '�X' => '閄', '�Y' => '閅', '�Z' => '閆', '�[' => '閇', '�\\' => '閈', '�]' => '閉', '�^' => '閊', '�_' => '開', '�`' => '閌', '�a' => '閍', '�b' => '閎', '�c' => '閏', '�d' => '閐', '�e' => '閑', '�f' => '閒', '�g' => '間', '�h' => '閔', '�i' => '閕', '�j' => '閖', '�k' => '閗', '�l' => '閘', '�m' => '閙', '�n' => '閚', '�o' => '閛', '�p' => '閜', '�q' => '閝', '�r' => '閞', '�s' => '閟', '�t' => '閠', '�u' => '閡', '�v' => '関', '�w' => '閣', '�x' => '閤', '�y' => '閥', '�z' => '閦', '�{' => '閧', '�|' => '閨', '�}' => '閩', '�~' => '閪', '�' => '閫', '�' => '閬', '�' => '閭', '�' => '閮', '�' => '閯', '�' => '閰', '�' => '閱', '�' => '閲', '�' => '閳', '�' => '閴', '�' => '閵', '�' => '閶', '�' => '閷', '�' => '閸', '�' => '閹', '�' => '閺', '�' => '閻', '�' => '閼', '�' => '閽', '�' => '閾', '�' => '閿', '�' => '闀', '�' => '闁', '�' => '闂', '�' => '闃', '�' => '闄', '�' => '闅', '�' => '闆', '�' => '闇', '�' => '闈', '�' => '闉', '�' => '闊', '�' => '闋', '�' => '椤', '�' => '棰', '�' => '椋', '�' => '椁', '�' => '楗', '�' => '棣', '�' => '椐', '�' => '楱', '�' => '椹', '�' => '楠', '�' => '楂', '�' => '楝', '�' => '榄', '�' => '楫', '�' => '榀', '�' => '榘', '�' => '楸', '�' => '椴', '�' => '槌', '�' => '榇', '�' => '榈', '�' => '槎', '�' => '榉', '�' => '楦', '�' => '楣', '�' => '楹', '�' => '榛', '�' => '榧', '�' => '榻', '�' => '榫', '�' => '榭', '�' => '槔', '�' => '榱', '��' => '槁', '��' => '槊', '��' => '槟', '��' => '榕', '��' => '槠', '��' => '榍', '��' => '槿', '��' => '樯', '��' => '槭', '��' => '樗', '��' => '樘', '��' => '橥', '��' => '槲', '��' => '橄', '��' => '樾', '��' => '檠', '��' => '橐', '��' => '橛', '��' => '樵', '��' => '檎', '��' => '橹', '��' => '樽', '��' => '樨', '��' => '橘', '��' => '橼', '��' => '檑', '��' => '檐', '��' => '檩', '��' => '檗', '��' => '檫', '��' => '猷', '��' => '獒', '��' => '殁', '��' => '殂', '��' => '殇', '��' => '殄', '��' => '殒', '��' => '殓', '��' => '殍', '��' => '殚', '��' => '殛', '��' => '殡', '��' => '殪', '��' => '轫', '��' => '轭', '��' => '轱', '��' => '轲', '��' => '轳', '��' => '轵', '��' => '轶', '��' => '轸', '�' => '轷', '�' => '轹', '�' => '轺', '�' => '轼', '�' => '轾', '�' => '辁', '�' => '辂', '�' => '辄', '�' => '辇', '�' => '辋', '�@' => '闌', '�A' => '闍', '�B' => '闎', '�C' => '闏', '�D' => '闐', '�E' => '闑', '�F' => '闒', '�G' => '闓', '�H' => '闔', '�I' => '闕', '�J' => '闖', '�K' => '闗', '�L' => '闘', '�M' => '闙', '�N' => '闚', '�O' => '闛', '�P' => '關', '�Q' => '闝', '�R' => '闞', '�S' => '闟', '�T' => '闠', '�U' => '闡', '�V' => '闢', '�W' => '闣', '�X' => '闤', '�Y' => '闥', '�Z' => '闦', '�[' => '闧', '�\\' => '闬', '�]' => '闿', '�^' => '阇', '�_' => '阓', '�`' => '阘', '�a' => '阛', '�b' => '阞', '�c' => '阠', '�d' => '阣', '�e' => '阤', '�f' => '阥', '�g' => '阦', '�h' => '阧', '�i' => '阨', '�j' => '阩', '�k' => '阫', '�l' => '阬', '�m' => '阭', '�n' => '阯', '�o' => '阰', '�p' => '阷', '�q' => '阸', '�r' => '阹', '�s' => '阺', '�t' => '阾', '�u' => '陁', '�v' => '陃', '�w' => '陊', '�x' => '陎', '�y' => '陏', '�z' => '陑', '�{' => '陒', '�|' => '陓', '�}' => '陖', '�~' => '陗', '�' => '陘', '�' => '陙', '�' => '陚', '�' => '陜', '�' => '陝', '�' => '陞', '�' => '陠', '�' => '陣', '�' => '陥', '�' => '陦', '�' => '陫', '�' => '陭', '�' => '陮', '�' => '陯', '�' => '陰', '�' => '陱', '�' => '陳', '�' => '陸', '�' => '陹', '�' => '険', '�' => '陻', '�' => '陼', '�' => '陽', '�' => '陾', '�' => '陿', '�' => '隀', '�' => '隁', '�' => '隂', '�' => '隃', '�' => '隄', '�' => '隇', '�' => '隉', '�' => '隊', '�' => '辍', '�' => '辎', '�' => '辏', '�' => '辘', '�' => '辚', '�' => '軎', '�' => '戋', '�' => '戗', '�' => '戛', '�' => '戟', '�' => '戢', '�' => '戡', '�' => '戥', '�' => '戤', '�' => '戬', '�' => '臧', '�' => '瓯', '�' => '瓴', '�' => '瓿', '�' => '甏', '�' => '甑', '�' => '甓', '�' => '攴', '�' => '旮', '�' => '旯', '�' => '旰', '�' => '昊', '�' => '昙', '�' => '杲', '�' => '昃', '�' => '昕', '�' => '昀', '�' => '炅', '��' => '曷', '��' => '昝', '��' => '昴', '��' => '昱', '��' => '昶', '��' => '昵', '��' => '耆', '��' => '晟', '��' => '晔', '��' => '晁', '��' => '晏', '��' => '晖', '��' => '晡', '��' => '晗', '��' => '晷', '��' => '暄', '��' => '暌', '��' => '暧', '��' => '暝', '��' => '暾', '��' => '曛', '��' => '曜', '��' => '曦', '��' => '曩', '��' => '贲', '��' => '贳', '��' => '贶', '��' => '贻', '��' => '贽', '��' => '赀', '��' => '赅', '��' => '赆', '��' => '赈', '��' => '赉', '��' => '赇', '��' => '赍', '��' => '赕', '��' => '赙', '��' => '觇', '��' => '觊', '��' => '觋', '��' => '觌', '��' => '觎', '��' => '觏', '��' => '觐', '��' => '觑', '��' => '牮', '��' => '犟', '��' => '牝', '��' => '牦', '��' => '牯', '�' => '牾', '�' => '牿', '�' => '犄', '�' => '犋', '�' => '犍', '�' => '犏', '�' => '犒', '�' => '挈', '�' => '挲', '�' => '掰', '�@' => '隌', '�A' => '階', '�B' => '隑', '�C' => '隒', '�D' => '隓', '�E' => '隕', '�F' => '隖', '�G' => '隚', '�H' => '際', '�I' => '隝', '�J' => '隞', '�K' => '隟', '�L' => '隠', '�M' => '隡', '�N' => '隢', '�O' => '隣', '�P' => '隤', '�Q' => '隥', '�R' => '隦', '�S' => '隨', '�T' => '隩', '�U' => '險', '�V' => '隫', '�W' => '隬', '�X' => '隭', '�Y' => '隮', '�Z' => '隯', '�[' => '隱', '�\\' => '隲', '�]' => '隴', '�^' => '隵', '�_' => '隷', '�`' => '隸', '�a' => '隺', '�b' => '隻', '�c' => '隿', '�d' => '雂', '�e' => '雃', '�f' => '雈', '�g' => '雊', '�h' => '雋', '�i' => '雐', '�j' => '雑', '�k' => '雓', '�l' => '雔', '�m' => '雖', '�n' => '雗', '�o' => '雘', '�p' => '雙', '�q' => '雚', '�r' => '雛', '�s' => '雜', '�t' => '雝', '�u' => '雞', '�v' => '雟', '�w' => '雡', '�x' => '離', '�y' => '難', '�z' => '雤', '�{' => '雥', '�|' => '雦', '�}' => '雧', '�~' => '雫', '�' => '雬', '�' => '雭', '�' => '雮', '�' => '雰', '�' => '雱', '�' => '雲', '�' => '雴', '�' => '雵', '�' => '雸', '�' => '雺', '�' => '電', '�' => '雼', '�' => '雽', '�' => '雿', '�' => '霂', '�' => '霃', '�' => '霅', '�' => '霊', '�' => '霋', '�' => '霌', '�' => '霐', '�' => '霑', '�' => '霒', '�' => '霔', '�' => '霕', '�' => '霗', '�' => '霘', '�' => '霙', '�' => '霚', '�' => '霛', '�' => '霝', '�' => '霟', '�' => '霠', '�' => '搿', '�' => '擘', '�' => '耄', '�' => '毪', '�' => '毳', '�' => '毽', '�' => '毵', '�' => '毹', '�' => '氅', '�' => '氇', '�' => '氆', '�' => '氍', '�' => '氕', '�' => '氘', '�' => '氙', '�' => '氚', '�' => '氡', '�' => '氩', '�' => '氤', '�' => '氪', '�' => '氲', '�' => '攵', '�' => '敕', '�' => '敫', '�' => '牍', '�' => '牒', '�' => '牖', '�' => '爰', '�' => '虢', '�' => '刖', '�' => '肟', '�' => '肜', '�' => '肓', '��' => '肼', '��' => '朊', '��' => '肽', '��' => '肱', '��' => '肫', '��' => '肭', '��' => '肴', '��' => '肷', '��' => '胧', '��' => '胨', '��' => '胩', '��' => '胪', '��' => '胛', '��' => '胂', '��' => '胄', '��' => '胙', '��' => '胍', '��' => '胗', '��' => '朐', '��' => '胝', '��' => '胫', '��' => '胱', '��' => '胴', '��' => '胭', '��' => '脍', '��' => '脎', '��' => '胲', '��' => '胼', '��' => '朕', '��' => '脒', '��' => '豚', '��' => '脶', '��' => '脞', '��' => '脬', '��' => '脘', '��' => '脲', '��' => '腈', '��' => '腌', '��' => '腓', '��' => '腴', '��' => '腙', '��' => '腚', '��' => '腱', '��' => '腠', '��' => '腩', '��' => '腼', '��' => '腽', '��' => '腭', '��' => '腧', '��' => '塍', '��' => '媵', '�' => '膈', '�' => '膂', '�' => '膑', '�' => '滕', '�' => '膣', '�' => '膪', '�' => '臌', '�' => '朦', '�' => '臊', '�' => '膻', '�@' => '霡', '�A' => '霢', '�B' => '霣', '�C' => '霤', '�D' => '霥', '�E' => '霦', '�F' => '霧', '�G' => '霨', '�H' => '霩', '�I' => '霫', '�J' => '霬', '�K' => '霮', '�L' => '霯', '�M' => '霱', '�N' => '霳', '�O' => '霴', '�P' => '霵', '�Q' => '霶', '�R' => '霷', '�S' => '霺', '�T' => '霻', '�U' => '霼', '�V' => '霽', '�W' => '霿', '�X' => '靀', '�Y' => '靁', '�Z' => '靂', '�[' => '靃', '�\\' => '靄', '�]' => '靅', '�^' => '靆', '�_' => '靇', '�`' => '靈', '�a' => '靉', '�b' => '靊', '�c' => '靋', '�d' => '靌', '�e' => '靍', '�f' => '靎', '�g' => '靏', '�h' => '靐', '�i' => '靑', '�j' => '靔', '�k' => '靕', '�l' => '靗', '�m' => '靘', '�n' => '靚', '�o' => '靜', '�p' => '靝', '�q' => '靟', '�r' => '靣', '�s' => '靤', '�t' => '靦', '�u' => '靧', '�v' => '靨', '�w' => '靪', '�x' => '靫', '�y' => '靬', '�z' => '靭', '�{' => '靮', '�|' => '靯', '�}' => '靰', '�~' => '靱', '�' => '靲', '�' => '靵', '�' => '靷', '�' => '靸', '�' => '靹', '�' => '靺', '�' => '靻', '�' => '靽', '�' => '靾', '�' => '靿', '�' => '鞀', '�' => '鞁', '�' => '鞂', '�' => '鞃', '�' => '鞄', '�' => '鞆', '�' => '鞇', '�' => '鞈', '�' => '鞉', '�' => '鞊', '�' => '鞌', '�' => '鞎', '�' => '鞏', '�' => '鞐', '�' => '鞓', '�' => '鞕', '�' => '鞖', '�' => '鞗', '�' => '鞙', '�' => '鞚', '�' => '鞛', '�' => '鞜', '�' => '鞝', '�' => '臁', '�' => '膦', '�' => '欤', '�' => '欷', '�' => '欹', '�' => '歃', '�' => '歆', '�' => '歙', '�' => '飑', '�' => '飒', '�' => '飓', '�' => '飕', '�' => '飙', '�' => '飚', '�' => '殳', '�' => '彀', '�' => '毂', '�' => '觳', '�' => '斐', '�' => '齑', '�' => '斓', '�' => '於', '�' => '旆', '�' => '旄', '�' => '旃', '�' => '旌', '�' => '旎', '�' => '旒', '�' => '旖', '�' => '炀', '�' => '炜', '�' => '炖', '�' => '炝', '��' => '炻', '��' => '烀', '��' => '炷', '��' => '炫', '��' => '炱', '��' => '烨', '��' => '烊', '��' => '焐', '��' => '焓', '��' => '焖', '��' => '焯', '��' => '焱', '��' => '煳', '��' => '煜', '��' => '煨', '��' => '煅', '��' => '煲', '��' => '煊', '��' => '煸', '��' => '煺', '��' => '熘', '��' => '熳', '��' => '熵', '��' => '熨', '��' => '熠', '��' => '燠', '��' => '燔', '��' => '燧', '��' => '燹', '��' => '爝', '��' => '爨', '��' => '灬', '��' => '焘', '��' => '煦', '��' => '熹', '��' => '戾', '��' => '戽', '��' => '扃', '��' => '扈', '��' => '扉', '��' => '礻', '��' => '祀', '��' => '祆', '��' => '祉', '��' => '祛', '��' => '祜', '��' => '祓', '��' => '祚', '��' => '祢', '��' => '祗', '��' => '祠', '�' => '祯', '�' => '祧', '�' => '祺', '�' => '禅', '�' => '禊', '�' => '禚', '�' => '禧', '�' => '禳', '�' => '忑', '�' => '忐', '�@' => '鞞', '�A' => '鞟', '�B' => '鞡', '�C' => '鞢', '�D' => '鞤', '�E' => '鞥', '�F' => '鞦', '�G' => '鞧', '�H' => '鞨', '�I' => '鞩', '�J' => '鞪', '�K' => '鞬', '�L' => '鞮', '�M' => '鞰', '�N' => '鞱', '�O' => '鞳', '�P' => '鞵', '�Q' => '鞶', '�R' => '鞷', '�S' => '鞸', '�T' => '鞹', '�U' => '鞺', '�V' => '鞻', '�W' => '鞼', '�X' => '鞽', '�Y' => '鞾', '�Z' => '鞿', '�[' => '韀', '�\\' => '韁', '�]' => '韂', '�^' => '韃', '�_' => '韄', '�`' => '韅', '�a' => '韆', '�b' => '韇', '�c' => '韈', '�d' => '韉', '�e' => '韊', '�f' => '韋', '�g' => '韌', '�h' => '韍', '�i' => '韎', '�j' => '韏', '�k' => '韐', '�l' => '韑', '�m' => '韒', '�n' => '韓', '�o' => '韔', '�p' => '韕', '�q' => '韖', '�r' => '韗', '�s' => '韘', '�t' => '韙', '�u' => '韚', '�v' => '韛', '�w' => '韜', '�x' => '韝', '�y' => '韞', '�z' => '韟', '�{' => '韠', '�|' => '韡', '�}' => '韢', '�~' => '韣', '�' => '韤', '�' => '韥', '�' => '韨', '�' => '韮', '�' => '韯', '�' => '韰', '�' => '韱', '�' => '韲', '�' => '韴', '�' => '韷', '�' => '韸', '�' => '韹', '�' => '韺', '�' => '韻', '�' => '韼', '�' => '韽', '�' => '韾', '�' => '響', '�' => '頀', '�' => '頁', '�' => '頂', '�' => '頃', '�' => '頄', '�' => '項', '�' => '順', '�' => '頇', '�' => '須', '�' => '頉', '�' => '頊', '�' => '頋', '�' => '頌', '�' => '頍', '�' => '頎', '�' => '怼', '�' => '恝', '�' => '恚', '�' => '恧', '�' => '恁', '�' => '恙', '�' => '恣', '�' => '悫', '�' => '愆', '�' => '愍', '�' => '慝', '�' => '憩', '�' => '憝', '�' => '懋', '�' => '懑', '�' => '戆', '�' => '肀', '�' => '聿', '�' => '沓', '�' => '泶', '�' => '淼', '�' => '矶', '�' => '矸', '�' => '砀', '�' => '砉', '�' => '砗', '�' => '砘', '�' => '砑', '�' => '斫', '�' => '砭', '�' => '砜', '�' => '砝', '�' => '砹', '��' => '砺', '��' => '砻', '��' => '砟', '��' => '砼', '��' => '砥', '��' => '砬', '��' => '砣', '��' => '砩', '��' => '硎', '��' => '硭', '��' => '硖', '��' => '硗', '��' => '砦', '��' => '硐', '��' => '硇', '��' => '硌', '��' => '硪', '��' => '碛', '��' => '碓', '��' => '碚', '��' => '碇', '��' => '碜', '��' => '碡', '��' => '碣', '��' => '碲', '��' => '碹', '��' => '碥', '��' => '磔', '��' => '磙', '��' => '磉', '��' => '磬', '��' => '磲', '��' => '礅', '��' => '磴', '��' => '礓', '��' => '礤', '��' => '礞', '��' => '礴', '��' => '龛', '��' => '黹', '��' => '黻', '��' => '黼', '��' => '盱', '��' => '眄', '��' => '眍', '��' => '盹', '��' => '眇', '��' => '眈', '��' => '眚', '��' => '眢', '��' => '眙', '�' => '眭', '�' => '眦', '�' => '眵', '�' => '眸', '�' => '睐', '�' => '睑', '�' => '睇', '�' => '睃', '�' => '睚', '�' => '睨', '�@' => '頏', '�A' => '預', '�B' => '頑', '�C' => '頒', '�D' => '頓', '�E' => '頔', '�F' => '頕', '�G' => '頖', '�H' => '頗', '�I' => '領', '�J' => '頙', '�K' => '頚', '�L' => '頛', '�M' => '頜', '�N' => '頝', '�O' => '頞', '�P' => '頟', '�Q' => '頠', '�R' => '頡', '�S' => '頢', '�T' => '頣', '�U' => '頤', '�V' => '頥', '�W' => '頦', '�X' => '頧', '�Y' => '頨', '�Z' => '頩', '�[' => '頪', '�\\' => '頫', '�]' => '頬', '�^' => '頭', '�_' => '頮', '�`' => '頯', '�a' => '頰', '�b' => '頱', '�c' => '頲', '�d' => '頳', '�e' => '頴', '�f' => '頵', '�g' => '頶', '�h' => '頷', '�i' => '頸', '�j' => '頹', '�k' => '頺', '�l' => '頻', '�m' => '頼', '�n' => '頽', '�o' => '頾', '�p' => '頿', '�q' => '顀', '�r' => '顁', '�s' => '顂', '�t' => '顃', '�u' => '顄', '�v' => '顅', '�w' => '顆', '�x' => '顇', '�y' => '顈', '�z' => '顉', '�{' => '顊', '�|' => '顋', '�}' => '題', '�~' => '額', '�' => '顎', '�' => '顏', '�' => '顐', '�' => '顑', '�' => '顒', '�' => '顓', '�' => '顔', '�' => '顕', '�' => '顖', '�' => '顗', '�' => '願', '�' => '顙', '�' => '顚', '�' => '顛', '�' => '顜', '�' => '顝', '�' => '類', '�' => '顟', '�' => '顠', '�' => '顡', '�' => '顢', '�' => '顣', '�' => '顤', '�' => '顥', '�' => '顦', '�' => '顧', '�' => '顨', '�' => '顩', '�' => '顪', '�' => '顫', '�' => '顬', '�' => '顭', '�' => '顮', '�' => '睢', '�' => '睥', '�' => '睿', '�' => '瞍', '�' => '睽', '�' => '瞀', '�' => '瞌', '�' => '瞑', '�' => '瞟', '�' => '瞠', '�' => '瞰', '�' => '瞵', '�' => '瞽', '�' => '町', '�' => '畀', '�' => '畎', '�' => '畋', '�' => '畈', '�' => '畛', '�' => '畲', '�' => '畹', '�' => '疃', '�' => '罘', '�' => '罡', '�' => '罟', '�' => '詈', '�' => '罨', '�' => '罴', '�' => '罱', '�' => '罹', '�' => '羁', '�' => '罾', '�' => '盍', '��' => '盥', '��' => '蠲', '��' => '钅', '��' => '钆', '��' => '钇', '��' => '钋', '��' => '钊', '��' => '钌', '��' => '钍', '��' => '钏', '��' => '钐', '��' => '钔', '��' => '钗', '��' => '钕', '��' => '钚', '��' => '钛', '��' => '钜', '��' => '钣', '��' => '钤', '��' => '钫', '��' => '钪', '��' => '钭', '��' => '钬', '��' => '钯', '��' => '钰', '��' => '钲', '��' => '钴', '��' => '钶', '��' => '钷', '��' => '钸', '��' => '钹', '��' => '钺', '��' => '钼', '��' => '钽', '��' => '钿', '��' => '铄', '��' => '铈', '��' => '铉', '��' => '铊', '��' => '铋', '��' => '铌', '��' => '铍', '��' => '铎', '��' => '铐', '��' => '铑', '��' => '铒', '��' => '铕', '��' => '铖', '��' => '铗', '��' => '铙', '��' => '铘', '�' => '铛', '�' => '铞', '�' => '铟', '�' => '铠', '�' => '铢', '�' => '铤', '�' => '铥', '�' => '铧', '�' => '铨', '�' => '铪', '�@' => '顯', '�A' => '顰', '�B' => '顱', '�C' => '顲', '�D' => '顳', '�E' => '顴', '�F' => '颋', '�G' => '颎', '�H' => '颒', '�I' => '颕', '�J' => '颙', '�K' => '颣', '�L' => '風', '�M' => '颩', '�N' => '颪', '�O' => '颫', '�P' => '颬', '�Q' => '颭', '�R' => '颮', '�S' => '颯', '�T' => '颰', '�U' => '颱', '�V' => '颲', '�W' => '颳', '�X' => '颴', '�Y' => '颵', '�Z' => '颶', '�[' => '颷', '�\\' => '颸', '�]' => '颹', '�^' => '颺', '�_' => '颻', '�`' => '颼', '�a' => '颽', '�b' => '颾', '�c' => '颿', '�d' => '飀', '�e' => '飁', '�f' => '飂', '�g' => '飃', '�h' => '飄', '�i' => '飅', '�j' => '飆', '�k' => '飇', '�l' => '飈', '�m' => '飉', '�n' => '飊', '�o' => '飋', '�p' => '飌', '�q' => '飍', '�r' => '飏', '�s' => '飐', '�t' => '飔', '�u' => '飖', '�v' => '飗', '�w' => '飛', '�x' => '飜', '�y' => '飝', '�z' => '飠', '�{' => '飡', '�|' => '飢', '�}' => '飣', '�~' => '飤', '�' => '飥', '�' => '飦', '�' => '飩', '�' => '飪', '�' => '飫', '�' => '飬', '�' => '飭', '�' => '飮', '�' => '飯', '�' => '飰', '�' => '飱', '�' => '飲', '�' => '飳', '�' => '飴', '�' => '飵', '�' => '飶', '�' => '飷', '�' => '飸', '�' => '飹', '�' => '飺', '�' => '飻', '�' => '飼', '�' => '飽', '�' => '飾', '�' => '飿', '�' => '餀', '�' => '餁', '�' => '餂', '�' => '餃', '�' => '餄', '�' => '餅', '�' => '餆', '�' => '餇', '�' => '铩', '�' => '铫', '�' => '铮', '�' => '铯', '�' => '铳', '�' => '铴', '�' => '铵', '�' => '铷', '�' => '铹', '�' => '铼', '�' => '铽', '�' => '铿', '�' => '锃', '�' => '锂', '�' => '锆', '�' => '锇', '�' => '锉', '�' => '锊', '�' => '锍', '�' => '锎', '�' => '锏', '�' => '锒', '�' => '锓', '�' => '锔', '�' => '锕', '�' => '锖', '�' => '锘', '�' => '锛', '�' => '锝', '�' => '锞', '�' => '锟', '�' => '锢', '�' => '锪', '��' => '锫', '��' => '锩', '��' => '锬', '��' => '锱', '��' => '锲', '��' => '锴', '��' => '锶', '��' => '锷', '��' => '锸', '��' => '锼', '��' => '锾', '��' => '锿', '��' => '镂', '��' => '锵', '��' => '镄', '��' => '镅', '��' => '镆', '��' => '镉', '��' => '镌', '��' => '镎', '��' => '镏', '��' => '镒', '��' => '镓', '��' => '镔', '��' => '镖', '��' => '镗', '��' => '镘', '��' => '镙', '��' => '镛', '��' => '镞', '��' => '镟', '��' => '镝', '��' => '镡', '��' => '镢', '��' => '镤', '��' => '镥', '��' => '镦', '��' => '镧', '��' => '镨', '��' => '镩', '��' => '镪', '��' => '镫', '��' => '镬', '��' => '镯', '��' => '镱', '��' => '镲', '��' => '镳', '��' => '锺', '��' => '矧', '��' => '矬', '��' => '雉', '�' => '秕', '�' => '秭', '�' => '秣', '�' => '秫', '�' => '稆', '�' => '嵇', '�' => '稃', '�' => '稂', '�' => '稞', '�' => '稔', '�@' => '餈', '�A' => '餉', '�B' => '養', '�C' => '餋', '�D' => '餌', '�E' => '餎', '�F' => '餏', '�G' => '餑', '�H' => '餒', '�I' => '餓', '�J' => '餔', '�K' => '餕', '�L' => '餖', '�M' => '餗', '�N' => '餘', '�O' => '餙', '�P' => '餚', '�Q' => '餛', '�R' => '餜', '�S' => '餝', '�T' => '餞', '�U' => '餟', '�V' => '餠', '�W' => '餡', '�X' => '餢', '�Y' => '餣', '�Z' => '餤', '�[' => '餥', '�\\' => '餦', '�]' => '餧', '�^' => '館', '�_' => '餩', '�`' => '餪', '�a' => '餫', '�b' => '餬', '�c' => '餭', '�d' => '餯', '�e' => '餰', '�f' => '餱', '�g' => '餲', '�h' => '餳', '�i' => '餴', '�j' => '餵', '�k' => '餶', '�l' => '餷', '�m' => '餸', '�n' => '餹', '�o' => '餺', '�p' => '餻', '�q' => '餼', '�r' => '餽', '�s' => '餾', '�t' => '餿', '�u' => '饀', '�v' => '饁', '�w' => '饂', '�x' => '饃', '�y' => '饄', '�z' => '饅', '�{' => '饆', '�|' => '饇', '�}' => '饈', '�~' => '饉', '�' => '饊', '�' => '饋', '�' => '饌', '�' => '饍', '�' => '饎', '�' => '饏', '�' => '饐', '�' => '饑', '�' => '饒', '�' => '饓', '�' => '饖', '�' => '饗', '�' => '饘', '�' => '饙', '�' => '饚', '�' => '饛', '�' => '饜', '�' => '饝', '�' => '饞', '�' => '饟', '�' => '饠', '�' => '饡', '�' => '饢', '�' => '饤', '�' => '饦', '�' => '饳', '�' => '饸', '�' => '饹', '�' => '饻', '�' => '饾', '�' => '馂', '�' => '馃', '�' => '馉', '�' => '稹', '�' => '稷', '�' => '穑', '�' => '黏', '�' => '馥', '�' => '穰', '�' => '皈', '�' => '皎', '�' => '皓', '�' => '皙', '�' => '皤', '�' => '瓞', '�' => '瓠', '�' => '甬', '�' => '鸠', '�' => '鸢', '�' => '鸨', '�' => '鸩', '�' => '鸪', '�' => '鸫', '�' => '鸬', '�' => '鸲', '�' => '鸱', '�' => '鸶', '�' => '鸸', '�' => '鸷', '�' => '鸹', '�' => '鸺', '�' => '鸾', '�' => '鹁', '�' => '鹂', '�' => '鹄', '�' => '鹆', '��' => '鹇', '��' => '鹈', '��' => '鹉', '��' => '鹋', '��' => '鹌', '��' => '鹎', '��' => '鹑', '��' => '鹕', '��' => '鹗', '��' => '鹚', '��' => '鹛', '��' => '鹜', '��' => '鹞', '��' => '鹣', '��' => '鹦', '��' => '鹧', '��' => '鹨', '��' => '鹩', '��' => '鹪', '��' => '鹫', '��' => '鹬', '��' => '鹱', '��' => '鹭', '��' => '鹳', '��' => '疒', '��' => '疔', '��' => '疖', '��' => '疠', '��' => '疝', '��' => '疬', '��' => '疣', '��' => '疳', '��' => '疴', '��' => '疸', '��' => '痄', '��' => '疱', '��' => '疰', '��' => '痃', '��' => '痂', '��' => '痖', '��' => '痍', '��' => '痣', '��' => '痨', '��' => '痦', '��' => '痤', '��' => '痫', '��' => '痧', '��' => '瘃', '��' => '痱', '��' => '痼', '��' => '痿', '�' => '瘐', '�' => '瘀', '�' => '瘅', '�' => '瘌', '�' => '瘗', '�' => '瘊', '�' => '瘥', '�' => '瘘', '�' => '瘕', '�' => '瘙', '�@' => '馌', '�A' => '馎', '�B' => '馚', '�C' => '馛', '�D' => '馜', '�E' => '馝', '�F' => '馞', '�G' => '馟', '�H' => '馠', '�I' => '馡', '�J' => '馢', '�K' => '馣', '�L' => '馤', '�M' => '馦', '�N' => '馧', '�O' => '馩', '�P' => '馪', '�Q' => '馫', '�R' => '馬', '�S' => '馭', '�T' => '馮', '�U' => '馯', '�V' => '馰', '�W' => '馱', '�X' => '馲', '�Y' => '馳', '�Z' => '馴', '�[' => '馵', '�\\' => '馶', '�]' => '馷', '�^' => '馸', '�_' => '馹', '�`' => '馺', '�a' => '馻', '�b' => '馼', '�c' => '馽', '�d' => '馾', '�e' => '馿', '�f' => '駀', '�g' => '駁', '�h' => '駂', '�i' => '駃', '�j' => '駄', '�k' => '駅', '�l' => '駆', '�m' => '駇', '�n' => '駈', '�o' => '駉', '�p' => '駊', '�q' => '駋', '�r' => '駌', '�s' => '駍', '�t' => '駎', '�u' => '駏', '�v' => '駐', '�w' => '駑', '�x' => '駒', '�y' => '駓', '�z' => '駔', '�{' => '駕', '�|' => '駖', '�}' => '駗', '�~' => '駘', '�' => '駙', '�' => '駚', '�' => '駛', '�' => '駜', '�' => '駝', '�' => '駞', '�' => '駟', '�' => '駠', '�' => '駡', '�' => '駢', '�' => '駣', '�' => '駤', '�' => '駥', '�' => '駦', '�' => '駧', '�' => '駨', '�' => '駩', '�' => '駪', '�' => '駫', '�' => '駬', '�' => '駭', '�' => '駮', '�' => '駯', '�' => '駰', '�' => '駱', '�' => '駲', '�' => '駳', '�' => '駴', '�' => '駵', '�' => '駶', '�' => '駷', '�' => '駸', '�' => '駹', '�' => '瘛', '�' => '瘼', '�' => '瘢', '�' => '瘠', '�' => '癀', '�' => '瘭', '�' => '瘰', '�' => '瘿', '�' => '瘵', '�' => '癃', '�' => '瘾', '�' => '瘳', '�' => '癍', '�' => '癞', '�' => '癔', '�' => '癜', '�' => '癖', '�' => '癫', '�' => '癯', '�' => '翊', '�' => '竦', '�' => '穸', '�' => '穹', '�' => '窀', '�' => '窆', '�' => '窈', '�' => '窕', '�' => '窦', '�' => '窠', '�' => '窬', '�' => '窨', '�' => '窭', '�' => '窳', '��' => '衤', '��' => '衩', '��' => '衲', '��' => '衽', '��' => '衿', '��' => '袂', '��' => '袢', '��' => '裆', '��' => '袷', '��' => '袼', '��' => '裉', '��' => '裢', '��' => '裎', '��' => '裣', '��' => '裥', '��' => '裱', '��' => '褚', '��' => '裼', '��' => '裨', '��' => '裾', '��' => '裰', '��' => '褡', '��' => '褙', '��' => '褓', '��' => '褛', '��' => '褊', '��' => '褴', '��' => '褫', '��' => '褶', '��' => '襁', '��' => '襦', '��' => '襻', '��' => '疋', '��' => '胥', '��' => '皲', '��' => '皴', '��' => '矜', '��' => '耒', '��' => '耔', '��' => '耖', '��' => '耜', '��' => '耠', '��' => '耢', '��' => '耥', '��' => '耦', '��' => '耧', '��' => '耩', '��' => '耨', '��' => '耱', '��' => '耋', '��' => '耵', '�' => '聃', '�' => '聆', '�' => '聍', '�' => '聒', '�' => '聩', '�' => '聱', '�' => '覃', '�' => '顸', '�' => '颀', '�' => '颃', '�@' => '駺', '�A' => '駻', '�B' => '駼', '�C' => '駽', '�D' => '駾', '�E' => '駿', '�F' => '騀', '�G' => '騁', '�H' => '騂', '�I' => '騃', '�J' => '騄', '�K' => '騅', '�L' => '騆', '�M' => '騇', '�N' => '騈', '�O' => '騉', '�P' => '騊', '�Q' => '騋', '�R' => '騌', '�S' => '騍', '�T' => '騎', '�U' => '騏', '�V' => '騐', '�W' => '騑', '�X' => '騒', '�Y' => '験', '�Z' => '騔', '�[' => '騕', '�\\' => '騖', '�]' => '騗', '�^' => '騘', '�_' => '騙', '�`' => '騚', '�a' => '騛', '�b' => '騜', '�c' => '騝', '�d' => '騞', '�e' => '騟', '�f' => '騠', '�g' => '騡', '�h' => '騢', '�i' => '騣', '�j' => '騤', '�k' => '騥', '�l' => '騦', '�m' => '騧', '�n' => '騨', '�o' => '騩', '�p' => '騪', '�q' => '騫', '�r' => '騬', '�s' => '騭', '�t' => '騮', '�u' => '騯', '�v' => '騰', '�w' => '騱', '�x' => '騲', '�y' => '騳', '�z' => '騴', '�{' => '騵', '�|' => '騶', '�}' => '騷', '�~' => '騸', '�' => '騹', '�' => '騺', '�' => '騻', '�' => '騼', '�' => '騽', '�' => '騾', '�' => '騿', '�' => '驀', '�' => '驁', '�' => '驂', '�' => '驃', '�' => '驄', '�' => '驅', '�' => '驆', '�' => '驇', '�' => '驈', '�' => '驉', '�' => '驊', '�' => '驋', '�' => '驌', '�' => '驍', '�' => '驎', '�' => '驏', '�' => '驐', '�' => '驑', '�' => '驒', '�' => '驓', '�' => '驔', '�' => '驕', '�' => '驖', '�' => '驗', '�' => '驘', '�' => '驙', '�' => '颉', '�' => '颌', '�' => '颍', '�' => '颏', '�' => '颔', '�' => '颚', '�' => '颛', '�' => '颞', '�' => '颟', '�' => '颡', '�' => '颢', '�' => '颥', '�' => '颦', '�' => '虍', '�' => '虔', '�' => '虬', '�' => '虮', '�' => '虿', '�' => '虺', '�' => '虼', '�' => '虻', '�' => '蚨', '�' => '蚍', '�' => '蚋', '�' => '蚬', '�' => '蚝', '�' => '蚧', '�' => '蚣', '�' => '蚪', '�' => '蚓', '�' => '蚩', '�' => '蚶', '�' => '蛄', '��' => '蚵', '��' => '蛎', '��' => '蚰', '��' => '蚺', '��' => '蚱', '��' => '蚯', '��' => '蛉', '��' => '蛏', '��' => '蚴', '��' => '蛩', '��' => '蛱', '��' => '蛲', '��' => '蛭', '��' => '蛳', '��' => '蛐', '��' => '蜓', '��' => '蛞', '��' => '蛴', '��' => '蛟', '��' => '蛘', '��' => '蛑', '��' => '蜃', '��' => '蜇', '��' => '蛸', '��' => '蜈', '��' => '蜊', '��' => '蜍', '��' => '蜉', '��' => '蜣', '��' => '蜻', '��' => '蜞', '��' => '蜥', '��' => '蜮', '��' => '蜚', '��' => '蜾', '��' => '蝈', '��' => '蜴', '��' => '蜱', '��' => '蜩', '��' => '蜷', '��' => '蜿', '��' => '螂', '��' => '蜢', '��' => '蝽', '��' => '蝾', '��' => '蝻', '��' => '蝠', '��' => '蝰', '��' => '蝌', '��' => '蝮', '��' => '螋', '�' => '蝓', '�' => '蝣', '�' => '蝼', '�' => '蝤', '�' => '蝙', '�' => '蝥', '�' => '螓', '�' => '螯', '�' => '螨', '�' => '蟒', '�@' => '驚', '�A' => '驛', '�B' => '驜', '�C' => '驝', '�D' => '驞', '�E' => '驟', '�F' => '驠', '�G' => '驡', '�H' => '驢', '�I' => '驣', '�J' => '驤', '�K' => '驥', '�L' => '驦', '�M' => '驧', '�N' => '驨', '�O' => '驩', '�P' => '驪', '�Q' => '驫', '�R' => '驲', '�S' => '骃', '�T' => '骉', '�U' => '骍', '�V' => '骎', '�W' => '骔', '�X' => '骕', '�Y' => '骙', '�Z' => '骦', '�[' => '骩', '�\\' => '骪', '�]' => '骫', '�^' => '骬', '�_' => '骭', '�`' => '骮', '�a' => '骯', '�b' => '骲', '�c' => '骳', '�d' => '骴', '�e' => '骵', '�f' => '骹', '�g' => '骻', '�h' => '骽', '�i' => '骾', '�j' => '骿', '�k' => '髃', '�l' => '髄', '�m' => '髆', '�n' => '髇', '�o' => '髈', '�p' => '髉', '�q' => '髊', '�r' => '髍', '�s' => '髎', '�t' => '髏', '�u' => '髐', '�v' => '髒', '�w' => '體', '�x' => '髕', '�y' => '髖', '�z' => '髗', '�{' => '髙', '�|' => '髚', '�}' => '髛', '�~' => '髜', '�' => '髝', '�' => '髞', '�' => '髠', '�' => '髢', '�' => '髣', '�' => '髤', '�' => '髥', '�' => '髧', '�' => '髨', '�' => '髩', '�' => '髪', '�' => '髬', '�' => '髮', '�' => '髰', '�' => '髱', '�' => '髲', '�' => '髳', '�' => '髴', '�' => '髵', '�' => '髶', '�' => '髷', '�' => '髸', '�' => '髺', '�' => '髼', '�' => '髽', '�' => '髾', '�' => '髿', '�' => '鬀', '�' => '鬁', '�' => '鬂', '�' => '鬄', '�' => '鬅', '�' => '鬆', '�' => '蟆', '�' => '螈', '�' => '螅', '�' => '螭', '�' => '螗', '�' => '螃', '�' => '螫', '�' => '蟥', '�' => '螬', '�' => '螵', '�' => '螳', '�' => '蟋', '�' => '蟓', '�' => '螽', '�' => '蟑', '�' => '蟀', '�' => '蟊', '�' => '蟛', '�' => '蟪', '�' => '蟠', '�' => '蟮', '�' => '蠖', '�' => '蠓', '�' => '蟾', '�' => '蠊', '�' => '蠛', '�' => '蠡', '�' => '蠹', '�' => '蠼', '�' => '缶', '�' => '罂', '�' => '罄', '�' => '罅', '��' => '舐', '��' => '竺', '��' => '竽', '��' => '笈', '��' => '笃', '��' => '笄', '��' => '笕', '��' => '笊', '��' => '笫', '��' => '笏', '��' => '筇', '��' => '笸', '��' => '笪', '��' => '笙', '��' => '笮', '��' => '笱', '��' => '笠', '��' => '笥', '��' => '笤', '��' => '笳', '��' => '笾', '��' => '笞', '��' => '筘', '��' => '筚', '��' => '筅', '��' => '筵', '��' => '筌', '��' => '筝', '��' => '筠', '��' => '筮', '��' => '筻', '��' => '筢', '��' => '筲', '��' => '筱', '��' => '箐', '��' => '箦', '��' => '箧', '��' => '箸', '��' => '箬', '��' => '箝', '��' => '箨', '��' => '箅', '��' => '箪', '��' => '箜', '��' => '箢', '��' => '箫', '��' => '箴', '��' => '篑', '��' => '篁', '��' => '篌', '��' => '篝', '�' => '篚', '�' => '篥', '�' => '篦', '�' => '篪', '�' => '簌', '�' => '篾', '�' => '篼', '�' => '簏', '�' => '簖', '�' => '簋', '�@' => '鬇', '�A' => '鬉', '�B' => '鬊', '�C' => '鬋', '�D' => '鬌', '�E' => '鬍', '�F' => '鬎', '�G' => '鬐', '�H' => '鬑', '�I' => '鬒', '�J' => '鬔', '�K' => '鬕', '�L' => '鬖', '�M' => '鬗', '�N' => '鬘', '�O' => '鬙', '�P' => '鬚', '�Q' => '鬛', '�R' => '鬜', '�S' => '鬝', '�T' => '鬞', '�U' => '鬠', '�V' => '鬡', '�W' => '鬢', '�X' => '鬤', '�Y' => '鬥', '�Z' => '鬦', '�[' => '鬧', '�\\' => '鬨', '�]' => '鬩', '�^' => '鬪', '�_' => '鬫', '�`' => '鬬', '�a' => '鬭', '�b' => '鬮', '�c' => '鬰', '�d' => '鬱', '�e' => '鬳', '�f' => '鬴', '�g' => '鬵', '�h' => '鬶', '�i' => '鬷', '�j' => '鬸', '�k' => '鬹', '�l' => '鬺', '�m' => '鬽', '�n' => '鬾', '�o' => '鬿', '�p' => '魀', '�q' => '魆', '�r' => '魊', '�s' => '魋', '�t' => '魌', '�u' => '魎', '�v' => '魐', '�w' => '魒', '�x' => '魓', '�y' => '魕', '�z' => '魖', '�{' => '魗', '�|' => '魘', '�}' => '魙', '�~' => '魚', '�' => '魛', '�' => '魜', '�' => '魝', '�' => '魞', '�' => '魟', '�' => '魠', '�' => '魡', '�' => '魢', '�' => '魣', '�' => '魤', '�' => '魥', '�' => '魦', '�' => '魧', '�' => '魨', '�' => '魩', '�' => '魪', '�' => '魫', '�' => '魬', '�' => '魭', '�' => '魮', '�' => '魯', '�' => '魰', '�' => '魱', '�' => '魲', '�' => '魳', '�' => '魴', '�' => '魵', '�' => '魶', '�' => '魷', '�' => '魸', '�' => '魹', '�' => '魺', '�' => '魻', '�' => '簟', '�' => '簪', '�' => '簦', '�' => '簸', '�' => '籁', '�' => '籀', '�' => '臾', '�' => '舁', '�' => '舂', '�' => '舄', '�' => '臬', '�' => '衄', '�' => '舡', '�' => '舢', '�' => '舣', '�' => '舭', '�' => '舯', '�' => '舨', '�' => '舫', '�' => '舸', '�' => '舻', '�' => '舳', '�' => '舴', '�' => '舾', '�' => '艄', '�' => '艉', '�' => '艋', '�' => '艏', '�' => '艚', '�' => '艟', '�' => '艨', '�' => '衾', '�' => '袅', '��' => '袈', '��' => '裘', '��' => '裟', '��' => '襞', '��' => '羝', '��' => '羟', '��' => '羧', '��' => '羯', '��' => '羰', '��' => '羲', '��' => '籼', '��' => '敉', '��' => '粑', '��' => '粝', '��' => '粜', '��' => '粞', '��' => '粢', '��' => '粲', '��' => '粼', '��' => '粽', '��' => '糁', '��' => '糇', '��' => '糌', '��' => '糍', '��' => '糈', '��' => '糅', '��' => '糗', '��' => '糨', '��' => '艮', '��' => '暨', '��' => '羿', '��' => '翎', '��' => '翕', '��' => '翥', '��' => '翡', '��' => '翦', '��' => '翩', '��' => '翮', '��' => '翳', '��' => '糸', '��' => '絷', '��' => '綦', '��' => '綮', '��' => '繇', '��' => '纛', '��' => '麸', '��' => '麴', '��' => '赳', '��' => '趄', '��' => '趔', '��' => '趑', '�' => '趱', '�' => '赧', '�' => '赭', '�' => '豇', '�' => '豉', '�' => '酊', '�' => '酐', '�' => '酎', '�' => '酏', '�' => '酤', '�@' => '魼', '�A' => '魽', '�B' => '魾', '�C' => '魿', '�D' => '鮀', '�E' => '鮁', '�F' => '鮂', '�G' => '鮃', '�H' => '鮄', '�I' => '鮅', '�J' => '鮆', '�K' => '鮇', '�L' => '鮈', '�M' => '鮉', '�N' => '鮊', '�O' => '鮋', '�P' => '鮌', '�Q' => '鮍', '�R' => '鮎', '�S' => '鮏', '�T' => '鮐', '�U' => '鮑', '�V' => '鮒', '�W' => '鮓', '�X' => '鮔', '�Y' => '鮕', '�Z' => '鮖', '�[' => '鮗', '�\\' => '鮘', '�]' => '鮙', '�^' => '鮚', '�_' => '鮛', '�`' => '鮜', '�a' => '鮝', '�b' => '鮞', '�c' => '鮟', '�d' => '鮠', '�e' => '鮡', '�f' => '鮢', '�g' => '鮣', '�h' => '鮤', '�i' => '鮥', '�j' => '鮦', '�k' => '鮧', '�l' => '鮨', '�m' => '鮩', '�n' => '鮪', '�o' => '鮫', '�p' => '鮬', '�q' => '鮭', '�r' => '鮮', '�s' => '鮯', '�t' => '鮰', '�u' => '鮱', '�v' => '鮲', '�w' => '鮳', '�x' => '鮴', '�y' => '鮵', '�z' => '鮶', '�{' => '鮷', '�|' => '鮸', '�}' => '鮹', '�~' => '鮺', '��' => '鮻', '��' => '鮼', '��' => '鮽', '��' => '鮾', '��' => '鮿', '��' => '鯀', '��' => '鯁', '��' => '鯂', '��' => '鯃', '��' => '鯄', '��' => '鯅', '��' => '鯆', '��' => '鯇', '��' => '鯈', '��' => '鯉', '��' => '鯊', '��' => '鯋', '��' => '鯌', '��' => '鯍', '��' => '鯎', '��' => '鯏', '��' => '鯐', '��' => '鯑', '��' => '鯒', '��' => '鯓', '��' => '鯔', '��' => '鯕', '��' => '鯖', '��' => '鯗', '��' => '鯘', '��' => '鯙', '��' => '鯚', '��' => '鯛', '��' => '酢', '��' => '酡', '��' => '酰', '��' => '酩', '��' => '酯', '��' => '酽', '��' => '酾', '��' => '酲', '��' => '酴', '��' => '酹', '��' => '醌', '��' => '醅', '��' => '醐', '��' => '醍', '��' => '醑', '��' => '醢', '��' => '醣', '��' => '醪', '��' => '醭', '��' => '醮', '��' => '醯', '��' => '醵', '��' => '醴', '��' => '醺', '��' => '豕', '��' => '鹾', '��' => '趸', '��' => '跫', '��' => '踅', '��' => '蹙', '��' => '蹩', '��' => '趵', '��' => '趿', '��' => '趼', '��' => '趺', '��' => '跄', '��' => '跖', '��' => '跗', '��' => '跚', '��' => '跞', '��' => '跎', '��' => '跏', '��' => '跛', '��' => '跆', '��' => '跬', '��' => '跷', '��' => '跸', '��' => '跣', '��' => '跹', '��' => '跻', '��' => '跤', '��' => '踉', '��' => '跽', '��' => '踔', '��' => '踝', '��' => '踟', '��' => '踬', '��' => '踮', '��' => '踣', '��' => '踯', '��' => '踺', '��' => '蹀', '��' => '踹', '��' => '踵', '��' => '踽', '��' => '踱', '��' => '蹉', '��' => '蹁', '��' => '蹂', '��' => '蹑', '��' => '蹒', '��' => '蹊', '��' => '蹰', '��' => '蹶', '��' => '蹼', '��' => '蹯', '��' => '蹴', '��' => '躅', '��' => '躏', '��' => '躔', '��' => '躐', '��' => '躜', '��' => '躞', '��' => '豸', '��' => '貂', '��' => '貊', '��' => '貅', '��' => '貘', '��' => '貔', '��' => '斛', '��' => '觖', '��' => '觞', '��' => '觚', '��' => '觜', '�@' => '鯜', '�A' => '鯝', '�B' => '鯞', '�C' => '鯟', '�D' => '鯠', '�E' => '鯡', '�F' => '鯢', '�G' => '鯣', '�H' => '鯤', '�I' => '鯥', '�J' => '鯦', '�K' => '鯧', '�L' => '鯨', '�M' => '鯩', '�N' => '鯪', '�O' => '鯫', '�P' => '鯬', '�Q' => '鯭', '�R' => '鯮', '�S' => '鯯', '�T' => '鯰', '�U' => '鯱', '�V' => '鯲', '�W' => '鯳', '�X' => '鯴', '�Y' => '鯵', '�Z' => '鯶', '�[' => '鯷', '�\\' => '鯸', '�]' => '鯹', '�^' => '鯺', '�_' => '鯻', '�`' => '鯼', '�a' => '鯽', '�b' => '鯾', '�c' => '鯿', '�d' => '鰀', '�e' => '鰁', '�f' => '鰂', '�g' => '鰃', '�h' => '鰄', '�i' => '鰅', '�j' => '鰆', '�k' => '鰇', '�l' => '鰈', '�m' => '鰉', '�n' => '鰊', '�o' => '鰋', '�p' => '鰌', '�q' => '鰍', '�r' => '鰎', '�s' => '鰏', '�t' => '鰐', '�u' => '鰑', '�v' => '鰒', '�w' => '鰓', '�x' => '鰔', '�y' => '鰕', '�z' => '鰖', '�{' => '鰗', '�|' => '鰘', '�}' => '鰙', '�~' => '鰚', '��' => '鰛', '��' => '鰜', '��' => '鰝', '��' => '鰞', '��' => '鰟', '��' => '鰠', '��' => '鰡', '��' => '鰢', '��' => '鰣', '��' => '鰤', '��' => '鰥', '��' => '鰦', '��' => '鰧', '��' => '鰨', '��' => '鰩', '��' => '鰪', '��' => '鰫', '��' => '鰬', '��' => '鰭', '��' => '鰮', '��' => '鰯', '��' => '鰰', '��' => '鰱', '��' => '鰲', '��' => '鰳', '��' => '鰴', '��' => '鰵', '��' => '鰶', '��' => '鰷', '��' => '鰸', '��' => '鰹', '��' => '鰺', '��' => '鰻', '��' => '觥', '��' => '觫', '��' => '觯', '��' => '訾', '��' => '謦', '��' => '靓', '��' => '雩', '��' => '雳', '��' => '雯', '��' => '霆', '��' => '霁', '��' => '霈', '��' => '霏', '��' => '霎', '��' => '霪', '��' => '霭', '��' => '霰', '��' => '霾', '��' => '龀', '��' => '龃', '��' => '龅', '��' => '龆', '��' => '龇', '��' => '龈', '��' => '龉', '��' => '龊', '��' => '龌', '��' => '黾', '��' => '鼋', '��' => '鼍', '��' => '隹', '��' => '隼', '��' => '隽', '��' => '雎', '��' => '雒', '��' => '瞿', '��' => '雠', '��' => '銎', '��' => '銮', '��' => '鋈', '��' => '錾', '��' => '鍪', '��' => '鏊', '��' => '鎏', '��' => '鐾', '��' => '鑫', '��' => '鱿', '��' => '鲂', '��' => '鲅', '��' => '鲆', '��' => '鲇', '��' => '鲈', '��' => '稣', '��' => '鲋', '��' => '鲎', '��' => '鲐', '��' => '鲑', '��' => '鲒', '��' => '鲔', '��' => '鲕', '��' => '鲚', '��' => '鲛', '��' => '鲞', '��' => '鲟', '��' => '鲠', '��' => '鲡', '��' => '鲢', '��' => '鲣', '��' => '鲥', '��' => '鲦', '��' => '鲧', '��' => '鲨', '��' => '鲩', '��' => '鲫', '��' => '鲭', '��' => '鲮', '��' => '鲰', '��' => '鲱', '��' => '鲲', '��' => '鲳', '��' => '鲴', '��' => '鲵', '��' => '鲶', '��' => '鲷', '��' => '鲺', '��' => '鲻', '��' => '鲼', '��' => '鲽', '��' => '鳄', '��' => '鳅', '��' => '鳆', '��' => '鳇', '��' => '鳊', '��' => '鳋', '�@' => '鰼', '�A' => '鰽', '�B' => '鰾', '�C' => '鰿', '�D' => '鱀', '�E' => '鱁', '�F' => '鱂', '�G' => '鱃', '�H' => '鱄', '�I' => '鱅', '�J' => '鱆', '�K' => '鱇', '�L' => '鱈', '�M' => '鱉', '�N' => '鱊', '�O' => '鱋', '�P' => '鱌', '�Q' => '鱍', '�R' => '鱎', '�S' => '鱏', '�T' => '鱐', '�U' => '鱑', '�V' => '鱒', '�W' => '鱓', '�X' => '鱔', '�Y' => '鱕', '�Z' => '鱖', '�[' => '鱗', '�\\' => '鱘', '�]' => '鱙', '�^' => '鱚', '�_' => '鱛', '�`' => '鱜', '�a' => '鱝', '�b' => '鱞', '�c' => '鱟', '�d' => '鱠', '�e' => '鱡', '�f' => '鱢', '�g' => '鱣', '�h' => '鱤', '�i' => '鱥', '�j' => '鱦', '�k' => '鱧', '�l' => '鱨', '�m' => '鱩', '�n' => '鱪', '�o' => '鱫', '�p' => '鱬', '�q' => '鱭', '�r' => '鱮', '�s' => '鱯', '�t' => '鱰', '�u' => '鱱', '�v' => '鱲', '�w' => '鱳', '�x' => '鱴', '�y' => '鱵', '�z' => '鱶', '�{' => '鱷', '�|' => '鱸', '�}' => '鱹', '�~' => '鱺', '��' => '鱻', '��' => '鱽', '��' => '鱾', '��' => '鲀', '��' => '鲃', '��' => '鲄', '��' => '鲉', '��' => '鲊', '��' => '鲌', '��' => '鲏', '��' => '鲓', '��' => '鲖', '��' => '鲗', '��' => '鲘', '��' => '鲙', '��' => '鲝', '��' => '鲪', '��' => '鲬', '��' => '鲯', '��' => '鲹', '��' => '鲾', '��' => '鲿', '��' => '鳀', '��' => '鳁', '��' => '鳂', '��' => '鳈', '��' => '鳉', '��' => '鳑', '��' => '鳒', '��' => '鳚', '��' => '鳛', '��' => '鳠', '��' => '鳡', '��' => '鳌', '��' => '鳍', '��' => '鳎', '��' => '鳏', '��' => '鳐', '��' => '鳓', '��' => '鳔', '��' => '鳕', '��' => '鳗', '��' => '鳘', '��' => '鳙', '��' => '鳜', '��' => '鳝', '��' => '鳟', '��' => '鳢', '��' => '靼', '��' => '鞅', '��' => '鞑', '��' => '鞒', '��' => '鞔', '��' => '鞯', '��' => '鞫', '��' => '鞣', '��' => '鞲', '��' => '鞴', '��' => '骱', '��' => '骰', '��' => '骷', '��' => '鹘', '��' => '骶', '��' => '骺', '��' => '骼', '��' => '髁', '��' => '髀', '��' => '髅', '��' => '髂', '��' => '髋', '��' => '髌', '��' => '髑', '��' => '魅', '��' => '魃', '��' => '魇', '��' => '魉', '��' => '魈', '��' => '魍', '��' => '魑', '��' => '飨', '��' => '餍', '��' => '餮', '��' => '饕', '��' => '饔', '��' => '髟', '��' => '髡', '��' => '髦', '��' => '髯', '��' => '髫', '��' => '髻', '��' => '髭', '��' => '髹', '��' => '鬈', '��' => '鬏', '��' => '鬓', '��' => '鬟', '��' => '鬣', '��' => '麽', '��' => '麾', '��' => '縻', '��' => '麂', '��' => '麇', '��' => '麈', '��' => '麋', '��' => '麒', '��' => '鏖', '��' => '麝', '��' => '麟', '��' => '黛', '��' => '黜', '��' => '黝', '��' => '黠', '��' => '黟', '��' => '黢', '��' => '黩', '��' => '黧', '��' => '黥', '��' => '黪', '��' => '黯', '��' => '鼢', '��' => '鼬', '��' => '鼯', '��' => '鼹', '��' => '鼷', '��' => '鼽', '��' => '鼾', '��' => '齄', '�@' => '鳣', '�A' => '鳤', '�B' => '鳥', '�C' => '鳦', '�D' => '鳧', '�E' => '鳨', '�F' => '鳩', '�G' => '鳪', '�H' => '鳫', '�I' => '鳬', '�J' => '鳭', '�K' => '鳮', '�L' => '鳯', '�M' => '鳰', '�N' => '鳱', '�O' => '鳲', '�P' => '鳳', '�Q' => '鳴', '�R' => '鳵', '�S' => '鳶', '�T' => '鳷', '�U' => '鳸', '�V' => '鳹', '�W' => '鳺', '�X' => '鳻', '�Y' => '鳼', '�Z' => '鳽', '�[' => '鳾', '�\\' => '鳿', '�]' => '鴀', '�^' => '鴁', '�_' => '鴂', '�`' => '鴃', '�a' => '鴄', '�b' => '鴅', '�c' => '鴆', '�d' => '鴇', '�e' => '鴈', '�f' => '鴉', '�g' => '鴊', '�h' => '鴋', '�i' => '鴌', '�j' => '鴍', '�k' => '鴎', '�l' => '鴏', '�m' => '鴐', '�n' => '鴑', '�o' => '鴒', '�p' => '鴓', '�q' => '鴔', '�r' => '鴕', '�s' => '鴖', '�t' => '鴗', '�u' => '鴘', '�v' => '鴙', '�w' => '鴚', '�x' => '鴛', '�y' => '鴜', '�z' => '鴝', '�{' => '鴞', '�|' => '鴟', '�}' => '鴠', '�~' => '鴡', '��' => '鴢', '��' => '鴣', '��' => '鴤', '��' => '鴥', '��' => '鴦', '��' => '鴧', '��' => '鴨', '��' => '鴩', '��' => '鴪', '��' => '鴫', '��' => '鴬', '��' => '鴭', '��' => '鴮', '��' => '鴯', '��' => '鴰', '��' => '鴱', '��' => '鴲', '��' => '鴳', '��' => '鴴', '��' => '鴵', '��' => '鴶', '��' => '鴷', '��' => '鴸', '��' => '鴹', '��' => '鴺', '��' => '鴻', '��' => '鴼', '��' => '鴽', '��' => '鴾', '��' => '鴿', '��' => '鵀', '��' => '鵁', '��' => '鵂', '�@' => '鵃', '�A' => '鵄', '�B' => '鵅', '�C' => '鵆', '�D' => '鵇', '�E' => '鵈', '�F' => '鵉', '�G' => '鵊', '�H' => '鵋', '�I' => '鵌', '�J' => '鵍', '�K' => '鵎', '�L' => '鵏', '�M' => '鵐', '�N' => '鵑', '�O' => '鵒', '�P' => '鵓', '�Q' => '鵔', '�R' => '鵕', '�S' => '鵖', '�T' => '鵗', '�U' => '鵘', '�V' => '鵙', '�W' => '鵚', '�X' => '鵛', '�Y' => '鵜', '�Z' => '鵝', '�[' => '鵞', '�\\' => '鵟', '�]' => '鵠', '�^' => '鵡', '�_' => '鵢', '�`' => '鵣', '�a' => '鵤', '�b' => '鵥', '�c' => '鵦', '�d' => '鵧', '�e' => '鵨', '�f' => '鵩', '�g' => '鵪', '�h' => '鵫', '�i' => '鵬', '�j' => '鵭', '�k' => '鵮', '�l' => '鵯', '�m' => '鵰', '�n' => '鵱', '�o' => '鵲', '�p' => '鵳', '�q' => '鵴', '�r' => '鵵', '�s' => '鵶', '�t' => '鵷', '�u' => '鵸', '�v' => '鵹', '�w' => '鵺', '�x' => '鵻', '�y' => '鵼', '�z' => '鵽', '�{' => '鵾', '�|' => '鵿', '�}' => '鶀', '�~' => '鶁', '��' => '鶂', '��' => '鶃', '��' => '鶄', '��' => '鶅', '��' => '鶆', '��' => '鶇', '��' => '鶈', '��' => '鶉', '��' => '鶊', '��' => '鶋', '��' => '鶌', '��' => '鶍', '��' => '鶎', '��' => '鶏', '��' => '鶐', '��' => '鶑', '��' => '鶒', '��' => '鶓', '��' => '鶔', '��' => '鶕', '��' => '鶖', '��' => '鶗', '��' => '鶘', '��' => '鶙', '��' => '鶚', '��' => '鶛', '��' => '鶜', '��' => '鶝', '��' => '鶞', '��' => '鶟', '��' => '鶠', '��' => '鶡', '��' => '鶢', '�@' => '鶣', '�A' => '鶤', '�B' => '鶥', '�C' => '鶦', '�D' => '鶧', '�E' => '鶨', '�F' => '鶩', '�G' => '鶪', '�H' => '鶫', '�I' => '鶬', '�J' => '鶭', '�K' => '鶮', '�L' => '鶯', '�M' => '鶰', '�N' => '鶱', '�O' => '鶲', '�P' => '鶳', '�Q' => '鶴', '�R' => '鶵', '�S' => '鶶', '�T' => '鶷', '�U' => '鶸', '�V' => '鶹', '�W' => '鶺', '�X' => '鶻', '�Y' => '鶼', '�Z' => '鶽', '�[' => '鶾', '�\\' => '鶿', '�]' => '鷀', '�^' => '鷁', '�_' => '鷂', '�`' => '鷃', '�a' => '鷄', '�b' => '鷅', '�c' => '鷆', '�d' => '鷇', '�e' => '鷈', '�f' => '鷉', '�g' => '鷊', '�h' => '鷋', '�i' => '鷌', '�j' => '鷍', '�k' => '鷎', '�l' => '鷏', '�m' => '鷐', '�n' => '鷑', '�o' => '鷒', '�p' => '鷓', '�q' => '鷔', '�r' => '鷕', '�s' => '鷖', '�t' => '鷗', '�u' => '鷘', '�v' => '鷙', '�w' => '鷚', '�x' => '鷛', '�y' => '鷜', '�z' => '鷝', '�{' => '鷞', '�|' => '鷟', '�}' => '鷠', '�~' => '鷡', '��' => '鷢', '��' => '鷣', '��' => '鷤', '��' => '鷥', '��' => '鷦', '��' => '鷧', '��' => '鷨', '��' => '鷩', '��' => '鷪', '��' => '鷫', '��' => '鷬', '��' => '鷭', '��' => '鷮', '��' => '鷯', '��' => '鷰', '��' => '鷱', '��' => '鷲', '��' => '鷳', '��' => '鷴', '��' => '鷵', '��' => '鷶', '��' => '鷷', '��' => '鷸', '��' => '鷹', '��' => '鷺', '��' => '鷻', '��' => '鷼', '��' => '鷽', '��' => '鷾', '��' => '鷿', '��' => '鸀', '��' => '鸁', '��' => '鸂', '�@' => '鸃', '�A' => '鸄', '�B' => '鸅', '�C' => '鸆', '�D' => '鸇', '�E' => '鸈', '�F' => '鸉', '�G' => '鸊', '�H' => '鸋', '�I' => '鸌', '�J' => '鸍', '�K' => '鸎', '�L' => '鸏', '�M' => '鸐', '�N' => '鸑', '�O' => '鸒', '�P' => '鸓', '�Q' => '鸔', '�R' => '鸕', '�S' => '鸖', '�T' => '鸗', '�U' => '鸘', '�V' => '鸙', '�W' => '鸚', '�X' => '鸛', '�Y' => '鸜', '�Z' => '鸝', '�[' => '鸞', '�\\' => '鸤', '�]' => '鸧', '�^' => '鸮', '�_' => '鸰', '�`' => '鸴', '�a' => '鸻', '�b' => '鸼', '�c' => '鹀', '�d' => '鹍', '�e' => '鹐', '�f' => '鹒', '�g' => '鹓', '�h' => '鹔', '�i' => '鹖', '�j' => '鹙', '�k' => '鹝', '�l' => '鹟', '�m' => '鹠', '�n' => '鹡', '�o' => '鹢', '�p' => '鹥', '�q' => '鹮', '�r' => '鹯', '�s' => '鹲', '�t' => '鹴', '�u' => '鹵', '�v' => '鹶', '�w' => '鹷', '�x' => '鹸', '�y' => '鹹', '�z' => '鹺', '�{' => '鹻', '�|' => '鹼', '�}' => '鹽', '�~' => '麀', '��' => '麁', '��' => '麃', '��' => '麄', '��' => '麅', '��' => '麆', '��' => '麉', '��' => '麊', '��' => '麌', '��' => '麍', '��' => '麎', '��' => '麏', '��' => '麐', '��' => '麑', '��' => '麔', '��' => '麕', '��' => '麖', '��' => '麗', '��' => '麘', '��' => '麙', '��' => '麚', '��' => '麛', '��' => '麜', '��' => '麞', '��' => '麠', '��' => '麡', '��' => '麢', '��' => '麣', '��' => '麤', '��' => '麥', '��' => '麧', '��' => '麨', '��' => '麩', '��' => '麪', '�@' => '麫', '�A' => '麬', '�B' => '麭', '�C' => '麮', '�D' => '麯', '�E' => '麰', '�F' => '麱', '�G' => '麲', '�H' => '麳', '�I' => '麵', '�J' => '麶', '�K' => '麷', '�L' => '麹', '�M' => '麺', '�N' => '麼', '�O' => '麿', '�P' => '黀', '�Q' => '黁', '�R' => '黂', '�S' => '黃', '�T' => '黅', '�U' => '黆', '�V' => '黇', '�W' => '黈', '�X' => '黊', '�Y' => '黋', '�Z' => '黌', '�[' => '黐', '�\\' => '黒', '�]' => '黓', '�^' => '黕', '�_' => '黖', '�`' => '黗', '�a' => '黙', '�b' => '黚', '�c' => '點', '�d' => '黡', '�e' => '黣', '�f' => '黤', '�g' => '黦', '�h' => '黨', '�i' => '黫', '�j' => '黬', '�k' => '黭', '�l' => '黮', '�m' => '黰', '�n' => '黱', '�o' => '黲', '�p' => '黳', '�q' => '黴', '�r' => '黵', '�s' => '黶', '�t' => '黷', '�u' => '黸', '�v' => '黺', '�w' => '黽', '�x' => '黿', '�y' => '鼀', '�z' => '鼁', '�{' => '鼂', '�|' => '鼃', '�}' => '鼄', '�~' => '鼅', '��' => '鼆', '��' => '鼇', '��' => '鼈', '��' => '鼉', '��' => '鼊', '��' => '鼌', '��' => '鼏', '��' => '鼑', '��' => '鼒', '��' => '鼔', '��' => '鼕', '��' => '鼖', '��' => '鼘', '��' => '鼚', '��' => '鼛', '��' => '鼜', '��' => '鼝', '��' => '鼞', '��' => '鼟', '��' => '鼡', '��' => '鼣', '��' => '鼤', '��' => '鼥', '��' => '鼦', '��' => '鼧', '��' => '鼨', '��' => '鼩', '��' => '鼪', '��' => '鼫', '��' => '鼭', '��' => '鼮', '��' => '鼰', '��' => '鼱', '�@' => '鼲', '�A' => '鼳', '�B' => '鼴', '�C' => '鼵', '�D' => '鼶', '�E' => '鼸', '�F' => '鼺', '�G' => '鼼', '�H' => '鼿', '�I' => '齀', '�J' => '齁', '�K' => '齂', '�L' => '齃', '�M' => '齅', '�N' => '齆', '�O' => '齇', '�P' => '齈', '�Q' => '齉', '�R' => '齊', '�S' => '齋', '�T' => '齌', '�U' => '齍', '�V' => '齎', '�W' => '齏', '�X' => '齒', '�Y' => '齓', '�Z' => '齔', '�[' => '齕', '�\\' => '齖', '�]' => '齗', '�^' => '齘', '�_' => '齙', '�`' => '齚', '�a' => '齛', '�b' => '齜', '�c' => '齝', '�d' => '齞', '�e' => '齟', '�f' => '齠', '�g' => '齡', '�h' => '齢', '�i' => '齣', '�j' => '齤', '�k' => '齥', '�l' => '齦', '�m' => '齧', '�n' => '齨', '�o' => '齩', '�p' => '齪', '�q' => '齫', '�r' => '齬', '�s' => '齭', '�t' => '齮', '�u' => '齯', '�v' => '齰', '�w' => '齱', '�x' => '齲', '�y' => '齳', '�z' => '齴', '�{' => '齵', '�|' => '齶', '�}' => '齷', '�~' => '齸', '��' => '齹', '��' => '齺', '��' => '齻', '��' => '齼', '��' => '齽', '��' => '齾', '��' => '龁', '��' => '龂', '��' => '龍', '��' => '龎', '��' => '龏', '��' => '龐', '��' => '龑', '��' => '龒', '��' => '龓', '��' => '龔', '��' => '龕', '��' => '龖', '��' => '龗', '��' => '龘', '��' => '龜', '��' => '龝', '��' => '龞', '��' => '龡', '��' => '龢', '��' => '龣', '��' => '龤', '��' => '龥', '��' => '郎', '��' => '凉', '��' => '秊', '��' => '裏', '��' => '隣', '�@' => '兀', '�A' => '嗀', '�B' => '﨎', '�C' => '﨏', '�D' => '﨑', '�E' => '﨓', '�F' => '﨔', '�G' => '礼', '�H' => '﨟', '�I' => '蘒', '�J' => '﨡', '�K' => '﨣', '�L' => '﨤', '�M' => '﨧', '�N' => '﨨', '�O' => '﨩', ); $result =& $data; unset($data); return $result; PK2A#]x�z���Cvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-9.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => 'º', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ğ', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'İ', '�' => 'Ş', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ğ', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ı', '�' => 'ş', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]#�����?vendor/symfony/polyfill-iconv/Resources/charset/from.cp1026.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', ' ' => '', ' ' => '', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => '', '!' => '', '"' => '', '#' => '', '$' => '', '%' => ' ', '&' => '', '\'' => '', '(' => '', ')' => '', '*' => '', '+' => '', ',' => '', '-' => '', '.' => '', '/' => '', 0 => '', 1 => '', 2 => '', 3 => '', 4 => '', 5 => '', 6 => '', 7 => '', 8 => '', 9 => '', ':' => '', ';' => '', '<' => '', '=' => '', '>' => '', '?' => '', '@' => ' ', 'A' => ' ', 'B' => 'â', 'C' => 'ä', 'D' => 'à', 'E' => 'á', 'F' => 'ã', 'G' => 'å', 'H' => '{', 'I' => 'ñ', 'J' => 'Ç', 'K' => '.', 'L' => '<', 'M' => '(', 'N' => '+', 'O' => '!', 'P' => '&', 'Q' => 'é', 'R' => 'ê', 'S' => 'ë', 'T' => 'è', 'U' => 'í', 'V' => 'î', 'W' => 'ï', 'X' => 'ì', 'Y' => 'ß', 'Z' => 'Ğ', '[' => 'İ', '\\' => '*', ']' => ')', '^' => ';', '_' => '^', '`' => '-', 'a' => '/', 'b' => 'Â', 'c' => 'Ä', 'd' => 'À', 'e' => 'Á', 'f' => 'Ã', 'g' => 'Å', 'h' => '[', 'i' => 'Ñ', 'j' => 'ş', 'k' => ',', 'l' => '%', 'm' => '_', 'n' => '>', 'o' => '?', 'p' => 'ø', 'q' => 'É', 'r' => 'Ê', 's' => 'Ë', 't' => 'È', 'u' => 'Í', 'v' => 'Î', 'w' => 'Ï', 'x' => 'Ì', 'y' => 'ı', 'z' => ':', '{' => 'Ö', '|' => 'Ş', '}' => '\'', '~' => '=', '' => 'Ü', '�' => 'Ø', '�' => 'a', '�' => 'b', '�' => 'c', '�' => 'd', '�' => 'e', '�' => 'f', '�' => 'g', '�' => 'h', '�' => 'i', '�' => '«', '�' => '»', '�' => '}', '�' => '`', '�' => '¦', '�' => '±', '�' => '°', '�' => 'j', '�' => 'k', '�' => 'l', '�' => 'm', '�' => 'n', '�' => 'o', '�' => 'p', '�' => 'q', '�' => 'r', '�' => 'ª', '�' => 'º', '�' => 'æ', '�' => '¸', '�' => 'Æ', '�' => '¤', '�' => 'µ', '�' => 'ö', '�' => 's', '�' => 't', '�' => 'u', '�' => 'v', '�' => 'w', '�' => 'x', '�' => 'y', '�' => 'z', '�' => '¡', '�' => '¿', '�' => ']', '�' => '$', '�' => '@', '�' => '®', '�' => '¢', '�' => '£', '�' => '¥', '�' => '·', '�' => '©', '�' => '§', '�' => '¶', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¬', '�' => '|', '�' => '¯', '�' => '¨', '�' => '´', '�' => '×', '�' => 'ç', '�' => 'A', '�' => 'B', '�' => 'C', '�' => 'D', '�' => 'E', '�' => 'F', '�' => 'G', '�' => 'H', '�' => 'I', '�' => '', '�' => 'ô', '�' => '~', '�' => 'ò', '�' => 'ó', '�' => 'õ', '�' => 'ğ', '�' => 'J', '�' => 'K', '�' => 'L', '�' => 'M', '�' => 'N', '�' => 'O', '�' => 'P', '�' => 'Q', '�' => 'R', '�' => '¹', '�' => 'û', '�' => '\\', '�' => 'ù', '�' => 'ú', '�' => 'ÿ', '�' => 'ü', '�' => '÷', '�' => 'S', '�' => 'T', '�' => 'U', '�' => 'V', '�' => 'W', '�' => 'X', '�' => 'Y', '�' => 'Z', '�' => '²', '�' => 'Ô', '�' => '#', '�' => 'Ò', '�' => 'Ó', '�' => 'Õ', '�' => '0', '�' => '1', '�' => '2', '�' => '3', '�' => '4', '�' => '5', '�' => '6', '�' => '7', '�' => '8', '�' => '9', '�' => '³', '�' => 'Û', '�' => '"', '�' => 'Ù', '�' => 'Ú', '�' => '', ); $result =& $data; unset($data); return $result; PK2A#]�`i���>vendor/symfony/polyfill-iconv/Resources/charset/from.cp852.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Ç', '�' => 'ü', '�' => 'é', '�' => 'â', '�' => 'ä', '�' => 'ů', '�' => 'ć', '�' => 'ç', '�' => 'ł', '�' => 'ë', '�' => 'Ő', '�' => 'ő', '�' => 'î', '�' => 'Ź', '�' => 'Ä', '�' => 'Ć', '�' => 'É', '�' => 'Ĺ', '�' => 'ĺ', '�' => 'ô', '�' => 'ö', '�' => 'Ľ', '�' => 'ľ', '�' => 'Ś', '�' => 'ś', '�' => 'Ö', '�' => 'Ü', '�' => 'Ť', '�' => 'ť', '�' => 'Ł', '�' => '×', '�' => 'č', '�' => 'á', '�' => 'í', '�' => 'ó', '�' => 'ú', '�' => 'Ą', '�' => 'ą', '�' => 'Ž', '�' => 'ž', '�' => 'Ę', '�' => 'ę', '�' => '¬', '�' => 'ź', '�' => 'Č', '�' => 'ş', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'Á', '�' => 'Â', '�' => 'Ě', '�' => 'Ş', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => 'Ż', '�' => 'ż', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'Ă', '�' => 'ă', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '¤', '�' => 'đ', '�' => 'Đ', '�' => 'Ď', '�' => 'Ë', '�' => 'ď', '�' => 'Ň', '�' => 'Í', '�' => 'Î', '�' => 'ě', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => 'Ţ', '�' => 'Ů', '�' => '▀', '�' => 'Ó', '�' => 'ß', '�' => 'Ô', '�' => 'Ń', '�' => 'ń', '�' => 'ň', '�' => 'Š', '�' => 'š', '�' => 'Ŕ', '�' => 'Ú', '�' => 'ŕ', '�' => 'Ű', '�' => 'ý', '�' => 'Ý', '�' => 'ţ', '�' => '´', '�' => '', '�' => '˝', '�' => '˛', '�' => 'ˇ', '�' => '˘', '�' => '§', '�' => '÷', '�' => '¸', '�' => '°', '�' => '¨', '�' => '˙', '�' => 'ű', '�' => 'Ř', '�' => 'ř', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�A���>vendor/symfony/polyfill-iconv/Resources/charset/from.cp737.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'Α', '�' => 'Β', '�' => 'Γ', '�' => 'Δ', '�' => 'Ε', '�' => 'Ζ', '�' => 'Η', '�' => 'Θ', '�' => 'Ι', '�' => 'Κ', '�' => 'Λ', '�' => 'Μ', '�' => 'Ν', '�' => 'Ξ', '�' => 'Ο', '�' => 'Π', '�' => 'Ρ', '�' => 'Σ', '�' => 'Τ', '�' => 'Υ', '�' => 'Φ', '�' => 'Χ', '�' => 'Ψ', '�' => 'Ω', '�' => 'α', '�' => 'β', '�' => 'γ', '�' => 'δ', '�' => 'ε', '�' => 'ζ', '�' => 'η', '�' => 'θ', '�' => 'ι', '�' => 'κ', '�' => 'λ', '�' => 'μ', '�' => 'ν', '�' => 'ξ', '�' => 'ο', '�' => 'π', '�' => 'ρ', '�' => 'σ', '�' => 'ς', '�' => 'τ', '�' => 'υ', '�' => 'φ', '�' => 'χ', '�' => 'ψ', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => '╡', '�' => '╢', '�' => '╖', '�' => '╕', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => '╜', '�' => '╛', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => '╞', '�' => '╟', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '╧', '�' => '╨', '�' => '╤', '�' => '╥', '�' => '╙', '�' => '╘', '�' => '╒', '�' => '╓', '�' => '╫', '�' => '╪', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => '▌', '�' => '▐', '�' => '▀', '�' => 'ω', '�' => 'ά', '�' => 'έ', '�' => 'ή', '�' => 'ϊ', '�' => 'ί', '�' => 'ό', '�' => 'ύ', '�' => 'ϋ', '�' => 'ώ', '�' => 'Ά', '�' => 'Έ', '�' => 'Ή', '�' => 'Ί', '�' => 'Ό', '�' => 'Ύ', '�' => 'Ώ', '�' => '±', '�' => '≥', '�' => '≤', '�' => 'Ϊ', '�' => 'Ϋ', '�' => '÷', '�' => '≈', '�' => '°', '�' => '∙', '�' => '·', '�' => '√', '�' => 'ⁿ', '�' => '²', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]�b|��>vendor/symfony/polyfill-iconv/Resources/charset/from.cp855.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => 'ђ', '�' => 'Ђ', '�' => 'ѓ', '�' => 'Ѓ', '�' => 'ё', '�' => 'Ё', '�' => 'є', '�' => 'Є', '�' => 'ѕ', '�' => 'Ѕ', '�' => 'і', '�' => 'І', '�' => 'ї', '�' => 'Ї', '�' => 'ј', '�' => 'Ј', '�' => 'љ', '�' => 'Љ', '�' => 'њ', '�' => 'Њ', '�' => 'ћ', '�' => 'Ћ', '�' => 'ќ', '�' => 'Ќ', '�' => 'ў', '�' => 'Ў', '�' => 'џ', '�' => 'Џ', '�' => 'ю', '�' => 'Ю', '�' => 'ъ', '�' => 'Ъ', '�' => 'а', '�' => 'А', '�' => 'б', '�' => 'Б', '�' => 'ц', '�' => 'Ц', '�' => 'д', '�' => 'Д', '�' => 'е', '�' => 'Е', '�' => 'ф', '�' => 'Ф', '�' => 'г', '�' => 'Г', '�' => '«', '�' => '»', '�' => '░', '�' => '▒', '�' => '▓', '�' => '│', '�' => '┤', '�' => 'х', '�' => 'Х', '�' => 'и', '�' => 'И', '�' => '╣', '�' => '║', '�' => '╗', '�' => '╝', '�' => 'й', '�' => 'Й', '�' => '┐', '�' => '└', '�' => '┴', '�' => '┬', '�' => '├', '�' => '─', '�' => '┼', '�' => 'к', '�' => 'К', '�' => '╚', '�' => '╔', '�' => '╩', '�' => '╦', '�' => '╠', '�' => '═', '�' => '╬', '�' => '¤', '�' => 'л', '�' => 'Л', '�' => 'м', '�' => 'М', '�' => 'н', '�' => 'Н', '�' => 'о', '�' => 'О', '�' => 'п', '�' => '┘', '�' => '┌', '�' => '█', '�' => '▄', '�' => 'П', '�' => 'я', '�' => '▀', '�' => 'Я', '�' => 'р', '�' => 'Р', '�' => 'с', '�' => 'С', '�' => 'т', '�' => 'Т', '�' => 'у', '�' => 'У', '�' => 'ж', '�' => 'Ж', '�' => 'в', '�' => 'В', '�' => 'ь', '�' => 'Ь', '�' => '№', '�' => '', '�' => 'ы', '�' => 'Ы', '�' => 'з', '�' => 'З', '�' => 'ш', '�' => 'Ш', '�' => 'э', '�' => 'Э', '�' => 'щ', '�' => 'Щ', '�' => 'ч', '�' => 'Ч', '�' => '§', '�' => '■', '�' => ' ', ); $result =& $data; unset($data); return $result; PK2A#]^ӎ��!�!<vendor/symfony/polyfill-iconv/Resources/charset/translit.phpnu�[���<?php return array ( 'µ' => 'μ', '¼' => ' 1⁄4 ', '½' => ' 1⁄2 ', '¾' => ' 3⁄4 ', 'IJ' => 'IJ', 'ij' => 'ij', 'Ŀ' => 'L·', 'ŀ' => 'l·', 'ʼn' => 'ʼn', 'ſ' => 's', 'DŽ' => 'DŽ', 'Dž' => 'Dž', 'dž' => 'dž', 'LJ' => 'LJ', 'Lj' => 'Lj', 'lj' => 'lj', 'NJ' => 'NJ', 'Nj' => 'Nj', 'nj' => 'nj', 'DZ' => 'DZ', 'Dz' => 'Dz', 'dz' => 'dz', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϒ' => 'Υ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϲ' => 'ς', 'ϴ' => 'Θ', 'ϵ' => 'ε', 'Ϲ' => 'Σ', 'և' => 'եւ', 'ٵ' => 'اٴ', 'ٶ' => 'وٴ', 'ٷ' => 'ۇٴ', 'ٸ' => 'يٴ', 'ำ' => 'ํา', 'ຳ' => 'ໍາ', 'ໜ' => 'ຫນ', 'ໝ' => 'ຫມ', 'ཷ' => 'ྲཱྀ', 'ཹ' => 'ླཱྀ', 'ẚ' => 'aʾ', '․' => '.', '‥' => '..', '…' => '...', '″' => '′′', '‴' => '′′′', '‶' => '‵‵', '‷' => '‵‵‵', '‼' => '!!', '⁇' => '??', '⁈' => '?!', '⁉' => '!?', '⁗' => '′′′′', '₨' => 'Rs', '℀' => 'a/c', '℁' => 'a/s', 'ℂ' => 'C', '℃' => '°C', '℅' => 'c/o', '℆' => 'c/u', 'ℇ' => 'Ɛ', '℉' => '°F', 'ℊ' => 'g', 'ℋ' => 'H', 'ℌ' => 'H', 'ℍ' => 'H', 'ℎ' => 'h', 'ℏ' => 'ħ', 'ℐ' => 'I', 'ℑ' => 'I', 'ℒ' => 'L', 'ℓ' => 'l', 'ℕ' => 'N', '№' => 'No', 'ℙ' => 'P', 'ℚ' => 'Q', 'ℛ' => 'R', 'ℜ' => 'R', 'ℝ' => 'R', '℡' => 'TEL', 'ℤ' => 'Z', 'ℨ' => 'Z', 'ℬ' => 'B', 'ℭ' => 'C', 'ℯ' => 'e', 'ℰ' => 'E', 'ℱ' => 'F', 'ℳ' => 'M', 'ℴ' => 'o', 'ℵ' => 'א', 'ℶ' => 'ב', 'ℷ' => 'ג', 'ℸ' => 'ד', 'ℹ' => 'i', '℻' => 'FAX', 'ℼ' => 'π', 'ℽ' => 'γ', 'ℾ' => 'Γ', 'ℿ' => 'Π', '⅀' => '∑', 'ⅅ' => 'D', 'ⅆ' => 'd', 'ⅇ' => 'e', 'ⅈ' => 'i', 'ⅉ' => 'j', '⅐' => ' 1⁄7 ', '⅑' => ' 1⁄9 ', '⅒' => ' 1⁄10 ', '⅓' => ' 1⁄3 ', '⅔' => ' 2⁄3 ', '⅕' => ' 1⁄5 ', '⅖' => ' 2⁄5 ', '⅗' => ' 3⁄5 ', '⅘' => ' 4⁄5 ', '⅙' => ' 1⁄6 ', '⅚' => ' 5⁄6 ', '⅛' => ' 1⁄8 ', '⅜' => ' 3⁄8 ', '⅝' => ' 5⁄8 ', '⅞' => ' 7⁄8 ', '⅟' => ' 1⁄ ', 'Ⅰ' => 'I', 'Ⅱ' => 'II', 'Ⅲ' => 'III', 'Ⅳ' => 'IV', 'Ⅴ' => 'V', 'Ⅵ' => 'VI', 'Ⅶ' => 'VII', 'Ⅷ' => 'VIII', 'Ⅸ' => 'IX', 'Ⅹ' => 'X', 'Ⅺ' => 'XI', 'Ⅻ' => 'XII', 'Ⅼ' => 'L', 'Ⅽ' => 'C', 'Ⅾ' => 'D', 'Ⅿ' => 'M', 'ⅰ' => 'i', 'ⅱ' => 'ii', 'ⅲ' => 'iii', 'ⅳ' => 'iv', 'ⅴ' => 'v', 'ⅵ' => 'vi', 'ⅶ' => 'vii', 'ⅷ' => 'viii', 'ⅸ' => 'ix', 'ⅹ' => 'x', 'ⅺ' => 'xi', 'ⅻ' => 'xii', 'ⅼ' => 'l', 'ⅽ' => 'c', 'ⅾ' => 'd', 'ⅿ' => 'm', '↉' => ' 0⁄3 ', '∬' => '∫∫', '∭' => '∫∫∫', '∯' => '∮∮', '∰' => '∮∮∮', '①' => '(1)', '②' => '(2)', '③' => '(3)', '④' => '(4)', '⑤' => '(5)', '⑥' => '(6)', '⑦' => '(7)', '⑧' => '(8)', '⑨' => '(9)', '⑩' => '(10)', '⑪' => '(11)', '⑫' => '(12)', '⑬' => '(13)', '⑭' => '(14)', '⑮' => '(15)', '⑯' => '(16)', '⑰' => '(17)', '⑱' => '(18)', '⑲' => '(19)', '⑳' => '(20)', '⑴' => '(1)', '⑵' => '(2)', '⑶' => '(3)', '⑷' => '(4)', '⑸' => '(5)', '⑹' => '(6)', '⑺' => '(7)', '⑻' => '(8)', '⑼' => '(9)', '⑽' => '(10)', '⑾' => '(11)', '⑿' => '(12)', '⒀' => '(13)', '⒁' => '(14)', '⒂' => '(15)', '⒃' => '(16)', '⒄' => '(17)', '⒅' => '(18)', '⒆' => '(19)', '⒇' => '(20)', '⒈' => '1.', '⒉' => '2.', '⒊' => '3.', '⒋' => '4.', '⒌' => '5.', '⒍' => '6.', '⒎' => '7.', '⒏' => '8.', '⒐' => '9.', '⒑' => '10.', '⒒' => '11.', '⒓' => '12.', '⒔' => '13.', '⒕' => '14.', '⒖' => '15.', '⒗' => '16.', '⒘' => '17.', '⒙' => '18.', '⒚' => '19.', '⒛' => '20.', '⒜' => '(a)', '⒝' => '(b)', '⒞' => '(c)', '⒟' => '(d)', '⒠' => '(e)', '⒡' => '(f)', '⒢' => '(g)', '⒣' => '(h)', '⒤' => '(i)', '⒥' => '(j)', '⒦' => '(k)', '⒧' => '(l)', '⒨' => '(m)', '⒩' => '(n)', '⒪' => '(o)', '⒫' => '(p)', '⒬' => '(q)', '⒭' => '(r)', '⒮' => '(s)', '⒯' => '(t)', '⒰' => '(u)', '⒱' => '(v)', '⒲' => '(w)', '⒳' => '(x)', '⒴' => '(y)', '⒵' => '(z)', 'Ⓐ' => '(A)', 'Ⓑ' => '(B)', 'Ⓒ' => '(C)', 'Ⓓ' => '(D)', 'Ⓔ' => '(E)', 'Ⓕ' => '(F)', 'Ⓖ' => '(G)', 'Ⓗ' => '(H)', 'Ⓘ' => '(I)', 'Ⓙ' => '(J)', 'Ⓚ' => '(K)', 'Ⓛ' => '(L)', 'Ⓜ' => '(M)', 'Ⓝ' => '(N)', 'Ⓞ' => '(O)', 'Ⓟ' => '(P)', 'Ⓠ' => '(Q)', 'Ⓡ' => '(R)', 'Ⓢ' => '(S)', 'Ⓣ' => '(T)', 'Ⓤ' => '(U)', 'Ⓥ' => '(V)', 'Ⓦ' => '(W)', 'Ⓧ' => '(X)', 'Ⓨ' => '(Y)', 'Ⓩ' => '(Z)', 'ⓐ' => '(a)', 'ⓑ' => '(b)', 'ⓒ' => '(c)', 'ⓓ' => '(d)', 'ⓔ' => '(e)', 'ⓕ' => '(f)', 'ⓖ' => '(g)', 'ⓗ' => '(h)', 'ⓘ' => '(i)', 'ⓙ' => '(j)', 'ⓚ' => '(k)', 'ⓛ' => '(l)', 'ⓜ' => '(m)', 'ⓝ' => '(n)', 'ⓞ' => '(o)', 'ⓟ' => '(p)', 'ⓠ' => '(q)', 'ⓡ' => '(r)', 'ⓢ' => '(s)', 'ⓣ' => '(t)', 'ⓤ' => '(u)', 'ⓥ' => '(v)', 'ⓦ' => '(w)', 'ⓧ' => '(x)', 'ⓨ' => '(y)', 'ⓩ' => '(z)', '⓪' => '(0)', '⨌' => '∫∫∫∫', '⩴' => '::=', '⩵' => '==', '⩶' => '===', '⺟' => '母', '⻳' => '龟', '⼀' => '一', '⼁' => '丨', '⼂' => '丶', '⼃' => '丿', '⼄' => '乙', '⼅' => '亅', '⼆' => '二', '⼇' => '亠', '⼈' => '人', '⼉' => '儿', '⼊' => '入', '⼋' => '八', '⼌' => '冂', '⼍' => '冖', '⼎' => '冫', '⼏' => '几', '⼐' => '凵', '⼑' => '刀', '⼒' => '力', '⼓' => '勹', '⼔' => '匕', '⼕' => '匚', '⼖' => '匸', '⼗' => '十', '⼘' => '卜', '⼙' => '卩', '⼚' => '厂', '⼛' => '厶', '⼜' => '又', '⼝' => '口', '⼞' => '囗', '⼟' => '土', '⼠' => '士', '⼡' => '夂', '⼢' => '夊', '⼣' => '夕', '⼤' => '大', '⼥' => '女', '⼦' => '子', '⼧' => '宀', '⼨' => '寸', '⼩' => '小', '⼪' => '尢', '⼫' => '尸', '⼬' => '屮', '⼭' => '山', '⼮' => '巛', '⼯' => '工', '⼰' => '己', '⼱' => '巾', '⼲' => '干', '⼳' => '幺', '⼴' => '广', '⼵' => '廴', '⼶' => '廾', '⼷' => '弋', '⼸' => '弓', '⼹' => '彐', '⼺' => '彡', '⼻' => '彳', '⼼' => '心', '⼽' => '戈', '⼾' => '戶', '⼿' => '手', '⽀' => '支', '⽁' => '攴', '⽂' => '文', '⽃' => '斗', '⽄' => '斤', '⽅' => '方', '⽆' => '无', '⽇' => '日', '⽈' => '曰', '⽉' => '月', '⽊' => '木', '⽋' => '欠', '⽌' => '止', '⽍' => '歹', '⽎' => '殳', '⽏' => '毋', '⽐' => '比', '⽑' => '毛', '⽒' => '氏', '⽓' => '气', '⽔' => '水', '⽕' => '火', '⽖' => '爪', '⽗' => '父', '⽘' => '爻', '⽙' => '爿', '⽚' => '片', '⽛' => '牙', '⽜' => '牛', '⽝' => '犬', '⽞' => '玄', '⽟' => '玉', '⽠' => '瓜', '⽡' => '瓦', '⽢' => '甘', '⽣' => '生', '⽤' => '用', '⽥' => '田', '⽦' => '疋', '⽧' => '疒', '⽨' => '癶', '⽩' => '白', '⽪' => '皮', '⽫' => '皿', '⽬' => '目', '⽭' => '矛', '⽮' => '矢', '⽯' => '石', '⽰' => '示', '⽱' => '禸', '⽲' => '禾', '⽳' => '穴', '⽴' => '立', '⽵' => '竹', '⽶' => '米', '⽷' => '糸', '⽸' => '缶', '⽹' => '网', '⽺' => '羊', '⽻' => '羽', '⽼' => '老', '⽽' => '而', '⽾' => '耒', '⽿' => '耳', '⾀' => '聿', '⾁' => '肉', '⾂' => '臣', '⾃' => '自', '⾄' => '至', '⾅' => '臼', '⾆' => '舌', '⾇' => '舛', '⾈' => '舟', '⾉' => '艮', '⾊' => '色', '⾋' => '艸', '⾌' => '虍', '⾍' => '虫', '⾎' => '血', '⾏' => '行', '⾐' => '衣', '⾑' => '襾', '⾒' => '見', '⾓' => '角', '⾔' => '言', '⾕' => '谷', '⾖' => '豆', '⾗' => '豕', '⾘' => '豸', '⾙' => '貝', '⾚' => '赤', '⾛' => '走', '⾜' => '足', '⾝' => '身', '⾞' => '車', '⾟' => '辛', '⾠' => '辰', '⾡' => '辵', '⾢' => '邑', '⾣' => '酉', '⾤' => '釆', '⾥' => '里', '⾦' => '金', '⾧' => '長', '⾨' => '門', '⾩' => '阜', '⾪' => '隶', '⾫' => '隹', '⾬' => '雨', '⾭' => '靑', '⾮' => '非', '⾯' => '面', '⾰' => '革', '⾱' => '韋', '⾲' => '韭', '⾳' => '音', '⾴' => '頁', '⾵' => '風', '⾶' => '飛', '⾷' => '食', '⾸' => '首', '⾹' => '香', '⾺' => '馬', '⾻' => '骨', '⾼' => '高', '⾽' => '髟', '⾾' => '鬥', '⾿' => '鬯', '⿀' => '鬲', '⿁' => '鬼', '⿂' => '魚', '⿃' => '鳥', '⿄' => '鹵', '⿅' => '鹿', '⿆' => '麥', '⿇' => '麻', '⿈' => '黃', '⿉' => '黍', '⿊' => '黑', '⿋' => '黹', '⿌' => '黽', '⿍' => '鼎', '⿎' => '鼓', '⿏' => '鼠', '⿐' => '鼻', '⿑' => '齊', '⿒' => '齒', '⿓' => '龍', '⿔' => '龜', '⿕' => '龠', ' ' => ' ', '〶' => '〒', '〸' => '十', '〹' => '卄', '〺' => '卅', 'ㄱ' => 'ᄀ', 'ㄲ' => 'ᄁ', 'ㄳ' => 'ᆪ', 'ㄴ' => 'ᄂ', 'ㄵ' => 'ᆬ', 'ㄶ' => 'ᆭ', 'ㄷ' => 'ᄃ', 'ㄸ' => 'ᄄ', 'ㄹ' => 'ᄅ', 'ㄺ' => 'ᆰ', 'ㄻ' => 'ᆱ', 'ㄼ' => 'ᆲ', 'ㄽ' => 'ᆳ', 'ㄾ' => 'ᆴ', 'ㄿ' => 'ᆵ', 'ㅀ' => 'ᄚ', 'ㅁ' => 'ᄆ', 'ㅂ' => 'ᄇ', 'ㅃ' => 'ᄈ', 'ㅄ' => 'ᄡ', 'ㅅ' => 'ᄉ', 'ㅆ' => 'ᄊ', 'ㅇ' => 'ᄋ', 'ㅈ' => 'ᄌ', 'ㅉ' => 'ᄍ', 'ㅊ' => 'ᄎ', 'ㅋ' => 'ᄏ', 'ㅌ' => 'ᄐ', 'ㅍ' => 'ᄑ', 'ㅎ' => 'ᄒ', 'ㅏ' => 'ᅡ', 'ㅐ' => 'ᅢ', 'ㅑ' => 'ᅣ', 'ㅒ' => 'ᅤ', 'ㅓ' => 'ᅥ', 'ㅔ' => 'ᅦ', 'ㅕ' => 'ᅧ', 'ㅖ' => 'ᅨ', 'ㅗ' => 'ᅩ', 'ㅘ' => 'ᅪ', 'ㅙ' => 'ᅫ', 'ㅚ' => 'ᅬ', 'ㅛ' => 'ᅭ', 'ㅜ' => 'ᅮ', 'ㅝ' => 'ᅯ', 'ㅞ' => 'ᅰ', 'ㅟ' => 'ᅱ', 'ㅠ' => 'ᅲ', 'ㅡ' => 'ᅳ', 'ㅢ' => 'ᅴ', 'ㅣ' => 'ᅵ', 'ㅤ' => 'ᅠ', 'ㅥ' => 'ᄔ', 'ㅦ' => 'ᄕ', 'ㅧ' => 'ᇇ', 'ㅨ' => 'ᇈ', 'ㅩ' => 'ᇌ', 'ㅪ' => 'ᇎ', 'ㅫ' => 'ᇓ', 'ㅬ' => 'ᇗ', 'ㅭ' => 'ᇙ', 'ㅮ' => 'ᄜ', 'ㅯ' => 'ᇝ', 'ㅰ' => 'ᇟ', 'ㅱ' => 'ᄝ', 'ㅲ' => 'ᄞ', 'ㅳ' => 'ᄠ', 'ㅴ' => 'ᄢ', 'ㅵ' => 'ᄣ', 'ㅶ' => 'ᄧ', 'ㅷ' => 'ᄩ', 'ㅸ' => 'ᄫ', 'ㅹ' => 'ᄬ', 'ㅺ' => 'ᄭ', 'ㅻ' => 'ᄮ', 'ㅼ' => 'ᄯ', 'ㅽ' => 'ᄲ', 'ㅾ' => 'ᄶ', 'ㅿ' => 'ᅀ', 'ㆀ' => 'ᅇ', 'ㆁ' => 'ᅌ', 'ㆂ' => 'ᇱ', 'ㆃ' => 'ᇲ', 'ㆄ' => 'ᅗ', 'ㆅ' => 'ᅘ', 'ㆆ' => 'ᅙ', 'ㆇ' => 'ᆄ', 'ㆈ' => 'ᆅ', 'ㆉ' => 'ᆈ', 'ㆊ' => 'ᆑ', 'ㆋ' => 'ᆒ', 'ㆌ' => 'ᆔ', 'ㆍ' => 'ᆞ', 'ㆎ' => 'ᆡ', '㈀' => '(ᄀ)', '㈁' => '(ᄂ)', '㈂' => '(ᄃ)', '㈃' => '(ᄅ)', '㈄' => '(ᄆ)', '㈅' => '(ᄇ)', '㈆' => '(ᄉ)', '㈇' => '(ᄋ)', '㈈' => '(ᄌ)', '㈉' => '(ᄎ)', '㈊' => '(ᄏ)', '㈋' => '(ᄐ)', '㈌' => '(ᄑ)', '㈍' => '(ᄒ)', '㈎' => '(가)', '㈏' => '(나)', '㈐' => '(다)', '㈑' => '(라)', '㈒' => '(마)', '㈓' => '(바)', '㈔' => '(사)', '㈕' => '(아)', '㈖' => '(자)', '㈗' => '(차)', '㈘' => '(카)', '㈙' => '(타)', '㈚' => '(파)', '㈛' => '(하)', '㈜' => '(주)', '㈝' => '(오전)', '㈞' => '(오후)', '㈠' => '(一)', '㈡' => '(二)', '㈢' => '(三)', '㈣' => '(四)', '㈤' => '(五)', '㈥' => '(六)', '㈦' => '(七)', '㈧' => '(八)', '㈨' => '(九)', '㈩' => '(十)', '㈪' => '(月)', '㈫' => '(火)', '㈬' => '(水)', '㈭' => '(木)', '㈮' => '(金)', '㈯' => '(土)', '㈰' => '(日)', '㈱' => '(株)', '㈲' => '(有)', '㈳' => '(社)', '㈴' => '(名)', '㈵' => '(特)', '㈶' => '(財)', '㈷' => '(祝)', '㈸' => '(労)', '㈹' => '(代)', '㈺' => '(呼)', '㈻' => '(学)', '㈼' => '(監)', '㈽' => '(企)', '㈾' => '(資)', '㈿' => '(協)', '㉀' => '(祭)', '㉁' => '(休)', '㉂' => '(自)', '㉃' => '(至)', '㉄' => '(問)', '㉅' => '(幼)', '㉆' => '(文)', '㉇' => '(箏)', '㉐' => 'PTE', '㉑' => '(21)', '㉒' => '(22)', '㉓' => '(23)', '㉔' => '(24)', '㉕' => '(25)', '㉖' => '(26)', '㉗' => '(27)', '㉘' => '(28)', '㉙' => '(29)', '㉚' => '(30)', '㉛' => '(31)', '㉜' => '(32)', '㉝' => '(33)', '㉞' => '(34)', '㉟' => '(35)', '㉠' => '(ᄀ)', '㉡' => '(ᄂ)', '㉢' => '(ᄃ)', '㉣' => '(ᄅ)', '㉤' => '(ᄆ)', '㉥' => '(ᄇ)', '㉦' => '(ᄉ)', '㉧' => '(ᄋ)', '㉨' => '(ᄌ)', '㉩' => '(ᄎ)', '㉪' => '(ᄏ)', '㉫' => '(ᄐ)', '㉬' => '(ᄑ)', '㉭' => '(ᄒ)', '㉮' => '(가)', '㉯' => '(나)', '㉰' => '(다)', '㉱' => '(라)', '㉲' => '(마)', '㉳' => '(바)', '㉴' => '(사)', '㉵' => '(아)', '㉶' => '(자)', '㉷' => '(차)', '㉸' => '(카)', '㉹' => '(타)', '㉺' => '(파)', '㉻' => '(하)', '㉼' => '(참고)', '㉽' => '(주의)', '㉾' => '(우)', '㊀' => '(一)', '㊁' => '(二)', '㊂' => '(三)', '㊃' => '(四)', '㊄' => '(五)', '㊅' => '(六)', '㊆' => '(七)', '㊇' => '(八)', '㊈' => '(九)', '㊉' => '(十)', '㊊' => '(月)', '㊋' => '(火)', '㊌' => '(水)', '㊍' => '(木)', '㊎' => '(金)', '㊏' => '(土)', '㊐' => '(日)', '㊑' => '(株)', '㊒' => '(有)', '㊓' => '(社)', '㊔' => '(名)', '㊕' => '(特)', '㊖' => '(財)', '㊗' => '(祝)', '㊘' => '(労)', '㊙' => '(秘)', '㊚' => '(男)', '㊛' => '(女)', '㊜' => '(適)', '㊝' => '(優)', '㊞' => '(印)', '㊟' => '(注)', '㊠' => '(項)', '㊡' => '(休)', '㊢' => '(写)', '㊣' => '(正)', '㊤' => '(上)', '㊥' => '(中)', '㊦' => '(下)', '㊧' => '(左)', '㊨' => '(右)', '㊩' => '(医)', '㊪' => '(宗)', '㊫' => '(学)', '㊬' => '(監)', '㊭' => '(企)', '㊮' => '(資)', '㊯' => '(協)', '㊰' => '(夜)', '㊱' => '(36)', '㊲' => '(37)', '㊳' => '(38)', '㊴' => '(39)', '㊵' => '(40)', '㊶' => '(41)', '㊷' => '(42)', '㊸' => '(43)', '㊹' => '(44)', '㊺' => '(45)', '㊻' => '(46)', '㊼' => '(47)', '㊽' => '(48)', '㊾' => '(49)', '㊿' => '(50)', '㋀' => '1月', '㋁' => '2月', '㋂' => '3月', '㋃' => '4月', '㋄' => '5月', '㋅' => '6月', '㋆' => '7月', '㋇' => '8月', '㋈' => '9月', '㋉' => '10月', '㋊' => '11月', '㋋' => '12月', '㋌' => 'Hg', '㋍' => 'erg', '㋎' => 'eV', '㋏' => 'LTD', '㋐' => '(ア)', '㋑' => '(イ)', '㋒' => '(ウ)', '㋓' => '(エ)', '㋔' => '(オ)', '㋕' => '(カ)', '㋖' => '(キ)', '㋗' => '(ク)', '㋘' => '(ケ)', '㋙' => '(コ)', '㋚' => '(サ)', '㋛' => '(シ)', '㋜' => '(ス)', '㋝' => '(セ)', '㋞' => '(ソ)', '㋟' => '(タ)', '㋠' => '(チ)', '㋡' => '(ツ)', '㋢' => '(テ)', '㋣' => '(ト)', '㋤' => '(ナ)', '㋥' => '(ニ)', '㋦' => '(ヌ)', '㋧' => '(ネ)', '㋨' => '(ノ)', '㋩' => '(ハ)', '㋪' => '(ヒ)', '㋫' => '(フ)', '㋬' => '(ヘ)', '㋭' => '(ホ)', '㋮' => '(マ)', '㋯' => '(ミ)', '㋰' => '(ム)', '㋱' => '(メ)', '㋲' => '(モ)', '㋳' => '(ヤ)', '㋴' => '(ユ)', '㋵' => '(ヨ)', '㋶' => '(ラ)', '㋷' => '(リ)', '㋸' => '(ル)', '㋹' => '(レ)', '㋺' => '(ロ)', '㋻' => '(ワ)', '㋼' => '(ヰ)', '㋽' => '(ヱ)', '㋾' => '(ヲ)', '㋿' => '令和', '㌀' => 'アパート', '㌁' => 'アルファ', '㌂' => 'アンペア', '㌃' => 'アール', '㌄' => 'イニング', '㌅' => 'インチ', '㌆' => 'ウォン', '㌇' => 'エスクード', '㌈' => 'エーカー', '㌉' => 'オンス', '㌊' => 'オーム', '㌋' => 'カイリ', '㌌' => 'カラット', '㌍' => 'カロリー', '㌎' => 'ガロン', '㌏' => 'ガンマ', '㌐' => 'ギガ', '㌑' => 'ギニー', '㌒' => 'キュリー', '㌓' => 'ギルダー', '㌔' => 'キロ', '㌕' => 'キログラム', '㌖' => 'キロメートル', '㌗' => 'キロワット', '㌘' => 'グラム', '㌙' => 'グラムトン', '㌚' => 'クルゼイロ', '㌛' => 'クローネ', '㌜' => 'ケース', '㌝' => 'コルナ', '㌞' => 'コーポ', '㌟' => 'サイクル', '㌠' => 'サンチーム', '㌡' => 'シリング', '㌢' => 'センチ', '㌣' => 'セント', '㌤' => 'ダース', '㌥' => 'デシ', '㌦' => 'ドル', '㌧' => 'トン', '㌨' => 'ナノ', '㌩' => 'ノット', '㌪' => 'ハイツ', '㌫' => 'パーセント', '㌬' => 'パーツ', '㌭' => 'バーレル', '㌮' => 'ピアストル', '㌯' => 'ピクル', '㌰' => 'ピコ', '㌱' => 'ビル', '㌲' => 'ファラッド', '㌳' => 'フィート', '㌴' => 'ブッシェル', '㌵' => 'フラン', '㌶' => 'ヘクタール', '㌷' => 'ペソ', '㌸' => 'ペニヒ', '㌹' => 'ヘルツ', '㌺' => 'ペンス', '㌻' => 'ページ', '㌼' => 'ベータ', '㌽' => 'ポイント', '㌾' => 'ボルト', '㌿' => 'ホン', '㍀' => 'ポンド', '㍁' => 'ホール', '㍂' => 'ホーン', '㍃' => 'マイクロ', '㍄' => 'マイル', '㍅' => 'マッハ', '㍆' => 'マルク', '㍇' => 'マンション', '㍈' => 'ミクロン', '㍉' => 'ミリ', '㍊' => 'ミリバール', '㍋' => 'メガ', '㍌' => 'メガトン', '㍍' => 'メートル', '㍎' => 'ヤード', '㍏' => 'ヤール', '㍐' => 'ユアン', '㍑' => 'リットル', '㍒' => 'リラ', '㍓' => 'ルピー', '㍔' => 'ルーブル', '㍕' => 'レム', '㍖' => 'レントゲン', '㍗' => 'ワット', '㍘' => '0点', '㍙' => '1点', '㍚' => '2点', '㍛' => '3点', '㍜' => '4点', '㍝' => '5点', '㍞' => '6点', '㍟' => '7点', '㍠' => '8点', '㍡' => '9点', '㍢' => '10点', '㍣' => '11点', '㍤' => '12点', '㍥' => '13点', '㍦' => '14点', '㍧' => '15点', '㍨' => '16点', '㍩' => '17点', '㍪' => '18点', '㍫' => '19点', '㍬' => '20点', '㍭' => '21点', '㍮' => '22点', '㍯' => '23点', '㍰' => '24点', '㍱' => 'hPa', '㍲' => 'da', '㍳' => 'AU', '㍴' => 'bar', '㍵' => 'oV', '㍶' => 'pc', '㍷' => 'dm', '㍸' => 'dm²', '㍹' => 'dm³', '㍺' => 'IU', '㍻' => '平成', '㍼' => '昭和', '㍽' => '大正', '㍾' => '明治', '㍿' => '株式会社', '㎀' => 'pA', '㎁' => 'nA', '㎂' => 'μA', '㎃' => 'mA', '㎄' => 'kA', '㎅' => 'KB', '㎆' => 'MB', '㎇' => 'GB', '㎈' => 'cal', '㎉' => 'kcal', '㎊' => 'pF', '㎋' => 'nF', '㎌' => 'μF', '㎍' => 'μg', '㎎' => 'mg', '㎏' => 'kg', '㎐' => 'Hz', '㎑' => 'kHz', '㎒' => 'MHz', '㎓' => 'GHz', '㎔' => 'THz', '㎕' => 'μℓ', '㎖' => 'mℓ', '㎗' => 'dℓ', '㎘' => 'kℓ', '㎙' => 'fm', '㎚' => 'nm', '㎛' => 'μm', '㎜' => 'mm', '㎝' => 'cm', '㎞' => 'km', '㎟' => 'mm²', '㎠' => 'cm²', '㎡' => 'm²', '㎢' => 'km²', '㎣' => 'mm³', '㎤' => 'cm³', '㎥' => 'm³', '㎦' => 'km³', '㎧' => 'm∕s', '㎨' => 'm∕s²', '㎩' => 'Pa', '㎪' => 'kPa', '㎫' => 'MPa', '㎬' => 'GPa', '㎭' => 'rad', '㎮' => 'rad∕s', '㎯' => 'rad∕s²', '㎰' => 'ps', '㎱' => 'ns', '㎲' => 'μs', '㎳' => 'ms', '㎴' => 'pV', '㎵' => 'nV', '㎶' => 'μV', '㎷' => 'mV', '㎸' => 'kV', '㎹' => 'MV', '㎺' => 'pW', '㎻' => 'nW', '㎼' => 'μW', '㎽' => 'mW', '㎾' => 'kW', '㎿' => 'MW', '㏀' => 'kΩ', '㏁' => 'MΩ', '㏂' => 'a.m.', '㏃' => 'Bq', '㏄' => 'cc', '㏅' => 'cd', '㏆' => 'C∕kg', '㏇' => 'Co.', '㏈' => 'dB', '㏉' => 'Gy', '㏊' => 'ha', '㏋' => 'HP', '㏌' => 'in', '㏍' => 'KK', '㏎' => 'KM', '㏏' => 'kt', '㏐' => 'lm', '㏑' => 'ln', '㏒' => 'log', '㏓' => 'lx', '㏔' => 'mb', '㏕' => 'mil', '㏖' => 'mol', '㏗' => 'PH', '㏘' => 'p.m.', '㏙' => 'PPM', '㏚' => 'PR', '㏛' => 'sr', '㏜' => 'Sv', '㏝' => 'Wb', '㏞' => 'V∕m', '㏟' => 'A∕m', '㏠' => '1日', '㏡' => '2日', '㏢' => '3日', '㏣' => '4日', '㏤' => '5日', '㏥' => '6日', '㏦' => '7日', '㏧' => '8日', '㏨' => '9日', '㏩' => '10日', '㏪' => '11日', '㏫' => '12日', '㏬' => '13日', '㏭' => '14日', '㏮' => '15日', '㏯' => '16日', '㏰' => '17日', '㏱' => '18日', '㏲' => '19日', '㏳' => '20日', '㏴' => '21日', '㏵' => '22日', '㏶' => '23日', '㏷' => '24日', '㏸' => '25日', '㏹' => '26日', '㏺' => '27日', '㏻' => '28日', '㏼' => '29日', '㏽' => '30日', '㏾' => '31日', '㏿' => 'gal', '豈' => '豈', '更' => '更', '車' => '車', '賈' => '賈', '滑' => '滑', '串' => '串', '句' => '句', '龜' => '龜', '龜' => '龜', '契' => '契', '金' => '金', '喇' => '喇', '奈' => '奈', '懶' => '懶', '癩' => '癩', '羅' => '羅', '蘿' => '蘿', '螺' => '螺', '裸' => '裸', '邏' => '邏', '樂' => '樂', '洛' => '洛', '烙' => '烙', '珞' => '珞', '落' => '落', '酪' => '酪', '駱' => '駱', '亂' => '亂', '卵' => '卵', '欄' => '欄', '爛' => '爛', '蘭' => '蘭', '鸞' => '鸞', '嵐' => '嵐', '濫' => '濫', '藍' => '藍', '襤' => '襤', '拉' => '拉', '臘' => '臘', '蠟' => '蠟', '廊' => '廊', '朗' => '朗', '浪' => '浪', '狼' => '狼', '郎' => '郎', '來' => '來', '冷' => '冷', '勞' => '勞', '擄' => '擄', '櫓' => '櫓', '爐' => '爐', '盧' => '盧', '老' => '老', '蘆' => '蘆', '虜' => '虜', '路' => '路', '露' => '露', '魯' => '魯', '鷺' => '鷺', '碌' => '碌', '祿' => '祿', '綠' => '綠', '菉' => '菉', '錄' => '錄', '鹿' => '鹿', '論' => '論', '壟' => '壟', '弄' => '弄', '籠' => '籠', '聾' => '聾', '牢' => '牢', '磊' => '磊', '賂' => '賂', '雷' => '雷', '壘' => '壘', '屢' => '屢', '樓' => '樓', '淚' => '淚', '漏' => '漏', '累' => '累', '縷' => '縷', '陋' => '陋', '勒' => '勒', '肋' => '肋', '凜' => '凜', '凌' => '凌', '稜' => '稜', '綾' => '綾', '菱' => '菱', '陵' => '陵', '讀' => '讀', '拏' => '拏', '樂' => '樂', '諾' => '諾', '丹' => '丹', '寧' => '寧', '怒' => '怒', '率' => '率', '異' => '異', '北' => '北', '磻' => '磻', '便' => '便', '復' => '復', '不' => '不', '泌' => '泌', '數' => '數', '索' => '索', '參' => '參', '塞' => '塞', '省' => '省', '葉' => '葉', '說' => '說', '殺' => '殺', '辰' => '辰', '沈' => '沈', '拾' => '拾', '若' => '若', '掠' => '掠', '略' => '略', '亮' => '亮', '兩' => '兩', '凉' => '凉', '梁' => '梁', '糧' => '糧', '良' => '良', '諒' => '諒', '量' => '量', '勵' => '勵', '呂' => '呂', '女' => '女', '廬' => '廬', '旅' => '旅', '濾' => '濾', '礪' => '礪', '閭' => '閭', '驪' => '驪', '麗' => '麗', '黎' => '黎', '力' => '力', '曆' => '曆', '歷' => '歷', '轢' => '轢', '年' => '年', '憐' => '憐', '戀' => '戀', '撚' => '撚', '漣' => '漣', '煉' => '煉', '璉' => '璉', '秊' => '秊', '練' => '練', '聯' => '聯', '輦' => '輦', '蓮' => '蓮', '連' => '連', '鍊' => '鍊', '列' => '列', '劣' => '劣', '咽' => '咽', '烈' => '烈', '裂' => '裂', '說' => '說', '廉' => '廉', '念' => '念', '捻' => '捻', '殮' => '殮', '簾' => '簾', '獵' => '獵', '令' => '令', '囹' => '囹', '寧' => '寧', '嶺' => '嶺', '怜' => '怜', '玲' => '玲', '瑩' => '瑩', '羚' => '羚', '聆' => '聆', '鈴' => '鈴', '零' => '零', '靈' => '靈', '領' => '領', '例' => '例', '禮' => '禮', '醴' => '醴', '隸' => '隸', '惡' => '惡', '了' => '了', '僚' => '僚', '寮' => '寮', '尿' => '尿', '料' => '料', '樂' => '樂', '燎' => '燎', '療' => '療', '蓼' => '蓼', '遼' => '遼', '龍' => '龍', '暈' => '暈', '阮' => '阮', '劉' => '劉', '杻' => '杻', '柳' => '柳', '流' => '流', '溜' => '溜', '琉' => '琉', '留' => '留', '硫' => '硫', '紐' => '紐', '類' => '類', '六' => '六', '戮' => '戮', '陸' => '陸', '倫' => '倫', '崙' => '崙', '淪' => '淪', '輪' => '輪', '律' => '律', '慄' => '慄', '栗' => '栗', '率' => '率', '隆' => '隆', '利' => '利', '吏' => '吏', '履' => '履', '易' => '易', '李' => '李', '梨' => '梨', '泥' => '泥', '理' => '理', '痢' => '痢', '罹' => '罹', '裏' => '裏', '裡' => '裡', '里' => '里', '離' => '離', '匿' => '匿', '溺' => '溺', '吝' => '吝', '燐' => '燐', '璘' => '璘', '藺' => '藺', '隣' => '隣', '鱗' => '鱗', '麟' => '麟', '林' => '林', '淋' => '淋', '臨' => '臨', '立' => '立', '笠' => '笠', '粒' => '粒', '狀' => '狀', '炙' => '炙', '識' => '識', '什' => '什', '茶' => '茶', '刺' => '刺', '切' => '切', '度' => '度', '拓' => '拓', '糖' => '糖', '宅' => '宅', '洞' => '洞', '暴' => '暴', '輻' => '輻', '行' => '行', '降' => '降', '見' => '見', '廓' => '廓', '兀' => '兀', '嗀' => '嗀', '﨎' => '' . "\0" . '', '﨏' => '' . "\0" . '', '塚' => '塚', '﨑' => '' . "\0" . '', '晴' => '晴', '﨓' => '' . "\0" . '', '﨔' => '' . "\0" . '', '凞' => '凞', '猪' => '猪', '益' => '益', '礼' => '礼', '神' => '神', '祥' => '祥', '福' => '福', '靖' => '靖', '精' => '精', '羽' => '羽', '﨟' => '' . "\0" . '', '蘒' => '蘒', '﨡' => '' . "\0" . '', '諸' => '諸', '﨣' => '' . "\0" . '', '﨤' => '' . "\0" . '', '逸' => '逸', '都' => '都', '﨧' => '' . "\0" . '', '﨨' => '' . "\0" . '', '﨩' => '' . "\0" . '', '飯' => '飯', '飼' => '飼', '館' => '館', '鶴' => '鶴', '郞' => '郞', '隷' => '隷', '侮' => '侮', '僧' => '僧', '免' => '免', '勉' => '勉', '勤' => '勤', '卑' => '卑', '喝' => '喝', '嘆' => '嘆', '器' => '器', '塀' => '塀', '墨' => '墨', '層' => '層', '屮' => '屮', '悔' => '悔', '慨' => '慨', '憎' => '憎', '懲' => '懲', '敏' => '敏', '既' => '既', '暑' => '暑', '梅' => '梅', '海' => '海', '渚' => '渚', '漢' => '漢', '煮' => '煮', '爫' => '爫', '琢' => '琢', '碑' => '碑', '社' => '社', '祉' => '祉', '祈' => '祈', '祐' => '祐', '祖' => '祖', '祝' => '祝', '禍' => '禍', '禎' => '禎', '穀' => '穀', '突' => '突', '節' => '節', '練' => '練', '縉' => '縉', '繁' => '繁', '署' => '署', '者' => '者', '臭' => '臭', '艹' => '艹', '艹' => '艹', '著' => '著', '褐' => '褐', '視' => '視', '謁' => '謁', '謹' => '謹', '賓' => '賓', '贈' => '贈', '辶' => '辶', '逸' => '逸', '難' => '難', '響' => '響', '頻' => '頻', '恵' => '恵', '𤋮' => '𤋮', '舘' => '舘', '並' => '並', '况' => '况', '全' => '全', '侀' => '侀', '充' => '充', '冀' => '冀', '勇' => '勇', '勺' => '勺', '喝' => '喝', '啕' => '啕', '喙' => '喙', '嗢' => '嗢', '塚' => '塚', '墳' => '墳', '奄' => '奄', '奔' => '奔', '婢' => '婢', '嬨' => '嬨', '廒' => '廒', '廙' => '廙', '彩' => '彩', '徭' => '徭', '惘' => '惘', '慎' => '慎', '愈' => '愈', '憎' => '憎', '慠' => '慠', '懲' => '懲', '戴' => '戴', '揄' => '揄', '搜' => '搜', '摒' => '摒', '敖' => '敖', '晴' => '晴', '朗' => '朗', '望' => '望', '杖' => '杖', '歹' => '歹', '殺' => '殺', '流' => '流', '滛' => '滛', '滋' => '滋', '漢' => '漢', '瀞' => '瀞', '煮' => '煮', '瞧' => '瞧', '爵' => '爵', '犯' => '犯', '猪' => '猪', '瑱' => '瑱', '甆' => '甆', '画' => '画', '瘝' => '瘝', '瘟' => '瘟', '益' => '益', '盛' => '盛', '直' => '直', '睊' => '睊', '着' => '着', '磌' => '磌', '窱' => '窱', '節' => '節', '类' => '类', '絛' => '絛', '練' => '練', '缾' => '缾', '者' => '者', '荒' => '荒', '華' => '華', '蝹' => '蝹', '襁' => '襁', '覆' => '覆', '視' => '視', '調' => '調', '諸' => '諸', '請' => '請', '謁' => '謁', '諾' => '諾', '諭' => '諭', '謹' => '謹', '變' => '變', '贈' => '贈', '輸' => '輸', '遲' => '遲', '醙' => '醙', '鉶' => '鉶', '陼' => '陼', '難' => '難', '靖' => '靖', '韛' => '韛', '響' => '響', '頋' => '頋', '頻' => '頻', '鬒' => '鬒', '龜' => '龜', '𢡊' => '𢡊', '𢡄' => '𢡄', '𣏕' => '𣏕', '㮝' => '㮝', '䀘' => '䀘', '䀹' => '䀹', '𥉉' => '𥉉', '𥳐' => '𥳐', '𧻓' => '𧻓', '齃' => '齃', '龎' => '龎', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'ſt', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ', 'ﬠ' => 'ע', 'ﬡ' => 'א', 'ﬢ' => 'ד', 'ﬣ' => 'ה', 'ﬤ' => 'כ', 'ﬥ' => 'ל', 'ﬦ' => 'ם', 'ﬧ' => 'ר', 'ﬨ' => 'ת', '﬩' => '+', 'ﭏ' => 'אל', '﹉' => '‾', '﹊' => '‾', '﹋' => '‾', '﹌' => '‾', '﹍' => '_', '﹎' => '_', '﹏' => '_', '﹐' => ',', '﹑' => '、', '﹒' => '.', '﹔' => ';', '﹕' => ':', '﹖' => '?', '﹗' => '!', '﹘' => '—', '﹙' => '(', '﹚' => ')', '﹛' => '{', '﹜' => '}', '﹝' => '〔', '﹞' => '〕', '﹟' => '#', '﹠' => '&', '﹡' => '*', '﹢' => '+', '﹣' => '-', '﹤' => '<', '﹥' => '>', '﹦' => '=', '﹨' => '\\', '﹩' => '$', '﹪' => '%', '﹫' => '@', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', ''' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', '0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '⦅' => '⦅', '⦆' => '⦆', '。' => '。', '「' => '「', '」' => '」', '、' => '、', '・' => '・', 'ヲ' => 'ヲ', 'ァ' => 'ァ', 'ィ' => 'ィ', 'ゥ' => 'ゥ', 'ェ' => 'ェ', 'ォ' => 'ォ', 'ャ' => 'ャ', 'ュ' => 'ュ', 'ョ' => 'ョ', 'ッ' => 'ッ', 'ー' => 'ー', 'ア' => 'ア', 'イ' => 'イ', 'ウ' => 'ウ', 'エ' => 'エ', 'オ' => 'オ', 'カ' => 'カ', 'キ' => 'キ', 'ク' => 'ク', 'ケ' => 'ケ', 'コ' => 'コ', 'サ' => 'サ', 'シ' => 'シ', 'ス' => 'ス', 'セ' => 'セ', 'ソ' => 'ソ', 'タ' => 'タ', 'チ' => 'チ', 'ツ' => 'ツ', 'テ' => 'テ', 'ト' => 'ト', 'ナ' => 'ナ', 'ニ' => 'ニ', 'ヌ' => 'ヌ', 'ネ' => 'ネ', 'ノ' => 'ノ', 'ハ' => 'ハ', 'ヒ' => 'ヒ', 'フ' => 'フ', 'ヘ' => 'ヘ', 'ホ' => 'ホ', 'マ' => 'マ', 'ミ' => 'ミ', 'ム' => 'ム', 'メ' => 'メ', 'モ' => 'モ', 'ヤ' => 'ヤ', 'ユ' => 'ユ', 'ヨ' => 'ヨ', 'ラ' => 'ラ', 'リ' => 'リ', 'ル' => 'ル', 'レ' => 'レ', 'ロ' => 'ロ', 'ワ' => 'ワ', 'ン' => 'ン', '゙' => '゙', '゚' => '゚', 'ᅠ' => 'ㅤ', 'ᄀ' => 'ㄱ', 'ᄁ' => 'ㄲ', 'ᆪ' => 'ㄳ', 'ᄂ' => 'ㄴ', 'ᆬ' => 'ㄵ', 'ᆭ' => 'ㄶ', 'ᄃ' => 'ㄷ', 'ᄄ' => 'ㄸ', 'ᄅ' => 'ㄹ', 'ᆰ' => 'ㄺ', 'ᆱ' => 'ㄻ', 'ᆲ' => 'ㄼ', 'ᆳ' => 'ㄽ', 'ᆴ' => 'ㄾ', 'ᆵ' => 'ㄿ', 'ᄚ' => 'ㅀ', 'ᄆ' => 'ㅁ', 'ᄇ' => 'ㅂ', 'ᄈ' => 'ㅃ', 'ᄡ' => 'ㅄ', 'ᄉ' => 'ㅅ', 'ᄊ' => 'ㅆ', 'ᄋ' => 'ㅇ', 'ᄌ' => 'ㅈ', 'ᄍ' => 'ㅉ', 'ᄎ' => 'ㅊ', 'ᄏ' => 'ㅋ', 'ᄐ' => 'ㅌ', 'ᄑ' => 'ㅍ', 'ᄒ' => 'ㅎ', 'ᅡ' => 'ㅏ', 'ᅢ' => 'ㅐ', 'ᅣ' => 'ㅑ', 'ᅤ' => 'ㅒ', 'ᅥ' => 'ㅓ', 'ᅦ' => 'ㅔ', 'ᅧ' => 'ㅕ', 'ᅨ' => 'ㅖ', 'ᅩ' => 'ㅗ', 'ᅪ' => 'ㅘ', 'ᅫ' => 'ㅙ', 'ᅬ' => 'ㅚ', 'ᅭ' => 'ㅛ', 'ᅮ' => 'ㅜ', 'ᅯ' => 'ㅝ', 'ᅰ' => 'ㅞ', 'ᅱ' => 'ㅟ', 'ᅲ' => 'ㅠ', 'ᅳ' => 'ㅡ', 'ᅴ' => 'ㅢ', 'ᅵ' => 'ㅣ', '¢' => '¢', '£' => '£', '¬' => '¬', ' ̄' => '¯', '¦' => '¦', '¥' => '¥', '₩' => '₩', '│' => '│', '←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '■' => '■', '○' => '○', '𝐀' => 'A', '𝐁' => 'B', '𝐂' => 'C', '𝐃' => 'D', '𝐄' => 'E', '𝐅' => 'F', '𝐆' => 'G', '𝐇' => 'H', '𝐈' => 'I', '𝐉' => 'J', '𝐊' => 'K', '𝐋' => 'L', '𝐌' => 'M', '𝐍' => 'N', '𝐎' => 'O', '𝐏' => 'P', '𝐐' => 'Q', '𝐑' => 'R', '𝐒' => 'S', '𝐓' => 'T', '𝐔' => 'U', '𝐕' => 'V', '𝐖' => 'W', '𝐗' => 'X', '𝐘' => 'Y', '𝐙' => 'Z', '𝐚' => 'a', '𝐛' => 'b', '𝐜' => 'c', '𝐝' => 'd', '𝐞' => 'e', '𝐟' => 'f', '𝐠' => 'g', '𝐡' => 'h', '𝐢' => 'i', '𝐣' => 'j', '𝐤' => 'k', '𝐥' => 'l', '𝐦' => 'm', '𝐧' => 'n', '𝐨' => 'o', '𝐩' => 'p', '𝐪' => 'q', '𝐫' => 'r', '𝐬' => 's', '𝐭' => 't', '𝐮' => 'u', '𝐯' => 'v', '𝐰' => 'w', '𝐱' => 'x', '𝐲' => 'y', '𝐳' => 'z', '𝐴' => 'A', '𝐵' => 'B', '𝐶' => 'C', '𝐷' => 'D', '𝐸' => 'E', '𝐹' => 'F', '𝐺' => 'G', '𝐻' => 'H', '𝐼' => 'I', '𝐽' => 'J', '𝐾' => 'K', '𝐿' => 'L', '𝑀' => 'M', '𝑁' => 'N', '𝑂' => 'O', '𝑃' => 'P', '𝑄' => 'Q', '𝑅' => 'R', '𝑆' => 'S', '𝑇' => 'T', '𝑈' => 'U', '𝑉' => 'V', '𝑊' => 'W', '𝑋' => 'X', '𝑌' => 'Y', '𝑍' => 'Z', '𝑎' => 'a', '𝑏' => 'b', '𝑐' => 'c', '𝑑' => 'd', '𝑒' => 'e', '𝑓' => 'f', '𝑔' => 'g', '𝑖' => 'i', '𝑗' => 'j', '𝑘' => 'k', '𝑙' => 'l', '𝑚' => 'm', '𝑛' => 'n', '𝑜' => 'o', '𝑝' => 'p', '𝑞' => 'q', '𝑟' => 'r', '𝑠' => 's', '𝑡' => 't', '𝑢' => 'u', '𝑣' => 'v', '𝑤' => 'w', '𝑥' => 'x', '𝑦' => 'y', '𝑧' => 'z', '𝑨' => 'A', '𝑩' => 'B', '𝑪' => 'C', '𝑫' => 'D', '𝑬' => 'E', '𝑭' => 'F', '𝑮' => 'G', '𝑯' => 'H', '𝑰' => 'I', '𝑱' => 'J', '𝑲' => 'K', '𝑳' => 'L', '𝑴' => 'M', '𝑵' => 'N', '𝑶' => 'O', '𝑷' => 'P', '𝑸' => 'Q', '𝑹' => 'R', '𝑺' => 'S', '𝑻' => 'T', '𝑼' => 'U', '𝑽' => 'V', '𝑾' => 'W', '𝑿' => 'X', '𝒀' => 'Y', '𝒁' => 'Z', '𝒂' => 'a', '𝒃' => 'b', '𝒄' => 'c', '𝒅' => 'd', '𝒆' => 'e', '𝒇' => 'f', '𝒈' => 'g', '𝒉' => 'h', '𝒊' => 'i', '𝒋' => 'j', '𝒌' => 'k', '𝒍' => 'l', '𝒎' => 'm', '𝒏' => 'n', '𝒐' => 'o', '𝒑' => 'p', '𝒒' => 'q', '𝒓' => 'r', '𝒔' => 's', '𝒕' => 't', '𝒖' => 'u', '𝒗' => 'v', '𝒘' => 'w', '𝒙' => 'x', '𝒚' => 'y', '𝒛' => 'z', '𝒜' => 'A', '𝒞' => 'C', '𝒟' => 'D', '𝒢' => 'G', '𝒥' => 'J', '𝒦' => 'K', '𝒩' => 'N', '𝒪' => 'O', '𝒫' => 'P', '𝒬' => 'Q', '𝒮' => 'S', '𝒯' => 'T', '𝒰' => 'U', '𝒱' => 'V', '𝒲' => 'W', '𝒳' => 'X', '𝒴' => 'Y', '𝒵' => 'Z', '𝒶' => 'a', '𝒷' => 'b', '𝒸' => 'c', '𝒹' => 'd', '𝒻' => 'f', '𝒽' => 'h', '𝒾' => 'i', '𝒿' => 'j', '𝓀' => 'k', '𝓁' => 'l', '𝓂' => 'm', '𝓃' => 'n', '𝓅' => 'p', '𝓆' => 'q', '𝓇' => 'r', '𝓈' => 's', '𝓉' => 't', '𝓊' => 'u', '𝓋' => 'v', '𝓌' => 'w', '𝓍' => 'x', '𝓎' => 'y', '𝓏' => 'z', '𝓐' => 'A', '𝓑' => 'B', '𝓒' => 'C', '𝓓' => 'D', '𝓔' => 'E', '𝓕' => 'F', '𝓖' => 'G', '𝓗' => 'H', '𝓘' => 'I', '𝓙' => 'J', '𝓚' => 'K', '𝓛' => 'L', '𝓜' => 'M', '𝓝' => 'N', '𝓞' => 'O', '𝓟' => 'P', '𝓠' => 'Q', '𝓡' => 'R', '𝓢' => 'S', '𝓣' => 'T', '𝓤' => 'U', '𝓥' => 'V', '𝓦' => 'W', '𝓧' => 'X', '𝓨' => 'Y', '𝓩' => 'Z', '𝓪' => 'a', '𝓫' => 'b', '𝓬' => 'c', '𝓭' => 'd', '𝓮' => 'e', '𝓯' => 'f', '𝓰' => 'g', '𝓱' => 'h', '𝓲' => 'i', '𝓳' => 'j', '𝓴' => 'k', '𝓵' => 'l', '𝓶' => 'm', '𝓷' => 'n', '𝓸' => 'o', '𝓹' => 'p', '𝓺' => 'q', '𝓻' => 'r', '𝓼' => 's', '𝓽' => 't', '𝓾' => 'u', '𝓿' => 'v', '𝔀' => 'w', '𝔁' => 'x', '𝔂' => 'y', '𝔃' => 'z', '𝔄' => 'A', '𝔅' => 'B', '𝔇' => 'D', '𝔈' => 'E', '𝔉' => 'F', '𝔊' => 'G', '𝔍' => 'J', '𝔎' => 'K', '𝔏' => 'L', '𝔐' => 'M', '𝔑' => 'N', '𝔒' => 'O', '𝔓' => 'P', '𝔔' => 'Q', '𝔖' => 'S', '𝔗' => 'T', '𝔘' => 'U', '𝔙' => 'V', '𝔚' => 'W', '𝔛' => 'X', '𝔜' => 'Y', '𝔞' => 'a', '𝔟' => 'b', '𝔠' => 'c', '𝔡' => 'd', '𝔢' => 'e', '𝔣' => 'f', '𝔤' => 'g', '𝔥' => 'h', '𝔦' => 'i', '𝔧' => 'j', '𝔨' => 'k', '𝔩' => 'l', '𝔪' => 'm', '𝔫' => 'n', '𝔬' => 'o', '𝔭' => 'p', '𝔮' => 'q', '𝔯' => 'r', '𝔰' => 's', '𝔱' => 't', '𝔲' => 'u', '𝔳' => 'v', '𝔴' => 'w', '𝔵' => 'x', '𝔶' => 'y', '𝔷' => 'z', '𝔸' => 'A', '𝔹' => 'B', '𝔻' => 'D', '𝔼' => 'E', '𝔽' => 'F', '𝔾' => 'G', '𝕀' => 'I', '𝕁' => 'J', '𝕂' => 'K', '𝕃' => 'L', '𝕄' => 'M', '𝕆' => 'O', '𝕊' => 'S', '𝕋' => 'T', '𝕌' => 'U', '𝕍' => 'V', '𝕎' => 'W', '𝕏' => 'X', '𝕐' => 'Y', '𝕒' => 'a', '𝕓' => 'b', '𝕔' => 'c', '𝕕' => 'd', '𝕖' => 'e', '𝕗' => 'f', '𝕘' => 'g', '𝕙' => 'h', '𝕚' => 'i', '𝕛' => 'j', '𝕜' => 'k', '𝕝' => 'l', '𝕞' => 'm', '𝕟' => 'n', '𝕠' => 'o', '𝕡' => 'p', '𝕢' => 'q', '𝕣' => 'r', '𝕤' => 's', '𝕥' => 't', '𝕦' => 'u', '𝕧' => 'v', '𝕨' => 'w', '𝕩' => 'x', '𝕪' => 'y', '𝕫' => 'z', '𝕬' => 'A', '𝕭' => 'B', '𝕮' => 'C', '𝕯' => 'D', '𝕰' => 'E', '𝕱' => 'F', '𝕲' => 'G', '𝕳' => 'H', '𝕴' => 'I', '𝕵' => 'J', '𝕶' => 'K', '𝕷' => 'L', '𝕸' => 'M', '𝕹' => 'N', '𝕺' => 'O', '𝕻' => 'P', '𝕼' => 'Q', '𝕽' => 'R', '𝕾' => 'S', '𝕿' => 'T', '𝖀' => 'U', '𝖁' => 'V', '𝖂' => 'W', '𝖃' => 'X', '𝖄' => 'Y', '𝖅' => 'Z', '𝖆' => 'a', '𝖇' => 'b', '𝖈' => 'c', '𝖉' => 'd', '𝖊' => 'e', '𝖋' => 'f', '𝖌' => 'g', '𝖍' => 'h', '𝖎' => 'i', '𝖏' => 'j', '𝖐' => 'k', '𝖑' => 'l', '𝖒' => 'm', '𝖓' => 'n', '𝖔' => 'o', '𝖕' => 'p', '𝖖' => 'q', '𝖗' => 'r', '𝖘' => 's', '𝖙' => 't', '𝖚' => 'u', '𝖛' => 'v', '𝖜' => 'w', '𝖝' => 'x', '𝖞' => 'y', '𝖟' => 'z', '𝖠' => 'A', '𝖡' => 'B', '𝖢' => 'C', '𝖣' => 'D', '𝖤' => 'E', '𝖥' => 'F', '𝖦' => 'G', '𝖧' => 'H', '𝖨' => 'I', '𝖩' => 'J', '𝖪' => 'K', '𝖫' => 'L', '𝖬' => 'M', '𝖭' => 'N', '𝖮' => 'O', '𝖯' => 'P', '𝖰' => 'Q', '𝖱' => 'R', '𝖲' => 'S', '𝖳' => 'T', '𝖴' => 'U', '𝖵' => 'V', '𝖶' => 'W', '𝖷' => 'X', '𝖸' => 'Y', '𝖹' => 'Z', '𝖺' => 'a', '𝖻' => 'b', '𝖼' => 'c', '𝖽' => 'd', '𝖾' => 'e', '𝖿' => 'f', '𝗀' => 'g', '𝗁' => 'h', '𝗂' => 'i', '𝗃' => 'j', '𝗄' => 'k', '𝗅' => 'l', '𝗆' => 'm', '𝗇' => 'n', '𝗈' => 'o', '𝗉' => 'p', '𝗊' => 'q', '𝗋' => 'r', '𝗌' => 's', '𝗍' => 't', '𝗎' => 'u', '𝗏' => 'v', '𝗐' => 'w', '𝗑' => 'x', '𝗒' => 'y', '𝗓' => 'z', '𝗔' => 'A', '𝗕' => 'B', '𝗖' => 'C', '𝗗' => 'D', '𝗘' => 'E', '𝗙' => 'F', '𝗚' => 'G', '𝗛' => 'H', '𝗜' => 'I', '𝗝' => 'J', '𝗞' => 'K', '𝗟' => 'L', '𝗠' => 'M', '𝗡' => 'N', '𝗢' => 'O', '𝗣' => 'P', '𝗤' => 'Q', '𝗥' => 'R', '𝗦' => 'S', '𝗧' => 'T', '𝗨' => 'U', '𝗩' => 'V', '𝗪' => 'W', '𝗫' => 'X', '𝗬' => 'Y', '𝗭' => 'Z', '𝗮' => 'a', '𝗯' => 'b', '𝗰' => 'c', '𝗱' => 'd', '𝗲' => 'e', '𝗳' => 'f', '𝗴' => 'g', '𝗵' => 'h', '𝗶' => 'i', '𝗷' => 'j', '𝗸' => 'k', '𝗹' => 'l', '𝗺' => 'm', '𝗻' => 'n', '𝗼' => 'o', '𝗽' => 'p', '𝗾' => 'q', '𝗿' => 'r', '𝘀' => 's', '𝘁' => 't', '𝘂' => 'u', '𝘃' => 'v', '𝘄' => 'w', '𝘅' => 'x', '𝘆' => 'y', '𝘇' => 'z', '𝘈' => 'A', '𝘉' => 'B', '𝘊' => 'C', '𝘋' => 'D', '𝘌' => 'E', '𝘍' => 'F', '𝘎' => 'G', '𝘏' => 'H', '𝘐' => 'I', '𝘑' => 'J', '𝘒' => 'K', '𝘓' => 'L', '𝘔' => 'M', '𝘕' => 'N', '𝘖' => 'O', '𝘗' => 'P', '𝘘' => 'Q', '𝘙' => 'R', '𝘚' => 'S', '𝘛' => 'T', '𝘜' => 'U', '𝘝' => 'V', '𝘞' => 'W', '𝘟' => 'X', '𝘠' => 'Y', '𝘡' => 'Z', '𝘢' => 'a', '𝘣' => 'b', '𝘤' => 'c', '𝘥' => 'd', '𝘦' => 'e', '𝘧' => 'f', '𝘨' => 'g', '𝘩' => 'h', '𝘪' => 'i', '𝘫' => 'j', '𝘬' => 'k', '𝘭' => 'l', '𝘮' => 'm', '𝘯' => 'n', '𝘰' => 'o', '𝘱' => 'p', '𝘲' => 'q', '𝘳' => 'r', '𝘴' => 's', '𝘵' => 't', '𝘶' => 'u', '𝘷' => 'v', '𝘸' => 'w', '𝘹' => 'x', '𝘺' => 'y', '𝘻' => 'z', '𝘼' => 'A', '𝘽' => 'B', '𝘾' => 'C', '𝘿' => 'D', '𝙀' => 'E', '𝙁' => 'F', '𝙂' => 'G', '𝙃' => 'H', '𝙄' => 'I', '𝙅' => 'J', '𝙆' => 'K', '𝙇' => 'L', '𝙈' => 'M', '𝙉' => 'N', '𝙊' => 'O', '𝙋' => 'P', '𝙌' => 'Q', '𝙍' => 'R', '𝙎' => 'S', '𝙏' => 'T', '𝙐' => 'U', '𝙑' => 'V', '𝙒' => 'W', '𝙓' => 'X', '𝙔' => 'Y', '𝙕' => 'Z', '𝙖' => 'a', '𝙗' => 'b', '𝙘' => 'c', '𝙙' => 'd', '𝙚' => 'e', '𝙛' => 'f', '𝙜' => 'g', '𝙝' => 'h', '𝙞' => 'i', '𝙟' => 'j', '𝙠' => 'k', '𝙡' => 'l', '𝙢' => 'm', '𝙣' => 'n', '𝙤' => 'o', '𝙥' => 'p', '𝙦' => 'q', '𝙧' => 'r', '𝙨' => 's', '𝙩' => 't', '𝙪' => 'u', '𝙫' => 'v', '𝙬' => 'w', '𝙭' => 'x', '𝙮' => 'y', '𝙯' => 'z', '𝙰' => 'A', '𝙱' => 'B', '𝙲' => 'C', '𝙳' => 'D', '𝙴' => 'E', '𝙵' => 'F', '𝙶' => 'G', '𝙷' => 'H', '𝙸' => 'I', '𝙹' => 'J', '𝙺' => 'K', '𝙻' => 'L', '𝙼' => 'M', '𝙽' => 'N', '𝙾' => 'O', '𝙿' => 'P', '𝚀' => 'Q', '𝚁' => 'R', '𝚂' => 'S', '𝚃' => 'T', '𝚄' => 'U', '𝚅' => 'V', '𝚆' => 'W', '𝚇' => 'X', '𝚈' => 'Y', '𝚉' => 'Z', '𝚊' => 'a', '𝚋' => 'b', '𝚌' => 'c', '𝚍' => 'd', '𝚎' => 'e', '𝚏' => 'f', '𝚐' => 'g', '𝚑' => 'h', '𝚒' => 'i', '𝚓' => 'j', '𝚔' => 'k', '𝚕' => 'l', '𝚖' => 'm', '𝚗' => 'n', '𝚘' => 'o', '𝚙' => 'p', '𝚚' => 'q', '𝚛' => 'r', '𝚜' => 's', '𝚝' => 't', '𝚞' => 'u', '𝚟' => 'v', '𝚠' => 'w', '𝚡' => 'x', '𝚢' => 'y', '𝚣' => 'z', '𝚤' => 'ı', '𝚥' => 'ȷ', '𝚨' => 'Α', '𝚩' => 'Β', '𝚪' => 'Γ', '𝚫' => 'Δ', '𝚬' => 'Ε', '𝚭' => 'Ζ', '𝚮' => 'Η', '𝚯' => 'Θ', '𝚰' => 'Ι', '𝚱' => 'Κ', '𝚲' => 'Λ', '𝚳' => 'Μ', '𝚴' => 'Ν', '𝚵' => 'Ξ', '𝚶' => 'Ο', '𝚷' => 'Π', '𝚸' => 'Ρ', '𝚹' => 'ϴ', '𝚺' => 'Σ', '𝚻' => 'Τ', '𝚼' => 'Υ', '𝚽' => 'Φ', '𝚾' => 'Χ', '𝚿' => 'Ψ', '𝛀' => 'Ω', '𝛁' => '∇', '𝛂' => 'α', '𝛃' => 'β', '𝛄' => 'γ', '𝛅' => 'δ', '𝛆' => 'ε', '𝛇' => 'ζ', '𝛈' => 'η', '𝛉' => 'θ', '𝛊' => 'ι', '𝛋' => 'κ', '𝛌' => 'λ', '𝛍' => 'μ', '𝛎' => 'ν', '𝛏' => 'ξ', '𝛐' => 'ο', '𝛑' => 'π', '𝛒' => 'ρ', '𝛓' => 'ς', '𝛔' => 'σ', '𝛕' => 'τ', '𝛖' => 'υ', '𝛗' => 'φ', '𝛘' => 'χ', '𝛙' => 'ψ', '𝛚' => 'ω', '𝛛' => '∂', '𝛜' => 'ϵ', '𝛝' => 'ϑ', '𝛞' => 'ϰ', '𝛟' => 'ϕ', '𝛠' => 'ϱ', '𝛡' => 'ϖ', '𝛢' => 'Α', '𝛣' => 'Β', '𝛤' => 'Γ', '𝛥' => 'Δ', '𝛦' => 'Ε', '𝛧' => 'Ζ', '𝛨' => 'Η', '𝛩' => 'Θ', '𝛪' => 'Ι', '𝛫' => 'Κ', '𝛬' => 'Λ', '𝛭' => 'Μ', '𝛮' => 'Ν', '𝛯' => 'Ξ', '𝛰' => 'Ο', '𝛱' => 'Π', '𝛲' => 'Ρ', '𝛳' => 'ϴ', '𝛴' => 'Σ', '𝛵' => 'Τ', '𝛶' => 'Υ', '𝛷' => 'Φ', '𝛸' => 'Χ', '𝛹' => 'Ψ', '𝛺' => 'Ω', '𝛻' => '∇', '𝛼' => 'α', '𝛽' => 'β', '𝛾' => 'γ', '𝛿' => 'δ', '𝜀' => 'ε', '𝜁' => 'ζ', '𝜂' => 'η', '𝜃' => 'θ', '𝜄' => 'ι', '𝜅' => 'κ', '𝜆' => 'λ', '𝜇' => 'μ', '𝜈' => 'ν', '𝜉' => 'ξ', '𝜊' => 'ο', '𝜋' => 'π', '𝜌' => 'ρ', '𝜍' => 'ς', '𝜎' => 'σ', '𝜏' => 'τ', '𝜐' => 'υ', '𝜑' => 'φ', '𝜒' => 'χ', '𝜓' => 'ψ', '𝜔' => 'ω', '𝜕' => '∂', '𝜖' => 'ϵ', '𝜗' => 'ϑ', '𝜘' => 'ϰ', '𝜙' => 'ϕ', '𝜚' => 'ϱ', '𝜛' => 'ϖ', '𝜜' => 'Α', '𝜝' => 'Β', '𝜞' => 'Γ', '𝜟' => 'Δ', '𝜠' => 'Ε', '𝜡' => 'Ζ', '𝜢' => 'Η', '𝜣' => 'Θ', '𝜤' => 'Ι', '𝜥' => 'Κ', '𝜦' => 'Λ', '𝜧' => 'Μ', '𝜨' => 'Ν', '𝜩' => 'Ξ', '𝜪' => 'Ο', '𝜫' => 'Π', '𝜬' => 'Ρ', '𝜭' => 'ϴ', '𝜮' => 'Σ', '𝜯' => 'Τ', '𝜰' => 'Υ', '𝜱' => 'Φ', '𝜲' => 'Χ', '𝜳' => 'Ψ', '𝜴' => 'Ω', '𝜵' => '∇', '𝜶' => 'α', '𝜷' => 'β', '𝜸' => 'γ', '𝜹' => 'δ', '𝜺' => 'ε', '𝜻' => 'ζ', '𝜼' => 'η', '𝜽' => 'θ', '𝜾' => 'ι', '𝜿' => 'κ', '𝝀' => 'λ', '𝝁' => 'μ', '𝝂' => 'ν', '𝝃' => 'ξ', '𝝄' => 'ο', '𝝅' => 'π', '𝝆' => 'ρ', '𝝇' => 'ς', '𝝈' => 'σ', '𝝉' => 'τ', '𝝊' => 'υ', '𝝋' => 'φ', '𝝌' => 'χ', '𝝍' => 'ψ', '𝝎' => 'ω', '𝝏' => '∂', '𝝐' => 'ϵ', '𝝑' => 'ϑ', '𝝒' => 'ϰ', '𝝓' => 'ϕ', '𝝔' => 'ϱ', '𝝕' => 'ϖ', '𝝖' => 'Α', '𝝗' => 'Β', '𝝘' => 'Γ', '𝝙' => 'Δ', '𝝚' => 'Ε', '𝝛' => 'Ζ', '𝝜' => 'Η', '𝝝' => 'Θ', '𝝞' => 'Ι', '𝝟' => 'Κ', '𝝠' => 'Λ', '𝝡' => 'Μ', '𝝢' => 'Ν', '𝝣' => 'Ξ', '𝝤' => 'Ο', '𝝥' => 'Π', '𝝦' => 'Ρ', '𝝧' => 'ϴ', '𝝨' => 'Σ', '𝝩' => 'Τ', '𝝪' => 'Υ', '𝝫' => 'Φ', '𝝬' => 'Χ', '𝝭' => 'Ψ', '𝝮' => 'Ω', '𝝯' => '∇', '𝝰' => 'α', '𝝱' => 'β', '𝝲' => 'γ', '𝝳' => 'δ', '𝝴' => 'ε', '𝝵' => 'ζ', '𝝶' => 'η', '𝝷' => 'θ', '𝝸' => 'ι', '𝝹' => 'κ', '𝝺' => 'λ', '𝝻' => 'μ', '𝝼' => 'ν', '𝝽' => 'ξ', '𝝾' => 'ο', '𝝿' => 'π', '𝞀' => 'ρ', '𝞁' => 'ς', '𝞂' => 'σ', '𝞃' => 'τ', '𝞄' => 'υ', '𝞅' => 'φ', '𝞆' => 'χ', '𝞇' => 'ψ', '𝞈' => 'ω', '𝞉' => '∂', '𝞊' => 'ϵ', '𝞋' => 'ϑ', '𝞌' => 'ϰ', '𝞍' => 'ϕ', '𝞎' => 'ϱ', '𝞏' => 'ϖ', '𝞐' => 'Α', '𝞑' => 'Β', '𝞒' => 'Γ', '𝞓' => 'Δ', '𝞔' => 'Ε', '𝞕' => 'Ζ', '𝞖' => 'Η', '𝞗' => 'Θ', '𝞘' => 'Ι', '𝞙' => 'Κ', '𝞚' => 'Λ', '𝞛' => 'Μ', '𝞜' => 'Ν', '𝞝' => 'Ξ', '𝞞' => 'Ο', '𝞟' => 'Π', '𝞠' => 'Ρ', '𝞡' => 'ϴ', '𝞢' => 'Σ', '𝞣' => 'Τ', '𝞤' => 'Υ', '𝞥' => 'Φ', '𝞦' => 'Χ', '𝞧' => 'Ψ', '𝞨' => 'Ω', '𝞩' => '∇', '𝞪' => 'α', '𝞫' => 'β', '𝞬' => 'γ', '𝞭' => 'δ', '𝞮' => 'ε', '𝞯' => 'ζ', '𝞰' => 'η', '𝞱' => 'θ', '𝞲' => 'ι', '𝞳' => 'κ', '𝞴' => 'λ', '𝞵' => 'μ', '𝞶' => 'ν', '𝞷' => 'ξ', '𝞸' => 'ο', '𝞹' => 'π', '𝞺' => 'ρ', '𝞻' => 'ς', '𝞼' => 'σ', '𝞽' => 'τ', '𝞾' => 'υ', '𝞿' => 'φ', '𝟀' => 'χ', '𝟁' => 'ψ', '𝟂' => 'ω', '𝟃' => '∂', '𝟄' => 'ϵ', '𝟅' => 'ϑ', '𝟆' => 'ϰ', '𝟇' => 'ϕ', '𝟈' => 'ϱ', '𝟉' => 'ϖ', '𝟊' => 'Ϝ', '𝟋' => 'ϝ', '𝟎' => '0', '𝟏' => '1', '𝟐' => '2', '𝟑' => '3', '𝟒' => '4', '𝟓' => '5', '𝟔' => '6', '𝟕' => '7', '𝟖' => '8', '𝟗' => '9', '𝟘' => '0', '𝟙' => '1', '𝟚' => '2', '𝟛' => '3', '𝟜' => '4', '𝟝' => '5', '𝟞' => '6', '𝟟' => '7', '𝟠' => '8', '𝟡' => '9', '𝟢' => '0', '𝟣' => '1', '𝟤' => '2', '𝟥' => '3', '𝟦' => '4', '𝟧' => '5', '𝟨' => '6', '𝟩' => '7', '𝟪' => '8', '𝟫' => '9', '𝟬' => '0', '𝟭' => '1', '𝟮' => '2', '𝟯' => '3', '𝟰' => '4', '𝟱' => '5', '𝟲' => '6', '𝟳' => '7', '𝟴' => '8', '𝟵' => '9', '𝟶' => '0', '𝟷' => '1', '𝟸' => '2', '𝟹' => '3', '𝟺' => '4', '𝟻' => '5', '𝟼' => '6', '𝟽' => '7', '𝟾' => '8', '𝟿' => '9', '𞸀' => 'ا', '𞸁' => 'ب', '𞸂' => 'ج', '𞸃' => 'د', '𞸅' => 'و', '𞸆' => 'ز', '𞸇' => 'ح', '𞸈' => 'ط', '𞸉' => 'ي', '𞸊' => 'ك', '𞸋' => 'ل', '𞸌' => 'م', '𞸍' => 'ن', '𞸎' => 'س', '𞸏' => 'ع', '𞸐' => 'ف', '𞸑' => 'ص', '𞸒' => 'ق', '𞸓' => 'ر', '𞸔' => 'ش', '𞸕' => 'ت', '𞸖' => 'ث', '𞸗' => 'خ', '𞸘' => 'ذ', '𞸙' => 'ض', '𞸚' => 'ظ', '𞸛' => 'غ', '𞸜' => 'ٮ', '𞸝' => 'ں', '𞸞' => 'ڡ', '𞸟' => 'ٯ', '𞸡' => 'ب', '𞸢' => 'ج', '𞸤' => 'ه', '𞸧' => 'ح', '𞸩' => 'ي', '𞸪' => 'ك', '𞸫' => 'ل', '𞸬' => 'م', '𞸭' => 'ن', '𞸮' => 'س', '𞸯' => 'ع', '𞸰' => 'ف', '𞸱' => 'ص', '𞸲' => 'ق', '𞸴' => 'ش', '𞸵' => 'ت', '𞸶' => 'ث', '𞸷' => 'خ', '𞸹' => 'ض', '𞸻' => 'غ', '𞹂' => 'ج', '𞹇' => 'ح', '𞹉' => 'ي', '𞹋' => 'ل', '𞹍' => 'ن', '𞹎' => 'س', '𞹏' => 'ع', '𞹑' => 'ص', '𞹒' => 'ق', '𞹔' => 'ش', '𞹗' => 'خ', '𞹙' => 'ض', '𞹛' => 'غ', '𞹝' => 'ں', '𞹟' => 'ٯ', '𞹡' => 'ب', '𞹢' => 'ج', '𞹤' => 'ه', '𞹧' => 'ح', '𞹨' => 'ط', '𞹩' => 'ي', '𞹪' => 'ك', '𞹬' => 'م', '𞹭' => 'ن', '𞹮' => 'س', '𞹯' => 'ع', '𞹰' => 'ف', '𞹱' => 'ص', '𞹲' => 'ق', '𞹴' => 'ش', '𞹵' => 'ت', '𞹶' => 'ث', '𞹷' => 'خ', '𞹹' => 'ض', '𞹺' => 'ظ', '𞹻' => 'غ', '𞹼' => 'ٮ', '𞹾' => 'ڡ', '𞺀' => 'ا', '𞺁' => 'ب', '𞺂' => 'ج', '𞺃' => 'د', '𞺄' => 'ه', '𞺅' => 'و', '𞺆' => 'ز', '𞺇' => 'ح', '𞺈' => 'ط', '𞺉' => 'ي', '𞺋' => 'ل', '𞺌' => 'م', '𞺍' => 'ن', '𞺎' => 'س', '𞺏' => 'ع', '𞺐' => 'ف', '𞺑' => 'ص', '𞺒' => 'ق', '𞺓' => 'ر', '𞺔' => 'ش', '𞺕' => 'ت', '𞺖' => 'ث', '𞺗' => 'خ', '𞺘' => 'ذ', '𞺙' => 'ض', '𞺚' => 'ظ', '𞺛' => 'غ', '𞺡' => 'ب', '𞺢' => 'ج', '𞺣' => 'د', '𞺥' => 'و', '𞺦' => 'ز', '𞺧' => 'ح', '𞺨' => 'ط', '𞺩' => 'ي', '𞺫' => 'ل', '𞺬' => 'م', '𞺭' => 'ن', '𞺮' => 'س', '𞺯' => 'ع', '𞺰' => 'ف', '𞺱' => 'ص', '𞺲' => 'ق', '𞺳' => 'ر', '𞺴' => 'ش', '𞺵' => 'ت', '𞺶' => 'ث', '𞺷' => 'خ', '𞺸' => 'ذ', '𞺹' => 'ض', '𞺺' => 'ظ', '𞺻' => 'غ', '🄀' => '0.', '🄁' => '0,', '🄂' => '1,', '🄃' => '2,', '🄄' => '3,', '🄅' => '4,', '🄆' => '5,', '🄇' => '6,', '🄈' => '7,', '🄉' => '8,', '🄊' => '9,', '🄐' => '(A)', '🄑' => '(B)', '🄒' => '(C)', '🄓' => '(D)', '🄔' => '(E)', '🄕' => '(F)', '🄖' => '(G)', '🄗' => '(H)', '🄘' => '(I)', '🄙' => '(J)', '🄚' => '(K)', '🄛' => '(L)', '🄜' => '(M)', '🄝' => '(N)', '🄞' => '(O)', '🄟' => '(P)', '🄠' => '(Q)', '🄡' => '(R)', '🄢' => '(S)', '🄣' => '(T)', '🄤' => '(U)', '🄥' => '(V)', '🄦' => '(W)', '🄧' => '(X)', '🄨' => '(Y)', '🄩' => '(Z)', '🄪' => '〔S〕', '🄫' => '(C)', '🄬' => '(R)', '🄭' => '(CD)', '🄮' => '(WZ)', '🄰' => 'A', '🄱' => 'B', '🄲' => 'C', '🄳' => 'D', '🄴' => 'E', '🄵' => 'F', '🄶' => 'G', '🄷' => 'H', '🄸' => 'I', '🄹' => 'J', '🄺' => 'K', '🄻' => 'L', '🄼' => 'M', '🄽' => 'N', '🄾' => 'O', '🄿' => 'P', '🅀' => 'Q', '🅁' => 'R', '🅂' => 'S', '🅃' => 'T', '🅄' => 'U', '🅅' => 'V', '🅆' => 'W', '🅇' => 'X', '🅈' => 'Y', '🅉' => 'Z', '🅊' => 'HV', '🅋' => 'MV', '🅌' => 'SD', '🅍' => 'SS', '🅎' => 'PPV', '🅏' => 'WC', '🆐' => 'DJ', '🈀' => 'ほか', '🈁' => 'ココ', '🈂' => 'サ', '🈐' => '手', '🈑' => '字', '🈒' => '双', '🈓' => 'デ', '🈔' => '二', '🈕' => '多', '🈖' => '解', '🈗' => '天', '🈘' => '交', '🈙' => '映', '🈚' => '無', '🈛' => '料', '🈜' => '前', '🈝' => '後', '🈞' => '再', '🈟' => '新', '🈠' => '初', '🈡' => '終', '🈢' => '生', '🈣' => '販', '🈤' => '声', '🈥' => '吹', '🈦' => '演', '🈧' => '投', '🈨' => '捕', '🈩' => '一', '🈪' => '三', '🈫' => '遊', '🈬' => '左', '🈭' => '中', '🈮' => '右', '🈯' => '指', '🈰' => '走', '🈱' => '打', '🈲' => '禁', '🈳' => '空', '🈴' => '合', '🈵' => '満', '🈶' => '有', '🈷' => '月', '🈸' => '申', '🈹' => '割', '🈺' => '営', '🈻' => '配', '🉀' => '〔本〕', '🉁' => '〔三〕', '🉂' => '〔二〕', '🉃' => '〔安〕', '🉄' => '〔点〕', '🉅' => '〔打〕', '🉆' => '〔盗〕', '🉇' => '〔勝〕', '🉈' => '〔敗〕', '🉐' => '(得)', '🉑' => '(可)', '🯰' => '0', '🯱' => '1', '🯲' => '2', '🯳' => '3', '🯴' => '4', '🯵' => '5', '🯶' => '6', '🯷' => '7', '🯸' => '8', '🯹' => '9', '丽' => '丽', '丸' => '丸', '乁' => '乁', '𠄢' => '𠄢', '你' => '你', '侮' => '侮', '侻' => '侻', '倂' => '倂', '偺' => '偺', '備' => '備', '僧' => '僧', '像' => '像', '㒞' => '㒞', '𠘺' => '𠘺', '免' => '免', '兔' => '兔', '兤' => '兤', '具' => '具', '𠔜' => '𠔜', '㒹' => '㒹', '內' => '內', '再' => '再', '𠕋' => '𠕋', '冗' => '冗', '冤' => '冤', '仌' => '仌', '冬' => '冬', '况' => '况', '𩇟' => '𩇟', '凵' => '凵', '刃' => '刃', '㓟' => '㓟', '刻' => '刻', '剆' => '剆', '割' => '割', '剷' => '剷', '㔕' => '㔕', '勇' => '勇', '勉' => '勉', '勤' => '勤', '勺' => '勺', '包' => '包', '匆' => '匆', '北' => '北', '卉' => '卉', '卑' => '卑', '博' => '博', '即' => '即', '卽' => '卽', '卿' => '卿', '卿' => '卿', '卿' => '卿', '𠨬' => '𠨬', '灰' => '灰', '及' => '及', '叟' => '叟', '𠭣' => '𠭣', '叫' => '叫', '叱' => '叱', '吆' => '吆', '咞' => '咞', '吸' => '吸', '呈' => '呈', '周' => '周', '咢' => '咢', '哶' => '哶', '唐' => '唐', '啓' => '啓', '啣' => '啣', '善' => '善', '善' => '善', '喙' => '喙', '喫' => '喫', '喳' => '喳', '嗂' => '嗂', '圖' => '圖', '嘆' => '嘆', '圗' => '圗', '噑' => '噑', '噴' => '噴', '切' => '切', '壮' => '壮', '城' => '城', '埴' => '埴', '堍' => '堍', '型' => '型', '堲' => '堲', '報' => '報', '墬' => '墬', '𡓤' => '𡓤', '売' => '売', '壷' => '壷', '夆' => '夆', '多' => '多', '夢' => '夢', '奢' => '奢', '𡚨' => '𡚨', '𡛪' => '𡛪', '姬' => '姬', '娛' => '娛', '娧' => '娧', '姘' => '姘', '婦' => '婦', '㛮' => '㛮', '㛼' => '㛼', '嬈' => '嬈', '嬾' => '嬾', '嬾' => '嬾', '𡧈' => '𡧈', '寃' => '寃', '寘' => '寘', '寧' => '寧', '寳' => '寳', '𡬘' => '𡬘', '寿' => '寿', '将' => '将', '当' => '当', '尢' => '尢', '㞁' => '㞁', '屠' => '屠', '屮' => '屮', '峀' => '峀', '岍' => '岍', '𡷤' => '𡷤', '嵃' => '嵃', '𡷦' => '𡷦', '嵮' => '嵮', '嵫' => '嵫', '嵼' => '嵼', '巡' => '巡', '巢' => '巢', '㠯' => '㠯', '巽' => '巽', '帨' => '帨', '帽' => '帽', '幩' => '幩', '㡢' => '㡢', '𢆃' => '𢆃', '㡼' => '㡼', '庰' => '庰', '庳' => '庳', '庶' => '庶', '廊' => '廊', '𪎒' => '𪎒', '廾' => '廾', '𢌱' => '𢌱', '𢌱' => '𢌱', '舁' => '舁', '弢' => '弢', '弢' => '弢', '㣇' => '㣇', '𣊸' => '𣊸', '𦇚' => '𦇚', '形' => '形', '彫' => '彫', '㣣' => '㣣', '徚' => '徚', '忍' => '忍', '志' => '志', '忹' => '忹', '悁' => '悁', '㤺' => '㤺', '㤜' => '㤜', '悔' => '悔', '𢛔' => '𢛔', '惇' => '惇', '慈' => '慈', '慌' => '慌', '慎' => '慎', '慌' => '慌', '慺' => '慺', '憎' => '憎', '憲' => '憲', '憤' => '憤', '憯' => '憯', '懞' => '懞', '懲' => '懲', '懶' => '懶', '成' => '成', '戛' => '戛', '扝' => '扝', '抱' => '抱', '拔' => '拔', '捐' => '捐', '𢬌' => '𢬌', '挽' => '挽', '拼' => '拼', '捨' => '捨', '掃' => '掃', '揤' => '揤', '𢯱' => '𢯱', '搢' => '搢', '揅' => '揅', '掩' => '掩', '㨮' => '㨮', '摩' => '摩', '摾' => '摾', '撝' => '撝', '摷' => '摷', '㩬' => '㩬', '敏' => '敏', '敬' => '敬', '𣀊' => '𣀊', '旣' => '旣', '書' => '書', '晉' => '晉', '㬙' => '㬙', '暑' => '暑', '㬈' => '㬈', '㫤' => '㫤', '冒' => '冒', '冕' => '冕', '最' => '最', '暜' => '暜', '肭' => '肭', '䏙' => '䏙', '朗' => '朗', '望' => '望', '朡' => '朡', '杞' => '杞', '杓' => '杓', '𣏃' => '𣏃', '㭉' => '㭉', '柺' => '柺', '枅' => '枅', '桒' => '桒', '梅' => '梅', '𣑭' => '𣑭', '梎' => '梎', '栟' => '栟', '椔' => '椔', '㮝' => '㮝', '楂' => '楂', '榣' => '榣', '槪' => '槪', '檨' => '檨', '𣚣' => '𣚣', '櫛' => '櫛', '㰘' => '㰘', '次' => '次', '𣢧' => '𣢧', '歔' => '歔', '㱎' => '㱎', '歲' => '歲', '殟' => '殟', '殺' => '殺', '殻' => '殻', '𣪍' => '𣪍', '𡴋' => '𡴋', '𣫺' => '𣫺', '汎' => '汎', '𣲼' => '𣲼', '沿' => '沿', '泍' => '泍', '汧' => '汧', '洖' => '洖', '派' => '派', '海' => '海', '流' => '流', '浩' => '浩', '浸' => '浸', '涅' => '涅', '𣴞' => '𣴞', '洴' => '洴', '港' => '港', '湮' => '湮', '㴳' => '㴳', '滋' => '滋', '滇' => '滇', '𣻑' => '𣻑', '淹' => '淹', '潮' => '潮', '𣽞' => '𣽞', '𣾎' => '𣾎', '濆' => '濆', '瀹' => '瀹', '瀞' => '瀞', '瀛' => '瀛', '㶖' => '㶖', '灊' => '灊', '災' => '災', '灷' => '灷', '炭' => '炭', '𠔥' => '𠔥', '煅' => '煅', '𤉣' => '𤉣', '熜' => '熜', '𤎫' => '𤎫', '爨' => '爨', '爵' => '爵', '牐' => '牐', '𤘈' => '𤘈', '犀' => '犀', '犕' => '犕', '𤜵' => '𤜵', '𤠔' => '𤠔', '獺' => '獺', '王' => '王', '㺬' => '㺬', '玥' => '玥', '㺸' => '㺸', '㺸' => '㺸', '瑇' => '瑇', '瑜' => '瑜', '瑱' => '瑱', '璅' => '璅', '瓊' => '瓊', '㼛' => '㼛', '甤' => '甤', '𤰶' => '𤰶', '甾' => '甾', '𤲒' => '𤲒', '異' => '異', '𢆟' => '𢆟', '瘐' => '瘐', '𤾡' => '𤾡', '𤾸' => '𤾸', '𥁄' => '𥁄', '㿼' => '㿼', '䀈' => '䀈', '直' => '直', '𥃳' => '𥃳', '𥃲' => '𥃲', '𥄙' => '𥄙', '𥄳' => '𥄳', '眞' => '眞', '真' => '真', '真' => '真', '睊' => '睊', '䀹' => '䀹', '瞋' => '瞋', '䁆' => '䁆', '䂖' => '䂖', '𥐝' => '𥐝', '硎' => '硎', '碌' => '碌', '磌' => '磌', '䃣' => '䃣', '𥘦' => '𥘦', '祖' => '祖', '𥚚' => '𥚚', '𥛅' => '𥛅', '福' => '福', '秫' => '秫', '䄯' => '䄯', '穀' => '穀', '穊' => '穊', '穏' => '穏', '𥥼' => '𥥼', '𥪧' => '𥪧', '𥪧' => '𥪧', '竮' => '竮', '䈂' => '䈂', '𥮫' => '𥮫', '篆' => '篆', '築' => '築', '䈧' => '䈧', '𥲀' => '𥲀', '糒' => '糒', '䊠' => '䊠', '糨' => '糨', '糣' => '糣', '紀' => '紀', '𥾆' => '𥾆', '絣' => '絣', '䌁' => '䌁', '緇' => '緇', '縂' => '縂', '繅' => '繅', '䌴' => '䌴', '𦈨' => '𦈨', '𦉇' => '𦉇', '䍙' => '䍙', '𦋙' => '𦋙', '罺' => '罺', '𦌾' => '𦌾', '羕' => '羕', '翺' => '翺', '者' => '者', '𦓚' => '𦓚', '𦔣' => '𦔣', '聠' => '聠', '𦖨' => '𦖨', '聰' => '聰', '𣍟' => '𣍟', '䏕' => '䏕', '育' => '育', '脃' => '脃', '䐋' => '䐋', '脾' => '脾', '媵' => '媵', '𦞧' => '𦞧', '𦞵' => '𦞵', '𣎓' => '𣎓', '𣎜' => '𣎜', '舁' => '舁', '舄' => '舄', '辞' => '辞', '䑫' => '䑫', '芑' => '芑', '芋' => '芋', '芝' => '芝', '劳' => '劳', '花' => '花', '芳' => '芳', '芽' => '芽', '苦' => '苦', '𦬼' => '𦬼', '若' => '若', '茝' => '茝', '荣' => '荣', '莭' => '莭', '茣' => '茣', '莽' => '莽', '菧' => '菧', '著' => '著', '荓' => '荓', '菊' => '菊', '菌' => '菌', '菜' => '菜', '𦰶' => '𦰶', '𦵫' => '𦵫', '𦳕' => '𦳕', '䔫' => '䔫', '蓱' => '蓱', '蓳' => '蓳', '蔖' => '蔖', '𧏊' => '𧏊', '蕤' => '蕤', '𦼬' => '𦼬', '䕝' => '䕝', '䕡' => '䕡', '𦾱' => '𦾱', '𧃒' => '𧃒', '䕫' => '䕫', '虐' => '虐', '虜' => '虜', '虧' => '虧', '虩' => '虩', '蚩' => '蚩', '蚈' => '蚈', '蜎' => '蜎', '蛢' => '蛢', '蝹' => '蝹', '蜨' => '蜨', '蝫' => '蝫', '螆' => '螆', '䗗' => '䗗', '蟡' => '蟡', '蠁' => '蠁', '䗹' => '䗹', '衠' => '衠', '衣' => '衣', '𧙧' => '𧙧', '裗' => '裗', '裞' => '裞', '䘵' => '䘵', '裺' => '裺', '㒻' => '㒻', '𧢮' => '𧢮', '𧥦' => '𧥦', '䚾' => '䚾', '䛇' => '䛇', '誠' => '誠', '諭' => '諭', '變' => '變', '豕' => '豕', '𧲨' => '𧲨', '貫' => '貫', '賁' => '賁', '贛' => '贛', '起' => '起', '𧼯' => '𧼯', '𠠄' => '𠠄', '跋' => '跋', '趼' => '趼', '跰' => '跰', '𠣞' => '𠣞', '軔' => '軔', '輸' => '輸', '𨗒' => '𨗒', '𨗭' => '𨗭', '邔' => '邔', '郱' => '郱', '鄑' => '鄑', '𨜮' => '𨜮', '鄛' => '鄛', '鈸' => '鈸', '鋗' => '鋗', '鋘' => '鋘', '鉼' => '鉼', '鏹' => '鏹', '鐕' => '鐕', '𨯺' => '𨯺', '開' => '開', '䦕' => '䦕', '閷' => '閷', '𨵷' => '𨵷', '䧦' => '䧦', '雃' => '雃', '嶲' => '嶲', '霣' => '霣', '𩅅' => '𩅅', '𩈚' => '𩈚', '䩮' => '䩮', '䩶' => '䩶', '韠' => '韠', '𩐊' => '𩐊', '䪲' => '䪲', '𩒖' => '𩒖', '頋' => '頋', '頋' => '頋', '頩' => '頩', '𩖶' => '𩖶', '飢' => '飢', '䬳' => '䬳', '餩' => '餩', '馧' => '馧', '駂' => '駂', '駾' => '駾', '䯎' => '䯎', '𩬰' => '𩬰', '鬒' => '鬒', '鱀' => '鱀', '鳽' => '鳽', '䳎' => '䳎', '䳭' => '䳭', '鵧' => '鵧', '𪃎' => '𪃎', '䳸' => '䳸', '𪄅' => '𪄅', '𪈎' => '𪈎', '𪊑' => '𪊑', '麻' => '麻', '䵖' => '䵖', '黹' => '黹', '黾' => '黾', '鼅' => '鼅', '鼏' => '鼏', '鼖' => '鼖', '鼻' => '鼻', '𪘀' => '𪘀', 'Æ' => 'AE', 'Ð' => 'D', 'Ø' => 'O', 'Þ' => 'TH', 'ß' => 'ss', 'æ' => 'ae', 'ð' => 'd', 'ø' => 'o', 'þ' => 'th', 'Đ' => 'D', 'đ' => 'd', 'Ħ' => 'H', 'ħ' => 'h', 'ı' => 'i', 'ĸ' => 'q', 'Ł' => 'L', 'ł' => 'l', 'Ŋ' => 'N', 'ŋ' => 'n', 'Œ' => 'OE', 'œ' => 'oe', 'Ŧ' => 'T', 'ŧ' => 't', 'ƀ' => 'b', 'Ɓ' => 'B', 'Ƃ' => 'B', 'ƃ' => 'b', 'Ƈ' => 'C', 'ƈ' => 'c', 'Ɖ' => 'D', 'Ɗ' => 'D', 'Ƌ' => 'D', 'ƌ' => 'd', 'Ɛ' => 'E', 'Ƒ' => 'F', 'ƒ' => 'f', 'Ɠ' => 'G', 'ƕ' => 'hv', 'Ɩ' => 'I', 'Ɨ' => 'I', 'Ƙ' => 'K', 'ƙ' => 'k', 'ƚ' => 'l', 'Ɲ' => 'N', 'ƞ' => 'n', 'Ƣ' => 'OI', 'ƣ' => 'oi', 'Ƥ' => 'P', 'ƥ' => 'p', 'ƫ' => 't', 'Ƭ' => 'T', 'ƭ' => 't', 'Ʈ' => 'T', 'Ʋ' => 'V', 'Ƴ' => 'Y', 'ƴ' => 'y', 'Ƶ' => 'Z', 'ƶ' => 'z', 'Ǥ' => 'G', 'ǥ' => 'g', 'ȡ' => 'd', 'Ȥ' => 'Z', 'ȥ' => 'z', 'ȴ' => 'l', 'ȵ' => 'n', 'ȶ' => 't', 'ȷ' => 'j', 'ȸ' => 'db', 'ȹ' => 'qp', 'Ⱥ' => 'A', 'Ȼ' => 'C', 'ȼ' => 'c', 'Ƚ' => 'L', 'Ⱦ' => 'T', 'ȿ' => 's', 'ɀ' => 'z', 'Ƀ' => 'B', 'Ʉ' => 'U', 'Ɇ' => 'E', 'ɇ' => 'e', 'Ɉ' => 'J', 'ɉ' => 'j', 'Ɍ' => 'R', 'ɍ' => 'r', 'Ɏ' => 'Y', 'ɏ' => 'y', 'ɓ' => 'b', 'ɕ' => 'c', 'ɖ' => 'd', 'ɗ' => 'd', 'ɛ' => 'e', 'ɟ' => 'j', 'ɠ' => 'g', 'ɡ' => 'g', 'ɢ' => 'G', 'ɦ' => 'h', 'ɧ' => 'h', 'ɨ' => 'i', 'ɪ' => 'I', 'ɫ' => 'l', 'ɬ' => 'l', 'ɭ' => 'l', 'ɱ' => 'm', 'ɲ' => 'n', 'ɳ' => 'n', 'ɴ' => 'N', 'ɶ' => 'OE', 'ɼ' => 'r', 'ɽ' => 'r', 'ɾ' => 'r', 'ʀ' => 'R', 'ʂ' => 's', 'ʈ' => 't', 'ʉ' => 'u', 'ʋ' => 'v', 'ʏ' => 'Y', 'ʐ' => 'z', 'ʑ' => 'z', 'ʙ' => 'B', 'ʛ' => 'G', 'ʜ' => 'H', 'ʝ' => 'j', 'ʟ' => 'L', 'ʠ' => 'q', 'ʣ' => 'dz', 'ʥ' => 'dz', 'ʦ' => 'ts', 'ʪ' => 'ls', 'ʫ' => 'lz', 'ᴀ' => 'A', 'ᴁ' => 'AE', 'ᴃ' => 'B', 'ᴄ' => 'C', 'ᴅ' => 'D', 'ᴆ' => 'D', 'ᴇ' => 'E', 'ᴊ' => 'J', 'ᴋ' => 'K', 'ᴌ' => 'L', 'ᴍ' => 'M', 'ᴏ' => 'O', 'ᴘ' => 'P', 'ᴛ' => 'T', 'ᴜ' => 'U', 'ᴠ' => 'V', 'ᴡ' => 'W', 'ᴢ' => 'Z', 'ᵫ' => 'ue', 'ᵬ' => 'b', 'ᵭ' => 'd', 'ᵮ' => 'f', 'ᵯ' => 'm', 'ᵰ' => 'n', 'ᵱ' => 'p', 'ᵲ' => 'r', 'ᵳ' => 'r', 'ᵴ' => 's', 'ᵵ' => 't', 'ᵶ' => 'z', 'ᵺ' => 'th', 'ᵻ' => 'I', 'ᵽ' => 'p', 'ᵾ' => 'U', 'ᶀ' => 'b', 'ᶁ' => 'd', 'ᶂ' => 'f', 'ᶃ' => 'g', 'ᶄ' => 'k', 'ᶅ' => 'l', 'ᶆ' => 'm', 'ᶇ' => 'n', 'ᶈ' => 'p', 'ᶉ' => 'r', 'ᶊ' => 's', 'ᶌ' => 'v', 'ᶍ' => 'x', 'ᶎ' => 'z', 'ᶏ' => 'a', 'ᶑ' => 'd', 'ᶒ' => 'e', 'ᶓ' => 'e', 'ᶖ' => 'i', 'ᶙ' => 'u', 'ẜ' => 's', 'ẝ' => 's', 'ẞ' => 'SS', 'Ỻ' => 'LL', 'ỻ' => 'll', 'Ỽ' => 'V', 'ỽ' => 'v', 'Ỿ' => 'Y', 'ỿ' => 'y', 'Ⱡ' => 'L', 'ⱡ' => 'l', 'Ɫ' => 'L', 'Ᵽ' => 'P', 'Ɽ' => 'R', 'ⱥ' => 'a', 'ⱦ' => 't', 'Ⱨ' => 'H', 'ⱨ' => 'h', 'Ⱪ' => 'K', 'ⱪ' => 'k', 'Ⱬ' => 'Z', 'ⱬ' => 'z', 'Ɱ' => 'M', 'ⱱ' => 'v', 'Ⱳ' => 'W', 'ⱳ' => 'w', 'ⱴ' => 'v', 'ⱸ' => 'e', 'ⱺ' => 'o', 'Ȿ' => 'S', 'Ɀ' => 'Z', 'ꜰ' => 'F', 'ꜱ' => 'S', 'Ꜳ' => 'AA', 'ꜳ' => 'aa', 'Ꜵ' => 'AO', 'ꜵ' => 'ao', 'Ꜷ' => 'AU', 'ꜷ' => 'au', 'Ꜹ' => 'AV', 'ꜹ' => 'av', 'Ꜻ' => 'AV', 'ꜻ' => 'av', 'Ꜽ' => 'AY', 'ꜽ' => 'ay', 'Ꝁ' => 'K', 'ꝁ' => 'k', 'Ꝃ' => 'K', 'ꝃ' => 'k', 'Ꝅ' => 'K', 'ꝅ' => 'k', 'Ꝇ' => 'L', 'ꝇ' => 'l', 'Ꝉ' => 'L', 'ꝉ' => 'l', 'Ꝋ' => 'O', 'ꝋ' => 'o', 'Ꝍ' => 'O', 'ꝍ' => 'o', 'Ꝏ' => 'OO', 'ꝏ' => 'oo', 'Ꝑ' => 'P', 'ꝑ' => 'p', 'Ꝓ' => 'P', 'ꝓ' => 'p', 'Ꝕ' => 'P', 'ꝕ' => 'p', 'Ꝗ' => 'Q', 'ꝗ' => 'q', 'Ꝙ' => 'Q', 'ꝙ' => 'q', 'Ꝟ' => 'V', 'ꝟ' => 'v', 'Ꝡ' => 'VY', 'ꝡ' => 'vy', 'Ꝥ' => 'TH', 'ꝥ' => 'th', 'Ꝧ' => 'TH', 'ꝧ' => 'th', 'ꝱ' => 'd', 'ꝲ' => 'l', 'ꝳ' => 'm', 'ꝴ' => 'n', 'ꝵ' => 'r', 'ꝶ' => 'R', 'ꝷ' => 't', 'Ꝺ' => 'D', 'ꝺ' => 'd', 'Ꝼ' => 'F', 'ꝼ' => 'f', 'Ꞇ' => 'T', 'ꞇ' => 't', 'Ꞑ' => 'N', 'ꞑ' => 'n', 'Ꞓ' => 'C', 'ꞓ' => 'c', 'Ꞡ' => 'G', 'ꞡ' => 'g', 'Ꞣ' => 'K', 'ꞣ' => 'k', 'Ꞥ' => 'N', 'ꞥ' => 'n', 'Ꞧ' => 'R', 'ꞧ' => 'r', 'Ꞩ' => 'S', 'ꞩ' => 's', 'Ɦ' => 'H', '©' => '(C)', '®' => '(R)', '₠' => 'CE', '₢' => 'Cr', '₣' => 'Fr.', '₤' => 'L.', '₧' => 'Pts', '₹' => 'Rs', '₺' => 'TL', '℗' => '(P)', '℘' => 'P', '℞' => 'Rx', '〇' => '0', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', 'ʹ' => '\'', 'ʺ' => '"', 'ʻ' => '\'', 'ʼ' => '\'', 'ʽ' => '\'', 'ˈ' => '\'', 'ˋ' => '`', '‘' => '\'', '’' => '\'', '‚' => ',', '‛' => '\'', '“' => '"', '”' => '"', '„' => ',,', '‟' => '"', '′' => '\'', '〝' => '"', '〞' => '"', '«' => '<<', '»' => '>>', '‹' => '<', '›' => '>', '' => '-', '‐' => '-', '‑' => '-', '‒' => '-', '–' => '-', '—' => '-', '―' => '-', '︱' => '-', '︲' => '-', '¡' => '!', '¿' => '?', '˂' => '<', '˃' => '>', '˄' => '^', 'ˆ' => '^', 'ː' => ':', '˜' => '~', '‖' => '||', '⁄' => '/', '⁅' => '[', '⁆' => ']', '⁎' => '*', '、' => ',', '。' => '.', '〈' => '<', '〉' => '>', '《' => '<<', '》' => '>>', '〔' => '[', '〕' => ']', '〘' => '[', '〙' => ']', '〚' => '[', '〛' => ']', '︐' => ',', '︑' => ',', '︒' => '.', '︓' => ':', '︔' => ';', '︕' => '!', '︖' => '?', '︙' => '...', '︰' => '..', '︵' => '(', '︶' => ')', '︷' => '{', '︸' => '}', '︹' => '[', '︺' => ']', '︽' => '<<', '︾' => '>>', '︿' => '<', '﹀' => '>', '﹇' => '[', '﹈' => ']', '±' => '+/-', '×' => '*', '÷' => '/', '˖' => '+', '˗' => '-', '−' => '-', '∕' => '/', '∖' => '\\', '∣' => '|', '∥' => '||', '≪' => '<<', '≫' => '>>', '⦅' => '((', '⦆' => '))', ); PK2A#]l����Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-14.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => 'Ḃ', '�' => 'ḃ', '�' => '£', '�' => 'Ċ', '�' => 'ċ', '�' => 'Ḋ', '�' => '§', '�' => 'Ẁ', '�' => '©', '�' => 'Ẃ', '�' => 'ḋ', '�' => 'Ỳ', '�' => '', '�' => '®', '�' => 'Ÿ', '�' => 'Ḟ', '�' => 'ḟ', '�' => 'Ġ', '�' => 'ġ', '�' => 'Ṁ', '�' => 'ṁ', '�' => '¶', '�' => 'Ṗ', '�' => 'ẁ', '�' => 'ṗ', '�' => 'ẃ', '�' => 'Ṡ', '�' => 'ỳ', '�' => 'Ẅ', '�' => 'ẅ', '�' => 'ṡ', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ŵ', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => 'Ṫ', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'Ý', '�' => 'Ŷ', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ŵ', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => 'ṫ', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ý', '�' => 'ŷ', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]�C�� � Evendor/symfony/polyfill-iconv/Resources/charset/from.windows-1253.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => '‰', '�' => '‹', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '™', '�' => '›', '�' => ' ', '�' => '΅', '�' => 'Ά', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '―', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '΄', '�' => 'µ', '�' => '¶', '�' => '·', '�' => 'Έ', '�' => 'Ή', '�' => 'Ί', '�' => '»', '�' => 'Ό', '�' => '½', '�' => 'Ύ', '�' => 'Ώ', '�' => 'ΐ', '�' => 'Α', '�' => 'Β', '�' => 'Γ', '�' => 'Δ', '�' => 'Ε', '�' => 'Ζ', '�' => 'Η', '�' => 'Θ', '�' => 'Ι', '�' => 'Κ', '�' => 'Λ', '�' => 'Μ', '�' => 'Ν', '�' => 'Ξ', '�' => 'Ο', '�' => 'Π', '�' => 'Ρ', '�' => 'Σ', '�' => 'Τ', '�' => 'Υ', '�' => 'Φ', '�' => 'Χ', '�' => 'Ψ', '�' => 'Ω', '�' => 'Ϊ', '�' => 'Ϋ', '�' => 'ά', '�' => 'έ', '�' => 'ή', '�' => 'ί', '�' => 'ΰ', '�' => 'α', '�' => 'β', '�' => 'γ', '�' => 'δ', '�' => 'ε', '�' => 'ζ', '�' => 'η', '�' => 'θ', '�' => 'ι', '�' => 'κ', '�' => 'λ', '�' => 'μ', '�' => 'ν', '�' => 'ξ', '�' => 'ο', '�' => 'π', '�' => 'ρ', '�' => 'ς', '�' => 'σ', '�' => 'τ', '�' => 'υ', '�' => 'φ', '�' => 'χ', '�' => 'ψ', '�' => 'ω', '�' => 'ϊ', '�' => 'ϋ', '�' => 'ό', '�' => 'ύ', '�' => 'ώ', ); $result =& $data; unset($data); return $result; PK2A#]��kkEvendor/symfony/polyfill-iconv/Resources/charset/from.windows-1254.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '€', '�' => '‚', '�' => 'ƒ', '�' => '„', '�' => '…', '�' => '†', '�' => '‡', '�' => 'ˆ', '�' => '‰', '�' => 'Š', '�' => '‹', '�' => 'Œ', '�' => '‘', '�' => '’', '�' => '“', '�' => '”', '�' => '•', '�' => '–', '�' => '—', '�' => '˜', '�' => '™', '�' => 'š', '�' => '›', '�' => 'œ', '�' => 'Ÿ', '�' => ' ', '�' => '¡', '�' => '¢', '�' => '£', '�' => '¤', '�' => '¥', '�' => '¦', '�' => '§', '�' => '¨', '�' => '©', '�' => 'ª', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => '¯', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '´', '�' => 'µ', '�' => '¶', '�' => '·', '�' => '¸', '�' => '¹', '�' => 'º', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => '¿', '�' => 'À', '�' => 'Á', '�' => 'Â', '�' => 'Ã', '�' => 'Ä', '�' => 'Å', '�' => 'Æ', '�' => 'Ç', '�' => 'È', '�' => 'É', '�' => 'Ê', '�' => 'Ë', '�' => 'Ì', '�' => 'Í', '�' => 'Î', '�' => 'Ï', '�' => 'Ğ', '�' => 'Ñ', '�' => 'Ò', '�' => 'Ó', '�' => 'Ô', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ø', '�' => 'Ù', '�' => 'Ú', '�' => 'Û', '�' => 'Ü', '�' => 'İ', '�' => 'Ş', '�' => 'ß', '�' => 'à', '�' => 'á', '�' => 'â', '�' => 'ã', '�' => 'ä', '�' => 'å', '�' => 'æ', '�' => 'ç', '�' => 'è', '�' => 'é', '�' => 'ê', '�' => 'ë', '�' => 'ì', '�' => 'í', '�' => 'î', '�' => 'ï', '�' => 'ğ', '�' => 'ñ', '�' => 'ò', '�' => 'ó', '�' => 'ô', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ø', '�' => 'ù', '�' => 'ú', '�' => 'û', '�' => 'ü', '�' => 'ı', '�' => 'ş', '�' => 'ÿ', ); $result =& $data; unset($data); return $result; PK2A#]�94|��Dvendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-13.phpnu�[���<?php static $data = array ( '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', ' ' => ' ', '' => '', '' => '', ' ' => ' ', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', ' ' => ' ', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', 0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => '', '�' => ' ', '�' => '”', '�' => '¢', '�' => '£', '�' => '¤', '�' => '„', '�' => '¦', '�' => '§', '�' => 'Ø', '�' => '©', '�' => 'Ŗ', '�' => '«', '�' => '¬', '�' => '', '�' => '®', '�' => 'Æ', '�' => '°', '�' => '±', '�' => '²', '�' => '³', '�' => '“', '�' => 'µ', '�' => '¶', '�' => '·', '�' => 'ø', '�' => '¹', '�' => 'ŗ', '�' => '»', '�' => '¼', '�' => '½', '�' => '¾', '�' => 'æ', '�' => 'Ą', '�' => 'Į', '�' => 'Ā', '�' => 'Ć', '�' => 'Ä', '�' => 'Å', '�' => 'Ę', '�' => 'Ē', '�' => 'Č', '�' => 'É', '�' => 'Ź', '�' => 'Ė', '�' => 'Ģ', '�' => 'Ķ', '�' => 'Ī', '�' => 'Ļ', '�' => 'Š', '�' => 'Ń', '�' => 'Ņ', '�' => 'Ó', '�' => 'Ō', '�' => 'Õ', '�' => 'Ö', '�' => '×', '�' => 'Ų', '�' => 'Ł', '�' => 'Ś', '�' => 'Ū', '�' => 'Ü', '�' => 'Ż', '�' => 'Ž', '�' => 'ß', '�' => 'ą', '�' => 'į', '�' => 'ā', '�' => 'ć', '�' => 'ä', '�' => 'å', '�' => 'ę', '�' => 'ē', '�' => 'č', '�' => 'é', '�' => 'ź', '�' => 'ė', '�' => 'ģ', '�' => 'ķ', '�' => 'ī', '�' => 'ļ', '�' => 'š', '�' => 'ń', '�' => 'ņ', '�' => 'ó', '�' => 'ō', '�' => 'õ', '�' => 'ö', '�' => '÷', '�' => 'ų', '�' => 'ł', '�' => 'ś', '�' => 'ū', '�' => 'ü', '�' => 'ż', '�' => 'ž', '�' => '’', ); $result =& $data; unset($data); return $result; PK2A#]rg����1vendor/symfony/deprecation-contracts/function.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (!function_exists('trigger_deprecation')) { /** * Triggers a silenced deprecation notice. * * @param string $package The name of the Composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The message of the deprecation * @param mixed ...$args Values to insert in the message using printf() formatting * * @author Nicolas Grekas <p@tchwork.com> */ function trigger_deprecation(string $package, string $version, string $message, ...$args): void { @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); } } PK2A#]�2)),vendor/symfony/deprecation-contracts/LICENSEnu�[���Copyright (c) 2020-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]��(�cc$vendor/symfony/string/LazyString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; /** * A string whose value is computed lazily by a callback. * * @author Nicolas Grekas <p@tchwork.com> */ class LazyString implements \Stringable, \JsonSerializable { private $value; /** * @param callable|array $callback A callable or a [Closure, method] lazy-callable * * @return static */ public static function fromCallable($callback, ...$arguments): self { if (!\is_callable($callback) && !(\is_array($callback) && isset($callback[0]) && $callback[0] instanceof \Closure && 2 >= \count($callback))) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, get_debug_type($callback))); } $lazyString = new static(); $lazyString->value = static function () use (&$callback, &$arguments, &$value): string { if (null !== $arguments) { if (!\is_callable($callback)) { $callback[0] = $callback[0](); $callback[1] = $callback[1] ?? '__invoke'; } $value = $callback(...$arguments); $callback = self::getPrettyName($callback); $arguments = null; } return $value ?? ''; }; return $lazyString; } /** * @param string|int|float|bool|\Stringable $value * * @return static */ public static function fromStringable($value): self { if (!self::isStringable($value)) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a scalar or a stringable object, "%s" given.', __METHOD__, get_debug_type($value))); } if (\is_object($value)) { return static::fromCallable([$value, '__toString']); } $lazyString = new static(); $lazyString->value = (string) $value; return $lazyString; } /** * Tells whether the provided value can be cast to string. */ final public static function isStringable($value): bool { return \is_string($value) || $value instanceof self || (\is_object($value) ? method_exists($value, '__toString') : \is_scalar($value)); } /** * Casts scalars and stringable objects to strings. * * @param object|string|int|float|bool $value * * @throws \TypeError When the provided value is not stringable */ final public static function resolve($value): string { return $value; } /** * @return string */ public function __toString() { if (\is_string($this->value)) { return $this->value; } try { return $this->value = ($this->value)(); } catch (\Throwable $e) { if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) { $type = explode(', ', $e->getMessage()); $type = substr(array_pop($type), 0, -\strlen(' returned')); $r = new \ReflectionFunction($this->value); $callback = $r->getStaticVariables()['callback']; $e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type)); } if (\PHP_VERSION_ID < 70400) { // leverage the ErrorHandler component with graceful fallback when it's not available return trigger_error($e, \E_USER_ERROR); } throw $e; } } public function __sleep(): array { $this->__toString(); return ['value']; } public function jsonSerialize(): string { return $this->__toString(); } private function __construct() { } private static function getPrettyName(callable $callback): string { if (\is_string($callback)) { return $callback; } if (\is_array($callback)) { $class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0]; $method = $callback[1]; } elseif ($callback instanceof \Closure) { $r = new \ReflectionFunction($callback); if (false !== strpos($r->name, '{closure}') || !$class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return $r->name; } $class = $class->name; $method = $r->name; } else { $class = get_debug_type($callback); $method = '__invoke'; } return $class.'::'.$method; } } PK2A#]j8��C2C2'vendor/symfony/string/UnicodeString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; /** * Represents a string of Unicode grapheme clusters encoded as UTF-8. * * A letter followed by combining characters (accents typically) form what Unicode defines * as a grapheme cluster: a character as humans mean it in written texts. This class knows * about the concept and won't split a letter apart from its combining accents. It also * ensures all string comparisons happen on their canonically-composed representation, * ignoring e.g. the order in which accents are listed when a letter has many of them. * * @see https://unicode.org/reports/tr15/ * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class UnicodeString extends AbstractUnicodeString { public function __construct(string $string = '') { $this->string = normalizer_is_normalized($string) ? $string : normalizer_normalize($string); if (false === $this->string) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } } public function append(string ...$suffix): AbstractString { $str = clone $this; $str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix)); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); if (false === $str->string) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $rx = '/('; while (65535 < $length) { $rx .= '\X{65535}'; $length -= 65535; } $rx .= '\X{'.$length.'})/u'; $str = clone $this; $chunks = []; foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function endsWith($suffix): bool { if ($suffix instanceof AbstractString) { $suffix = $suffix->string; } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { return parent::endsWith($suffix); } else { $suffix = (string) $suffix; } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form); if ('' === $suffix || false === $suffix) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8'); } return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)); } public function equalsTo($string): bool { if ($string instanceof AbstractString) { $string = $string->string; } elseif (\is_array($string) || $string instanceof \Traversable) { return parent::equalsTo($string); } else { $string = (string) $string; } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form); if ('' !== $string && false !== $string && $this->ignoreCase) { return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); } return $string === $this->string; } public function indexOf($needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOf($needle, $offset); } else { $needle = (string) $needle; } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); if ('' === $needle || false === $needle) { return null; } try { $i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset); } catch (\ValueError $e) { return null; } return false === $i ? null : $i; } public function indexOfLast($needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOfLast($needle, $offset); } else { $needle = (string) $needle; } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); if ('' === $needle || false === $needle) { return null; } $string = $this->string; if (0 > $offset) { // workaround https://bugs.php.net/74264 if (0 > $offset += grapheme_strlen($needle)) { $string = grapheme_substr($string, 0, $offset); } $offset = 0; } $i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset); return false === $i ? null : $i; } public function join(array $strings, string $lastGlue = null): AbstractString { $str = parent::join($strings, $lastGlue); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); return $str; } public function length(): int { return grapheme_strlen($this->string); } /** * @return static */ public function normalize(int $form = self::NFC): parent { $str = clone $this; if (\in_array($form, [self::NFC, self::NFKC], true)) { normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); } elseif (!\in_array($form, [self::NFD, self::NFKD], true)) { throw new InvalidArgumentException('Unsupported normalization form.'); } elseif (!normalizer_is_normalized($str->string, $form)) { $str->string = normalizer_normalize($str->string, $form); $str->ignoreCase = null; } return $str; } public function prepend(string ...$prefix): AbstractString { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); if (false === $str->string) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function replace(string $from, string $to): AbstractString { $str = clone $this; normalizer_is_normalized($from) ?: $from = normalizer_normalize($from); if ('' !== $from && false !== $from) { $tail = $str->string; $result = ''; $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; while ('' !== $tail && false !== $i = $indexOf($tail, $from)) { $slice = grapheme_substr($tail, 0, $i); $result .= $slice.$to; $tail = substr($tail, \strlen($slice) + \strlen($from)); } $str->string = $result.$tail; normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); if (false === $str->string) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } } return $str; } public function replaceMatches(string $fromRegexp, $to): AbstractString { $str = parent::replaceMatches($fromRegexp, $to); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); return $str; } public function slice(int $start = 0, int $length = null): AbstractString { $str = clone $this; if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) { $start = 0; } $str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647); return $str; } public function splice(string $replacement, int $start = 0, int $length = null): AbstractString { $str = clone $this; if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) { $start = 0; } $start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0; $length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length; $str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); if (false === $str->string) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function split(string $delimiter, int $limit = null, int $flags = null): array { if (1 > $limit = $limit ?? 2147483647) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter.'u', $limit, $flags); } normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter); if (false === $delimiter) { throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); } $str = clone $this; $tail = $this->string; $chunks = []; $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) { $str->string = grapheme_substr($tail, 0, $i); $chunks[] = clone $str; $tail = substr($tail, \strlen($str->string) + \strlen($delimiter)); --$limit; } $str->string = $tail; $chunks[] = clone $str; return $chunks; } public function startsWith($prefix): bool { if ($prefix instanceof AbstractString) { $prefix = $prefix->string; } elseif (\is_array($prefix) || $prefix instanceof \Traversable) { return parent::startsWith($prefix); } else { $prefix = (string) $prefix; } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form); if ('' === $prefix || false === $prefix) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8'); } return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES); } public function __wakeup() { if (!\is_string($this->string)) { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); } public function __clone() { if (null === $this->ignoreCase) { normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); } $this->ignoreCase = false; } } PK2A#]Q�ccCC6vendor/symfony/string/Inflector/InflectorInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; interface InflectorInterface { /** * Returns the singular forms of a string. * * If the method can't determine the form with certainty, several possible singulars are returned. * * @return string[] */ public function singularize(string $plural): array; /** * Returns the plural forms of a string. * * If the method can't determine the form with certainty, several possible plurals are returned. * * @return string[] */ public function pluralize(string $singular): array; } PK2A#]W����3vendor/symfony/string/Inflector/FrenchInflector.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; /** * French inflector. * * This class does only inflect nouns; not adjectives nor composed words like "soixante-dix". */ final class FrenchInflector implements InflectorInterface { /** * A list of all rules for pluralise. * * @see https://la-conjugaison.nouvelobs.com/regles/grammaire/le-pluriel-des-noms-121.php */ private const PLURALIZE_REGEXP = [ // First entry: regexp // Second entry: replacement // Words finishing with "s", "x" or "z" are invariables // Les mots finissant par "s", "x" ou "z" sont invariables ['/(s|x|z)$/i', '\1'], // Words finishing with "eau" are pluralized with a "x" // Les mots finissant par "eau" prennent tous un "x" au pluriel ['/(eau)$/i', '\1x'], // Words finishing with "au" are pluralized with a "x" excepted "landau" // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" ['/^(landau)$/i', '\1s'], ['/(au)$/i', '\1x'], // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" ['/^(pneu|bleu|émeu)$/i', '\1s'], ['/(eu)$/i', '\1x'], // Words finishing with "al" are pluralized with a "aux" excepted // Les mots finissant en "al" se terminent en "aux" sauf ['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'], ['/al$/i', '\1aux'], // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux ['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'], // Bijou, caillou, chou, genou, hibou, joujou et pou qui prennent un x au pluriel ['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'], // Invariable words ['/^(cinquante|soixante|mille)$/i', '\1'], // French titles ['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'], ['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'], ]; /** * A list of all rules for singularize. */ private const SINGULARIZE_REGEXP = [ // First entry: regexp // Second entry: replacement // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux ['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'], // Words finishing with "eau" are pluralized with a "x" // Les mots finissant par "eau" prennent tous un "x" au pluriel ['/(eau)x$/i', '\1'], // Words finishing with "al" are pluralized with a "aux" expected // Les mots finissant en "al" se terminent en "aux" sauf ['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'], // Words finishing with "au" are pluralized with a "x" excepted "landau" // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" ['/(au)x$/i', '\1'], // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" ['/(eu)x$/i', '\1'], // Words finishing with "ou" are pluralized with a "s" excepted bijou, caillou, chou, genou, hibou, joujou, pou // Les mots finissant par "ou" prennent un "s" sauf bijou, caillou, chou, genou, hibou, joujou, pou ['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'], // French titles ['/^mes(dame|demoiselle)s$/', 'ma\1'], ['/^Mes(dame|demoiselle)s$/', 'Ma\1'], ['/^mes(sieur|seigneur)s$/', 'mon\1'], ['/^Mes(sieur|seigneur)s$/', 'Mon\1'], // Default rule ['/s$/i', ''], ]; /** * A list of words which should not be inflected. * This list is only used by singularize. */ private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i'; /** * {@inheritdoc} */ public function singularize(string $plural): array { if ($this->isInflectedWord($plural)) { return [$plural]; } foreach (self::SINGULARIZE_REGEXP as $rule) { [$regexp, $replace] = $rule; if (1 === preg_match($regexp, $plural)) { return [preg_replace($regexp, $replace, $plural)]; } } return [$plural]; } /** * {@inheritdoc} */ public function pluralize(string $singular): array { if ($this->isInflectedWord($singular)) { return [$singular]; } foreach (self::PLURALIZE_REGEXP as $rule) { [$regexp, $replace] = $rule; if (1 === preg_match($regexp, $singular)) { return [preg_replace($regexp, $replace, $singular)]; } } return [$singular.'s']; } private function isInflectedWord(string $word): bool { return 1 === preg_match(self::UNINFLECTED, $word); } } PK2A#]��f8>8>4vendor/symfony/string/Inflector/EnglishInflector.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; final class EnglishInflector implements InflectorInterface { /** * Map English plural to singular suffixes. * * @see http://english-zone.com/spelling/plurals.html */ private const PLURAL_MAP = [ // First entry: plural suffix, reversed // Second entry: length of plural suffix // Third entry: Whether the suffix may succeed a vocal // Fourth entry: Whether the suffix may succeed a consonant // Fifth entry: singular suffix, normal // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['a', 1, true, true, ['on', 'um']], // nebulae (nebula) ['ea', 2, true, true, 'a'], // services (service) ['secivres', 8, true, true, 'service'], // mice (mouse), lice (louse) ['eci', 3, false, true, 'ouse'], // geese (goose) ['esee', 4, false, true, 'oose'], // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) ['i', 1, true, true, 'us'], // men (man), women (woman) ['nem', 3, true, true, 'man'], // children (child) ['nerdlihc', 8, true, true, 'child'], // oxen (ox) ['nexo', 4, false, false, 'ox'], // indices (index), appendices (appendix), prices (price) ['seci', 4, false, true, ['ex', 'ix', 'ice']], // codes (code) ['sedoc', 5, false, true, 'code'], // selfies (selfie) ['seifles', 7, true, true, 'selfie'], // zombies (zombie) ['seibmoz', 7, true, true, 'zombie'], // movies (movie) ['seivom', 6, true, true, 'movie'], // names (name) ['seman', 5, true, false, 'name'], // conspectuses (conspectus), prospectuses (prospectus) ['sesutcep', 8, true, true, 'pectus'], // feet (foot) ['teef', 4, true, true, 'foot'], // geese (goose) ['eseeg', 5, true, true, 'goose'], // teeth (tooth) ['hteet', 5, true, true, 'tooth'], // news (news) ['swen', 4, true, true, 'news'], // series (series) ['seires', 6, true, true, 'series'], // babies (baby) ['sei', 3, false, true, 'y'], // accesses (access), addresses (address), kisses (kiss) ['sess', 4, true, false, 'ss'], // statuses (status) ['sesutats', 8, true, true, 'status'], // analyses (analysis), ellipses (ellipsis), fungi (fungus), // neuroses (neurosis), theses (thesis), emphases (emphasis), // oases (oasis), crises (crisis), houses (house), bases (base), // atlases (atlas) ['ses', 3, true, true, ['s', 'se', 'sis']], // objectives (objective), alternative (alternatives) ['sevit', 5, true, true, 'tive'], // drives (drive) ['sevird', 6, false, true, 'drive'], // lives (life), wives (wife) ['sevi', 4, false, true, 'ife'], // moves (move) ['sevom', 5, true, true, 'move'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff) ['sev', 3, true, true, ['f', 've', 'ff']], // axes (axis), axes (ax), axes (axe) ['sexa', 4, false, false, ['ax', 'axe', 'axis']], // indexes (index), matrixes (matrix) ['sex', 3, true, false, 'x'], // quizzes (quiz) ['sezz', 4, true, false, 'z'], // bureaus (bureau) ['suae', 4, false, true, 'eau'], // fees (fee), trees (tree), employees (employee) ['see', 3, true, true, 'ee'], // edges (edge) ['segd', 4, true, true, 'dge'], // roses (rose), garages (garage), cassettes (cassette), // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), // shoes (shoe) ['se', 2, true, true, ['', 'e']], // status (status) ['sutats', 6, true, true, 'status'], // tags (tag) ['s', 1, true, true, ''], // chateaux (chateau) ['xuae', 4, false, true, 'eau'], // people (person) ['elpoep', 6, true, true, 'person'], ]; /** * Map English singular to plural suffixes. * * @see http://english-zone.com/spelling/plurals.html */ private const SINGULAR_MAP = [ // First entry: singular suffix, reversed // Second entry: length of singular suffix // Third entry: Whether the suffix may succeed a vocal // Fourth entry: Whether the suffix may succeed a consonant // Fifth entry: plural suffix, normal // criterion (criteria) ['airetirc', 8, false, false, 'criterion'], // nebulae (nebula) ['aluben', 6, false, false, 'nebulae'], // children (child) ['dlihc', 5, true, true, 'children'], // prices (price) ['eci', 3, false, true, 'ices'], // services (service) ['ecivres', 7, true, true, 'services'], // lives (life), wives (wife) ['efi', 3, false, true, 'ives'], // selfies (selfie) ['eifles', 6, true, true, 'selfies'], // movies (movie) ['eivom', 5, true, true, 'movies'], // lice (louse) ['esuol', 5, false, true, 'lice'], // mice (mouse) ['esuom', 5, false, true, 'mice'], // geese (goose) ['esoo', 4, false, true, 'eese'], // houses (house), bases (base) ['es', 2, true, true, 'ses'], // geese (goose) ['esoog', 5, true, true, 'geese'], // caves (cave) ['ev', 2, true, true, 'ves'], // drives (drive) ['evird', 5, false, true, 'drives'], // objectives (objective), alternative (alternatives) ['evit', 4, true, true, 'tives'], // moves (move) ['evom', 4, true, true, 'moves'], // staves (staff) ['ffats', 5, true, true, 'staves'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) ['ff', 2, true, true, 'ffs'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) ['f', 1, true, true, ['fs', 'ves']], // arches (arch) ['hc', 2, true, true, 'ches'], // bushes (bush) ['hs', 2, true, true, 'shes'], // teeth (tooth) ['htoot', 5, true, true, 'teeth'], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['mu', 2, true, true, 'a'], // men (man), women (woman) ['nam', 3, true, true, 'men'], // people (person) ['nosrep', 6, true, true, ['persons', 'people']], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['noi', 3, true, true, 'ions'], // coupon (coupons) ['nop', 3, true, true, 'pons'], // seasons (season), treasons (treason), poisons (poison), lessons (lesson) ['nos', 3, true, true, 'sons'], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['no', 2, true, true, 'a'], // echoes (echo) ['ohce', 4, true, true, 'echoes'], // heroes (hero) ['oreh', 4, true, true, 'heroes'], // atlases (atlas) ['salta', 5, true, true, 'atlases'], // irises (iris) ['siri', 4, true, true, 'irises'], // analyses (analysis), ellipses (ellipsis), neuroses (neurosis) // theses (thesis), emphases (emphasis), oases (oasis), // crises (crisis) ['sis', 3, true, true, 'ses'], // accesses (access), addresses (address), kisses (kiss) ['ss', 2, true, false, 'sses'], // syllabi (syllabus) ['suballys', 8, true, true, 'syllabi'], // buses (bus) ['sub', 3, true, true, 'buses'], // circuses (circus) ['suc', 3, true, true, 'cuses'], // status (status) ['sutats', 6, true, true, ['status', 'statuses']], // conspectuses (conspectus), prospectuses (prospectus) ['sutcep', 6, true, true, 'pectuses'], // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) ['su', 2, true, true, 'i'], // news (news) ['swen', 4, true, true, 'news'], // feet (foot) ['toof', 4, true, true, 'feet'], // chateaux (chateau), bureaus (bureau) ['uae', 3, false, true, ['eaus', 'eaux']], // oxen (ox) ['xo', 2, false, false, 'oxen'], // hoaxes (hoax) ['xaoh', 4, true, false, 'hoaxes'], // indices (index) ['xedni', 5, false, true, ['indicies', 'indexes']], // boxes (box) ['xo', 2, false, true, 'oxes'], // indexes (index), matrixes (matrix) ['x', 1, true, false, ['cies', 'xes']], // appendices (appendix) ['xi', 2, false, true, 'ices'], // babies (baby) ['y', 1, false, true, 'ies'], // quizzes (quiz) ['ziuq', 4, true, false, 'quizzes'], // waltzes (waltz) ['z', 1, true, true, 'zes'], ]; /** * A list of words which should not be inflected, reversed. */ private const UNINFLECTED = [ '', // data 'atad', // deer 'reed', // feedback 'kcabdeef', // fish 'hsif', // info 'ofni', // moose 'esoom', // series 'seires', // sheep 'peehs', // species 'seiceps', ]; /** * {@inheritdoc} */ public function singularize(string $plural): array { $pluralRev = strrev($plural); $lowerPluralRev = strtolower($pluralRev); $pluralLength = \strlen($lowerPluralRev); // Check if the word is one which is not inflected, return early if so if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) { return [$plural]; } // The outer loop iterates over the entries of the plural table // The inner loop $j iterates over the characters of the plural suffix // in the plural table to compare them with the characters of the actual // given plural suffix foreach (self::PLURAL_MAP as $map) { $suffix = $map[0]; $suffixLength = $map[1]; $j = 0; // Compare characters in the plural table and of the suffix of the // given plural one by one while ($suffix[$j] === $lowerPluralRev[$j]) { // Let $j point to the next character ++$j; // Successfully compared the last character // Add an entry with the singular suffix to the singular array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $pluralLength) { $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]); if (!$map[2] && $nextIsVocal) { // suffix may not succeed a vocal but next char is one break; } if (!$map[3] && !$nextIsVocal) { // suffix may not succeed a consonant but next char is one break; } } $newBase = substr($plural, 0, $pluralLength - $suffixLength); $newSuffix = $map[4]; // Check whether the first character in the plural suffix // is uppercased. If yes, uppercase the first character in // the singular suffix too $firstUpper = ctype_upper($pluralRev[$j - 1]); if (\is_array($newSuffix)) { $singulars = []; foreach ($newSuffix as $newSuffixEntry) { $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); } return $singulars; } return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; } // Suffix is longer than word if ($j === $pluralLength) { break; } } } // Assume that plural and singular is identical return [$plural]; } /** * {@inheritdoc} */ public function pluralize(string $singular): array { $singularRev = strrev($singular); $lowerSingularRev = strtolower($singularRev); $singularLength = \strlen($lowerSingularRev); // Check if the word is one which is not inflected, return early if so if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) { return [$singular]; } // The outer loop iterates over the entries of the singular table // The inner loop $j iterates over the characters of the singular suffix // in the singular table to compare them with the characters of the actual // given singular suffix foreach (self::SINGULAR_MAP as $map) { $suffix = $map[0]; $suffixLength = $map[1]; $j = 0; // Compare characters in the singular table and of the suffix of the // given plural one by one while ($suffix[$j] === $lowerSingularRev[$j]) { // Let $j point to the next character ++$j; // Successfully compared the last character // Add an entry with the plural suffix to the plural array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $singularLength) { $nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]); if (!$map[2] && $nextIsVocal) { // suffix may not succeed a vocal but next char is one break; } if (!$map[3] && !$nextIsVocal) { // suffix may not succeed a consonant but next char is one break; } } $newBase = substr($singular, 0, $singularLength - $suffixLength); $newSuffix = $map[4]; // Check whether the first character in the singular suffix // is uppercased. If yes, uppercase the first character in // the singular suffix too $firstUpper = ctype_upper($singularRev[$j - 1]); if (\is_array($newSuffix)) { $plurals = []; foreach ($newSuffix as $newSuffixEntry) { $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); } return $plurals; } return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; } // Suffix is longer than word if ($j === $singularLength) { break; } } } // Assume that plural is singular with a trailing `s` return [$singular.'s']; } } PK2A#]զ_�,,vendor/symfony/string/LICENSEnu�[���Copyright (c) 2019-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]�}ݙk�k/vendor/symfony/string/AbstractUnicodeString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a string of abstract Unicode characters. * * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). * This class is the abstract type to use as a type-hint when the logic you want to * implement is Unicode-aware but doesn't care about code points vs grapheme clusters. * * @author Nicolas Grekas <p@tchwork.com> * * @throws ExceptionInterface */ abstract class AbstractUnicodeString extends AbstractString { public const NFC = \Normalizer::NFC; public const NFD = \Normalizer::NFD; public const NFKC = \Normalizer::NFKC; public const NFKD = \Normalizer::NFKD; // all ASCII letters sorted by typical frequency of occurrence private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; // the subset of folded case mappings that is not in lower case mappings private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ']; private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ']; // the subset of upper case mappings that map one code point to many code points private const UPPER_FROM = ['ß', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ʼn', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ']; private const UPPER_TO = ['SS', 'FF', 'FI', 'FL', 'FFI', 'FFL', 'ST', 'ST', 'ԵՒ', 'ՄՆ', 'ՄԵ', 'ՄԻ', 'ՎՆ', 'ՄԽ', 'ʼN', 'Ϊ́', 'Ϋ́', 'J̌', 'H̱', 'T̈', 'W̊', 'Y̊', 'Aʾ', 'Υ̓', 'Υ̓̀', 'Υ̓́', 'Υ̓͂', 'Α͂', 'Η͂', 'Ϊ̀', 'Ϊ́', 'Ι͂', 'Ϊ͂', 'Ϋ̀', 'Ϋ́', 'Ρ̓', 'Υ͂', 'Ϋ͂', 'Ω͂']; // the subset of https://github.com/unicode-org/cldr/blob/master/common/transforms/Latin-ASCII.xml that is not in NFKD private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ʼn', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'DŽ', 'Dž', 'dž', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', 'ᴄ', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', 'ᴏ', 'ᴘ', 'ᴛ', 'ᴜ', 'ᴠ', 'ᴡ', 'ᴢ', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', 'ᶃ', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', 'ᶌ', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', 'ẝ', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', 'ℌ', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '〇', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '′', '″', '〝', '〞', '«', '»', '‹', '›', '‐', '‑', '‒', '–', '—', '―', '︱', '︲', '﹘', '‖', '⁄', '⁅', '⁆', '⁎', '、', '。', '〈', '〉', '《', '》', '〔', '〕', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '⦅', '⦆', '。', '、', '×', '÷', '−', '∕', '∖', '∣', '∥', '≪', '≫', '⦅', '⦆']; private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 1/3', ' 2/3', ' 1/5', ' 2/5', ' 3/5', ' 4/5', ' 1/6', ' 5/6', ' 1/8', ' 3/8', ' 5/8', ' 7/8', ' 1/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))']; private static $transliterators = []; private static $tableZero; private static $tableWide; /** * @return static */ public static function fromCodePoints(int ...$codes): self { $string = ''; foreach ($codes as $code) { if (0x80 > $code %= 0x200000) { $string .= \chr($code); } elseif (0x800 > $code) { $string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { $string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { $string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } } return new static($string); } /** * Generic UTF-8 to ASCII transliteration. * * Install the intl extension for best results. * * @param string[]|\Transliterator[]|\Closure[] $rules See "*-Latin" rules from Transliterator::listIDs() */ public function ascii(array $rules = []): self { $str = clone $this; $s = $str->string; $str->string = ''; array_unshift($rules, 'nfd'); $rules[] = 'latin-ascii'; if (\function_exists('transliterator_transliterate')) { $rules[] = 'any-latin/bgn'; } $rules[] = 'nfkd'; $rules[] = '[:nonspacing mark:] remove'; while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) { if (0 < --$i) { $str->string .= substr($s, 0, $i); $s = substr($s, $i); } if (!$rule = array_shift($rules)) { $rules = []; // An empty rule interrupts the next ones } if ($rule instanceof \Transliterator) { $s = $rule->transliterate($s); } elseif ($rule instanceof \Closure) { $s = $rule($s); } elseif ($rule) { if ('nfd' === $rule = strtolower($rule)) { normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD); } elseif ('nfkd' === $rule) { normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD); } elseif ('[:nonspacing mark:] remove' === $rule) { $s = preg_replace('/\p{Mn}++/u', '', $s); } elseif ('latin-ascii' === $rule) { $s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s); } elseif ('de-ascii' === $rule) { $s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s); $s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s); } elseif (\function_exists('transliterator_transliterate')) { if (null === $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule)) { if ('any-latin/bgn' === $rule) { $rule = 'any-latin'; $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule); } if (null === $transliterator) { throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule)); } self::$transliterators['any-latin/bgn'] = $transliterator; } $s = $transliterator->transliterate($s); } } elseif (!\function_exists('iconv')) { $s = preg_replace('/[^\x00-\x7F]/u', '?', $s); } else { $s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) { $c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]); if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) { throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class)); } return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?'); }, $s); } } $str->string .= $s; return $str; } public function camel(): parent { $str = clone $this; $str->string = str_replace(' ', '', preg_replace_callback('/\b.(?![A-Z]{2,})/u', static function ($m) use (&$i) { return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); }, preg_replace('/[^\pL0-9]++/u', ' ', $this->string))); return $str; } /** * @return int[] */ public function codePointsAt(int $offset): array { $str = $this->slice($offset, 1); if ('' === $str->string) { return []; } $codePoints = []; foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { $codePoints[] = mb_ord($c, 'UTF-8'); } return $codePoints; } public function folded(bool $compat = true): parent { $str = clone $this; if (!$compat || \PHP_VERSION_ID < 70300 || !\defined('Normalizer::NFKC_CF')) { $str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC); $str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $this->string), 'UTF-8'); } else { $str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF); } return $str; } public function join(array $strings, string $lastGlue = null): parent { $str = clone $this; $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; $str->string = implode($this->string, $strings).$tail; if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function lower(): parent { $str = clone $this; $str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8'); return $str; } public function match(string $regexp, int $flags = 0, int $offset = 0): array { $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; if ($this->ignoreCase) { $regexp .= 'i'; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && '_ERROR' === substr($k, -6)) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } return $matches; } /** * @return static */ public function normalize(int $form = self::NFC): self { if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) { throw new InvalidArgumentException('Unsupported normalization form.'); } $str = clone $this; normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); return $str; } public function padBoth(int $length, string $padStr = ' '): parent { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_BOTH); } public function padEnd(int $length, string $padStr = ' '): parent { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_RIGHT); } public function padStart(int $length, string $padStr = ' '): parent { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_LEFT); } public function replaceMatches(string $fromRegexp, $to): parent { if ($this->ignoreCase) { $fromRegexp .= 'i'; } if (\is_array($to) || $to instanceof \Closure) { if (!\is_callable($to)) { throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class)); } $replace = 'preg_replace_callback'; $to = static function (array $m) use ($to): string { $to = $to($m); if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) { throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.'); } return $to; }; } elseif ('' !== $to && !preg_match('//u', $to)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } else { $replace = 'preg_replace'; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && '_ERROR' === substr($k, -6)) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } $str = clone $this; $str->string = $string; return $str; } public function reverse(): parent { $str = clone $this; $str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY))); return $str; } public function snake(): parent { $str = $this->camel(); $str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8'); return $str; } public function title(bool $allWords = false): parent { $str = clone $this; $limit = $allWords ? -1 : 1; $str->string = preg_replace_callback('/\b./u', static function (array $m): string { return mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); }, $str->string, $limit); return $str; } public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string); return $str; } public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{[$chars]++$}uD", '', $str->string); return $str; } public function trimPrefix($prefix): parent { if (!$this->ignoreCase) { return parent::trimPrefix($prefix); } $str = clone $this; if ($prefix instanceof \Traversable) { $prefix = iterator_to_array($prefix, false); } elseif ($prefix instanceof parent) { $prefix = $prefix->string; } $prefix = implode('|', array_map('preg_quote', (array) $prefix)); $str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string); return $str; } public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{^[$chars]++}uD", '', $str->string); return $str; } public function trimSuffix($suffix): parent { if (!$this->ignoreCase) { return parent::trimSuffix($suffix); } $str = clone $this; if ($suffix instanceof \Traversable) { $suffix = iterator_to_array($suffix, false); } elseif ($suffix instanceof parent) { $suffix = $suffix->string; } $suffix = implode('|', array_map('preg_quote', (array) $suffix)); $str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string); return $str; } public function upper(): parent { $str = clone $this; $str->string = mb_strtoupper($str->string, 'UTF-8'); if (\PHP_VERSION_ID < 70300) { $str->string = str_replace(self::UPPER_FROM, self::UPPER_TO, $str->string); } return $str; } public function width(bool $ignoreAnsiDecoration = true): int { $width = 0; $s = str_replace(["\x00", "\x05", "\x07"], '', $this->string); if (false !== strpos($s, "\r")) { $s = str_replace(["\r\n", "\r"], "\n", $s); } if (!$ignoreAnsiDecoration) { $s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s); } foreach (explode("\n", $s) as $s) { if ($ignoreAnsiDecoration) { $s = preg_replace('/(?:\x1B(?: \[ [\x30-\x3F]*+ [\x20-\x2F]*+ [\x40-\x7E] | [P\]X^_] .*? \x1B\\\\ | [\x41-\x7E] )|[\p{Cc}\x7F]++)/xu', '', $s); } $lineWidth = $this->wcswidth($s); if ($lineWidth > $width) { $width = $lineWidth; } } return $width; } /** * @return static */ private function pad(int $len, self $pad, int $type): parent { $sLen = $this->length(); if ($len <= $sLen) { return clone $this; } $padLen = $pad->length(); $freeLen = $len - $sLen; $len = $freeLen % $padLen; switch ($type) { case \STR_PAD_RIGHT: return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); case \STR_PAD_LEFT: return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); case \STR_PAD_BOTH: $freeLen /= 2; $rightLen = ceil($freeLen); $len = $rightLen % $padLen; $str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : '')); $leftLen = floor($freeLen); $len = $leftLen % $padLen; return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : '')); default: throw new InvalidArgumentException('Invalid padding type.'); } } /** * Based on https://github.com/jquast/wcwidth, a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c. */ private function wcswidth(string $string): int { $width = 0; foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { $codePoint = mb_ord($c, 'UTF-8'); if (0 === $codePoint // NULL || 0x034F === $codePoint // COMBINING GRAPHEME JOINER || (0x200B <= $codePoint && 0x200F >= $codePoint) // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK || 0x2028 === $codePoint // LINE SEPARATOR || 0x2029 === $codePoint // PARAGRAPH SEPARATOR || (0x202A <= $codePoint && 0x202E >= $codePoint) // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE || (0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR ) { continue; } // Non printable characters if (32 > $codePoint // C0 control characters || (0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL ) { return -1; } if (null === self::$tableZero) { self::$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php'; } if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) { $lbound = 0; while ($ubound >= $lbound) { $mid = floor(($lbound + $ubound) / 2); if ($codePoint > self::$tableZero[$mid][1]) { $lbound = $mid + 1; } elseif ($codePoint < self::$tableZero[$mid][0]) { $ubound = $mid - 1; } else { continue 2; } } } if (null === self::$tableWide) { self::$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php'; } if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) { $lbound = 0; while ($ubound >= $lbound) { $mid = floor(($lbound + $ubound) / 2); if ($codePoint > self::$tableWide[$mid][1]) { $lbound = $mid + 1; } elseif ($codePoint < self::$tableWide[$mid][0]) { $ubound = $mid - 1; } else { $width += 2; continue 2; } } } ++$width; } return $width; } } PK2A#]�ې]]-vendor/symfony/string/Resources/functions.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; if (!\function_exists(u::class)) { function u(?string $string = ''): UnicodeString { return new UnicodeString($string ?? ''); } } if (!\function_exists(b::class)) { function b(?string $string = ''): ByteString { return new ByteString($string ?? ''); } } if (!\function_exists(s::class)) { /** * @return UnicodeString|ByteString */ function s(?string $string = ''): AbstractString { $string = $string ?? ''; return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string); } } PK2A#]W/3v�0�0<vendor/symfony/string/Resources/data/wcswidth_table_wide.phpnu�[���<?php /* * This file has been auto-generated by the Symfony String Component for internal use. * * Unicode version: 15.0.0 * Date: 2022-10-05T17:16:36+02:00 */ return [ [ 4352, 4447, ], [ 8986, 8987, ], [ 9001, 9001, ], [ 9002, 9002, ], [ 9193, 9196, ], [ 9200, 9200, ], [ 9203, 9203, ], [ 9725, 9726, ], [ 9748, 9749, ], [ 9800, 9811, ], [ 9855, 9855, ], [ 9875, 9875, ], [ 9889, 9889, ], [ 9898, 9899, ], [ 9917, 9918, ], [ 9924, 9925, ], [ 9934, 9934, ], [ 9940, 9940, ], [ 9962, 9962, ], [ 9970, 9971, ], [ 9973, 9973, ], [ 9978, 9978, ], [ 9981, 9981, ], [ 9989, 9989, ], [ 9994, 9995, ], [ 10024, 10024, ], [ 10060, 10060, ], [ 10062, 10062, ], [ 10067, 10069, ], [ 10071, 10071, ], [ 10133, 10135, ], [ 10160, 10160, ], [ 10175, 10175, ], [ 11035, 11036, ], [ 11088, 11088, ], [ 11093, 11093, ], [ 11904, 11929, ], [ 11931, 12019, ], [ 12032, 12245, ], [ 12272, 12283, ], [ 12288, 12288, ], [ 12289, 12291, ], [ 12292, 12292, ], [ 12293, 12293, ], [ 12294, 12294, ], [ 12295, 12295, ], [ 12296, 12296, ], [ 12297, 12297, ], [ 12298, 12298, ], [ 12299, 12299, ], [ 12300, 12300, ], [ 12301, 12301, ], [ 12302, 12302, ], [ 12303, 12303, ], [ 12304, 12304, ], [ 12305, 12305, ], [ 12306, 12307, ], [ 12308, 12308, ], [ 12309, 12309, ], [ 12310, 12310, ], [ 12311, 12311, ], [ 12312, 12312, ], [ 12313, 12313, ], [ 12314, 12314, ], [ 12315, 12315, ], [ 12316, 12316, ], [ 12317, 12317, ], [ 12318, 12319, ], [ 12320, 12320, ], [ 12321, 12329, ], [ 12330, 12333, ], [ 12334, 12335, ], [ 12336, 12336, ], [ 12337, 12341, ], [ 12342, 12343, ], [ 12344, 12346, ], [ 12347, 12347, ], [ 12348, 12348, ], [ 12349, 12349, ], [ 12350, 12350, ], [ 12353, 12438, ], [ 12441, 12442, ], [ 12443, 12444, ], [ 12445, 12446, ], [ 12447, 12447, ], [ 12448, 12448, ], [ 12449, 12538, ], [ 12539, 12539, ], [ 12540, 12542, ], [ 12543, 12543, ], [ 12549, 12591, ], [ 12593, 12686, ], [ 12688, 12689, ], [ 12690, 12693, ], [ 12694, 12703, ], [ 12704, 12735, ], [ 12736, 12771, ], [ 12784, 12799, ], [ 12800, 12830, ], [ 12832, 12841, ], [ 12842, 12871, ], [ 12880, 12880, ], [ 12881, 12895, ], [ 12896, 12927, ], [ 12928, 12937, ], [ 12938, 12976, ], [ 12977, 12991, ], [ 12992, 13055, ], [ 13056, 13311, ], [ 13312, 19903, ], [ 19968, 40959, ], [ 40960, 40980, ], [ 40981, 40981, ], [ 40982, 42124, ], [ 42128, 42182, ], [ 43360, 43388, ], [ 44032, 55203, ], [ 63744, 64109, ], [ 64110, 64111, ], [ 64112, 64217, ], [ 64218, 64255, ], [ 65040, 65046, ], [ 65047, 65047, ], [ 65048, 65048, ], [ 65049, 65049, ], [ 65072, 65072, ], [ 65073, 65074, ], [ 65075, 65076, ], [ 65077, 65077, ], [ 65078, 65078, ], [ 65079, 65079, ], [ 65080, 65080, ], [ 65081, 65081, ], [ 65082, 65082, ], [ 65083, 65083, ], [ 65084, 65084, ], [ 65085, 65085, ], [ 65086, 65086, ], [ 65087, 65087, ], [ 65088, 65088, ], [ 65089, 65089, ], [ 65090, 65090, ], [ 65091, 65091, ], [ 65092, 65092, ], [ 65093, 65094, ], [ 65095, 65095, ], [ 65096, 65096, ], [ 65097, 65100, ], [ 65101, 65103, ], [ 65104, 65106, ], [ 65108, 65111, ], [ 65112, 65112, ], [ 65113, 65113, ], [ 65114, 65114, ], [ 65115, 65115, ], [ 65116, 65116, ], [ 65117, 65117, ], [ 65118, 65118, ], [ 65119, 65121, ], [ 65122, 65122, ], [ 65123, 65123, ], [ 65124, 65126, ], [ 65128, 65128, ], [ 65129, 65129, ], [ 65130, 65131, ], [ 65281, 65283, ], [ 65284, 65284, ], [ 65285, 65287, ], [ 65288, 65288, ], [ 65289, 65289, ], [ 65290, 65290, ], [ 65291, 65291, ], [ 65292, 65292, ], [ 65293, 65293, ], [ 65294, 65295, ], [ 65296, 65305, ], [ 65306, 65307, ], [ 65308, 65310, ], [ 65311, 65312, ], [ 65313, 65338, ], [ 65339, 65339, ], [ 65340, 65340, ], [ 65341, 65341, ], [ 65342, 65342, ], [ 65343, 65343, ], [ 65344, 65344, ], [ 65345, 65370, ], [ 65371, 65371, ], [ 65372, 65372, ], [ 65373, 65373, ], [ 65374, 65374, ], [ 65375, 65375, ], [ 65376, 65376, ], [ 65504, 65505, ], [ 65506, 65506, ], [ 65507, 65507, ], [ 65508, 65508, ], [ 65509, 65510, ], [ 94176, 94177, ], [ 94178, 94178, ], [ 94179, 94179, ], [ 94180, 94180, ], [ 94192, 94193, ], [ 94208, 100343, ], [ 100352, 101119, ], [ 101120, 101589, ], [ 101632, 101640, ], [ 110576, 110579, ], [ 110581, 110587, ], [ 110589, 110590, ], [ 110592, 110847, ], [ 110848, 110882, ], [ 110898, 110898, ], [ 110928, 110930, ], [ 110933, 110933, ], [ 110948, 110951, ], [ 110960, 111355, ], [ 126980, 126980, ], [ 127183, 127183, ], [ 127374, 127374, ], [ 127377, 127386, ], [ 127488, 127490, ], [ 127504, 127547, ], [ 127552, 127560, ], [ 127568, 127569, ], [ 127584, 127589, ], [ 127744, 127776, ], [ 127789, 127797, ], [ 127799, 127868, ], [ 127870, 127891, ], [ 127904, 127946, ], [ 127951, 127955, ], [ 127968, 127984, ], [ 127988, 127988, ], [ 127992, 127994, ], [ 127995, 127999, ], [ 128000, 128062, ], [ 128064, 128064, ], [ 128066, 128252, ], [ 128255, 128317, ], [ 128331, 128334, ], [ 128336, 128359, ], [ 128378, 128378, ], [ 128405, 128406, ], [ 128420, 128420, ], [ 128507, 128511, ], [ 128512, 128591, ], [ 128640, 128709, ], [ 128716, 128716, ], [ 128720, 128722, ], [ 128725, 128727, ], [ 128732, 128735, ], [ 128747, 128748, ], [ 128756, 128764, ], [ 128992, 129003, ], [ 129008, 129008, ], [ 129292, 129338, ], [ 129340, 129349, ], [ 129351, 129535, ], [ 129648, 129660, ], [ 129664, 129672, ], [ 129680, 129725, ], [ 129727, 129733, ], [ 129742, 129755, ], [ 129760, 129768, ], [ 129776, 129784, ], [ 131072, 173791, ], [ 173792, 173823, ], [ 173824, 177977, ], [ 177978, 177983, ], [ 177984, 178205, ], [ 178206, 178207, ], [ 178208, 183969, ], [ 183970, 183983, ], [ 183984, 191456, ], [ 191457, 194559, ], [ 194560, 195101, ], [ 195102, 195103, ], [ 195104, 196605, ], [ 196608, 201546, ], [ 201547, 201551, ], [ 201552, 205743, ], [ 205744, 262141, ], ]; PK2A#]�"�:�:<vendor/symfony/string/Resources/data/wcswidth_table_zero.phpnu�[���<?php /* * This file has been auto-generated by the Symfony String Component for internal use. * * Unicode version: 15.0.0 * Date: 2022-10-05T17:16:37+02:00 */ return [ [ 768, 879, ], [ 1155, 1159, ], [ 1160, 1161, ], [ 1425, 1469, ], [ 1471, 1471, ], [ 1473, 1474, ], [ 1476, 1477, ], [ 1479, 1479, ], [ 1552, 1562, ], [ 1611, 1631, ], [ 1648, 1648, ], [ 1750, 1756, ], [ 1759, 1764, ], [ 1767, 1768, ], [ 1770, 1773, ], [ 1809, 1809, ], [ 1840, 1866, ], [ 1958, 1968, ], [ 2027, 2035, ], [ 2045, 2045, ], [ 2070, 2073, ], [ 2075, 2083, ], [ 2085, 2087, ], [ 2089, 2093, ], [ 2137, 2139, ], [ 2200, 2207, ], [ 2250, 2273, ], [ 2275, 2306, ], [ 2362, 2362, ], [ 2364, 2364, ], [ 2369, 2376, ], [ 2381, 2381, ], [ 2385, 2391, ], [ 2402, 2403, ], [ 2433, 2433, ], [ 2492, 2492, ], [ 2497, 2500, ], [ 2509, 2509, ], [ 2530, 2531, ], [ 2558, 2558, ], [ 2561, 2562, ], [ 2620, 2620, ], [ 2625, 2626, ], [ 2631, 2632, ], [ 2635, 2637, ], [ 2641, 2641, ], [ 2672, 2673, ], [ 2677, 2677, ], [ 2689, 2690, ], [ 2748, 2748, ], [ 2753, 2757, ], [ 2759, 2760, ], [ 2765, 2765, ], [ 2786, 2787, ], [ 2810, 2815, ], [ 2817, 2817, ], [ 2876, 2876, ], [ 2879, 2879, ], [ 2881, 2884, ], [ 2893, 2893, ], [ 2901, 2902, ], [ 2914, 2915, ], [ 2946, 2946, ], [ 3008, 3008, ], [ 3021, 3021, ], [ 3072, 3072, ], [ 3076, 3076, ], [ 3132, 3132, ], [ 3134, 3136, ], [ 3142, 3144, ], [ 3146, 3149, ], [ 3157, 3158, ], [ 3170, 3171, ], [ 3201, 3201, ], [ 3260, 3260, ], [ 3263, 3263, ], [ 3270, 3270, ], [ 3276, 3277, ], [ 3298, 3299, ], [ 3328, 3329, ], [ 3387, 3388, ], [ 3393, 3396, ], [ 3405, 3405, ], [ 3426, 3427, ], [ 3457, 3457, ], [ 3530, 3530, ], [ 3538, 3540, ], [ 3542, 3542, ], [ 3633, 3633, ], [ 3636, 3642, ], [ 3655, 3662, ], [ 3761, 3761, ], [ 3764, 3772, ], [ 3784, 3790, ], [ 3864, 3865, ], [ 3893, 3893, ], [ 3895, 3895, ], [ 3897, 3897, ], [ 3953, 3966, ], [ 3968, 3972, ], [ 3974, 3975, ], [ 3981, 3991, ], [ 3993, 4028, ], [ 4038, 4038, ], [ 4141, 4144, ], [ 4146, 4151, ], [ 4153, 4154, ], [ 4157, 4158, ], [ 4184, 4185, ], [ 4190, 4192, ], [ 4209, 4212, ], [ 4226, 4226, ], [ 4229, 4230, ], [ 4237, 4237, ], [ 4253, 4253, ], [ 4957, 4959, ], [ 5906, 5908, ], [ 5938, 5939, ], [ 5970, 5971, ], [ 6002, 6003, ], [ 6068, 6069, ], [ 6071, 6077, ], [ 6086, 6086, ], [ 6089, 6099, ], [ 6109, 6109, ], [ 6155, 6157, ], [ 6159, 6159, ], [ 6277, 6278, ], [ 6313, 6313, ], [ 6432, 6434, ], [ 6439, 6440, ], [ 6450, 6450, ], [ 6457, 6459, ], [ 6679, 6680, ], [ 6683, 6683, ], [ 6742, 6742, ], [ 6744, 6750, ], [ 6752, 6752, ], [ 6754, 6754, ], [ 6757, 6764, ], [ 6771, 6780, ], [ 6783, 6783, ], [ 6832, 6845, ], [ 6846, 6846, ], [ 6847, 6862, ], [ 6912, 6915, ], [ 6964, 6964, ], [ 6966, 6970, ], [ 6972, 6972, ], [ 6978, 6978, ], [ 7019, 7027, ], [ 7040, 7041, ], [ 7074, 7077, ], [ 7080, 7081, ], [ 7083, 7085, ], [ 7142, 7142, ], [ 7144, 7145, ], [ 7149, 7149, ], [ 7151, 7153, ], [ 7212, 7219, ], [ 7222, 7223, ], [ 7376, 7378, ], [ 7380, 7392, ], [ 7394, 7400, ], [ 7405, 7405, ], [ 7412, 7412, ], [ 7416, 7417, ], [ 7616, 7679, ], [ 8400, 8412, ], [ 8413, 8416, ], [ 8417, 8417, ], [ 8418, 8420, ], [ 8421, 8432, ], [ 11503, 11505, ], [ 11647, 11647, ], [ 11744, 11775, ], [ 12330, 12333, ], [ 12441, 12442, ], [ 42607, 42607, ], [ 42608, 42610, ], [ 42612, 42621, ], [ 42654, 42655, ], [ 42736, 42737, ], [ 43010, 43010, ], [ 43014, 43014, ], [ 43019, 43019, ], [ 43045, 43046, ], [ 43052, 43052, ], [ 43204, 43205, ], [ 43232, 43249, ], [ 43263, 43263, ], [ 43302, 43309, ], [ 43335, 43345, ], [ 43392, 43394, ], [ 43443, 43443, ], [ 43446, 43449, ], [ 43452, 43453, ], [ 43493, 43493, ], [ 43561, 43566, ], [ 43569, 43570, ], [ 43573, 43574, ], [ 43587, 43587, ], [ 43596, 43596, ], [ 43644, 43644, ], [ 43696, 43696, ], [ 43698, 43700, ], [ 43703, 43704, ], [ 43710, 43711, ], [ 43713, 43713, ], [ 43756, 43757, ], [ 43766, 43766, ], [ 44005, 44005, ], [ 44008, 44008, ], [ 44013, 44013, ], [ 64286, 64286, ], [ 65024, 65039, ], [ 65056, 65071, ], [ 66045, 66045, ], [ 66272, 66272, ], [ 66422, 66426, ], [ 68097, 68099, ], [ 68101, 68102, ], [ 68108, 68111, ], [ 68152, 68154, ], [ 68159, 68159, ], [ 68325, 68326, ], [ 68900, 68903, ], [ 69291, 69292, ], [ 69373, 69375, ], [ 69446, 69456, ], [ 69506, 69509, ], [ 69633, 69633, ], [ 69688, 69702, ], [ 69744, 69744, ], [ 69747, 69748, ], [ 69759, 69761, ], [ 69811, 69814, ], [ 69817, 69818, ], [ 69826, 69826, ], [ 69888, 69890, ], [ 69927, 69931, ], [ 69933, 69940, ], [ 70003, 70003, ], [ 70016, 70017, ], [ 70070, 70078, ], [ 70089, 70092, ], [ 70095, 70095, ], [ 70191, 70193, ], [ 70196, 70196, ], [ 70198, 70199, ], [ 70206, 70206, ], [ 70209, 70209, ], [ 70367, 70367, ], [ 70371, 70378, ], [ 70400, 70401, ], [ 70459, 70460, ], [ 70464, 70464, ], [ 70502, 70508, ], [ 70512, 70516, ], [ 70712, 70719, ], [ 70722, 70724, ], [ 70726, 70726, ], [ 70750, 70750, ], [ 70835, 70840, ], [ 70842, 70842, ], [ 70847, 70848, ], [ 70850, 70851, ], [ 71090, 71093, ], [ 71100, 71101, ], [ 71103, 71104, ], [ 71132, 71133, ], [ 71219, 71226, ], [ 71229, 71229, ], [ 71231, 71232, ], [ 71339, 71339, ], [ 71341, 71341, ], [ 71344, 71349, ], [ 71351, 71351, ], [ 71453, 71455, ], [ 71458, 71461, ], [ 71463, 71467, ], [ 71727, 71735, ], [ 71737, 71738, ], [ 71995, 71996, ], [ 71998, 71998, ], [ 72003, 72003, ], [ 72148, 72151, ], [ 72154, 72155, ], [ 72160, 72160, ], [ 72193, 72202, ], [ 72243, 72248, ], [ 72251, 72254, ], [ 72263, 72263, ], [ 72273, 72278, ], [ 72281, 72283, ], [ 72330, 72342, ], [ 72344, 72345, ], [ 72752, 72758, ], [ 72760, 72765, ], [ 72767, 72767, ], [ 72850, 72871, ], [ 72874, 72880, ], [ 72882, 72883, ], [ 72885, 72886, ], [ 73009, 73014, ], [ 73018, 73018, ], [ 73020, 73021, ], [ 73023, 73029, ], [ 73031, 73031, ], [ 73104, 73105, ], [ 73109, 73109, ], [ 73111, 73111, ], [ 73459, 73460, ], [ 73472, 73473, ], [ 73526, 73530, ], [ 73536, 73536, ], [ 73538, 73538, ], [ 78912, 78912, ], [ 78919, 78933, ], [ 92912, 92916, ], [ 92976, 92982, ], [ 94031, 94031, ], [ 94095, 94098, ], [ 94180, 94180, ], [ 113821, 113822, ], [ 118528, 118573, ], [ 118576, 118598, ], [ 119143, 119145, ], [ 119163, 119170, ], [ 119173, 119179, ], [ 119210, 119213, ], [ 119362, 119364, ], [ 121344, 121398, ], [ 121403, 121452, ], [ 121461, 121461, ], [ 121476, 121476, ], [ 121499, 121503, ], [ 121505, 121519, ], [ 122880, 122886, ], [ 122888, 122904, ], [ 122907, 122913, ], [ 122915, 122916, ], [ 122918, 122922, ], [ 123023, 123023, ], [ 123184, 123190, ], [ 123566, 123566, ], [ 123628, 123631, ], [ 124140, 124143, ], [ 125136, 125142, ], [ 125252, 125258, ], [ 917760, 917999, ], ]; PK2A#]$���QQ6vendor/symfony/string/Exception/ExceptionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; interface ExceptionInterface extends \Throwable { } PK2A#]e�����<vendor/symfony/string/Exception/InvalidArgumentException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } PK2A#]��0�pp4vendor/symfony/string/Exception/RuntimeException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; class RuntimeException extends \RuntimeException implements ExceptionInterface { } PK2A#]n^t{{)vendor/symfony/string/CodePointString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; /** * Represents a string of Unicode code points encoded as UTF-8. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class CodePointString extends AbstractUnicodeString { public function __construct(string $string = '') { if ('' !== $string && !preg_match('//u', $string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $this->string = $string; } public function append(string ...$suffix): AbstractString { $str = clone $this; $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $rx = '/('; while (65535 < $length) { $rx .= '.{65535}'; $length -= 65535; } $rx .= '.{'.$length.'})/us'; $str = clone $this; $chunks = []; foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function codePointsAt(int $offset): array { $str = $offset ? $this->slice($offset, 1) : $this; return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')]; } public function endsWith($suffix): bool { if ($suffix instanceof AbstractString) { $suffix = $suffix->string; } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { return parent::endsWith($suffix); } else { $suffix = (string) $suffix; } if ('' === $suffix || !preg_match('//u', $suffix)) { return false; } if ($this->ignoreCase) { return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string); } return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix)); } public function equalsTo($string): bool { if ($string instanceof AbstractString) { $string = $string->string; } elseif (\is_array($string) || $string instanceof \Traversable) { return parent::equalsTo($string); } else { $string = (string) $string; } if ('' !== $string && $this->ignoreCase) { return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); } return $string === $this->string; } public function indexOf($needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOf($needle, $offset); } else { $needle = (string) $needle; } if ('' === $needle) { return null; } $i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8'); return false === $i ? null : $i; } public function indexOfLast($needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOfLast($needle, $offset); } else { $needle = (string) $needle; } if ('' === $needle) { return null; } $i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8'); return false === $i ? null : $i; } public function length(): int { return mb_strlen($this->string, 'UTF-8'); } public function prepend(string ...$prefix): AbstractString { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function replace(string $from, string $to): AbstractString { $str = clone $this; if ('' === $from || !preg_match('//u', $from)) { return $str; } if ('' !== $to && !preg_match('//u', $to)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } if ($this->ignoreCase) { $str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string)); } else { $str->string = str_replace($from, $to, $this->string); } return $str; } public function slice(int $start = 0, int $length = null): AbstractString { $str = clone $this; $str->string = mb_substr($this->string, $start, $length, 'UTF-8'); return $str; } public function splice(string $replacement, int $start = 0, int $length = null): AbstractString { if (!preg_match('//u', $replacement)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str = clone $this; $start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0; $length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length; $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); return $str; } public function split(string $delimiter, int $limit = null, int $flags = null): array { if (1 > $limit = $limit ?? \PHP_INT_MAX) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter.'u', $limit, $flags); } if (!preg_match('//u', $delimiter)) { throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); } $str = clone $this; $chunks = $this->ignoreCase ? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit) : explode($delimiter, $this->string, $limit); foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } return $chunks; } public function startsWith($prefix): bool { if ($prefix instanceof AbstractString) { $prefix = $prefix->string; } elseif (\is_array($prefix) || $prefix instanceof \Traversable) { return parent::startsWith($prefix); } else { $prefix = (string) $prefix; } if ('' === $prefix || !preg_match('//u', $prefix)) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8'); } return 0 === strncmp($this->string, $prefix, \strlen($prefix)); } } PK2A#]e����.vendor/symfony/string/Slugger/AsciiSlugger.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Slugger; use Symfony\Component\String\AbstractUnicodeString; use Symfony\Component\String\UnicodeString; use Symfony\Contracts\Translation\LocaleAwareInterface; if (!interface_exists(LocaleAwareInterface::class)) { throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".'); } /** * @author Titouan Galopin <galopintitouan@gmail.com> */ class AsciiSlugger implements SluggerInterface, LocaleAwareInterface { private const LOCALE_TO_TRANSLITERATOR_ID = [ 'am' => 'Amharic-Latin', 'ar' => 'Arabic-Latin', 'az' => 'Azerbaijani-Latin', 'be' => 'Belarusian-Latin', 'bg' => 'Bulgarian-Latin', 'bn' => 'Bengali-Latin', 'de' => 'de-ASCII', 'el' => 'Greek-Latin', 'fa' => 'Persian-Latin', 'he' => 'Hebrew-Latin', 'hy' => 'Armenian-Latin', 'ka' => 'Georgian-Latin', 'kk' => 'Kazakh-Latin', 'ky' => 'Kirghiz-Latin', 'ko' => 'Korean-Latin', 'mk' => 'Macedonian-Latin', 'mn' => 'Mongolian-Latin', 'or' => 'Oriya-Latin', 'ps' => 'Pashto-Latin', 'ru' => 'Russian-Latin', 'sr' => 'Serbian-Latin', 'sr_Cyrl' => 'Serbian-Latin', 'th' => 'Thai-Latin', 'tk' => 'Turkmen-Latin', 'uk' => 'Ukrainian-Latin', 'uz' => 'Uzbek-Latin', 'zh' => 'Han-Latin', ]; private $defaultLocale; private $symbolsMap = [ 'en' => ['@' => 'at', '&' => 'and'], ]; /** * Cache of transliterators per locale. * * @var \Transliterator[] */ private $transliterators = []; /** * @param array|\Closure|null $symbolsMap */ public function __construct(string $defaultLocale = null, $symbolsMap = null) { if (null !== $symbolsMap && !\is_array($symbolsMap) && !$symbolsMap instanceof \Closure) { throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be array, Closure or null, "%s" given.', __METHOD__, \gettype($symbolsMap))); } $this->defaultLocale = $defaultLocale; $this->symbolsMap = $symbolsMap ?? $this->symbolsMap; } /** * {@inheritdoc} */ public function setLocale($locale) { $this->defaultLocale = $locale; } /** * {@inheritdoc} */ public function getLocale() { return $this->defaultLocale; } /** * {@inheritdoc} */ public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString { $locale = $locale ?? $this->defaultLocale; $transliterator = []; if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) { // Use the shortcut for German in UnicodeString::ascii() if possible (faster and no requirement on intl) $transliterator = ['de-ASCII']; } elseif (\function_exists('transliterator_transliterate') && $locale) { $transliterator = (array) $this->createTransliterator($locale); } if ($this->symbolsMap instanceof \Closure) { // If the symbols map is passed as a closure, there is no need to fallback to the parent locale // as the closure can just provide substitutions for all locales of interest. $symbolsMap = $this->symbolsMap; array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) { return $symbolsMap($s, $locale); }); } $unicodeString = (new UnicodeString($string))->ascii($transliterator); if (\is_array($this->symbolsMap)) { $map = null; if (isset($this->symbolsMap[$locale])) { $map = $this->symbolsMap[$locale]; } else { $parent = self::getParentLocale($locale); if ($parent && isset($this->symbolsMap[$parent])) { $map = $this->symbolsMap[$parent]; } } if ($map) { foreach ($map as $char => $replace) { $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); } } } return $unicodeString ->replaceMatches('/[^A-Za-z0-9]++/', $separator) ->trim($separator) ; } private function createTransliterator(string $locale): ?\Transliterator { if (\array_key_exists($locale, $this->transliterators)) { return $this->transliterators[$locale]; } // Exact locale supported, cache and return if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) { return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); } // Locale not supported and no parent, fallback to any-latin if (!$parent = self::getParentLocale($locale)) { return $this->transliterators[$locale] = null; } // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) { $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); } return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null; } private static function getParentLocale(?string $locale): ?string { if (!$locale) { return null; } if (false === $str = strrchr($locale, '_')) { // no parent locale return null; } return substr($locale, 0, -\strlen($str)); } } PK2A#]"�O���2vendor/symfony/string/Slugger/SluggerInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Slugger; use Symfony\Component\String\AbstractUnicodeString; /** * Creates a URL-friendly slug from a given string. * * @author Titouan Galopin <galopintitouan@gmail.com> */ interface SluggerInterface { /** * Creates a slug for the given string and locale, using appropriate transliteration when needed. */ public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString; } PK2A#]ráEL<L<$vendor/symfony/string/ByteString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a binary-safe string of bytes. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class ByteString extends AbstractString { private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; public function __construct(string $string = '') { $this->string = $string; } /* * The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03) * * https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16 * * Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE). * * Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/) */ public static function fromRandom(int $length = 16, string $alphabet = null): self { if ($length <= 0) { throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length)); } $alphabet = $alphabet ?? self::ALPHABET_ALPHANUMERIC; $alphabetSize = \strlen($alphabet); $bits = (int) ceil(log($alphabetSize, 2.0)); if ($bits <= 0 || $bits > 56) { throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.'); } $ret = ''; while ($length > 0) { $urandomLength = (int) ceil(2 * $length * $bits / 8.0); $data = random_bytes($urandomLength); $unpackedData = 0; $unpackedBits = 0; for ($i = 0; $i < $urandomLength && $length > 0; ++$i) { // Unpack 8 bits $unpackedData = ($unpackedData << 8) | \ord($data[$i]); $unpackedBits += 8; // While we have enough bits to select a character from the alphabet, keep // consuming the random data for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) { $index = ($unpackedData & ((1 << $bits) - 1)); $unpackedData >>= $bits; // Unfortunately, the alphabet size is not necessarily a power of two. // Worst case, it is 2^k + 1, which means we need (k+1) bits and we // have around a 50% chance of missing as k gets larger if ($index < $alphabetSize) { $ret .= $alphabet[$index]; --$length; } } } } return new static($ret); } public function bytesAt(int $offset): array { $str = $this->string[$offset] ?? ''; return '' === $str ? [] : [\ord($str)]; } public function append(string ...$suffix): parent { $str = clone $this; $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); return $str; } public function camel(): parent { $str = clone $this; $parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string)))); $parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]); $str->string = implode('', $parts); return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $str = clone $this; $chunks = []; foreach (str_split($this->string, $length) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function endsWith($suffix): bool { if ($suffix instanceof parent) { $suffix = $suffix->string; } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { return parent::endsWith($suffix); } else { $suffix = (string) $suffix; } return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase); } public function equalsTo($string): bool { if ($string instanceof parent) { $string = $string->string; } elseif (\is_array($string) || $string instanceof \Traversable) { return parent::equalsTo($string); } else { $string = (string) $string; } if ('' !== $string && $this->ignoreCase) { return 0 === strcasecmp($string, $this->string); } return $string === $this->string; } public function folded(): parent { $str = clone $this; $str->string = strtolower($str->string); return $str; } public function indexOf($needle, int $offset = 0): ?int { if ($needle instanceof parent) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOf($needle, $offset); } else { $needle = (string) $needle; } if ('' === $needle) { return null; } $i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset); return false === $i ? null : $i; } public function indexOfLast($needle, int $offset = 0): ?int { if ($needle instanceof parent) { $needle = $needle->string; } elseif (\is_array($needle) || $needle instanceof \Traversable) { return parent::indexOfLast($needle, $offset); } else { $needle = (string) $needle; } if ('' === $needle) { return null; } $i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset); return false === $i ? null : $i; } public function isUtf8(): bool { return '' === $this->string || preg_match('//u', $this->string); } public function join(array $strings, string $lastGlue = null): parent { $str = clone $this; $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; $str->string = implode($this->string, $strings).$tail; return $str; } public function length(): int { return \strlen($this->string); } public function lower(): parent { $str = clone $this; $str->string = strtolower($str->string); return $str; } public function match(string $regexp, int $flags = 0, int $offset = 0): array { $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; if ($this->ignoreCase) { $regexp .= 'i'; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && '_ERROR' === substr($k, -6)) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } return $matches; } public function padBoth(int $length, string $padStr = ' '): parent { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH); return $str; } public function padEnd(int $length, string $padStr = ' '): parent { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT); return $str; } public function padStart(int $length, string $padStr = ' '): parent { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT); return $str; } public function prepend(string ...$prefix): parent { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string; return $str; } public function replace(string $from, string $to): parent { $str = clone $this; if ('' !== $from) { $str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string); } return $str; } public function replaceMatches(string $fromRegexp, $to): parent { if ($this->ignoreCase) { $fromRegexp .= 'i'; } if (\is_array($to)) { if (!\is_callable($to)) { throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class)); } $replace = 'preg_replace_callback'; } else { $replace = $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace'; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { if (null === $string = $replace($fromRegexp, $to, $this->string)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && '_ERROR' === substr($k, -6)) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } $str = clone $this; $str->string = $string; return $str; } public function reverse(): parent { $str = clone $this; $str->string = strrev($str->string); return $str; } public function slice(int $start = 0, int $length = null): parent { $str = clone $this; $str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX); return $str; } public function snake(): parent { $str = $this->camel(); $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string)); return $str; } public function splice(string $replacement, int $start = 0, int $length = null): parent { $str = clone $this; $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); return $str; } public function split(string $delimiter, int $limit = null, int $flags = null): array { if (1 > $limit = $limit ?? \PHP_INT_MAX) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter, $limit, $flags); } $str = clone $this; $chunks = $this->ignoreCase ? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit) : explode($delimiter, $this->string, $limit); foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } return $chunks; } public function startsWith($prefix): bool { if ($prefix instanceof parent) { $prefix = $prefix->string; } elseif (!\is_string($prefix)) { return parent::startsWith($prefix); } return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix))); } public function title(bool $allWords = false): parent { $str = clone $this; $str->string = $allWords ? ucwords($str->string) : ucfirst($str->string); return $str; } public function toUnicodeString(string $fromEncoding = null): UnicodeString { return new UnicodeString($this->toCodePointString($fromEncoding)->string); } public function toCodePointString(string $fromEncoding = null): CodePointString { $u = new CodePointString(); if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) { $u->string = $this->string; return $u; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { try { $validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true); } catch (InvalidArgumentException $e) { if (!\function_exists('iconv')) { throw $e; } $u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string); return $u; } } finally { restore_error_handler(); } if (!$validEncoding) { throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252')); } $u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252'); return $u; } public function trim(string $chars = " \t\n\r\0\x0B\x0C"): parent { $str = clone $this; $str->string = trim($str->string, $chars); return $str; } public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): parent { $str = clone $this; $str->string = rtrim($str->string, $chars); return $str; } public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): parent { $str = clone $this; $str->string = ltrim($str->string, $chars); return $str; } public function upper(): parent { $str = clone $this; $str->string = strtoupper($str->string); return $str; } public function width(bool $ignoreAnsiDecoration = true): int { $string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string); return (new CodePointString($string))->width($ignoreAnsiDecoration); } } PK2A#]���V�N�N(vendor/symfony/string/AbstractString.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a string of abstract characters. * * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). * This class is the abstract type to use as a type-hint when the logic you want to * implement doesn't care about the exact variant it deals with. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ abstract class AbstractString implements \Stringable, \JsonSerializable { public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER; public const PREG_SET_ORDER = \PREG_SET_ORDER; public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE; public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL; public const PREG_SPLIT = 0; public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY; public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE; public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE; protected $string = ''; protected $ignoreCase = false; abstract public function __construct(string $string = ''); /** * Unwraps instances of AbstractString back to strings. * * @return string[]|array */ public static function unwrap(array $values): array { foreach ($values as $k => $v) { if ($v instanceof self) { $values[$k] = $v->__toString(); } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) { $values[$k] = $v; } } return $values; } /** * Wraps (and normalizes) strings in instances of AbstractString. * * @return static[]|array */ public static function wrap(array $values): array { $i = 0; $keys = null; foreach ($values as $k => $v) { if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) { $keys = $keys ?? array_keys($values); $keys[$i] = $j; } if (\is_string($v)) { $values[$k] = new static($v); } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) { $values[$k] = $v; } ++$i; } return null !== $keys ? array_combine($keys, $values) : $values; } /** * @param string|string[] $needle * * @return static */ public function after($needle, bool $includeNeedle = false, int $offset = 0): self { $str = clone $this; $i = \PHP_INT_MAX; foreach ((array) $needle as $n) { $n = (string) $n; $j = $this->indexOf($n, $offset); if (null !== $j && $j < $i) { $i = $j; $str->string = $n; } } if (\PHP_INT_MAX === $i) { return $str; } if (!$includeNeedle) { $i += $str->length(); } return $this->slice($i); } /** * @param string|string[] $needle * * @return static */ public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self { $str = clone $this; $i = null; foreach ((array) $needle as $n) { $n = (string) $n; $j = $this->indexOfLast($n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; $str->string = $n; } } if (null === $i) { return $str; } if (!$includeNeedle) { $i += $str->length(); } return $this->slice($i); } /** * @return static */ abstract public function append(string ...$suffix): self; /** * @param string|string[] $needle * * @return static */ public function before($needle, bool $includeNeedle = false, int $offset = 0): self { $str = clone $this; $i = \PHP_INT_MAX; foreach ((array) $needle as $n) { $n = (string) $n; $j = $this->indexOf($n, $offset); if (null !== $j && $j < $i) { $i = $j; $str->string = $n; } } if (\PHP_INT_MAX === $i) { return $str; } if ($includeNeedle) { $i += $str->length(); } return $this->slice(0, $i); } /** * @param string|string[] $needle * * @return static */ public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self { $str = clone $this; $i = null; foreach ((array) $needle as $n) { $n = (string) $n; $j = $this->indexOfLast($n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; $str->string = $n; } } if (null === $i) { return $str; } if ($includeNeedle) { $i += $str->length(); } return $this->slice(0, $i); } /** * @return int[] */ public function bytesAt(int $offset): array { $str = $this->slice($offset, 1); return '' === $str->string ? [] : array_values(unpack('C*', $str->string)); } /** * @return static */ abstract public function camel(): self; /** * @return static[] */ abstract public function chunk(int $length = 1): array; /** * @return static */ public function collapseWhitespace(): self { $str = clone $this; $str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C"); return $str; } /** * @param string|string[] $needle */ public function containsAny($needle): bool { return null !== $this->indexOf($needle); } /** * @param string|string[] $suffix */ public function endsWith($suffix): bool { if (!\is_array($suffix) && !$suffix instanceof \Traversable) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($suffix as $s) { if ($this->endsWith((string) $s)) { return true; } } return false; } /** * @return static */ public function ensureEnd(string $suffix): self { if (!$this->endsWith($suffix)) { return $this->append($suffix); } $suffix = preg_quote($suffix); $regex = '{('.$suffix.')(?:'.$suffix.')++$}D'; return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1'); } /** * @return static */ public function ensureStart(string $prefix): self { $prefix = new static($prefix); if (!$this->startsWith($prefix)) { return $this->prepend($prefix); } $str = clone $this; $i = $prefixLen = $prefix->length(); while ($this->indexOf($prefix, $i) === $i) { $str = $str->slice($prefixLen); $i += $prefixLen; } return $str; } /** * @param string|string[] $string */ public function equalsTo($string): bool { if (!\is_array($string) && !$string instanceof \Traversable) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($string as $s) { if ($this->equalsTo((string) $s)) { return true; } } return false; } /** * @return static */ abstract public function folded(): self; /** * @return static */ public function ignoreCase(): self { $str = clone $this; $str->ignoreCase = true; return $str; } /** * @param string|string[] $needle */ public function indexOf($needle, int $offset = 0): ?int { if (!\is_array($needle) && !$needle instanceof \Traversable) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } $i = \PHP_INT_MAX; foreach ($needle as $n) { $j = $this->indexOf((string) $n, $offset); if (null !== $j && $j < $i) { $i = $j; } } return \PHP_INT_MAX === $i ? null : $i; } /** * @param string|string[] $needle */ public function indexOfLast($needle, int $offset = 0): ?int { if (!\is_array($needle) && !$needle instanceof \Traversable) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } $i = null; foreach ($needle as $n) { $j = $this->indexOfLast((string) $n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; } } return $i; } public function isEmpty(): bool { return '' === $this->string; } /** * @return static */ abstract public function join(array $strings, string $lastGlue = null): self; public function jsonSerialize(): string { return $this->string; } abstract public function length(): int; /** * @return static */ abstract public function lower(): self; /** * Matches the string using a regular expression. * * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression. * * @return array All matches in a multi-dimensional array ordered according to flags */ abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array; /** * @return static */ abstract public function padBoth(int $length, string $padStr = ' '): self; /** * @return static */ abstract public function padEnd(int $length, string $padStr = ' '): self; /** * @return static */ abstract public function padStart(int $length, string $padStr = ' '): self; /** * @return static */ abstract public function prepend(string ...$prefix): self; /** * @return static */ public function repeat(int $multiplier): self { if (0 > $multiplier) { throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier)); } $str = clone $this; $str->string = str_repeat($str->string, $multiplier); return $str; } /** * @return static */ abstract public function replace(string $from, string $to): self; /** * @param string|callable $to * * @return static */ abstract public function replaceMatches(string $fromRegexp, $to): self; /** * @return static */ abstract public function reverse(): self; /** * @return static */ abstract public function slice(int $start = 0, int $length = null): self; /** * @return static */ abstract public function snake(): self; /** * @return static */ abstract public function splice(string $replacement, int $start = 0, int $length = null): self; /** * @return static[] */ public function split(string $delimiter, int $limit = null, int $flags = null): array { if (null === $flags) { throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.'); } if ($this->ignoreCase) { $delimiter .= 'i'; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && '_ERROR' === substr($k, -6)) { throw new RuntimeException('Splitting failed with '.$k.'.'); } } throw new RuntimeException('Splitting failed with unknown error code.'); } } finally { restore_error_handler(); } $str = clone $this; if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) { foreach ($chunks as &$chunk) { $str->string = $chunk[0]; $chunk[0] = clone $str; } } else { foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } } return $chunks; } /** * @param string|string[] $prefix */ public function startsWith($prefix): bool { if (!\is_array($prefix) && !$prefix instanceof \Traversable) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($prefix as $prefix) { if ($this->startsWith((string) $prefix)) { return true; } } return false; } /** * @return static */ abstract public function title(bool $allWords = false): self; public function toByteString(string $toEncoding = null): ByteString { $b = new ByteString(); $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding; if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') { $b->string = $this->string; return $b; } set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); try { try { $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8'); } catch (InvalidArgumentException $e) { if (!\function_exists('iconv')) { throw $e; } $b->string = iconv('UTF-8', $toEncoding, $this->string); } } finally { restore_error_handler(); } return $b; } public function toCodePointString(): CodePointString { return new CodePointString($this->string); } public function toString(): string { return $this->string; } public function toUnicodeString(): UnicodeString { return new UnicodeString($this->string); } /** * @return static */ abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; /** * @return static */ abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; /** * @param string|string[] $prefix * * @return static */ public function trimPrefix($prefix): self { if (\is_array($prefix) || $prefix instanceof \Traversable) { foreach ($prefix as $s) { $t = $this->trimPrefix($s); if ($t->string !== $this->string) { return $t; } } return clone $this; } $str = clone $this; if ($prefix instanceof self) { $prefix = $prefix->string; } else { $prefix = (string) $prefix; } if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) { $str->string = substr($this->string, \strlen($prefix)); } return $str; } /** * @return static */ abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; /** * @param string|string[] $suffix * * @return static */ public function trimSuffix($suffix): self { if (\is_array($suffix) || $suffix instanceof \Traversable) { foreach ($suffix as $s) { $t = $this->trimSuffix($s); if ($t->string !== $this->string) { return $t; } } return clone $this; } $str = clone $this; if ($suffix instanceof self) { $suffix = $suffix->string; } else { $suffix = (string) $suffix; } if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) { $str->string = substr($this->string, 0, -\strlen($suffix)); } return $str; } /** * @return static */ public function truncate(int $length, string $ellipsis = '', bool $cut = true): self { $stringLength = $this->length(); if ($stringLength <= $length) { return clone $this; } $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0; if ($length < $ellipsisLength) { $ellipsisLength = 0; } if (!$cut) { if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) { return clone $this; } $length += $ellipsisLength; } $str = $this->slice(0, $length - $ellipsisLength); return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str; } /** * @return static */ abstract public function upper(): self; /** * Returns the printable length on a terminal. */ abstract public function width(bool $ignoreAnsiDecoration = true): int; /** * @return static */ public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self { $lines = '' !== $break ? $this->split($break) : [clone $this]; $chars = []; $mask = ''; if (1 === \count($lines) && '' === $lines[0]->string) { return $lines[0]; } foreach ($lines as $i => $line) { if ($i) { $chars[] = $break; $mask .= '#'; } foreach ($line->chunk() as $char) { $chars[] = $char->string; $mask .= ' ' === $char->string ? ' ' : '?'; } } $string = ''; $j = 0; $b = $i = -1; $mask = wordwrap($mask, $width, '#', $cut); while (false !== $b = strpos($mask, '#', $b + 1)) { for (++$i; $i < $b; ++$i) { $string .= $chars[$j]; unset($chars[$j++]); } if ($break === $chars[$j] || ' ' === $chars[$j]) { unset($chars[$j++]); } $string .= $break; } $str = clone $this; $str->string = $string.implode('', $chars); return $str; } public function __sleep(): array { return ['string']; } public function __clone() { $this->ignoreCase = false; } public function __toString(): string { return $this->string; } } PK2A#]u���d%d%6vendor/symfony/polyfill-intl-normalizer/Normalizer.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Intl\Normalizer; /** * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension. * * It has been validated with Unicode 6.3 Normalization Conformance Test. * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations. * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class Normalizer { public const FORM_D = \Normalizer::FORM_D; public const FORM_KD = \Normalizer::FORM_KD; public const FORM_C = \Normalizer::FORM_C; public const FORM_KC = \Normalizer::FORM_KC; public const NFD = \Normalizer::NFD; public const NFKD = \Normalizer::NFKD; public const NFC = \Normalizer::NFC; public const NFKC = \Normalizer::NFKC; private static $C; private static $D; private static $KD; private static $cC; private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; public static function isNormalized(string $s, int $form = self::FORM_C) { if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) { return false; } if (!isset($s[strspn($s, self::$ASCII)])) { return true; } if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) { return true; } return self::normalize($s, $form) === $s; } public static function normalize(string $s, int $form = self::FORM_C) { if (!preg_match('//u', $s)) { return false; } switch ($form) { case self::NFC: $C = true; $K = false; break; case self::NFD: $C = false; $K = false; break; case self::NFKC: $C = true; $K = true; break; case self::NFKD: $C = false; $K = true; break; default: if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) { return $s; } if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form'); } if ('' === $s) { return ''; } if ($K && null === self::$KD) { self::$KD = self::getData('compatibilityDecomposition'); } if (null === self::$D) { self::$D = self::getData('canonicalDecomposition'); self::$cC = self::getData('combiningClass'); } if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) { mb_internal_encoding('8bit'); } $r = self::decompose($s, $K); if ($C) { if (null === self::$C) { self::$C = self::getData('canonicalComposition'); } $r = self::recompose($r); } if (null !== $mbEncoding) { mb_internal_encoding($mbEncoding); } return $r; } private static function recompose($s) { $ASCII = self::$ASCII; $compMap = self::$C; $combClass = self::$cC; $ulenMask = self::$ulenMask; $result = $tail = ''; $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"]; $len = \strlen($s); $lastUchr = substr($s, 0, $i); $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0; while ($i < $len) { if ($s[$i] < "\x80") { // ASCII chars if ($tail) { $lastUchr .= $tail; $tail = ''; } if ($j = strspn($s, $ASCII, $i + 1)) { $lastUchr .= substr($s, $i, $j); $i += $j; } $result .= $lastUchr; $lastUchr = $s[$i]; $lastUcls = 0; ++$i; continue; } $ulen = $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr || $lastUcls) { // Table lookup and combining chars composition $ucls = $combClass[$uchr] ?? 0; if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { $lastUchr = $compMap[$lastUchr.$uchr]; } elseif ($lastUcls = $ucls) { $tail .= $uchr; } else { if ($tail) { $lastUchr .= $tail; $tail = ''; } $result .= $lastUchr; $lastUchr = $uchr; } } else { // Hangul chars $L = \ord($lastUchr[2]) - 0x80; $V = \ord($uchr[2]) - 0xA1; $T = 0; $uchr = substr($s, $i + $ulen, 3); if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { $T = \ord($uchr[2]) - 0xA7; 0 > $T && $T += 0x40; $ulen += 3; } $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); } $i += $ulen; } return $result.$lastUchr.$tail; } private static function decompose($s, $c) { $result = ''; $ASCII = self::$ASCII; $decompMap = self::$D; $combClass = self::$cC; $ulenMask = self::$ulenMask; if ($c) { $compatMap = self::$KD; } $c = []; $i = 0; $len = \strlen($s); while ($i < $len) { if ($s[$i] < "\x80") { // ASCII chars if ($c) { ksort($c); $result .= implode('', $c); $c = []; } $j = 1 + strspn($s, $ASCII, $i + 1); $result .= substr($s, $i, $j); $i += $j; continue; } $ulen = $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { // Table lookup if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) { $uchr = $j; $j = \strlen($uchr); $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"]; if ($ulen != $j) { // Put trailing chars in $s $j -= $ulen; $i -= $j; if (0 > $i) { $s = str_repeat(' ', -$i).$s; $len -= $i; $i = 0; } while ($j--) { $s[$i + $j] = $uchr[$ulen + $j]; } $uchr = substr($uchr, 0, $ulen); } } if (isset($combClass[$uchr])) { // Combining chars, for sorting if (!isset($c[$combClass[$uchr]])) { $c[$combClass[$uchr]] = ''; } $c[$combClass[$uchr]] .= $uchr; continue; } } else { // Hangul chars $uchr = unpack('C*', $uchr); $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); if ($j %= 28) { $uchr .= $j < 25 ? ("\xE1\x86".\chr(0xA7 + $j)) : ("\xE1\x87".\chr(0x67 + $j)); } } if ($c) { ksort($c); $result .= implode('', $c); $c = []; } $result .= $uchr; } if ($c) { ksort($c); $result .= implode('', $c); } return $result; } private static function getData($file) { if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { return require $file; } return false; } } PK2A#]�,���7vendor/symfony/polyfill-intl-normalizer/bootstrap80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Intl\Normalizer as p; if (!function_exists('normalizer_is_normalized')) { function normalizer_is_normalized(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); } } if (!function_exists('normalizer_normalize')) { function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); } } PK2A#]c�,�ooXvendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.phpnu�[���<?php return array ( ' ' => ' ', '¨' => ' ̈', 'ª' => 'a', '¯' => ' ̄', '²' => '2', '³' => '3', '´' => ' ́', 'µ' => 'μ', '¸' => ' ̧', '¹' => '1', 'º' => 'o', '¼' => '1⁄4', '½' => '1⁄2', '¾' => '3⁄4', 'IJ' => 'IJ', 'ij' => 'ij', 'Ŀ' => 'L·', 'ŀ' => 'l·', 'ʼn' => 'ʼn', 'ſ' => 's', 'DŽ' => 'DŽ', 'Dž' => 'Dž', 'dž' => 'dž', 'LJ' => 'LJ', 'Lj' => 'Lj', 'lj' => 'lj', 'NJ' => 'NJ', 'Nj' => 'Nj', 'nj' => 'nj', 'DZ' => 'DZ', 'Dz' => 'Dz', 'dz' => 'dz', 'ʰ' => 'h', 'ʱ' => 'ɦ', 'ʲ' => 'j', 'ʳ' => 'r', 'ʴ' => 'ɹ', 'ʵ' => 'ɻ', 'ʶ' => 'ʁ', 'ʷ' => 'w', 'ʸ' => 'y', '˘' => ' ̆', '˙' => ' ̇', '˚' => ' ̊', '˛' => ' ̨', '˜' => ' ̃', '˝' => ' ̋', 'ˠ' => 'ɣ', 'ˡ' => 'l', 'ˢ' => 's', 'ˣ' => 'x', 'ˤ' => 'ʕ', 'ͺ' => ' ͅ', '΄' => ' ́', '΅' => ' ̈́', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϒ' => 'Υ', 'ϓ' => 'Ύ', 'ϔ' => 'Ϋ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϲ' => 'ς', 'ϴ' => 'Θ', 'ϵ' => 'ε', 'Ϲ' => 'Σ', 'և' => 'եւ', 'ٵ' => 'اٴ', 'ٶ' => 'وٴ', 'ٷ' => 'ۇٴ', 'ٸ' => 'يٴ', 'ำ' => 'ํา', 'ຳ' => 'ໍາ', 'ໜ' => 'ຫນ', 'ໝ' => 'ຫມ', '༌' => '་', 'ཷ' => 'ྲཱྀ', 'ཹ' => 'ླཱྀ', 'ჼ' => 'ნ', 'ᴬ' => 'A', 'ᴭ' => 'Æ', 'ᴮ' => 'B', 'ᴰ' => 'D', 'ᴱ' => 'E', 'ᴲ' => 'Ǝ', 'ᴳ' => 'G', 'ᴴ' => 'H', 'ᴵ' => 'I', 'ᴶ' => 'J', 'ᴷ' => 'K', 'ᴸ' => 'L', 'ᴹ' => 'M', 'ᴺ' => 'N', 'ᴼ' => 'O', 'ᴽ' => 'Ȣ', 'ᴾ' => 'P', 'ᴿ' => 'R', 'ᵀ' => 'T', 'ᵁ' => 'U', 'ᵂ' => 'W', 'ᵃ' => 'a', 'ᵄ' => 'ɐ', 'ᵅ' => 'ɑ', 'ᵆ' => 'ᴂ', 'ᵇ' => 'b', 'ᵈ' => 'd', 'ᵉ' => 'e', 'ᵊ' => 'ə', 'ᵋ' => 'ɛ', 'ᵌ' => 'ɜ', 'ᵍ' => 'g', 'ᵏ' => 'k', 'ᵐ' => 'm', 'ᵑ' => 'ŋ', 'ᵒ' => 'o', 'ᵓ' => 'ɔ', 'ᵔ' => 'ᴖ', 'ᵕ' => 'ᴗ', 'ᵖ' => 'p', 'ᵗ' => 't', 'ᵘ' => 'u', 'ᵙ' => 'ᴝ', 'ᵚ' => 'ɯ', 'ᵛ' => 'v', 'ᵜ' => 'ᴥ', 'ᵝ' => 'β', 'ᵞ' => 'γ', 'ᵟ' => 'δ', 'ᵠ' => 'φ', 'ᵡ' => 'χ', 'ᵢ' => 'i', 'ᵣ' => 'r', 'ᵤ' => 'u', 'ᵥ' => 'v', 'ᵦ' => 'β', 'ᵧ' => 'γ', 'ᵨ' => 'ρ', 'ᵩ' => 'φ', 'ᵪ' => 'χ', 'ᵸ' => 'н', 'ᶛ' => 'ɒ', 'ᶜ' => 'c', 'ᶝ' => 'ɕ', 'ᶞ' => 'ð', 'ᶟ' => 'ɜ', 'ᶠ' => 'f', 'ᶡ' => 'ɟ', 'ᶢ' => 'ɡ', 'ᶣ' => 'ɥ', 'ᶤ' => 'ɨ', 'ᶥ' => 'ɩ', 'ᶦ' => 'ɪ', 'ᶧ' => 'ᵻ', 'ᶨ' => 'ʝ', 'ᶩ' => 'ɭ', 'ᶪ' => 'ᶅ', 'ᶫ' => 'ʟ', 'ᶬ' => 'ɱ', 'ᶭ' => 'ɰ', 'ᶮ' => 'ɲ', 'ᶯ' => 'ɳ', 'ᶰ' => 'ɴ', 'ᶱ' => 'ɵ', 'ᶲ' => 'ɸ', 'ᶳ' => 'ʂ', 'ᶴ' => 'ʃ', 'ᶵ' => 'ƫ', 'ᶶ' => 'ʉ', 'ᶷ' => 'ʊ', 'ᶸ' => 'ᴜ', 'ᶹ' => 'ʋ', 'ᶺ' => 'ʌ', 'ᶻ' => 'z', 'ᶼ' => 'ʐ', 'ᶽ' => 'ʑ', 'ᶾ' => 'ʒ', 'ᶿ' => 'θ', 'ẚ' => 'aʾ', 'ẛ' => 'ṡ', '᾽' => ' ̓', '᾿' => ' ̓', '῀' => ' ͂', '῁' => ' ̈͂', '῍' => ' ̓̀', '῎' => ' ̓́', '῏' => ' ̓͂', '῝' => ' ̔̀', '῞' => ' ̔́', '῟' => ' ̔͂', '῭' => ' ̈̀', '΅' => ' ̈́', '´' => ' ́', '῾' => ' ̔', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', '‑' => '‐', '‗' => ' ̳', '․' => '.', '‥' => '..', '…' => '...', ' ' => ' ', '″' => '′′', '‴' => '′′′', '‶' => '‵‵', '‷' => '‵‵‵', '‼' => '!!', '‾' => ' ̅', '⁇' => '??', '⁈' => '?!', '⁉' => '!?', '⁗' => '′′′′', ' ' => ' ', '⁰' => '0', 'ⁱ' => 'i', '⁴' => '4', '⁵' => '5', '⁶' => '6', '⁷' => '7', '⁸' => '8', '⁹' => '9', '⁺' => '+', '⁻' => '−', '⁼' => '=', '⁽' => '(', '⁾' => ')', 'ⁿ' => 'n', '₀' => '0', '₁' => '1', '₂' => '2', '₃' => '3', '₄' => '4', '₅' => '5', '₆' => '6', '₇' => '7', '₈' => '8', '₉' => '9', '₊' => '+', '₋' => '−', '₌' => '=', '₍' => '(', '₎' => ')', 'ₐ' => 'a', 'ₑ' => 'e', 'ₒ' => 'o', 'ₓ' => 'x', 'ₔ' => 'ə', 'ₕ' => 'h', 'ₖ' => 'k', 'ₗ' => 'l', 'ₘ' => 'm', 'ₙ' => 'n', 'ₚ' => 'p', 'ₛ' => 's', 'ₜ' => 't', '₨' => 'Rs', '℀' => 'a/c', '℁' => 'a/s', 'ℂ' => 'C', '℃' => '°C', '℅' => 'c/o', '℆' => 'c/u', 'ℇ' => 'Ɛ', '℉' => '°F', 'ℊ' => 'g', 'ℋ' => 'H', 'ℌ' => 'H', 'ℍ' => 'H', 'ℎ' => 'h', 'ℏ' => 'ħ', 'ℐ' => 'I', 'ℑ' => 'I', 'ℒ' => 'L', 'ℓ' => 'l', 'ℕ' => 'N', '№' => 'No', 'ℙ' => 'P', 'ℚ' => 'Q', 'ℛ' => 'R', 'ℜ' => 'R', 'ℝ' => 'R', '℠' => 'SM', '℡' => 'TEL', '™' => 'TM', 'ℤ' => 'Z', 'ℨ' => 'Z', 'ℬ' => 'B', 'ℭ' => 'C', 'ℯ' => 'e', 'ℰ' => 'E', 'ℱ' => 'F', 'ℳ' => 'M', 'ℴ' => 'o', 'ℵ' => 'א', 'ℶ' => 'ב', 'ℷ' => 'ג', 'ℸ' => 'ד', 'ℹ' => 'i', '℻' => 'FAX', 'ℼ' => 'π', 'ℽ' => 'γ', 'ℾ' => 'Γ', 'ℿ' => 'Π', '⅀' => '∑', 'ⅅ' => 'D', 'ⅆ' => 'd', 'ⅇ' => 'e', 'ⅈ' => 'i', 'ⅉ' => 'j', '⅐' => '1⁄7', '⅑' => '1⁄9', '⅒' => '1⁄10', '⅓' => '1⁄3', '⅔' => '2⁄3', '⅕' => '1⁄5', '⅖' => '2⁄5', '⅗' => '3⁄5', '⅘' => '4⁄5', '⅙' => '1⁄6', '⅚' => '5⁄6', '⅛' => '1⁄8', '⅜' => '3⁄8', '⅝' => '5⁄8', '⅞' => '7⁄8', '⅟' => '1⁄', 'Ⅰ' => 'I', 'Ⅱ' => 'II', 'Ⅲ' => 'III', 'Ⅳ' => 'IV', 'Ⅴ' => 'V', 'Ⅵ' => 'VI', 'Ⅶ' => 'VII', 'Ⅷ' => 'VIII', 'Ⅸ' => 'IX', 'Ⅹ' => 'X', 'Ⅺ' => 'XI', 'Ⅻ' => 'XII', 'Ⅼ' => 'L', 'Ⅽ' => 'C', 'Ⅾ' => 'D', 'Ⅿ' => 'M', 'ⅰ' => 'i', 'ⅱ' => 'ii', 'ⅲ' => 'iii', 'ⅳ' => 'iv', 'ⅴ' => 'v', 'ⅵ' => 'vi', 'ⅶ' => 'vii', 'ⅷ' => 'viii', 'ⅸ' => 'ix', 'ⅹ' => 'x', 'ⅺ' => 'xi', 'ⅻ' => 'xii', 'ⅼ' => 'l', 'ⅽ' => 'c', 'ⅾ' => 'd', 'ⅿ' => 'm', '↉' => '0⁄3', '∬' => '∫∫', '∭' => '∫∫∫', '∯' => '∮∮', '∰' => '∮∮∮', '①' => '1', '②' => '2', '③' => '3', '④' => '4', '⑤' => '5', '⑥' => '6', '⑦' => '7', '⑧' => '8', '⑨' => '9', '⑩' => '10', '⑪' => '11', '⑫' => '12', '⑬' => '13', '⑭' => '14', '⑮' => '15', '⑯' => '16', '⑰' => '17', '⑱' => '18', '⑲' => '19', '⑳' => '20', '⑴' => '(1)', '⑵' => '(2)', '⑶' => '(3)', '⑷' => '(4)', '⑸' => '(5)', '⑹' => '(6)', '⑺' => '(7)', '⑻' => '(8)', '⑼' => '(9)', '⑽' => '(10)', '⑾' => '(11)', '⑿' => '(12)', '⒀' => '(13)', '⒁' => '(14)', '⒂' => '(15)', '⒃' => '(16)', '⒄' => '(17)', '⒅' => '(18)', '⒆' => '(19)', '⒇' => '(20)', '⒈' => '1.', '⒉' => '2.', '⒊' => '3.', '⒋' => '4.', '⒌' => '5.', '⒍' => '6.', '⒎' => '7.', '⒏' => '8.', '⒐' => '9.', '⒑' => '10.', '⒒' => '11.', '⒓' => '12.', '⒔' => '13.', '⒕' => '14.', '⒖' => '15.', '⒗' => '16.', '⒘' => '17.', '⒙' => '18.', '⒚' => '19.', '⒛' => '20.', '⒜' => '(a)', '⒝' => '(b)', '⒞' => '(c)', '⒟' => '(d)', '⒠' => '(e)', '⒡' => '(f)', '⒢' => '(g)', '⒣' => '(h)', '⒤' => '(i)', '⒥' => '(j)', '⒦' => '(k)', '⒧' => '(l)', '⒨' => '(m)', '⒩' => '(n)', '⒪' => '(o)', '⒫' => '(p)', '⒬' => '(q)', '⒭' => '(r)', '⒮' => '(s)', '⒯' => '(t)', '⒰' => '(u)', '⒱' => '(v)', '⒲' => '(w)', '⒳' => '(x)', '⒴' => '(y)', '⒵' => '(z)', 'Ⓐ' => 'A', 'Ⓑ' => 'B', 'Ⓒ' => 'C', 'Ⓓ' => 'D', 'Ⓔ' => 'E', 'Ⓕ' => 'F', 'Ⓖ' => 'G', 'Ⓗ' => 'H', 'Ⓘ' => 'I', 'Ⓙ' => 'J', 'Ⓚ' => 'K', 'Ⓛ' => 'L', 'Ⓜ' => 'M', 'Ⓝ' => 'N', 'Ⓞ' => 'O', 'Ⓟ' => 'P', 'Ⓠ' => 'Q', 'Ⓡ' => 'R', 'Ⓢ' => 'S', 'Ⓣ' => 'T', 'Ⓤ' => 'U', 'Ⓥ' => 'V', 'Ⓦ' => 'W', 'Ⓧ' => 'X', 'Ⓨ' => 'Y', 'Ⓩ' => 'Z', 'ⓐ' => 'a', 'ⓑ' => 'b', 'ⓒ' => 'c', 'ⓓ' => 'd', 'ⓔ' => 'e', 'ⓕ' => 'f', 'ⓖ' => 'g', 'ⓗ' => 'h', 'ⓘ' => 'i', 'ⓙ' => 'j', 'ⓚ' => 'k', 'ⓛ' => 'l', 'ⓜ' => 'm', 'ⓝ' => 'n', 'ⓞ' => 'o', 'ⓟ' => 'p', 'ⓠ' => 'q', 'ⓡ' => 'r', 'ⓢ' => 's', 'ⓣ' => 't', 'ⓤ' => 'u', 'ⓥ' => 'v', 'ⓦ' => 'w', 'ⓧ' => 'x', 'ⓨ' => 'y', 'ⓩ' => 'z', '⓪' => '0', '⨌' => '∫∫∫∫', '⩴' => '::=', '⩵' => '==', '⩶' => '===', 'ⱼ' => 'j', 'ⱽ' => 'V', 'ⵯ' => 'ⵡ', '⺟' => '母', '⻳' => '龟', '⼀' => '一', '⼁' => '丨', '⼂' => '丶', '⼃' => '丿', '⼄' => '乙', '⼅' => '亅', '⼆' => '二', '⼇' => '亠', '⼈' => '人', '⼉' => '儿', '⼊' => '入', '⼋' => '八', '⼌' => '冂', '⼍' => '冖', '⼎' => '冫', '⼏' => '几', '⼐' => '凵', '⼑' => '刀', '⼒' => '力', '⼓' => '勹', '⼔' => '匕', '⼕' => '匚', '⼖' => '匸', '⼗' => '十', '⼘' => '卜', '⼙' => '卩', '⼚' => '厂', '⼛' => '厶', '⼜' => '又', '⼝' => '口', '⼞' => '囗', '⼟' => '土', '⼠' => '士', '⼡' => '夂', '⼢' => '夊', '⼣' => '夕', '⼤' => '大', '⼥' => '女', '⼦' => '子', '⼧' => '宀', '⼨' => '寸', '⼩' => '小', '⼪' => '尢', '⼫' => '尸', '⼬' => '屮', '⼭' => '山', '⼮' => '巛', '⼯' => '工', '⼰' => '己', '⼱' => '巾', '⼲' => '干', '⼳' => '幺', '⼴' => '广', '⼵' => '廴', '⼶' => '廾', '⼷' => '弋', '⼸' => '弓', '⼹' => '彐', '⼺' => '彡', '⼻' => '彳', '⼼' => '心', '⼽' => '戈', '⼾' => '戶', '⼿' => '手', '⽀' => '支', '⽁' => '攴', '⽂' => '文', '⽃' => '斗', '⽄' => '斤', '⽅' => '方', '⽆' => '无', '⽇' => '日', '⽈' => '曰', '⽉' => '月', '⽊' => '木', '⽋' => '欠', '⽌' => '止', '⽍' => '歹', '⽎' => '殳', '⽏' => '毋', '⽐' => '比', '⽑' => '毛', '⽒' => '氏', '⽓' => '气', '⽔' => '水', '⽕' => '火', '⽖' => '爪', '⽗' => '父', '⽘' => '爻', '⽙' => '爿', '⽚' => '片', '⽛' => '牙', '⽜' => '牛', '⽝' => '犬', '⽞' => '玄', '⽟' => '玉', '⽠' => '瓜', '⽡' => '瓦', '⽢' => '甘', '⽣' => '生', '⽤' => '用', '⽥' => '田', '⽦' => '疋', '⽧' => '疒', '⽨' => '癶', '⽩' => '白', '⽪' => '皮', '⽫' => '皿', '⽬' => '目', '⽭' => '矛', '⽮' => '矢', '⽯' => '石', '⽰' => '示', '⽱' => '禸', '⽲' => '禾', '⽳' => '穴', '⽴' => '立', '⽵' => '竹', '⽶' => '米', '⽷' => '糸', '⽸' => '缶', '⽹' => '网', '⽺' => '羊', '⽻' => '羽', '⽼' => '老', '⽽' => '而', '⽾' => '耒', '⽿' => '耳', '⾀' => '聿', '⾁' => '肉', '⾂' => '臣', '⾃' => '自', '⾄' => '至', '⾅' => '臼', '⾆' => '舌', '⾇' => '舛', '⾈' => '舟', '⾉' => '艮', '⾊' => '色', '⾋' => '艸', '⾌' => '虍', '⾍' => '虫', '⾎' => '血', '⾏' => '行', '⾐' => '衣', '⾑' => '襾', '⾒' => '見', '⾓' => '角', '⾔' => '言', '⾕' => '谷', '⾖' => '豆', '⾗' => '豕', '⾘' => '豸', '⾙' => '貝', '⾚' => '赤', '⾛' => '走', '⾜' => '足', '⾝' => '身', '⾞' => '車', '⾟' => '辛', '⾠' => '辰', '⾡' => '辵', '⾢' => '邑', '⾣' => '酉', '⾤' => '釆', '⾥' => '里', '⾦' => '金', '⾧' => '長', '⾨' => '門', '⾩' => '阜', '⾪' => '隶', '⾫' => '隹', '⾬' => '雨', '⾭' => '靑', '⾮' => '非', '⾯' => '面', '⾰' => '革', '⾱' => '韋', '⾲' => '韭', '⾳' => '音', '⾴' => '頁', '⾵' => '風', '⾶' => '飛', '⾷' => '食', '⾸' => '首', '⾹' => '香', '⾺' => '馬', '⾻' => '骨', '⾼' => '高', '⾽' => '髟', '⾾' => '鬥', '⾿' => '鬯', '⿀' => '鬲', '⿁' => '鬼', '⿂' => '魚', '⿃' => '鳥', '⿄' => '鹵', '⿅' => '鹿', '⿆' => '麥', '⿇' => '麻', '⿈' => '黃', '⿉' => '黍', '⿊' => '黑', '⿋' => '黹', '⿌' => '黽', '⿍' => '鼎', '⿎' => '鼓', '⿏' => '鼠', '⿐' => '鼻', '⿑' => '齊', '⿒' => '齒', '⿓' => '龍', '⿔' => '龜', '⿕' => '龠', ' ' => ' ', '〶' => '〒', '〸' => '十', '〹' => '卄', '〺' => '卅', '゛' => ' ゙', '゜' => ' ゚', 'ゟ' => 'より', 'ヿ' => 'コト', 'ㄱ' => 'ᄀ', 'ㄲ' => 'ᄁ', 'ㄳ' => 'ᆪ', 'ㄴ' => 'ᄂ', 'ㄵ' => 'ᆬ', 'ㄶ' => 'ᆭ', 'ㄷ' => 'ᄃ', 'ㄸ' => 'ᄄ', 'ㄹ' => 'ᄅ', 'ㄺ' => 'ᆰ', 'ㄻ' => 'ᆱ', 'ㄼ' => 'ᆲ', 'ㄽ' => 'ᆳ', 'ㄾ' => 'ᆴ', 'ㄿ' => 'ᆵ', 'ㅀ' => 'ᄚ', 'ㅁ' => 'ᄆ', 'ㅂ' => 'ᄇ', 'ㅃ' => 'ᄈ', 'ㅄ' => 'ᄡ', 'ㅅ' => 'ᄉ', 'ㅆ' => 'ᄊ', 'ㅇ' => 'ᄋ', 'ㅈ' => 'ᄌ', 'ㅉ' => 'ᄍ', 'ㅊ' => 'ᄎ', 'ㅋ' => 'ᄏ', 'ㅌ' => 'ᄐ', 'ㅍ' => 'ᄑ', 'ㅎ' => 'ᄒ', 'ㅏ' => 'ᅡ', 'ㅐ' => 'ᅢ', 'ㅑ' => 'ᅣ', 'ㅒ' => 'ᅤ', 'ㅓ' => 'ᅥ', 'ㅔ' => 'ᅦ', 'ㅕ' => 'ᅧ', 'ㅖ' => 'ᅨ', 'ㅗ' => 'ᅩ', 'ㅘ' => 'ᅪ', 'ㅙ' => 'ᅫ', 'ㅚ' => 'ᅬ', 'ㅛ' => 'ᅭ', 'ㅜ' => 'ᅮ', 'ㅝ' => 'ᅯ', 'ㅞ' => 'ᅰ', 'ㅟ' => 'ᅱ', 'ㅠ' => 'ᅲ', 'ㅡ' => 'ᅳ', 'ㅢ' => 'ᅴ', 'ㅣ' => 'ᅵ', 'ㅤ' => 'ᅠ', 'ㅥ' => 'ᄔ', 'ㅦ' => 'ᄕ', 'ㅧ' => 'ᇇ', 'ㅨ' => 'ᇈ', 'ㅩ' => 'ᇌ', 'ㅪ' => 'ᇎ', 'ㅫ' => 'ᇓ', 'ㅬ' => 'ᇗ', 'ㅭ' => 'ᇙ', 'ㅮ' => 'ᄜ', 'ㅯ' => 'ᇝ', 'ㅰ' => 'ᇟ', 'ㅱ' => 'ᄝ', 'ㅲ' => 'ᄞ', 'ㅳ' => 'ᄠ', 'ㅴ' => 'ᄢ', 'ㅵ' => 'ᄣ', 'ㅶ' => 'ᄧ', 'ㅷ' => 'ᄩ', 'ㅸ' => 'ᄫ', 'ㅹ' => 'ᄬ', 'ㅺ' => 'ᄭ', 'ㅻ' => 'ᄮ', 'ㅼ' => 'ᄯ', 'ㅽ' => 'ᄲ', 'ㅾ' => 'ᄶ', 'ㅿ' => 'ᅀ', 'ㆀ' => 'ᅇ', 'ㆁ' => 'ᅌ', 'ㆂ' => 'ᇱ', 'ㆃ' => 'ᇲ', 'ㆄ' => 'ᅗ', 'ㆅ' => 'ᅘ', 'ㆆ' => 'ᅙ', 'ㆇ' => 'ᆄ', 'ㆈ' => 'ᆅ', 'ㆉ' => 'ᆈ', 'ㆊ' => 'ᆑ', 'ㆋ' => 'ᆒ', 'ㆌ' => 'ᆔ', 'ㆍ' => 'ᆞ', 'ㆎ' => 'ᆡ', '㆒' => '一', '㆓' => '二', '㆔' => '三', '㆕' => '四', '㆖' => '上', '㆗' => '中', '㆘' => '下', '㆙' => '甲', '㆚' => '乙', '㆛' => '丙', '㆜' => '丁', '㆝' => '天', '㆞' => '地', '㆟' => '人', '㈀' => '(ᄀ)', '㈁' => '(ᄂ)', '㈂' => '(ᄃ)', '㈃' => '(ᄅ)', '㈄' => '(ᄆ)', '㈅' => '(ᄇ)', '㈆' => '(ᄉ)', '㈇' => '(ᄋ)', '㈈' => '(ᄌ)', '㈉' => '(ᄎ)', '㈊' => '(ᄏ)', '㈋' => '(ᄐ)', '㈌' => '(ᄑ)', '㈍' => '(ᄒ)', '㈎' => '(가)', '㈏' => '(나)', '㈐' => '(다)', '㈑' => '(라)', '㈒' => '(마)', '㈓' => '(바)', '㈔' => '(사)', '㈕' => '(아)', '㈖' => '(자)', '㈗' => '(차)', '㈘' => '(카)', '㈙' => '(타)', '㈚' => '(파)', '㈛' => '(하)', '㈜' => '(주)', '㈝' => '(오전)', '㈞' => '(오후)', '㈠' => '(一)', '㈡' => '(二)', '㈢' => '(三)', '㈣' => '(四)', '㈤' => '(五)', '㈥' => '(六)', '㈦' => '(七)', '㈧' => '(八)', '㈨' => '(九)', '㈩' => '(十)', '㈪' => '(月)', '㈫' => '(火)', '㈬' => '(水)', '㈭' => '(木)', '㈮' => '(金)', '㈯' => '(土)', '㈰' => '(日)', '㈱' => '(株)', '㈲' => '(有)', '㈳' => '(社)', '㈴' => '(名)', '㈵' => '(特)', '㈶' => '(財)', '㈷' => '(祝)', '㈸' => '(労)', '㈹' => '(代)', '㈺' => '(呼)', '㈻' => '(学)', '㈼' => '(監)', '㈽' => '(企)', '㈾' => '(資)', '㈿' => '(協)', '㉀' => '(祭)', '㉁' => '(休)', '㉂' => '(自)', '㉃' => '(至)', '㉄' => '問', '㉅' => '幼', '㉆' => '文', '㉇' => '箏', '㉐' => 'PTE', '㉑' => '21', '㉒' => '22', '㉓' => '23', '㉔' => '24', '㉕' => '25', '㉖' => '26', '㉗' => '27', '㉘' => '28', '㉙' => '29', '㉚' => '30', '㉛' => '31', '㉜' => '32', '㉝' => '33', '㉞' => '34', '㉟' => '35', '㉠' => 'ᄀ', '㉡' => 'ᄂ', '㉢' => 'ᄃ', '㉣' => 'ᄅ', '㉤' => 'ᄆ', '㉥' => 'ᄇ', '㉦' => 'ᄉ', '㉧' => 'ᄋ', '㉨' => 'ᄌ', '㉩' => 'ᄎ', '㉪' => 'ᄏ', '㉫' => 'ᄐ', '㉬' => 'ᄑ', '㉭' => 'ᄒ', '㉮' => '가', '㉯' => '나', '㉰' => '다', '㉱' => '라', '㉲' => '마', '㉳' => '바', '㉴' => '사', '㉵' => '아', '㉶' => '자', '㉷' => '차', '㉸' => '카', '㉹' => '타', '㉺' => '파', '㉻' => '하', '㉼' => '참고', '㉽' => '주의', '㉾' => '우', '㊀' => '一', '㊁' => '二', '㊂' => '三', '㊃' => '四', '㊄' => '五', '㊅' => '六', '㊆' => '七', '㊇' => '八', '㊈' => '九', '㊉' => '十', '㊊' => '月', '㊋' => '火', '㊌' => '水', '㊍' => '木', '㊎' => '金', '㊏' => '土', '㊐' => '日', '㊑' => '株', '㊒' => '有', '㊓' => '社', '㊔' => '名', '㊕' => '特', '㊖' => '財', '㊗' => '祝', '㊘' => '労', '㊙' => '秘', '㊚' => '男', '㊛' => '女', '㊜' => '適', '㊝' => '優', '㊞' => '印', '㊟' => '注', '㊠' => '項', '㊡' => '休', '㊢' => '写', '㊣' => '正', '㊤' => '上', '㊥' => '中', '㊦' => '下', '㊧' => '左', '㊨' => '右', '㊩' => '医', '㊪' => '宗', '㊫' => '学', '㊬' => '監', '㊭' => '企', '㊮' => '資', '㊯' => '協', '㊰' => '夜', '㊱' => '36', '㊲' => '37', '㊳' => '38', '㊴' => '39', '㊵' => '40', '㊶' => '41', '㊷' => '42', '㊸' => '43', '㊹' => '44', '㊺' => '45', '㊻' => '46', '㊼' => '47', '㊽' => '48', '㊾' => '49', '㊿' => '50', '㋀' => '1月', '㋁' => '2月', '㋂' => '3月', '㋃' => '4月', '㋄' => '5月', '㋅' => '6月', '㋆' => '7月', '㋇' => '8月', '㋈' => '9月', '㋉' => '10月', '㋊' => '11月', '㋋' => '12月', '㋌' => 'Hg', '㋍' => 'erg', '㋎' => 'eV', '㋏' => 'LTD', '㋐' => 'ア', '㋑' => 'イ', '㋒' => 'ウ', '㋓' => 'エ', '㋔' => 'オ', '㋕' => 'カ', '㋖' => 'キ', '㋗' => 'ク', '㋘' => 'ケ', '㋙' => 'コ', '㋚' => 'サ', '㋛' => 'シ', '㋜' => 'ス', '㋝' => 'セ', '㋞' => 'ソ', '㋟' => 'タ', '㋠' => 'チ', '㋡' => 'ツ', '㋢' => 'テ', '㋣' => 'ト', '㋤' => 'ナ', '㋥' => 'ニ', '㋦' => 'ヌ', '㋧' => 'ネ', '㋨' => 'ノ', '㋩' => 'ハ', '㋪' => 'ヒ', '㋫' => 'フ', '㋬' => 'ヘ', '㋭' => 'ホ', '㋮' => 'マ', '㋯' => 'ミ', '㋰' => 'ム', '㋱' => 'メ', '㋲' => 'モ', '㋳' => 'ヤ', '㋴' => 'ユ', '㋵' => 'ヨ', '㋶' => 'ラ', '㋷' => 'リ', '㋸' => 'ル', '㋹' => 'レ', '㋺' => 'ロ', '㋻' => 'ワ', '㋼' => 'ヰ', '㋽' => 'ヱ', '㋾' => 'ヲ', '㋿' => '令和', '㌀' => 'アパート', '㌁' => 'アルファ', '㌂' => 'アンペア', '㌃' => 'アール', '㌄' => 'イニング', '㌅' => 'インチ', '㌆' => 'ウォン', '㌇' => 'エスクード', '㌈' => 'エーカー', '㌉' => 'オンス', '㌊' => 'オーム', '㌋' => 'カイリ', '㌌' => 'カラット', '㌍' => 'カロリー', '㌎' => 'ガロン', '㌏' => 'ガンマ', '㌐' => 'ギガ', '㌑' => 'ギニー', '㌒' => 'キュリー', '㌓' => 'ギルダー', '㌔' => 'キロ', '㌕' => 'キログラム', '㌖' => 'キロメートル', '㌗' => 'キロワット', '㌘' => 'グラム', '㌙' => 'グラムトン', '㌚' => 'クルゼイロ', '㌛' => 'クローネ', '㌜' => 'ケース', '㌝' => 'コルナ', '㌞' => 'コーポ', '㌟' => 'サイクル', '㌠' => 'サンチーム', '㌡' => 'シリング', '㌢' => 'センチ', '㌣' => 'セント', '㌤' => 'ダース', '㌥' => 'デシ', '㌦' => 'ドル', '㌧' => 'トン', '㌨' => 'ナノ', '㌩' => 'ノット', '㌪' => 'ハイツ', '㌫' => 'パーセント', '㌬' => 'パーツ', '㌭' => 'バーレル', '㌮' => 'ピアストル', '㌯' => 'ピクル', '㌰' => 'ピコ', '㌱' => 'ビル', '㌲' => 'ファラッド', '㌳' => 'フィート', '㌴' => 'ブッシェル', '㌵' => 'フラン', '㌶' => 'ヘクタール', '㌷' => 'ペソ', '㌸' => 'ペニヒ', '㌹' => 'ヘルツ', '㌺' => 'ペンス', '㌻' => 'ページ', '㌼' => 'ベータ', '㌽' => 'ポイント', '㌾' => 'ボルト', '㌿' => 'ホン', '㍀' => 'ポンド', '㍁' => 'ホール', '㍂' => 'ホーン', '㍃' => 'マイクロ', '㍄' => 'マイル', '㍅' => 'マッハ', '㍆' => 'マルク', '㍇' => 'マンション', '㍈' => 'ミクロン', '㍉' => 'ミリ', '㍊' => 'ミリバール', '㍋' => 'メガ', '㍌' => 'メガトン', '㍍' => 'メートル', '㍎' => 'ヤード', '㍏' => 'ヤール', '㍐' => 'ユアン', '㍑' => 'リットル', '㍒' => 'リラ', '㍓' => 'ルピー', '㍔' => 'ルーブル', '㍕' => 'レム', '㍖' => 'レントゲン', '㍗' => 'ワット', '㍘' => '0点', '㍙' => '1点', '㍚' => '2点', '㍛' => '3点', '㍜' => '4点', '㍝' => '5点', '㍞' => '6点', '㍟' => '7点', '㍠' => '8点', '㍡' => '9点', '㍢' => '10点', '㍣' => '11点', '㍤' => '12点', '㍥' => '13点', '㍦' => '14点', '㍧' => '15点', '㍨' => '16点', '㍩' => '17点', '㍪' => '18点', '㍫' => '19点', '㍬' => '20点', '㍭' => '21点', '㍮' => '22点', '㍯' => '23点', '㍰' => '24点', '㍱' => 'hPa', '㍲' => 'da', '㍳' => 'AU', '㍴' => 'bar', '㍵' => 'oV', '㍶' => 'pc', '㍷' => 'dm', '㍸' => 'dm2', '㍹' => 'dm3', '㍺' => 'IU', '㍻' => '平成', '㍼' => '昭和', '㍽' => '大正', '㍾' => '明治', '㍿' => '株式会社', '㎀' => 'pA', '㎁' => 'nA', '㎂' => 'μA', '㎃' => 'mA', '㎄' => 'kA', '㎅' => 'KB', '㎆' => 'MB', '㎇' => 'GB', '㎈' => 'cal', '㎉' => 'kcal', '㎊' => 'pF', '㎋' => 'nF', '㎌' => 'μF', '㎍' => 'μg', '㎎' => 'mg', '㎏' => 'kg', '㎐' => 'Hz', '㎑' => 'kHz', '㎒' => 'MHz', '㎓' => 'GHz', '㎔' => 'THz', '㎕' => 'μl', '㎖' => 'ml', '㎗' => 'dl', '㎘' => 'kl', '㎙' => 'fm', '㎚' => 'nm', '㎛' => 'μm', '㎜' => 'mm', '㎝' => 'cm', '㎞' => 'km', '㎟' => 'mm2', '㎠' => 'cm2', '㎡' => 'm2', '㎢' => 'km2', '㎣' => 'mm3', '㎤' => 'cm3', '㎥' => 'm3', '㎦' => 'km3', '㎧' => 'm∕s', '㎨' => 'm∕s2', '㎩' => 'Pa', '㎪' => 'kPa', '㎫' => 'MPa', '㎬' => 'GPa', '㎭' => 'rad', '㎮' => 'rad∕s', '㎯' => 'rad∕s2', '㎰' => 'ps', '㎱' => 'ns', '㎲' => 'μs', '㎳' => 'ms', '㎴' => 'pV', '㎵' => 'nV', '㎶' => 'μV', '㎷' => 'mV', '㎸' => 'kV', '㎹' => 'MV', '㎺' => 'pW', '㎻' => 'nW', '㎼' => 'μW', '㎽' => 'mW', '㎾' => 'kW', '㎿' => 'MW', '㏀' => 'kΩ', '㏁' => 'MΩ', '㏂' => 'a.m.', '㏃' => 'Bq', '㏄' => 'cc', '㏅' => 'cd', '㏆' => 'C∕kg', '㏇' => 'Co.', '㏈' => 'dB', '㏉' => 'Gy', '㏊' => 'ha', '㏋' => 'HP', '㏌' => 'in', '㏍' => 'KK', '㏎' => 'KM', '㏏' => 'kt', '㏐' => 'lm', '㏑' => 'ln', '㏒' => 'log', '㏓' => 'lx', '㏔' => 'mb', '㏕' => 'mil', '㏖' => 'mol', '㏗' => 'PH', '㏘' => 'p.m.', '㏙' => 'PPM', '㏚' => 'PR', '㏛' => 'sr', '㏜' => 'Sv', '㏝' => 'Wb', '㏞' => 'V∕m', '㏟' => 'A∕m', '㏠' => '1日', '㏡' => '2日', '㏢' => '3日', '㏣' => '4日', '㏤' => '5日', '㏥' => '6日', '㏦' => '7日', '㏧' => '8日', '㏨' => '9日', '㏩' => '10日', '㏪' => '11日', '㏫' => '12日', '㏬' => '13日', '㏭' => '14日', '㏮' => '15日', '㏯' => '16日', '㏰' => '17日', '㏱' => '18日', '㏲' => '19日', '㏳' => '20日', '㏴' => '21日', '㏵' => '22日', '㏶' => '23日', '㏷' => '24日', '㏸' => '25日', '㏹' => '26日', '㏺' => '27日', '㏻' => '28日', '㏼' => '29日', '㏽' => '30日', '㏾' => '31日', '㏿' => 'gal', 'ꚜ' => 'ъ', 'ꚝ' => 'ь', 'ꝰ' => 'ꝯ', 'ꟸ' => 'Ħ', 'ꟹ' => 'œ', 'ꭜ' => 'ꜧ', 'ꭝ' => 'ꬷ', 'ꭞ' => 'ɫ', 'ꭟ' => 'ꭒ', 'ꭩ' => 'ʍ', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ', 'ﬠ' => 'ע', 'ﬡ' => 'א', 'ﬢ' => 'ד', 'ﬣ' => 'ה', 'ﬤ' => 'כ', 'ﬥ' => 'ל', 'ﬦ' => 'ם', 'ﬧ' => 'ר', 'ﬨ' => 'ת', '﬩' => '+', 'ﭏ' => 'אל', 'ﭐ' => 'ٱ', 'ﭑ' => 'ٱ', 'ﭒ' => 'ٻ', 'ﭓ' => 'ٻ', 'ﭔ' => 'ٻ', 'ﭕ' => 'ٻ', 'ﭖ' => 'پ', 'ﭗ' => 'پ', 'ﭘ' => 'پ', 'ﭙ' => 'پ', 'ﭚ' => 'ڀ', 'ﭛ' => 'ڀ', 'ﭜ' => 'ڀ', 'ﭝ' => 'ڀ', 'ﭞ' => 'ٺ', 'ﭟ' => 'ٺ', 'ﭠ' => 'ٺ', 'ﭡ' => 'ٺ', 'ﭢ' => 'ٿ', 'ﭣ' => 'ٿ', 'ﭤ' => 'ٿ', 'ﭥ' => 'ٿ', 'ﭦ' => 'ٹ', 'ﭧ' => 'ٹ', 'ﭨ' => 'ٹ', 'ﭩ' => 'ٹ', 'ﭪ' => 'ڤ', 'ﭫ' => 'ڤ', 'ﭬ' => 'ڤ', 'ﭭ' => 'ڤ', 'ﭮ' => 'ڦ', 'ﭯ' => 'ڦ', 'ﭰ' => 'ڦ', 'ﭱ' => 'ڦ', 'ﭲ' => 'ڄ', 'ﭳ' => 'ڄ', 'ﭴ' => 'ڄ', 'ﭵ' => 'ڄ', 'ﭶ' => 'ڃ', 'ﭷ' => 'ڃ', 'ﭸ' => 'ڃ', 'ﭹ' => 'ڃ', 'ﭺ' => 'چ', 'ﭻ' => 'چ', 'ﭼ' => 'چ', 'ﭽ' => 'چ', 'ﭾ' => 'ڇ', 'ﭿ' => 'ڇ', 'ﮀ' => 'ڇ', 'ﮁ' => 'ڇ', 'ﮂ' => 'ڍ', 'ﮃ' => 'ڍ', 'ﮄ' => 'ڌ', 'ﮅ' => 'ڌ', 'ﮆ' => 'ڎ', 'ﮇ' => 'ڎ', 'ﮈ' => 'ڈ', 'ﮉ' => 'ڈ', 'ﮊ' => 'ژ', 'ﮋ' => 'ژ', 'ﮌ' => 'ڑ', 'ﮍ' => 'ڑ', 'ﮎ' => 'ک', 'ﮏ' => 'ک', 'ﮐ' => 'ک', 'ﮑ' => 'ک', 'ﮒ' => 'گ', 'ﮓ' => 'گ', 'ﮔ' => 'گ', 'ﮕ' => 'گ', 'ﮖ' => 'ڳ', 'ﮗ' => 'ڳ', 'ﮘ' => 'ڳ', 'ﮙ' => 'ڳ', 'ﮚ' => 'ڱ', 'ﮛ' => 'ڱ', 'ﮜ' => 'ڱ', 'ﮝ' => 'ڱ', 'ﮞ' => 'ں', 'ﮟ' => 'ں', 'ﮠ' => 'ڻ', 'ﮡ' => 'ڻ', 'ﮢ' => 'ڻ', 'ﮣ' => 'ڻ', 'ﮤ' => 'ۀ', 'ﮥ' => 'ۀ', 'ﮦ' => 'ہ', 'ﮧ' => 'ہ', 'ﮨ' => 'ہ', 'ﮩ' => 'ہ', 'ﮪ' => 'ھ', 'ﮫ' => 'ھ', 'ﮬ' => 'ھ', 'ﮭ' => 'ھ', 'ﮮ' => 'ے', 'ﮯ' => 'ے', 'ﮰ' => 'ۓ', 'ﮱ' => 'ۓ', 'ﯓ' => 'ڭ', 'ﯔ' => 'ڭ', 'ﯕ' => 'ڭ', 'ﯖ' => 'ڭ', 'ﯗ' => 'ۇ', 'ﯘ' => 'ۇ', 'ﯙ' => 'ۆ', 'ﯚ' => 'ۆ', 'ﯛ' => 'ۈ', 'ﯜ' => 'ۈ', 'ﯝ' => 'ۇٴ', 'ﯞ' => 'ۋ', 'ﯟ' => 'ۋ', 'ﯠ' => 'ۅ', 'ﯡ' => 'ۅ', 'ﯢ' => 'ۉ', 'ﯣ' => 'ۉ', 'ﯤ' => 'ې', 'ﯥ' => 'ې', 'ﯦ' => 'ې', 'ﯧ' => 'ې', 'ﯨ' => 'ى', 'ﯩ' => 'ى', 'ﯪ' => 'ئا', 'ﯫ' => 'ئا', 'ﯬ' => 'ئە', 'ﯭ' => 'ئە', 'ﯮ' => 'ئو', 'ﯯ' => 'ئو', 'ﯰ' => 'ئۇ', 'ﯱ' => 'ئۇ', 'ﯲ' => 'ئۆ', 'ﯳ' => 'ئۆ', 'ﯴ' => 'ئۈ', 'ﯵ' => 'ئۈ', 'ﯶ' => 'ئې', 'ﯷ' => 'ئې', 'ﯸ' => 'ئې', 'ﯹ' => 'ئى', 'ﯺ' => 'ئى', 'ﯻ' => 'ئى', 'ﯼ' => 'ی', 'ﯽ' => 'ی', 'ﯾ' => 'ی', 'ﯿ' => 'ی', 'ﰀ' => 'ئج', 'ﰁ' => 'ئح', 'ﰂ' => 'ئم', 'ﰃ' => 'ئى', 'ﰄ' => 'ئي', 'ﰅ' => 'بج', 'ﰆ' => 'بح', 'ﰇ' => 'بخ', 'ﰈ' => 'بم', 'ﰉ' => 'بى', 'ﰊ' => 'بي', 'ﰋ' => 'تج', 'ﰌ' => 'تح', 'ﰍ' => 'تخ', 'ﰎ' => 'تم', 'ﰏ' => 'تى', 'ﰐ' => 'تي', 'ﰑ' => 'ثج', 'ﰒ' => 'ثم', 'ﰓ' => 'ثى', 'ﰔ' => 'ثي', 'ﰕ' => 'جح', 'ﰖ' => 'جم', 'ﰗ' => 'حج', 'ﰘ' => 'حم', 'ﰙ' => 'خج', 'ﰚ' => 'خح', 'ﰛ' => 'خم', 'ﰜ' => 'سج', 'ﰝ' => 'سح', 'ﰞ' => 'سخ', 'ﰟ' => 'سم', 'ﰠ' => 'صح', 'ﰡ' => 'صم', 'ﰢ' => 'ضج', 'ﰣ' => 'ضح', 'ﰤ' => 'ضخ', 'ﰥ' => 'ضم', 'ﰦ' => 'طح', 'ﰧ' => 'طم', 'ﰨ' => 'ظم', 'ﰩ' => 'عج', 'ﰪ' => 'عم', 'ﰫ' => 'غج', 'ﰬ' => 'غم', 'ﰭ' => 'فج', 'ﰮ' => 'فح', 'ﰯ' => 'فخ', 'ﰰ' => 'فم', 'ﰱ' => 'فى', 'ﰲ' => 'في', 'ﰳ' => 'قح', 'ﰴ' => 'قم', 'ﰵ' => 'قى', 'ﰶ' => 'قي', 'ﰷ' => 'كا', 'ﰸ' => 'كج', 'ﰹ' => 'كح', 'ﰺ' => 'كخ', 'ﰻ' => 'كل', 'ﰼ' => 'كم', 'ﰽ' => 'كى', 'ﰾ' => 'كي', 'ﰿ' => 'لج', 'ﱀ' => 'لح', 'ﱁ' => 'لخ', 'ﱂ' => 'لم', 'ﱃ' => 'لى', 'ﱄ' => 'لي', 'ﱅ' => 'مج', 'ﱆ' => 'مح', 'ﱇ' => 'مخ', 'ﱈ' => 'مم', 'ﱉ' => 'مى', 'ﱊ' => 'مي', 'ﱋ' => 'نج', 'ﱌ' => 'نح', 'ﱍ' => 'نخ', 'ﱎ' => 'نم', 'ﱏ' => 'نى', 'ﱐ' => 'ني', 'ﱑ' => 'هج', 'ﱒ' => 'هم', 'ﱓ' => 'هى', 'ﱔ' => 'هي', 'ﱕ' => 'يج', 'ﱖ' => 'يح', 'ﱗ' => 'يخ', 'ﱘ' => 'يم', 'ﱙ' => 'يى', 'ﱚ' => 'يي', 'ﱛ' => 'ذٰ', 'ﱜ' => 'رٰ', 'ﱝ' => 'ىٰ', 'ﱞ' => ' ٌّ', 'ﱟ' => ' ٍّ', 'ﱠ' => ' َّ', 'ﱡ' => ' ُّ', 'ﱢ' => ' ِّ', 'ﱣ' => ' ّٰ', 'ﱤ' => 'ئر', 'ﱥ' => 'ئز', 'ﱦ' => 'ئم', 'ﱧ' => 'ئن', 'ﱨ' => 'ئى', 'ﱩ' => 'ئي', 'ﱪ' => 'بر', 'ﱫ' => 'بز', 'ﱬ' => 'بم', 'ﱭ' => 'بن', 'ﱮ' => 'بى', 'ﱯ' => 'بي', 'ﱰ' => 'تر', 'ﱱ' => 'تز', 'ﱲ' => 'تم', 'ﱳ' => 'تن', 'ﱴ' => 'تى', 'ﱵ' => 'تي', 'ﱶ' => 'ثر', 'ﱷ' => 'ثز', 'ﱸ' => 'ثم', 'ﱹ' => 'ثن', 'ﱺ' => 'ثى', 'ﱻ' => 'ثي', 'ﱼ' => 'فى', 'ﱽ' => 'في', 'ﱾ' => 'قى', 'ﱿ' => 'قي', 'ﲀ' => 'كا', 'ﲁ' => 'كل', 'ﲂ' => 'كم', 'ﲃ' => 'كى', 'ﲄ' => 'كي', 'ﲅ' => 'لم', 'ﲆ' => 'لى', 'ﲇ' => 'لي', 'ﲈ' => 'ما', 'ﲉ' => 'مم', 'ﲊ' => 'نر', 'ﲋ' => 'نز', 'ﲌ' => 'نم', 'ﲍ' => 'نن', 'ﲎ' => 'نى', 'ﲏ' => 'ني', 'ﲐ' => 'ىٰ', 'ﲑ' => 'ير', 'ﲒ' => 'يز', 'ﲓ' => 'يم', 'ﲔ' => 'ين', 'ﲕ' => 'يى', 'ﲖ' => 'يي', 'ﲗ' => 'ئج', 'ﲘ' => 'ئح', 'ﲙ' => 'ئخ', 'ﲚ' => 'ئم', 'ﲛ' => 'ئه', 'ﲜ' => 'بج', 'ﲝ' => 'بح', 'ﲞ' => 'بخ', 'ﲟ' => 'بم', 'ﲠ' => 'به', 'ﲡ' => 'تج', 'ﲢ' => 'تح', 'ﲣ' => 'تخ', 'ﲤ' => 'تم', 'ﲥ' => 'ته', 'ﲦ' => 'ثم', 'ﲧ' => 'جح', 'ﲨ' => 'جم', 'ﲩ' => 'حج', 'ﲪ' => 'حم', 'ﲫ' => 'خج', 'ﲬ' => 'خم', 'ﲭ' => 'سج', 'ﲮ' => 'سح', 'ﲯ' => 'سخ', 'ﲰ' => 'سم', 'ﲱ' => 'صح', 'ﲲ' => 'صخ', 'ﲳ' => 'صم', 'ﲴ' => 'ضج', 'ﲵ' => 'ضح', 'ﲶ' => 'ضخ', 'ﲷ' => 'ضم', 'ﲸ' => 'طح', 'ﲹ' => 'ظم', 'ﲺ' => 'عج', 'ﲻ' => 'عم', 'ﲼ' => 'غج', 'ﲽ' => 'غم', 'ﲾ' => 'فج', 'ﲿ' => 'فح', 'ﳀ' => 'فخ', 'ﳁ' => 'فم', 'ﳂ' => 'قح', 'ﳃ' => 'قم', 'ﳄ' => 'كج', 'ﳅ' => 'كح', 'ﳆ' => 'كخ', 'ﳇ' => 'كل', 'ﳈ' => 'كم', 'ﳉ' => 'لج', 'ﳊ' => 'لح', 'ﳋ' => 'لخ', 'ﳌ' => 'لم', 'ﳍ' => 'له', 'ﳎ' => 'مج', 'ﳏ' => 'مح', 'ﳐ' => 'مخ', 'ﳑ' => 'مم', 'ﳒ' => 'نج', 'ﳓ' => 'نح', 'ﳔ' => 'نخ', 'ﳕ' => 'نم', 'ﳖ' => 'نه', 'ﳗ' => 'هج', 'ﳘ' => 'هم', 'ﳙ' => 'هٰ', 'ﳚ' => 'يج', 'ﳛ' => 'يح', 'ﳜ' => 'يخ', 'ﳝ' => 'يم', 'ﳞ' => 'يه', 'ﳟ' => 'ئم', 'ﳠ' => 'ئه', 'ﳡ' => 'بم', 'ﳢ' => 'به', 'ﳣ' => 'تم', 'ﳤ' => 'ته', 'ﳥ' => 'ثم', 'ﳦ' => 'ثه', 'ﳧ' => 'سم', 'ﳨ' => 'سه', 'ﳩ' => 'شم', 'ﳪ' => 'شه', 'ﳫ' => 'كل', 'ﳬ' => 'كم', 'ﳭ' => 'لم', 'ﳮ' => 'نم', 'ﳯ' => 'نه', 'ﳰ' => 'يم', 'ﳱ' => 'يه', 'ﳲ' => 'ـَّ', 'ﳳ' => 'ـُّ', 'ﳴ' => 'ـِّ', 'ﳵ' => 'طى', 'ﳶ' => 'طي', 'ﳷ' => 'عى', 'ﳸ' => 'عي', 'ﳹ' => 'غى', 'ﳺ' => 'غي', 'ﳻ' => 'سى', 'ﳼ' => 'سي', 'ﳽ' => 'شى', 'ﳾ' => 'شي', 'ﳿ' => 'حى', 'ﴀ' => 'حي', 'ﴁ' => 'جى', 'ﴂ' => 'جي', 'ﴃ' => 'خى', 'ﴄ' => 'خي', 'ﴅ' => 'صى', 'ﴆ' => 'صي', 'ﴇ' => 'ضى', 'ﴈ' => 'ضي', 'ﴉ' => 'شج', 'ﴊ' => 'شح', 'ﴋ' => 'شخ', 'ﴌ' => 'شم', 'ﴍ' => 'شر', 'ﴎ' => 'سر', 'ﴏ' => 'صر', 'ﴐ' => 'ضر', 'ﴑ' => 'طى', 'ﴒ' => 'طي', 'ﴓ' => 'عى', 'ﴔ' => 'عي', 'ﴕ' => 'غى', 'ﴖ' => 'غي', 'ﴗ' => 'سى', 'ﴘ' => 'سي', 'ﴙ' => 'شى', 'ﴚ' => 'شي', 'ﴛ' => 'حى', 'ﴜ' => 'حي', 'ﴝ' => 'جى', 'ﴞ' => 'جي', 'ﴟ' => 'خى', 'ﴠ' => 'خي', 'ﴡ' => 'صى', 'ﴢ' => 'صي', 'ﴣ' => 'ضى', 'ﴤ' => 'ضي', 'ﴥ' => 'شج', 'ﴦ' => 'شح', 'ﴧ' => 'شخ', 'ﴨ' => 'شم', 'ﴩ' => 'شر', 'ﴪ' => 'سر', 'ﴫ' => 'صر', 'ﴬ' => 'ضر', 'ﴭ' => 'شج', 'ﴮ' => 'شح', 'ﴯ' => 'شخ', 'ﴰ' => 'شم', 'ﴱ' => 'سه', 'ﴲ' => 'شه', 'ﴳ' => 'طم', 'ﴴ' => 'سج', 'ﴵ' => 'سح', 'ﴶ' => 'سخ', 'ﴷ' => 'شج', 'ﴸ' => 'شح', 'ﴹ' => 'شخ', 'ﴺ' => 'طم', 'ﴻ' => 'ظم', 'ﴼ' => 'اً', 'ﴽ' => 'اً', 'ﵐ' => 'تجم', 'ﵑ' => 'تحج', 'ﵒ' => 'تحج', 'ﵓ' => 'تحم', 'ﵔ' => 'تخم', 'ﵕ' => 'تمج', 'ﵖ' => 'تمح', 'ﵗ' => 'تمخ', 'ﵘ' => 'جمح', 'ﵙ' => 'جمح', 'ﵚ' => 'حمي', 'ﵛ' => 'حمى', 'ﵜ' => 'سحج', 'ﵝ' => 'سجح', 'ﵞ' => 'سجى', 'ﵟ' => 'سمح', 'ﵠ' => 'سمح', 'ﵡ' => 'سمج', 'ﵢ' => 'سمم', 'ﵣ' => 'سمم', 'ﵤ' => 'صحح', 'ﵥ' => 'صحح', 'ﵦ' => 'صمم', 'ﵧ' => 'شحم', 'ﵨ' => 'شحم', 'ﵩ' => 'شجي', 'ﵪ' => 'شمخ', 'ﵫ' => 'شمخ', 'ﵬ' => 'شمم', 'ﵭ' => 'شمم', 'ﵮ' => 'ضحى', 'ﵯ' => 'ضخم', 'ﵰ' => 'ضخم', 'ﵱ' => 'طمح', 'ﵲ' => 'طمح', 'ﵳ' => 'طمم', 'ﵴ' => 'طمي', 'ﵵ' => 'عجم', 'ﵶ' => 'عمم', 'ﵷ' => 'عمم', 'ﵸ' => 'عمى', 'ﵹ' => 'غمم', 'ﵺ' => 'غمي', 'ﵻ' => 'غمى', 'ﵼ' => 'فخم', 'ﵽ' => 'فخم', 'ﵾ' => 'قمح', 'ﵿ' => 'قمم', 'ﶀ' => 'لحم', 'ﶁ' => 'لحي', 'ﶂ' => 'لحى', 'ﶃ' => 'لجج', 'ﶄ' => 'لجج', 'ﶅ' => 'لخم', 'ﶆ' => 'لخم', 'ﶇ' => 'لمح', 'ﶈ' => 'لمح', 'ﶉ' => 'محج', 'ﶊ' => 'محم', 'ﶋ' => 'محي', 'ﶌ' => 'مجح', 'ﶍ' => 'مجم', 'ﶎ' => 'مخج', 'ﶏ' => 'مخم', 'ﶒ' => 'مجخ', 'ﶓ' => 'همج', 'ﶔ' => 'همم', 'ﶕ' => 'نحم', 'ﶖ' => 'نحى', 'ﶗ' => 'نجم', 'ﶘ' => 'نجم', 'ﶙ' => 'نجى', 'ﶚ' => 'نمي', 'ﶛ' => 'نمى', 'ﶜ' => 'يمم', 'ﶝ' => 'يمم', 'ﶞ' => 'بخي', 'ﶟ' => 'تجي', 'ﶠ' => 'تجى', 'ﶡ' => 'تخي', 'ﶢ' => 'تخى', 'ﶣ' => 'تمي', 'ﶤ' => 'تمى', 'ﶥ' => 'جمي', 'ﶦ' => 'جحى', 'ﶧ' => 'جمى', 'ﶨ' => 'سخى', 'ﶩ' => 'صحي', 'ﶪ' => 'شحي', 'ﶫ' => 'ضحي', 'ﶬ' => 'لجي', 'ﶭ' => 'لمي', 'ﶮ' => 'يحي', 'ﶯ' => 'يجي', 'ﶰ' => 'يمي', 'ﶱ' => 'ممي', 'ﶲ' => 'قمي', 'ﶳ' => 'نحي', 'ﶴ' => 'قمح', 'ﶵ' => 'لحم', 'ﶶ' => 'عمي', 'ﶷ' => 'كمي', 'ﶸ' => 'نجح', 'ﶹ' => 'مخي', 'ﶺ' => 'لجم', 'ﶻ' => 'كمم', 'ﶼ' => 'لجم', 'ﶽ' => 'نجح', 'ﶾ' => 'جحي', 'ﶿ' => 'حجي', 'ﷀ' => 'مجي', 'ﷁ' => 'فمي', 'ﷂ' => 'بحي', 'ﷃ' => 'كمم', 'ﷄ' => 'عجم', 'ﷅ' => 'صمم', 'ﷆ' => 'سخي', 'ﷇ' => 'نجي', 'ﷰ' => 'صلے', 'ﷱ' => 'قلے', 'ﷲ' => 'الله', 'ﷳ' => 'اكبر', 'ﷴ' => 'محمد', 'ﷵ' => 'صلعم', 'ﷶ' => 'رسول', 'ﷷ' => 'عليه', 'ﷸ' => 'وسلم', 'ﷹ' => 'صلى', 'ﷺ' => 'صلى الله عليه وسلم', 'ﷻ' => 'جل جلاله', '﷼' => 'ریال', '︐' => ',', '︑' => '、', '︒' => '。', '︓' => ':', '︔' => ';', '︕' => '!', '︖' => '?', '︗' => '〖', '︘' => '〗', '︙' => '...', '︰' => '..', '︱' => '—', '︲' => '–', '︳' => '_', '︴' => '_', '︵' => '(', '︶' => ')', '︷' => '{', '︸' => '}', '︹' => '〔', '︺' => '〕', '︻' => '【', '︼' => '】', '︽' => '《', '︾' => '》', '︿' => '〈', '﹀' => '〉', '﹁' => '「', '﹂' => '」', '﹃' => '『', '﹄' => '』', '﹇' => '[', '﹈' => ']', '﹉' => ' ̅', '﹊' => ' ̅', '﹋' => ' ̅', '﹌' => ' ̅', '﹍' => '_', '﹎' => '_', '﹏' => '_', '﹐' => ',', '﹑' => '、', '﹒' => '.', '﹔' => ';', '﹕' => ':', '﹖' => '?', '﹗' => '!', '﹘' => '—', '﹙' => '(', '﹚' => ')', '﹛' => '{', '﹜' => '}', '﹝' => '〔', '﹞' => '〕', '﹟' => '#', '﹠' => '&', '﹡' => '*', '﹢' => '+', '﹣' => '-', '﹤' => '<', '﹥' => '>', '﹦' => '=', '﹨' => '\\', '﹩' => '$', '﹪' => '%', '﹫' => '@', 'ﹰ' => ' ً', 'ﹱ' => 'ـً', 'ﹲ' => ' ٌ', 'ﹴ' => ' ٍ', 'ﹶ' => ' َ', 'ﹷ' => 'ـَ', 'ﹸ' => ' ُ', 'ﹹ' => 'ـُ', 'ﹺ' => ' ِ', 'ﹻ' => 'ـِ', 'ﹼ' => ' ّ', 'ﹽ' => 'ـّ', 'ﹾ' => ' ْ', 'ﹿ' => 'ـْ', 'ﺀ' => 'ء', 'ﺁ' => 'آ', 'ﺂ' => 'آ', 'ﺃ' => 'أ', 'ﺄ' => 'أ', 'ﺅ' => 'ؤ', 'ﺆ' => 'ؤ', 'ﺇ' => 'إ', 'ﺈ' => 'إ', 'ﺉ' => 'ئ', 'ﺊ' => 'ئ', 'ﺋ' => 'ئ', 'ﺌ' => 'ئ', 'ﺍ' => 'ا', 'ﺎ' => 'ا', 'ﺏ' => 'ب', 'ﺐ' => 'ب', 'ﺑ' => 'ب', 'ﺒ' => 'ب', 'ﺓ' => 'ة', 'ﺔ' => 'ة', 'ﺕ' => 'ت', 'ﺖ' => 'ت', 'ﺗ' => 'ت', 'ﺘ' => 'ت', 'ﺙ' => 'ث', 'ﺚ' => 'ث', 'ﺛ' => 'ث', 'ﺜ' => 'ث', 'ﺝ' => 'ج', 'ﺞ' => 'ج', 'ﺟ' => 'ج', 'ﺠ' => 'ج', 'ﺡ' => 'ح', 'ﺢ' => 'ح', 'ﺣ' => 'ح', 'ﺤ' => 'ح', 'ﺥ' => 'خ', 'ﺦ' => 'خ', 'ﺧ' => 'خ', 'ﺨ' => 'خ', 'ﺩ' => 'د', 'ﺪ' => 'د', 'ﺫ' => 'ذ', 'ﺬ' => 'ذ', 'ﺭ' => 'ر', 'ﺮ' => 'ر', 'ﺯ' => 'ز', 'ﺰ' => 'ز', 'ﺱ' => 'س', 'ﺲ' => 'س', 'ﺳ' => 'س', 'ﺴ' => 'س', 'ﺵ' => 'ش', 'ﺶ' => 'ش', 'ﺷ' => 'ش', 'ﺸ' => 'ش', 'ﺹ' => 'ص', 'ﺺ' => 'ص', 'ﺻ' => 'ص', 'ﺼ' => 'ص', 'ﺽ' => 'ض', 'ﺾ' => 'ض', 'ﺿ' => 'ض', 'ﻀ' => 'ض', 'ﻁ' => 'ط', 'ﻂ' => 'ط', 'ﻃ' => 'ط', 'ﻄ' => 'ط', 'ﻅ' => 'ظ', 'ﻆ' => 'ظ', 'ﻇ' => 'ظ', 'ﻈ' => 'ظ', 'ﻉ' => 'ع', 'ﻊ' => 'ع', 'ﻋ' => 'ع', 'ﻌ' => 'ع', 'ﻍ' => 'غ', 'ﻎ' => 'غ', 'ﻏ' => 'غ', 'ﻐ' => 'غ', 'ﻑ' => 'ف', 'ﻒ' => 'ف', 'ﻓ' => 'ف', 'ﻔ' => 'ف', 'ﻕ' => 'ق', 'ﻖ' => 'ق', 'ﻗ' => 'ق', 'ﻘ' => 'ق', 'ﻙ' => 'ك', 'ﻚ' => 'ك', 'ﻛ' => 'ك', 'ﻜ' => 'ك', 'ﻝ' => 'ل', 'ﻞ' => 'ل', 'ﻟ' => 'ل', 'ﻠ' => 'ل', 'ﻡ' => 'م', 'ﻢ' => 'م', 'ﻣ' => 'م', 'ﻤ' => 'م', 'ﻥ' => 'ن', 'ﻦ' => 'ن', 'ﻧ' => 'ن', 'ﻨ' => 'ن', 'ﻩ' => 'ه', 'ﻪ' => 'ه', 'ﻫ' => 'ه', 'ﻬ' => 'ه', 'ﻭ' => 'و', 'ﻮ' => 'و', 'ﻯ' => 'ى', 'ﻰ' => 'ى', 'ﻱ' => 'ي', 'ﻲ' => 'ي', 'ﻳ' => 'ي', 'ﻴ' => 'ي', 'ﻵ' => 'لآ', 'ﻶ' => 'لآ', 'ﻷ' => 'لأ', 'ﻸ' => 'لأ', 'ﻹ' => 'لإ', 'ﻺ' => 'لإ', 'ﻻ' => 'لا', 'ﻼ' => 'لا', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', ''' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', '0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', 'A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D', 'E' => 'E', 'F' => 'F', 'G' => 'G', 'H' => 'H', 'I' => 'I', 'J' => 'J', 'K' => 'K', 'L' => 'L', 'M' => 'M', 'N' => 'N', 'O' => 'O', 'P' => 'P', 'Q' => 'Q', 'R' => 'R', 'S' => 'S', 'T' => 'T', 'U' => 'U', 'V' => 'V', 'W' => 'W', 'X' => 'X', 'Y' => 'Y', 'Z' => 'Z', '[' => '[', '\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'h', 'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'm' => 'm', 'n' => 'n', 'o' => 'o', 'p' => 'p', 'q' => 'q', 'r' => 'r', 's' => 's', 't' => 't', 'u' => 'u', 'v' => 'v', 'w' => 'w', 'x' => 'x', 'y' => 'y', 'z' => 'z', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '⦅' => '⦅', '⦆' => '⦆', '。' => '。', '「' => '「', '」' => '」', '、' => '、', '・' => '・', 'ヲ' => 'ヲ', 'ァ' => 'ァ', 'ィ' => 'ィ', 'ゥ' => 'ゥ', 'ェ' => 'ェ', 'ォ' => 'ォ', 'ャ' => 'ャ', 'ュ' => 'ュ', 'ョ' => 'ョ', 'ッ' => 'ッ', 'ー' => 'ー', 'ア' => 'ア', 'イ' => 'イ', 'ウ' => 'ウ', 'エ' => 'エ', 'オ' => 'オ', 'カ' => 'カ', 'キ' => 'キ', 'ク' => 'ク', 'ケ' => 'ケ', 'コ' => 'コ', 'サ' => 'サ', 'シ' => 'シ', 'ス' => 'ス', 'セ' => 'セ', 'ソ' => 'ソ', 'タ' => 'タ', 'チ' => 'チ', 'ツ' => 'ツ', 'テ' => 'テ', 'ト' => 'ト', 'ナ' => 'ナ', 'ニ' => 'ニ', 'ヌ' => 'ヌ', 'ネ' => 'ネ', 'ノ' => 'ノ', 'ハ' => 'ハ', 'ヒ' => 'ヒ', 'フ' => 'フ', 'ヘ' => 'ヘ', 'ホ' => 'ホ', 'マ' => 'マ', 'ミ' => 'ミ', 'ム' => 'ム', 'メ' => 'メ', 'モ' => 'モ', 'ヤ' => 'ヤ', 'ユ' => 'ユ', 'ヨ' => 'ヨ', 'ラ' => 'ラ', 'リ' => 'リ', 'ル' => 'ル', 'レ' => 'レ', 'ロ' => 'ロ', 'ワ' => 'ワ', 'ン' => 'ン', '゙' => '゙', '゚' => '゚', 'ᅠ' => 'ᅠ', 'ᄀ' => 'ᄀ', 'ᄁ' => 'ᄁ', 'ᆪ' => 'ᆪ', 'ᄂ' => 'ᄂ', 'ᆬ' => 'ᆬ', 'ᆭ' => 'ᆭ', 'ᄃ' => 'ᄃ', 'ᄄ' => 'ᄄ', 'ᄅ' => 'ᄅ', 'ᆰ' => 'ᆰ', 'ᆱ' => 'ᆱ', 'ᆲ' => 'ᆲ', 'ᆳ' => 'ᆳ', 'ᆴ' => 'ᆴ', 'ᆵ' => 'ᆵ', 'ᄚ' => 'ᄚ', 'ᄆ' => 'ᄆ', 'ᄇ' => 'ᄇ', 'ᄈ' => 'ᄈ', 'ᄡ' => 'ᄡ', 'ᄉ' => 'ᄉ', 'ᄊ' => 'ᄊ', 'ᄋ' => 'ᄋ', 'ᄌ' => 'ᄌ', 'ᄍ' => 'ᄍ', 'ᄎ' => 'ᄎ', 'ᄏ' => 'ᄏ', 'ᄐ' => 'ᄐ', 'ᄑ' => 'ᄑ', 'ᄒ' => 'ᄒ', 'ᅡ' => 'ᅡ', 'ᅢ' => 'ᅢ', 'ᅣ' => 'ᅣ', 'ᅤ' => 'ᅤ', 'ᅥ' => 'ᅥ', 'ᅦ' => 'ᅦ', 'ᅧ' => 'ᅧ', 'ᅨ' => 'ᅨ', 'ᅩ' => 'ᅩ', 'ᅪ' => 'ᅪ', 'ᅫ' => 'ᅫ', 'ᅬ' => 'ᅬ', 'ᅭ' => 'ᅭ', 'ᅮ' => 'ᅮ', 'ᅯ' => 'ᅯ', 'ᅰ' => 'ᅰ', 'ᅱ' => 'ᅱ', 'ᅲ' => 'ᅲ', 'ᅳ' => 'ᅳ', 'ᅴ' => 'ᅴ', 'ᅵ' => 'ᅵ', '¢' => '¢', '£' => '£', '¬' => '¬', ' ̄' => ' ̄', '¦' => '¦', '¥' => '¥', '₩' => '₩', '│' => '│', '←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '■' => '■', '○' => '○', '𝐀' => 'A', '𝐁' => 'B', '𝐂' => 'C', '𝐃' => 'D', '𝐄' => 'E', '𝐅' => 'F', '𝐆' => 'G', '𝐇' => 'H', '𝐈' => 'I', '𝐉' => 'J', '𝐊' => 'K', '𝐋' => 'L', '𝐌' => 'M', '𝐍' => 'N', '𝐎' => 'O', '𝐏' => 'P', '𝐐' => 'Q', '𝐑' => 'R', '𝐒' => 'S', '𝐓' => 'T', '𝐔' => 'U', '𝐕' => 'V', '𝐖' => 'W', '𝐗' => 'X', '𝐘' => 'Y', '𝐙' => 'Z', '𝐚' => 'a', '𝐛' => 'b', '𝐜' => 'c', '𝐝' => 'd', '𝐞' => 'e', '𝐟' => 'f', '𝐠' => 'g', '𝐡' => 'h', '𝐢' => 'i', '𝐣' => 'j', '𝐤' => 'k', '𝐥' => 'l', '𝐦' => 'm', '𝐧' => 'n', '𝐨' => 'o', '𝐩' => 'p', '𝐪' => 'q', '𝐫' => 'r', '𝐬' => 's', '𝐭' => 't', '𝐮' => 'u', '𝐯' => 'v', '𝐰' => 'w', '𝐱' => 'x', '𝐲' => 'y', '𝐳' => 'z', '𝐴' => 'A', '𝐵' => 'B', '𝐶' => 'C', '𝐷' => 'D', '𝐸' => 'E', '𝐹' => 'F', '𝐺' => 'G', '𝐻' => 'H', '𝐼' => 'I', '𝐽' => 'J', '𝐾' => 'K', '𝐿' => 'L', '𝑀' => 'M', '𝑁' => 'N', '𝑂' => 'O', '𝑃' => 'P', '𝑄' => 'Q', '𝑅' => 'R', '𝑆' => 'S', '𝑇' => 'T', '𝑈' => 'U', '𝑉' => 'V', '𝑊' => 'W', '𝑋' => 'X', '𝑌' => 'Y', '𝑍' => 'Z', '𝑎' => 'a', '𝑏' => 'b', '𝑐' => 'c', '𝑑' => 'd', '𝑒' => 'e', '𝑓' => 'f', '𝑔' => 'g', '𝑖' => 'i', '𝑗' => 'j', '𝑘' => 'k', '𝑙' => 'l', '𝑚' => 'm', '𝑛' => 'n', '𝑜' => 'o', '𝑝' => 'p', '𝑞' => 'q', '𝑟' => 'r', '𝑠' => 's', '𝑡' => 't', '𝑢' => 'u', '𝑣' => 'v', '𝑤' => 'w', '𝑥' => 'x', '𝑦' => 'y', '𝑧' => 'z', '𝑨' => 'A', '𝑩' => 'B', '𝑪' => 'C', '𝑫' => 'D', '𝑬' => 'E', '𝑭' => 'F', '𝑮' => 'G', '𝑯' => 'H', '𝑰' => 'I', '𝑱' => 'J', '𝑲' => 'K', '𝑳' => 'L', '𝑴' => 'M', '𝑵' => 'N', '𝑶' => 'O', '𝑷' => 'P', '𝑸' => 'Q', '𝑹' => 'R', '𝑺' => 'S', '𝑻' => 'T', '𝑼' => 'U', '𝑽' => 'V', '𝑾' => 'W', '𝑿' => 'X', '𝒀' => 'Y', '𝒁' => 'Z', '𝒂' => 'a', '𝒃' => 'b', '𝒄' => 'c', '𝒅' => 'd', '𝒆' => 'e', '𝒇' => 'f', '𝒈' => 'g', '𝒉' => 'h', '𝒊' => 'i', '𝒋' => 'j', '𝒌' => 'k', '𝒍' => 'l', '𝒎' => 'm', '𝒏' => 'n', '𝒐' => 'o', '𝒑' => 'p', '𝒒' => 'q', '𝒓' => 'r', '𝒔' => 's', '𝒕' => 't', '𝒖' => 'u', '𝒗' => 'v', '𝒘' => 'w', '𝒙' => 'x', '𝒚' => 'y', '𝒛' => 'z', '𝒜' => 'A', '𝒞' => 'C', '𝒟' => 'D', '𝒢' => 'G', '𝒥' => 'J', '𝒦' => 'K', '𝒩' => 'N', '𝒪' => 'O', '𝒫' => 'P', '𝒬' => 'Q', '𝒮' => 'S', '𝒯' => 'T', '𝒰' => 'U', '𝒱' => 'V', '𝒲' => 'W', '𝒳' => 'X', '𝒴' => 'Y', '𝒵' => 'Z', '𝒶' => 'a', '𝒷' => 'b', '𝒸' => 'c', '𝒹' => 'd', '𝒻' => 'f', '𝒽' => 'h', '𝒾' => 'i', '𝒿' => 'j', '𝓀' => 'k', '𝓁' => 'l', '𝓂' => 'm', '𝓃' => 'n', '𝓅' => 'p', '𝓆' => 'q', '𝓇' => 'r', '𝓈' => 's', '𝓉' => 't', '𝓊' => 'u', '𝓋' => 'v', '𝓌' => 'w', '𝓍' => 'x', '𝓎' => 'y', '𝓏' => 'z', '𝓐' => 'A', '𝓑' => 'B', '𝓒' => 'C', '𝓓' => 'D', '𝓔' => 'E', '𝓕' => 'F', '𝓖' => 'G', '𝓗' => 'H', '𝓘' => 'I', '𝓙' => 'J', '𝓚' => 'K', '𝓛' => 'L', '𝓜' => 'M', '𝓝' => 'N', '𝓞' => 'O', '𝓟' => 'P', '𝓠' => 'Q', '𝓡' => 'R', '𝓢' => 'S', '𝓣' => 'T', '𝓤' => 'U', '𝓥' => 'V', '𝓦' => 'W', '𝓧' => 'X', '𝓨' => 'Y', '𝓩' => 'Z', '𝓪' => 'a', '𝓫' => 'b', '𝓬' => 'c', '𝓭' => 'd', '𝓮' => 'e', '𝓯' => 'f', '𝓰' => 'g', '𝓱' => 'h', '𝓲' => 'i', '𝓳' => 'j', '𝓴' => 'k', '𝓵' => 'l', '𝓶' => 'm', '𝓷' => 'n', '𝓸' => 'o', '𝓹' => 'p', '𝓺' => 'q', '𝓻' => 'r', '𝓼' => 's', '𝓽' => 't', '𝓾' => 'u', '𝓿' => 'v', '𝔀' => 'w', '𝔁' => 'x', '𝔂' => 'y', '𝔃' => 'z', '𝔄' => 'A', '𝔅' => 'B', '𝔇' => 'D', '𝔈' => 'E', '𝔉' => 'F', '𝔊' => 'G', '𝔍' => 'J', '𝔎' => 'K', '𝔏' => 'L', '𝔐' => 'M', '𝔑' => 'N', '𝔒' => 'O', '𝔓' => 'P', '𝔔' => 'Q', '𝔖' => 'S', '𝔗' => 'T', '𝔘' => 'U', '𝔙' => 'V', '𝔚' => 'W', '𝔛' => 'X', '𝔜' => 'Y', '𝔞' => 'a', '𝔟' => 'b', '𝔠' => 'c', '𝔡' => 'd', '𝔢' => 'e', '𝔣' => 'f', '𝔤' => 'g', '𝔥' => 'h', '𝔦' => 'i', '𝔧' => 'j', '𝔨' => 'k', '𝔩' => 'l', '𝔪' => 'm', '𝔫' => 'n', '𝔬' => 'o', '𝔭' => 'p', '𝔮' => 'q', '𝔯' => 'r', '𝔰' => 's', '𝔱' => 't', '𝔲' => 'u', '𝔳' => 'v', '𝔴' => 'w', '𝔵' => 'x', '𝔶' => 'y', '𝔷' => 'z', '𝔸' => 'A', '𝔹' => 'B', '𝔻' => 'D', '𝔼' => 'E', '𝔽' => 'F', '𝔾' => 'G', '𝕀' => 'I', '𝕁' => 'J', '𝕂' => 'K', '𝕃' => 'L', '𝕄' => 'M', '𝕆' => 'O', '𝕊' => 'S', '𝕋' => 'T', '𝕌' => 'U', '𝕍' => 'V', '𝕎' => 'W', '𝕏' => 'X', '𝕐' => 'Y', '𝕒' => 'a', '𝕓' => 'b', '𝕔' => 'c', '𝕕' => 'd', '𝕖' => 'e', '𝕗' => 'f', '𝕘' => 'g', '𝕙' => 'h', '𝕚' => 'i', '𝕛' => 'j', '𝕜' => 'k', '𝕝' => 'l', '𝕞' => 'm', '𝕟' => 'n', '𝕠' => 'o', '𝕡' => 'p', '𝕢' => 'q', '𝕣' => 'r', '𝕤' => 's', '𝕥' => 't', '𝕦' => 'u', '𝕧' => 'v', '𝕨' => 'w', '𝕩' => 'x', '𝕪' => 'y', '𝕫' => 'z', '𝕬' => 'A', '𝕭' => 'B', '𝕮' => 'C', '𝕯' => 'D', '𝕰' => 'E', '𝕱' => 'F', '𝕲' => 'G', '𝕳' => 'H', '𝕴' => 'I', '𝕵' => 'J', '𝕶' => 'K', '𝕷' => 'L', '𝕸' => 'M', '𝕹' => 'N', '𝕺' => 'O', '𝕻' => 'P', '𝕼' => 'Q', '𝕽' => 'R', '𝕾' => 'S', '𝕿' => 'T', '𝖀' => 'U', '𝖁' => 'V', '𝖂' => 'W', '𝖃' => 'X', '𝖄' => 'Y', '𝖅' => 'Z', '𝖆' => 'a', '𝖇' => 'b', '𝖈' => 'c', '𝖉' => 'd', '𝖊' => 'e', '𝖋' => 'f', '𝖌' => 'g', '𝖍' => 'h', '𝖎' => 'i', '𝖏' => 'j', '𝖐' => 'k', '𝖑' => 'l', '𝖒' => 'm', '𝖓' => 'n', '𝖔' => 'o', '𝖕' => 'p', '𝖖' => 'q', '𝖗' => 'r', '𝖘' => 's', '𝖙' => 't', '𝖚' => 'u', '𝖛' => 'v', '𝖜' => 'w', '𝖝' => 'x', '𝖞' => 'y', '𝖟' => 'z', '𝖠' => 'A', '𝖡' => 'B', '𝖢' => 'C', '𝖣' => 'D', '𝖤' => 'E', '𝖥' => 'F', '𝖦' => 'G', '𝖧' => 'H', '𝖨' => 'I', '𝖩' => 'J', '𝖪' => 'K', '𝖫' => 'L', '𝖬' => 'M', '𝖭' => 'N', '𝖮' => 'O', '𝖯' => 'P', '𝖰' => 'Q', '𝖱' => 'R', '𝖲' => 'S', '𝖳' => 'T', '𝖴' => 'U', '𝖵' => 'V', '𝖶' => 'W', '𝖷' => 'X', '𝖸' => 'Y', '𝖹' => 'Z', '𝖺' => 'a', '𝖻' => 'b', '𝖼' => 'c', '𝖽' => 'd', '𝖾' => 'e', '𝖿' => 'f', '𝗀' => 'g', '𝗁' => 'h', '𝗂' => 'i', '𝗃' => 'j', '𝗄' => 'k', '𝗅' => 'l', '𝗆' => 'm', '𝗇' => 'n', '𝗈' => 'o', '𝗉' => 'p', '𝗊' => 'q', '𝗋' => 'r', '𝗌' => 's', '𝗍' => 't', '𝗎' => 'u', '𝗏' => 'v', '𝗐' => 'w', '𝗑' => 'x', '𝗒' => 'y', '𝗓' => 'z', '𝗔' => 'A', '𝗕' => 'B', '𝗖' => 'C', '𝗗' => 'D', '𝗘' => 'E', '𝗙' => 'F', '𝗚' => 'G', '𝗛' => 'H', '𝗜' => 'I', '𝗝' => 'J', '𝗞' => 'K', '𝗟' => 'L', '𝗠' => 'M', '𝗡' => 'N', '𝗢' => 'O', '𝗣' => 'P', '𝗤' => 'Q', '𝗥' => 'R', '𝗦' => 'S', '𝗧' => 'T', '𝗨' => 'U', '𝗩' => 'V', '𝗪' => 'W', '𝗫' => 'X', '𝗬' => 'Y', '𝗭' => 'Z', '𝗮' => 'a', '𝗯' => 'b', '𝗰' => 'c', '𝗱' => 'd', '𝗲' => 'e', '𝗳' => 'f', '𝗴' => 'g', '𝗵' => 'h', '𝗶' => 'i', '𝗷' => 'j', '𝗸' => 'k', '𝗹' => 'l', '𝗺' => 'm', '𝗻' => 'n', '𝗼' => 'o', '𝗽' => 'p', '𝗾' => 'q', '𝗿' => 'r', '𝘀' => 's', '𝘁' => 't', '𝘂' => 'u', '𝘃' => 'v', '𝘄' => 'w', '𝘅' => 'x', '𝘆' => 'y', '𝘇' => 'z', '𝘈' => 'A', '𝘉' => 'B', '𝘊' => 'C', '𝘋' => 'D', '𝘌' => 'E', '𝘍' => 'F', '𝘎' => 'G', '𝘏' => 'H', '𝘐' => 'I', '𝘑' => 'J', '𝘒' => 'K', '𝘓' => 'L', '𝘔' => 'M', '𝘕' => 'N', '𝘖' => 'O', '𝘗' => 'P', '𝘘' => 'Q', '𝘙' => 'R', '𝘚' => 'S', '𝘛' => 'T', '𝘜' => 'U', '𝘝' => 'V', '𝘞' => 'W', '𝘟' => 'X', '𝘠' => 'Y', '𝘡' => 'Z', '𝘢' => 'a', '𝘣' => 'b', '𝘤' => 'c', '𝘥' => 'd', '𝘦' => 'e', '𝘧' => 'f', '𝘨' => 'g', '𝘩' => 'h', '𝘪' => 'i', '𝘫' => 'j', '𝘬' => 'k', '𝘭' => 'l', '𝘮' => 'm', '𝘯' => 'n', '𝘰' => 'o', '𝘱' => 'p', '𝘲' => 'q', '𝘳' => 'r', '𝘴' => 's', '𝘵' => 't', '𝘶' => 'u', '𝘷' => 'v', '𝘸' => 'w', '𝘹' => 'x', '𝘺' => 'y', '𝘻' => 'z', '𝘼' => 'A', '𝘽' => 'B', '𝘾' => 'C', '𝘿' => 'D', '𝙀' => 'E', '𝙁' => 'F', '𝙂' => 'G', '𝙃' => 'H', '𝙄' => 'I', '𝙅' => 'J', '𝙆' => 'K', '𝙇' => 'L', '𝙈' => 'M', '𝙉' => 'N', '𝙊' => 'O', '𝙋' => 'P', '𝙌' => 'Q', '𝙍' => 'R', '𝙎' => 'S', '𝙏' => 'T', '𝙐' => 'U', '𝙑' => 'V', '𝙒' => 'W', '𝙓' => 'X', '𝙔' => 'Y', '𝙕' => 'Z', '𝙖' => 'a', '𝙗' => 'b', '𝙘' => 'c', '𝙙' => 'd', '𝙚' => 'e', '𝙛' => 'f', '𝙜' => 'g', '𝙝' => 'h', '𝙞' => 'i', '𝙟' => 'j', '𝙠' => 'k', '𝙡' => 'l', '𝙢' => 'm', '𝙣' => 'n', '𝙤' => 'o', '𝙥' => 'p', '𝙦' => 'q', '𝙧' => 'r', '𝙨' => 's', '𝙩' => 't', '𝙪' => 'u', '𝙫' => 'v', '𝙬' => 'w', '𝙭' => 'x', '𝙮' => 'y', '𝙯' => 'z', '𝙰' => 'A', '𝙱' => 'B', '𝙲' => 'C', '𝙳' => 'D', '𝙴' => 'E', '𝙵' => 'F', '𝙶' => 'G', '𝙷' => 'H', '𝙸' => 'I', '𝙹' => 'J', '𝙺' => 'K', '𝙻' => 'L', '𝙼' => 'M', '𝙽' => 'N', '𝙾' => 'O', '𝙿' => 'P', '𝚀' => 'Q', '𝚁' => 'R', '𝚂' => 'S', '𝚃' => 'T', '𝚄' => 'U', '𝚅' => 'V', '𝚆' => 'W', '𝚇' => 'X', '𝚈' => 'Y', '𝚉' => 'Z', '𝚊' => 'a', '𝚋' => 'b', '𝚌' => 'c', '𝚍' => 'd', '𝚎' => 'e', '𝚏' => 'f', '𝚐' => 'g', '𝚑' => 'h', '𝚒' => 'i', '𝚓' => 'j', '𝚔' => 'k', '𝚕' => 'l', '𝚖' => 'm', '𝚗' => 'n', '𝚘' => 'o', '𝚙' => 'p', '𝚚' => 'q', '𝚛' => 'r', '𝚜' => 's', '𝚝' => 't', '𝚞' => 'u', '𝚟' => 'v', '𝚠' => 'w', '𝚡' => 'x', '𝚢' => 'y', '𝚣' => 'z', '𝚤' => 'ı', '𝚥' => 'ȷ', '𝚨' => 'Α', '𝚩' => 'Β', '𝚪' => 'Γ', '𝚫' => 'Δ', '𝚬' => 'Ε', '𝚭' => 'Ζ', '𝚮' => 'Η', '𝚯' => 'Θ', '𝚰' => 'Ι', '𝚱' => 'Κ', '𝚲' => 'Λ', '𝚳' => 'Μ', '𝚴' => 'Ν', '𝚵' => 'Ξ', '𝚶' => 'Ο', '𝚷' => 'Π', '𝚸' => 'Ρ', '𝚹' => 'Θ', '𝚺' => 'Σ', '𝚻' => 'Τ', '𝚼' => 'Υ', '𝚽' => 'Φ', '𝚾' => 'Χ', '𝚿' => 'Ψ', '𝛀' => 'Ω', '𝛁' => '∇', '𝛂' => 'α', '𝛃' => 'β', '𝛄' => 'γ', '𝛅' => 'δ', '𝛆' => 'ε', '𝛇' => 'ζ', '𝛈' => 'η', '𝛉' => 'θ', '𝛊' => 'ι', '𝛋' => 'κ', '𝛌' => 'λ', '𝛍' => 'μ', '𝛎' => 'ν', '𝛏' => 'ξ', '𝛐' => 'ο', '𝛑' => 'π', '𝛒' => 'ρ', '𝛓' => 'ς', '𝛔' => 'σ', '𝛕' => 'τ', '𝛖' => 'υ', '𝛗' => 'φ', '𝛘' => 'χ', '𝛙' => 'ψ', '𝛚' => 'ω', '𝛛' => '∂', '𝛜' => 'ε', '𝛝' => 'θ', '𝛞' => 'κ', '𝛟' => 'φ', '𝛠' => 'ρ', '𝛡' => 'π', '𝛢' => 'Α', '𝛣' => 'Β', '𝛤' => 'Γ', '𝛥' => 'Δ', '𝛦' => 'Ε', '𝛧' => 'Ζ', '𝛨' => 'Η', '𝛩' => 'Θ', '𝛪' => 'Ι', '𝛫' => 'Κ', '𝛬' => 'Λ', '𝛭' => 'Μ', '𝛮' => 'Ν', '𝛯' => 'Ξ', '𝛰' => 'Ο', '𝛱' => 'Π', '𝛲' => 'Ρ', '𝛳' => 'Θ', '𝛴' => 'Σ', '𝛵' => 'Τ', '𝛶' => 'Υ', '𝛷' => 'Φ', '𝛸' => 'Χ', '𝛹' => 'Ψ', '𝛺' => 'Ω', '𝛻' => '∇', '𝛼' => 'α', '𝛽' => 'β', '𝛾' => 'γ', '𝛿' => 'δ', '𝜀' => 'ε', '𝜁' => 'ζ', '𝜂' => 'η', '𝜃' => 'θ', '𝜄' => 'ι', '𝜅' => 'κ', '𝜆' => 'λ', '𝜇' => 'μ', '𝜈' => 'ν', '𝜉' => 'ξ', '𝜊' => 'ο', '𝜋' => 'π', '𝜌' => 'ρ', '𝜍' => 'ς', '𝜎' => 'σ', '𝜏' => 'τ', '𝜐' => 'υ', '𝜑' => 'φ', '𝜒' => 'χ', '𝜓' => 'ψ', '𝜔' => 'ω', '𝜕' => '∂', '𝜖' => 'ε', '𝜗' => 'θ', '𝜘' => 'κ', '𝜙' => 'φ', '𝜚' => 'ρ', '𝜛' => 'π', '𝜜' => 'Α', '𝜝' => 'Β', '𝜞' => 'Γ', '𝜟' => 'Δ', '𝜠' => 'Ε', '𝜡' => 'Ζ', '𝜢' => 'Η', '𝜣' => 'Θ', '𝜤' => 'Ι', '𝜥' => 'Κ', '𝜦' => 'Λ', '𝜧' => 'Μ', '𝜨' => 'Ν', '𝜩' => 'Ξ', '𝜪' => 'Ο', '𝜫' => 'Π', '𝜬' => 'Ρ', '𝜭' => 'Θ', '𝜮' => 'Σ', '𝜯' => 'Τ', '𝜰' => 'Υ', '𝜱' => 'Φ', '𝜲' => 'Χ', '𝜳' => 'Ψ', '𝜴' => 'Ω', '𝜵' => '∇', '𝜶' => 'α', '𝜷' => 'β', '𝜸' => 'γ', '𝜹' => 'δ', '𝜺' => 'ε', '𝜻' => 'ζ', '𝜼' => 'η', '𝜽' => 'θ', '𝜾' => 'ι', '𝜿' => 'κ', '𝝀' => 'λ', '𝝁' => 'μ', '𝝂' => 'ν', '𝝃' => 'ξ', '𝝄' => 'ο', '𝝅' => 'π', '𝝆' => 'ρ', '𝝇' => 'ς', '𝝈' => 'σ', '𝝉' => 'τ', '𝝊' => 'υ', '𝝋' => 'φ', '𝝌' => 'χ', '𝝍' => 'ψ', '𝝎' => 'ω', '𝝏' => '∂', '𝝐' => 'ε', '𝝑' => 'θ', '𝝒' => 'κ', '𝝓' => 'φ', '𝝔' => 'ρ', '𝝕' => 'π', '𝝖' => 'Α', '𝝗' => 'Β', '𝝘' => 'Γ', '𝝙' => 'Δ', '𝝚' => 'Ε', '𝝛' => 'Ζ', '𝝜' => 'Η', '𝝝' => 'Θ', '𝝞' => 'Ι', '𝝟' => 'Κ', '𝝠' => 'Λ', '𝝡' => 'Μ', '𝝢' => 'Ν', '𝝣' => 'Ξ', '𝝤' => 'Ο', '𝝥' => 'Π', '𝝦' => 'Ρ', '𝝧' => 'Θ', '𝝨' => 'Σ', '𝝩' => 'Τ', '𝝪' => 'Υ', '𝝫' => 'Φ', '𝝬' => 'Χ', '𝝭' => 'Ψ', '𝝮' => 'Ω', '𝝯' => '∇', '𝝰' => 'α', '𝝱' => 'β', '𝝲' => 'γ', '𝝳' => 'δ', '𝝴' => 'ε', '𝝵' => 'ζ', '𝝶' => 'η', '𝝷' => 'θ', '𝝸' => 'ι', '𝝹' => 'κ', '𝝺' => 'λ', '𝝻' => 'μ', '𝝼' => 'ν', '𝝽' => 'ξ', '𝝾' => 'ο', '𝝿' => 'π', '𝞀' => 'ρ', '𝞁' => 'ς', '𝞂' => 'σ', '𝞃' => 'τ', '𝞄' => 'υ', '𝞅' => 'φ', '𝞆' => 'χ', '𝞇' => 'ψ', '𝞈' => 'ω', '𝞉' => '∂', '𝞊' => 'ε', '𝞋' => 'θ', '𝞌' => 'κ', '𝞍' => 'φ', '𝞎' => 'ρ', '𝞏' => 'π', '𝞐' => 'Α', '𝞑' => 'Β', '𝞒' => 'Γ', '𝞓' => 'Δ', '𝞔' => 'Ε', '𝞕' => 'Ζ', '𝞖' => 'Η', '𝞗' => 'Θ', '𝞘' => 'Ι', '𝞙' => 'Κ', '𝞚' => 'Λ', '𝞛' => 'Μ', '𝞜' => 'Ν', '𝞝' => 'Ξ', '𝞞' => 'Ο', '𝞟' => 'Π', '𝞠' => 'Ρ', '𝞡' => 'Θ', '𝞢' => 'Σ', '𝞣' => 'Τ', '𝞤' => 'Υ', '𝞥' => 'Φ', '𝞦' => 'Χ', '𝞧' => 'Ψ', '𝞨' => 'Ω', '𝞩' => '∇', '𝞪' => 'α', '𝞫' => 'β', '𝞬' => 'γ', '𝞭' => 'δ', '𝞮' => 'ε', '𝞯' => 'ζ', '𝞰' => 'η', '𝞱' => 'θ', '𝞲' => 'ι', '𝞳' => 'κ', '𝞴' => 'λ', '𝞵' => 'μ', '𝞶' => 'ν', '𝞷' => 'ξ', '𝞸' => 'ο', '𝞹' => 'π', '𝞺' => 'ρ', '𝞻' => 'ς', '𝞼' => 'σ', '𝞽' => 'τ', '𝞾' => 'υ', '𝞿' => 'φ', '𝟀' => 'χ', '𝟁' => 'ψ', '𝟂' => 'ω', '𝟃' => '∂', '𝟄' => 'ε', '𝟅' => 'θ', '𝟆' => 'κ', '𝟇' => 'φ', '𝟈' => 'ρ', '𝟉' => 'π', '𝟊' => 'Ϝ', '𝟋' => 'ϝ', '𝟎' => '0', '𝟏' => '1', '𝟐' => '2', '𝟑' => '3', '𝟒' => '4', '𝟓' => '5', '𝟔' => '6', '𝟕' => '7', '𝟖' => '8', '𝟗' => '9', '𝟘' => '0', '𝟙' => '1', '𝟚' => '2', '𝟛' => '3', '𝟜' => '4', '𝟝' => '5', '𝟞' => '6', '𝟟' => '7', '𝟠' => '8', '𝟡' => '9', '𝟢' => '0', '𝟣' => '1', '𝟤' => '2', '𝟥' => '3', '𝟦' => '4', '𝟧' => '5', '𝟨' => '6', '𝟩' => '7', '𝟪' => '8', '𝟫' => '9', '𝟬' => '0', '𝟭' => '1', '𝟮' => '2', '𝟯' => '3', '𝟰' => '4', '𝟱' => '5', '𝟲' => '6', '𝟳' => '7', '𝟴' => '8', '𝟵' => '9', '𝟶' => '0', '𝟷' => '1', '𝟸' => '2', '𝟹' => '3', '𝟺' => '4', '𝟻' => '5', '𝟼' => '6', '𝟽' => '7', '𝟾' => '8', '𝟿' => '9', '𞸀' => 'ا', '𞸁' => 'ب', '𞸂' => 'ج', '𞸃' => 'د', '𞸅' => 'و', '𞸆' => 'ز', '𞸇' => 'ح', '𞸈' => 'ط', '𞸉' => 'ي', '𞸊' => 'ك', '𞸋' => 'ل', '𞸌' => 'م', '𞸍' => 'ن', '𞸎' => 'س', '𞸏' => 'ع', '𞸐' => 'ف', '𞸑' => 'ص', '𞸒' => 'ق', '𞸓' => 'ر', '𞸔' => 'ش', '𞸕' => 'ت', '𞸖' => 'ث', '𞸗' => 'خ', '𞸘' => 'ذ', '𞸙' => 'ض', '𞸚' => 'ظ', '𞸛' => 'غ', '𞸜' => 'ٮ', '𞸝' => 'ں', '𞸞' => 'ڡ', '𞸟' => 'ٯ', '𞸡' => 'ب', '𞸢' => 'ج', '𞸤' => 'ه', '𞸧' => 'ح', '𞸩' => 'ي', '𞸪' => 'ك', '𞸫' => 'ل', '𞸬' => 'م', '𞸭' => 'ن', '𞸮' => 'س', '𞸯' => 'ع', '𞸰' => 'ف', '𞸱' => 'ص', '𞸲' => 'ق', '𞸴' => 'ش', '𞸵' => 'ت', '𞸶' => 'ث', '𞸷' => 'خ', '𞸹' => 'ض', '𞸻' => 'غ', '𞹂' => 'ج', '𞹇' => 'ح', '𞹉' => 'ي', '𞹋' => 'ل', '𞹍' => 'ن', '𞹎' => 'س', '𞹏' => 'ع', '𞹑' => 'ص', '𞹒' => 'ق', '𞹔' => 'ش', '𞹗' => 'خ', '𞹙' => 'ض', '𞹛' => 'غ', '𞹝' => 'ں', '𞹟' => 'ٯ', '𞹡' => 'ب', '𞹢' => 'ج', '𞹤' => 'ه', '𞹧' => 'ح', '𞹨' => 'ط', '𞹩' => 'ي', '𞹪' => 'ك', '𞹬' => 'م', '𞹭' => 'ن', '𞹮' => 'س', '𞹯' => 'ع', '𞹰' => 'ف', '𞹱' => 'ص', '𞹲' => 'ق', '𞹴' => 'ش', '𞹵' => 'ت', '𞹶' => 'ث', '𞹷' => 'خ', '𞹹' => 'ض', '𞹺' => 'ظ', '𞹻' => 'غ', '𞹼' => 'ٮ', '𞹾' => 'ڡ', '𞺀' => 'ا', '𞺁' => 'ب', '𞺂' => 'ج', '𞺃' => 'د', '𞺄' => 'ه', '𞺅' => 'و', '𞺆' => 'ز', '𞺇' => 'ح', '𞺈' => 'ط', '𞺉' => 'ي', '𞺋' => 'ل', '𞺌' => 'م', '𞺍' => 'ن', '𞺎' => 'س', '𞺏' => 'ع', '𞺐' => 'ف', '𞺑' => 'ص', '𞺒' => 'ق', '𞺓' => 'ر', '𞺔' => 'ش', '𞺕' => 'ت', '𞺖' => 'ث', '𞺗' => 'خ', '𞺘' => 'ذ', '𞺙' => 'ض', '𞺚' => 'ظ', '𞺛' => 'غ', '𞺡' => 'ب', '𞺢' => 'ج', '𞺣' => 'د', '𞺥' => 'و', '𞺦' => 'ز', '𞺧' => 'ح', '𞺨' => 'ط', '𞺩' => 'ي', '𞺫' => 'ل', '𞺬' => 'م', '𞺭' => 'ن', '𞺮' => 'س', '𞺯' => 'ع', '𞺰' => 'ف', '𞺱' => 'ص', '𞺲' => 'ق', '𞺳' => 'ر', '𞺴' => 'ش', '𞺵' => 'ت', '𞺶' => 'ث', '𞺷' => 'خ', '𞺸' => 'ذ', '𞺹' => 'ض', '𞺺' => 'ظ', '𞺻' => 'غ', '🄀' => '0.', '🄁' => '0,', '🄂' => '1,', '🄃' => '2,', '🄄' => '3,', '🄅' => '4,', '🄆' => '5,', '🄇' => '6,', '🄈' => '7,', '🄉' => '8,', '🄊' => '9,', '🄐' => '(A)', '🄑' => '(B)', '🄒' => '(C)', '🄓' => '(D)', '🄔' => '(E)', '🄕' => '(F)', '🄖' => '(G)', '🄗' => '(H)', '🄘' => '(I)', '🄙' => '(J)', '🄚' => '(K)', '🄛' => '(L)', '🄜' => '(M)', '🄝' => '(N)', '🄞' => '(O)', '🄟' => '(P)', '🄠' => '(Q)', '🄡' => '(R)', '🄢' => '(S)', '🄣' => '(T)', '🄤' => '(U)', '🄥' => '(V)', '🄦' => '(W)', '🄧' => '(X)', '🄨' => '(Y)', '🄩' => '(Z)', '🄪' => '〔S〕', '🄫' => 'C', '🄬' => 'R', '🄭' => 'CD', '🄮' => 'WZ', '🄰' => 'A', '🄱' => 'B', '🄲' => 'C', '🄳' => 'D', '🄴' => 'E', '🄵' => 'F', '🄶' => 'G', '🄷' => 'H', '🄸' => 'I', '🄹' => 'J', '🄺' => 'K', '🄻' => 'L', '🄼' => 'M', '🄽' => 'N', '🄾' => 'O', '🄿' => 'P', '🅀' => 'Q', '🅁' => 'R', '🅂' => 'S', '🅃' => 'T', '🅄' => 'U', '🅅' => 'V', '🅆' => 'W', '🅇' => 'X', '🅈' => 'Y', '🅉' => 'Z', '🅊' => 'HV', '🅋' => 'MV', '🅌' => 'SD', '🅍' => 'SS', '🅎' => 'PPV', '🅏' => 'WC', '🅪' => 'MC', '🅫' => 'MD', '🅬' => 'MR', '🆐' => 'DJ', '🈀' => 'ほか', '🈁' => 'ココ', '🈂' => 'サ', '🈐' => '手', '🈑' => '字', '🈒' => '双', '🈓' => 'デ', '🈔' => '二', '🈕' => '多', '🈖' => '解', '🈗' => '天', '🈘' => '交', '🈙' => '映', '🈚' => '無', '🈛' => '料', '🈜' => '前', '🈝' => '後', '🈞' => '再', '🈟' => '新', '🈠' => '初', '🈡' => '終', '🈢' => '生', '🈣' => '販', '🈤' => '声', '🈥' => '吹', '🈦' => '演', '🈧' => '投', '🈨' => '捕', '🈩' => '一', '🈪' => '三', '🈫' => '遊', '🈬' => '左', '🈭' => '中', '🈮' => '右', '🈯' => '指', '🈰' => '走', '🈱' => '打', '🈲' => '禁', '🈳' => '空', '🈴' => '合', '🈵' => '満', '🈶' => '有', '🈷' => '月', '🈸' => '申', '🈹' => '割', '🈺' => '営', '🈻' => '配', '🉀' => '〔本〕', '🉁' => '〔三〕', '🉂' => '〔二〕', '🉃' => '〔安〕', '🉄' => '〔点〕', '🉅' => '〔打〕', '🉆' => '〔盗〕', '🉇' => '〔勝〕', '🉈' => '〔敗〕', '🉐' => '得', '🉑' => '可', '🯰' => '0', '🯱' => '1', '🯲' => '2', '🯳' => '3', '🯴' => '4', '🯵' => '5', '🯶' => '6', '🯷' => '7', '🯸' => '8', '🯹' => '9', ); PK2A#]�je�{�{�Tvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.phpnu�[���<?php return array ( 'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'ÿ' => 'ÿ', 'Ā' => 'Ā', 'ā' => 'ā', 'Ă' => 'Ă', 'ă' => 'ă', 'Ą' => 'Ą', 'ą' => 'ą', 'Ć' => 'Ć', 'ć' => 'ć', 'Ĉ' => 'Ĉ', 'ĉ' => 'ĉ', 'Ċ' => 'Ċ', 'ċ' => 'ċ', 'Č' => 'Č', 'č' => 'č', 'Ď' => 'Ď', 'ď' => 'ď', 'Ē' => 'Ē', 'ē' => 'ē', 'Ĕ' => 'Ĕ', 'ĕ' => 'ĕ', 'Ė' => 'Ė', 'ė' => 'ė', 'Ę' => 'Ę', 'ę' => 'ę', 'Ě' => 'Ě', 'ě' => 'ě', 'Ĝ' => 'Ĝ', 'ĝ' => 'ĝ', 'Ğ' => 'Ğ', 'ğ' => 'ğ', 'Ġ' => 'Ġ', 'ġ' => 'ġ', 'Ģ' => 'Ģ', 'ģ' => 'ģ', 'Ĥ' => 'Ĥ', 'ĥ' => 'ĥ', 'Ĩ' => 'Ĩ', 'ĩ' => 'ĩ', 'Ī' => 'Ī', 'ī' => 'ī', 'Ĭ' => 'Ĭ', 'ĭ' => 'ĭ', 'Į' => 'Į', 'į' => 'į', 'İ' => 'İ', 'Ĵ' => 'Ĵ', 'ĵ' => 'ĵ', 'Ķ' => 'Ķ', 'ķ' => 'ķ', 'Ĺ' => 'Ĺ', 'ĺ' => 'ĺ', 'Ļ' => 'Ļ', 'ļ' => 'ļ', 'Ľ' => 'Ľ', 'ľ' => 'ľ', 'Ń' => 'Ń', 'ń' => 'ń', 'Ņ' => 'Ņ', 'ņ' => 'ņ', 'Ň' => 'Ň', 'ň' => 'ň', 'Ō' => 'Ō', 'ō' => 'ō', 'Ŏ' => 'Ŏ', 'ŏ' => 'ŏ', 'Ő' => 'Ő', 'ő' => 'ő', 'Ŕ' => 'Ŕ', 'ŕ' => 'ŕ', 'Ŗ' => 'Ŗ', 'ŗ' => 'ŗ', 'Ř' => 'Ř', 'ř' => 'ř', 'Ś' => 'Ś', 'ś' => 'ś', 'Ŝ' => 'Ŝ', 'ŝ' => 'ŝ', 'Ş' => 'Ş', 'ş' => 'ş', 'Š' => 'Š', 'š' => 'š', 'Ţ' => 'Ţ', 'ţ' => 'ţ', 'Ť' => 'Ť', 'ť' => 'ť', 'Ũ' => 'Ũ', 'ũ' => 'ũ', 'Ū' => 'Ū', 'ū' => 'ū', 'Ŭ' => 'Ŭ', 'ŭ' => 'ŭ', 'Ů' => 'Ů', 'ů' => 'ů', 'Ű' => 'Ű', 'ű' => 'ű', 'Ų' => 'Ų', 'ų' => 'ų', 'Ŵ' => 'Ŵ', 'ŵ' => 'ŵ', 'Ŷ' => 'Ŷ', 'ŷ' => 'ŷ', 'Ÿ' => 'Ÿ', 'Ź' => 'Ź', 'ź' => 'ź', 'Ż' => 'Ż', 'ż' => 'ż', 'Ž' => 'Ž', 'ž' => 'ž', 'Ơ' => 'Ơ', 'ơ' => 'ơ', 'Ư' => 'Ư', 'ư' => 'ư', 'Ǎ' => 'Ǎ', 'ǎ' => 'ǎ', 'Ǐ' => 'Ǐ', 'ǐ' => 'ǐ', 'Ǒ' => 'Ǒ', 'ǒ' => 'ǒ', 'Ǔ' => 'Ǔ', 'ǔ' => 'ǔ', 'Ǖ' => 'Ǖ', 'ǖ' => 'ǖ', 'Ǘ' => 'Ǘ', 'ǘ' => 'ǘ', 'Ǚ' => 'Ǚ', 'ǚ' => 'ǚ', 'Ǜ' => 'Ǜ', 'ǜ' => 'ǜ', 'Ǟ' => 'Ǟ', 'ǟ' => 'ǟ', 'Ǡ' => 'Ǡ', 'ǡ' => 'ǡ', 'Ǣ' => 'Ǣ', 'ǣ' => 'ǣ', 'Ǧ' => 'Ǧ', 'ǧ' => 'ǧ', 'Ǩ' => 'Ǩ', 'ǩ' => 'ǩ', 'Ǫ' => 'Ǫ', 'ǫ' => 'ǫ', 'Ǭ' => 'Ǭ', 'ǭ' => 'ǭ', 'Ǯ' => 'Ǯ', 'ǯ' => 'ǯ', 'ǰ' => 'ǰ', 'Ǵ' => 'Ǵ', 'ǵ' => 'ǵ', 'Ǹ' => 'Ǹ', 'ǹ' => 'ǹ', 'Ǻ' => 'Ǻ', 'ǻ' => 'ǻ', 'Ǽ' => 'Ǽ', 'ǽ' => 'ǽ', 'Ǿ' => 'Ǿ', 'ǿ' => 'ǿ', 'Ȁ' => 'Ȁ', 'ȁ' => 'ȁ', 'Ȃ' => 'Ȃ', 'ȃ' => 'ȃ', 'Ȅ' => 'Ȅ', 'ȅ' => 'ȅ', 'Ȇ' => 'Ȇ', 'ȇ' => 'ȇ', 'Ȉ' => 'Ȉ', 'ȉ' => 'ȉ', 'Ȋ' => 'Ȋ', 'ȋ' => 'ȋ', 'Ȍ' => 'Ȍ', 'ȍ' => 'ȍ', 'Ȏ' => 'Ȏ', 'ȏ' => 'ȏ', 'Ȑ' => 'Ȑ', 'ȑ' => 'ȑ', 'Ȓ' => 'Ȓ', 'ȓ' => 'ȓ', 'Ȕ' => 'Ȕ', 'ȕ' => 'ȕ', 'Ȗ' => 'Ȗ', 'ȗ' => 'ȗ', 'Ș' => 'Ș', 'ș' => 'ș', 'Ț' => 'Ț', 'ț' => 'ț', 'Ȟ' => 'Ȟ', 'ȟ' => 'ȟ', 'Ȧ' => 'Ȧ', 'ȧ' => 'ȧ', 'Ȩ' => 'Ȩ', 'ȩ' => 'ȩ', 'Ȫ' => 'Ȫ', 'ȫ' => 'ȫ', 'Ȭ' => 'Ȭ', 'ȭ' => 'ȭ', 'Ȯ' => 'Ȯ', 'ȯ' => 'ȯ', 'Ȱ' => 'Ȱ', 'ȱ' => 'ȱ', 'Ȳ' => 'Ȳ', 'ȳ' => 'ȳ', '̀' => '̀', '́' => '́', '̓' => '̓', '̈́' => '̈́', 'ʹ' => 'ʹ', ';' => ';', '΅' => '΅', 'Ά' => 'Ά', '·' => '·', 'Έ' => 'Έ', 'Ή' => 'Ή', 'Ί' => 'Ί', 'Ό' => 'Ό', 'Ύ' => 'Ύ', 'Ώ' => 'Ώ', 'ΐ' => 'ΐ', 'Ϊ' => 'Ϊ', 'Ϋ' => 'Ϋ', 'ά' => 'ά', 'έ' => 'έ', 'ή' => 'ή', 'ί' => 'ί', 'ΰ' => 'ΰ', 'ϊ' => 'ϊ', 'ϋ' => 'ϋ', 'ό' => 'ό', 'ύ' => 'ύ', 'ώ' => 'ώ', 'ϓ' => 'ϓ', 'ϔ' => 'ϔ', 'Ѐ' => 'Ѐ', 'Ё' => 'Ё', 'Ѓ' => 'Ѓ', 'Ї' => 'Ї', 'Ќ' => 'Ќ', 'Ѝ' => 'Ѝ', 'Ў' => 'Ў', 'Й' => 'Й', 'й' => 'й', 'ѐ' => 'ѐ', 'ё' => 'ё', 'ѓ' => 'ѓ', 'ї' => 'ї', 'ќ' => 'ќ', 'ѝ' => 'ѝ', 'ў' => 'ў', 'Ѷ' => 'Ѷ', 'ѷ' => 'ѷ', 'Ӂ' => 'Ӂ', 'ӂ' => 'ӂ', 'Ӑ' => 'Ӑ', 'ӑ' => 'ӑ', 'Ӓ' => 'Ӓ', 'ӓ' => 'ӓ', 'Ӗ' => 'Ӗ', 'ӗ' => 'ӗ', 'Ӛ' => 'Ӛ', 'ӛ' => 'ӛ', 'Ӝ' => 'Ӝ', 'ӝ' => 'ӝ', 'Ӟ' => 'Ӟ', 'ӟ' => 'ӟ', 'Ӣ' => 'Ӣ', 'ӣ' => 'ӣ', 'Ӥ' => 'Ӥ', 'ӥ' => 'ӥ', 'Ӧ' => 'Ӧ', 'ӧ' => 'ӧ', 'Ӫ' => 'Ӫ', 'ӫ' => 'ӫ', 'Ӭ' => 'Ӭ', 'ӭ' => 'ӭ', 'Ӯ' => 'Ӯ', 'ӯ' => 'ӯ', 'Ӱ' => 'Ӱ', 'ӱ' => 'ӱ', 'Ӳ' => 'Ӳ', 'ӳ' => 'ӳ', 'Ӵ' => 'Ӵ', 'ӵ' => 'ӵ', 'Ӹ' => 'Ӹ', 'ӹ' => 'ӹ', 'آ' => 'آ', 'أ' => 'أ', 'ؤ' => 'ؤ', 'إ' => 'إ', 'ئ' => 'ئ', 'ۀ' => 'ۀ', 'ۂ' => 'ۂ', 'ۓ' => 'ۓ', 'ऩ' => 'ऩ', 'ऱ' => 'ऱ', 'ऴ' => 'ऴ', 'क़' => 'क़', 'ख़' => 'ख़', 'ग़' => 'ग़', 'ज़' => 'ज़', 'ड़' => 'ड़', 'ढ़' => 'ढ़', 'फ़' => 'फ़', 'य़' => 'य़', 'ো' => 'ো', 'ৌ' => 'ৌ', 'ড়' => 'ড়', 'ঢ়' => 'ঢ়', 'য়' => 'য়', 'ਲ਼' => 'ਲ਼', 'ਸ਼' => 'ਸ਼', 'ਖ਼' => 'ਖ਼', 'ਗ਼' => 'ਗ਼', 'ਜ਼' => 'ਜ਼', 'ਫ਼' => 'ਫ਼', 'ୈ' => 'ୈ', 'ୋ' => 'ୋ', 'ୌ' => 'ୌ', 'ଡ଼' => 'ଡ଼', 'ଢ଼' => 'ଢ଼', 'ஔ' => 'ஔ', 'ொ' => 'ொ', 'ோ' => 'ோ', 'ௌ' => 'ௌ', 'ై' => 'ై', 'ೀ' => 'ೀ', 'ೇ' => 'ೇ', 'ೈ' => 'ೈ', 'ೊ' => 'ೊ', 'ೋ' => 'ೋ', 'ൊ' => 'ൊ', 'ോ' => 'ോ', 'ൌ' => 'ൌ', 'ේ' => 'ේ', 'ො' => 'ො', 'ෝ' => 'ෝ', 'ෞ' => 'ෞ', 'གྷ' => 'གྷ', 'ཌྷ' => 'ཌྷ', 'དྷ' => 'དྷ', 'བྷ' => 'བྷ', 'ཛྷ' => 'ཛྷ', 'ཀྵ' => 'ཀྵ', 'ཱི' => 'ཱི', 'ཱུ' => 'ཱུ', 'ྲྀ' => 'ྲྀ', 'ླྀ' => 'ླྀ', 'ཱྀ' => 'ཱྀ', 'ྒྷ' => 'ྒྷ', 'ྜྷ' => 'ྜྷ', 'ྡྷ' => 'ྡྷ', 'ྦྷ' => 'ྦྷ', 'ྫྷ' => 'ྫྷ', 'ྐྵ' => 'ྐྵ', 'ဦ' => 'ဦ', 'ᬆ' => 'ᬆ', 'ᬈ' => 'ᬈ', 'ᬊ' => 'ᬊ', 'ᬌ' => 'ᬌ', 'ᬎ' => 'ᬎ', 'ᬒ' => 'ᬒ', 'ᬻ' => 'ᬻ', 'ᬽ' => 'ᬽ', 'ᭀ' => 'ᭀ', 'ᭁ' => 'ᭁ', 'ᭃ' => 'ᭃ', 'Ḁ' => 'Ḁ', 'ḁ' => 'ḁ', 'Ḃ' => 'Ḃ', 'ḃ' => 'ḃ', 'Ḅ' => 'Ḅ', 'ḅ' => 'ḅ', 'Ḇ' => 'Ḇ', 'ḇ' => 'ḇ', 'Ḉ' => 'Ḉ', 'ḉ' => 'ḉ', 'Ḋ' => 'Ḋ', 'ḋ' => 'ḋ', 'Ḍ' => 'Ḍ', 'ḍ' => 'ḍ', 'Ḏ' => 'Ḏ', 'ḏ' => 'ḏ', 'Ḑ' => 'Ḑ', 'ḑ' => 'ḑ', 'Ḓ' => 'Ḓ', 'ḓ' => 'ḓ', 'Ḕ' => 'Ḕ', 'ḕ' => 'ḕ', 'Ḗ' => 'Ḗ', 'ḗ' => 'ḗ', 'Ḙ' => 'Ḙ', 'ḙ' => 'ḙ', 'Ḛ' => 'Ḛ', 'ḛ' => 'ḛ', 'Ḝ' => 'Ḝ', 'ḝ' => 'ḝ', 'Ḟ' => 'Ḟ', 'ḟ' => 'ḟ', 'Ḡ' => 'Ḡ', 'ḡ' => 'ḡ', 'Ḣ' => 'Ḣ', 'ḣ' => 'ḣ', 'Ḥ' => 'Ḥ', 'ḥ' => 'ḥ', 'Ḧ' => 'Ḧ', 'ḧ' => 'ḧ', 'Ḩ' => 'Ḩ', 'ḩ' => 'ḩ', 'Ḫ' => 'Ḫ', 'ḫ' => 'ḫ', 'Ḭ' => 'Ḭ', 'ḭ' => 'ḭ', 'Ḯ' => 'Ḯ', 'ḯ' => 'ḯ', 'Ḱ' => 'Ḱ', 'ḱ' => 'ḱ', 'Ḳ' => 'Ḳ', 'ḳ' => 'ḳ', 'Ḵ' => 'Ḵ', 'ḵ' => 'ḵ', 'Ḷ' => 'Ḷ', 'ḷ' => 'ḷ', 'Ḹ' => 'Ḹ', 'ḹ' => 'ḹ', 'Ḻ' => 'Ḻ', 'ḻ' => 'ḻ', 'Ḽ' => 'Ḽ', 'ḽ' => 'ḽ', 'Ḿ' => 'Ḿ', 'ḿ' => 'ḿ', 'Ṁ' => 'Ṁ', 'ṁ' => 'ṁ', 'Ṃ' => 'Ṃ', 'ṃ' => 'ṃ', 'Ṅ' => 'Ṅ', 'ṅ' => 'ṅ', 'Ṇ' => 'Ṇ', 'ṇ' => 'ṇ', 'Ṉ' => 'Ṉ', 'ṉ' => 'ṉ', 'Ṋ' => 'Ṋ', 'ṋ' => 'ṋ', 'Ṍ' => 'Ṍ', 'ṍ' => 'ṍ', 'Ṏ' => 'Ṏ', 'ṏ' => 'ṏ', 'Ṑ' => 'Ṑ', 'ṑ' => 'ṑ', 'Ṓ' => 'Ṓ', 'ṓ' => 'ṓ', 'Ṕ' => 'Ṕ', 'ṕ' => 'ṕ', 'Ṗ' => 'Ṗ', 'ṗ' => 'ṗ', 'Ṙ' => 'Ṙ', 'ṙ' => 'ṙ', 'Ṛ' => 'Ṛ', 'ṛ' => 'ṛ', 'Ṝ' => 'Ṝ', 'ṝ' => 'ṝ', 'Ṟ' => 'Ṟ', 'ṟ' => 'ṟ', 'Ṡ' => 'Ṡ', 'ṡ' => 'ṡ', 'Ṣ' => 'Ṣ', 'ṣ' => 'ṣ', 'Ṥ' => 'Ṥ', 'ṥ' => 'ṥ', 'Ṧ' => 'Ṧ', 'ṧ' => 'ṧ', 'Ṩ' => 'Ṩ', 'ṩ' => 'ṩ', 'Ṫ' => 'Ṫ', 'ṫ' => 'ṫ', 'Ṭ' => 'Ṭ', 'ṭ' => 'ṭ', 'Ṯ' => 'Ṯ', 'ṯ' => 'ṯ', 'Ṱ' => 'Ṱ', 'ṱ' => 'ṱ', 'Ṳ' => 'Ṳ', 'ṳ' => 'ṳ', 'Ṵ' => 'Ṵ', 'ṵ' => 'ṵ', 'Ṷ' => 'Ṷ', 'ṷ' => 'ṷ', 'Ṹ' => 'Ṹ', 'ṹ' => 'ṹ', 'Ṻ' => 'Ṻ', 'ṻ' => 'ṻ', 'Ṽ' => 'Ṽ', 'ṽ' => 'ṽ', 'Ṿ' => 'Ṿ', 'ṿ' => 'ṿ', 'Ẁ' => 'Ẁ', 'ẁ' => 'ẁ', 'Ẃ' => 'Ẃ', 'ẃ' => 'ẃ', 'Ẅ' => 'Ẅ', 'ẅ' => 'ẅ', 'Ẇ' => 'Ẇ', 'ẇ' => 'ẇ', 'Ẉ' => 'Ẉ', 'ẉ' => 'ẉ', 'Ẋ' => 'Ẋ', 'ẋ' => 'ẋ', 'Ẍ' => 'Ẍ', 'ẍ' => 'ẍ', 'Ẏ' => 'Ẏ', 'ẏ' => 'ẏ', 'Ẑ' => 'Ẑ', 'ẑ' => 'ẑ', 'Ẓ' => 'Ẓ', 'ẓ' => 'ẓ', 'Ẕ' => 'Ẕ', 'ẕ' => 'ẕ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẛ' => 'ẛ', 'Ạ' => 'Ạ', 'ạ' => 'ạ', 'Ả' => 'Ả', 'ả' => 'ả', 'Ấ' => 'Ấ', 'ấ' => 'ấ', 'Ầ' => 'Ầ', 'ầ' => 'ầ', 'Ẩ' => 'Ẩ', 'ẩ' => 'ẩ', 'Ẫ' => 'Ẫ', 'ẫ' => 'ẫ', 'Ậ' => 'Ậ', 'ậ' => 'ậ', 'Ắ' => 'Ắ', 'ắ' => 'ắ', 'Ằ' => 'Ằ', 'ằ' => 'ằ', 'Ẳ' => 'Ẳ', 'ẳ' => 'ẳ', 'Ẵ' => 'Ẵ', 'ẵ' => 'ẵ', 'Ặ' => 'Ặ', 'ặ' => 'ặ', 'Ẹ' => 'Ẹ', 'ẹ' => 'ẹ', 'Ẻ' => 'Ẻ', 'ẻ' => 'ẻ', 'Ẽ' => 'Ẽ', 'ẽ' => 'ẽ', 'Ế' => 'Ế', 'ế' => 'ế', 'Ề' => 'Ề', 'ề' => 'ề', 'Ể' => 'Ể', 'ể' => 'ể', 'Ễ' => 'Ễ', 'ễ' => 'ễ', 'Ệ' => 'Ệ', 'ệ' => 'ệ', 'Ỉ' => 'Ỉ', 'ỉ' => 'ỉ', 'Ị' => 'Ị', 'ị' => 'ị', 'Ọ' => 'Ọ', 'ọ' => 'ọ', 'Ỏ' => 'Ỏ', 'ỏ' => 'ỏ', 'Ố' => 'Ố', 'ố' => 'ố', 'Ồ' => 'Ồ', 'ồ' => 'ồ', 'Ổ' => 'Ổ', 'ổ' => 'ổ', 'Ỗ' => 'Ỗ', 'ỗ' => 'ỗ', 'Ộ' => 'Ộ', 'ộ' => 'ộ', 'Ớ' => 'Ớ', 'ớ' => 'ớ', 'Ờ' => 'Ờ', 'ờ' => 'ờ', 'Ở' => 'Ở', 'ở' => 'ở', 'Ỡ' => 'Ỡ', 'ỡ' => 'ỡ', 'Ợ' => 'Ợ', 'ợ' => 'ợ', 'Ụ' => 'Ụ', 'ụ' => 'ụ', 'Ủ' => 'Ủ', 'ủ' => 'ủ', 'Ứ' => 'Ứ', 'ứ' => 'ứ', 'Ừ' => 'Ừ', 'ừ' => 'ừ', 'Ử' => 'Ử', 'ử' => 'ử', 'Ữ' => 'Ữ', 'ữ' => 'ữ', 'Ự' => 'Ự', 'ự' => 'ự', 'Ỳ' => 'Ỳ', 'ỳ' => 'ỳ', 'Ỵ' => 'Ỵ', 'ỵ' => 'ỵ', 'Ỷ' => 'Ỷ', 'ỷ' => 'ỷ', 'Ỹ' => 'Ỹ', 'ỹ' => 'ỹ', 'ἀ' => 'ἀ', 'ἁ' => 'ἁ', 'ἂ' => 'ἂ', 'ἃ' => 'ἃ', 'ἄ' => 'ἄ', 'ἅ' => 'ἅ', 'ἆ' => 'ἆ', 'ἇ' => 'ἇ', 'Ἀ' => 'Ἀ', 'Ἁ' => 'Ἁ', 'Ἂ' => 'Ἂ', 'Ἃ' => 'Ἃ', 'Ἄ' => 'Ἄ', 'Ἅ' => 'Ἅ', 'Ἆ' => 'Ἆ', 'Ἇ' => 'Ἇ', 'ἐ' => 'ἐ', 'ἑ' => 'ἑ', 'ἒ' => 'ἒ', 'ἓ' => 'ἓ', 'ἔ' => 'ἔ', 'ἕ' => 'ἕ', 'Ἐ' => 'Ἐ', 'Ἑ' => 'Ἑ', 'Ἒ' => 'Ἒ', 'Ἓ' => 'Ἓ', 'Ἔ' => 'Ἔ', 'Ἕ' => 'Ἕ', 'ἠ' => 'ἠ', 'ἡ' => 'ἡ', 'ἢ' => 'ἢ', 'ἣ' => 'ἣ', 'ἤ' => 'ἤ', 'ἥ' => 'ἥ', 'ἦ' => 'ἦ', 'ἧ' => 'ἧ', 'Ἠ' => 'Ἠ', 'Ἡ' => 'Ἡ', 'Ἢ' => 'Ἢ', 'Ἣ' => 'Ἣ', 'Ἤ' => 'Ἤ', 'Ἥ' => 'Ἥ', 'Ἦ' => 'Ἦ', 'Ἧ' => 'Ἧ', 'ἰ' => 'ἰ', 'ἱ' => 'ἱ', 'ἲ' => 'ἲ', 'ἳ' => 'ἳ', 'ἴ' => 'ἴ', 'ἵ' => 'ἵ', 'ἶ' => 'ἶ', 'ἷ' => 'ἷ', 'Ἰ' => 'Ἰ', 'Ἱ' => 'Ἱ', 'Ἲ' => 'Ἲ', 'Ἳ' => 'Ἳ', 'Ἴ' => 'Ἴ', 'Ἵ' => 'Ἵ', 'Ἶ' => 'Ἶ', 'Ἷ' => 'Ἷ', 'ὀ' => 'ὀ', 'ὁ' => 'ὁ', 'ὂ' => 'ὂ', 'ὃ' => 'ὃ', 'ὄ' => 'ὄ', 'ὅ' => 'ὅ', 'Ὀ' => 'Ὀ', 'Ὁ' => 'Ὁ', 'Ὂ' => 'Ὂ', 'Ὃ' => 'Ὃ', 'Ὄ' => 'Ὄ', 'Ὅ' => 'Ὅ', 'ὐ' => 'ὐ', 'ὑ' => 'ὑ', 'ὒ' => 'ὒ', 'ὓ' => 'ὓ', 'ὔ' => 'ὔ', 'ὕ' => 'ὕ', 'ὖ' => 'ὖ', 'ὗ' => 'ὗ', 'Ὑ' => 'Ὑ', 'Ὓ' => 'Ὓ', 'Ὕ' => 'Ὕ', 'Ὗ' => 'Ὗ', 'ὠ' => 'ὠ', 'ὡ' => 'ὡ', 'ὢ' => 'ὢ', 'ὣ' => 'ὣ', 'ὤ' => 'ὤ', 'ὥ' => 'ὥ', 'ὦ' => 'ὦ', 'ὧ' => 'ὧ', 'Ὠ' => 'Ὠ', 'Ὡ' => 'Ὡ', 'Ὢ' => 'Ὢ', 'Ὣ' => 'Ὣ', 'Ὤ' => 'Ὤ', 'Ὥ' => 'Ὥ', 'Ὦ' => 'Ὦ', 'Ὧ' => 'Ὧ', 'ὰ' => 'ὰ', 'ά' => 'ά', 'ὲ' => 'ὲ', 'έ' => 'έ', 'ὴ' => 'ὴ', 'ή' => 'ή', 'ὶ' => 'ὶ', 'ί' => 'ί', 'ὸ' => 'ὸ', 'ό' => 'ό', 'ὺ' => 'ὺ', 'ύ' => 'ύ', 'ὼ' => 'ὼ', 'ώ' => 'ώ', 'ᾀ' => 'ᾀ', 'ᾁ' => 'ᾁ', 'ᾂ' => 'ᾂ', 'ᾃ' => 'ᾃ', 'ᾄ' => 'ᾄ', 'ᾅ' => 'ᾅ', 'ᾆ' => 'ᾆ', 'ᾇ' => 'ᾇ', 'ᾈ' => 'ᾈ', 'ᾉ' => 'ᾉ', 'ᾊ' => 'ᾊ', 'ᾋ' => 'ᾋ', 'ᾌ' => 'ᾌ', 'ᾍ' => 'ᾍ', 'ᾎ' => 'ᾎ', 'ᾏ' => 'ᾏ', 'ᾐ' => 'ᾐ', 'ᾑ' => 'ᾑ', 'ᾒ' => 'ᾒ', 'ᾓ' => 'ᾓ', 'ᾔ' => 'ᾔ', 'ᾕ' => 'ᾕ', 'ᾖ' => 'ᾖ', 'ᾗ' => 'ᾗ', 'ᾘ' => 'ᾘ', 'ᾙ' => 'ᾙ', 'ᾚ' => 'ᾚ', 'ᾛ' => 'ᾛ', 'ᾜ' => 'ᾜ', 'ᾝ' => 'ᾝ', 'ᾞ' => 'ᾞ', 'ᾟ' => 'ᾟ', 'ᾠ' => 'ᾠ', 'ᾡ' => 'ᾡ', 'ᾢ' => 'ᾢ', 'ᾣ' => 'ᾣ', 'ᾤ' => 'ᾤ', 'ᾥ' => 'ᾥ', 'ᾦ' => 'ᾦ', 'ᾧ' => 'ᾧ', 'ᾨ' => 'ᾨ', 'ᾩ' => 'ᾩ', 'ᾪ' => 'ᾪ', 'ᾫ' => 'ᾫ', 'ᾬ' => 'ᾬ', 'ᾭ' => 'ᾭ', 'ᾮ' => 'ᾮ', 'ᾯ' => 'ᾯ', 'ᾰ' => 'ᾰ', 'ᾱ' => 'ᾱ', 'ᾲ' => 'ᾲ', 'ᾳ' => 'ᾳ', 'ᾴ' => 'ᾴ', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾷ', 'Ᾰ' => 'Ᾰ', 'Ᾱ' => 'Ᾱ', 'Ὰ' => 'Ὰ', 'Ά' => 'Ά', 'ᾼ' => 'ᾼ', 'ι' => 'ι', '῁' => '῁', 'ῂ' => 'ῂ', 'ῃ' => 'ῃ', 'ῄ' => 'ῄ', 'ῆ' => 'ῆ', 'ῇ' => 'ῇ', 'Ὲ' => 'Ὲ', 'Έ' => 'Έ', 'Ὴ' => 'Ὴ', 'Ή' => 'Ή', 'ῌ' => 'ῌ', '῍' => '῍', '῎' => '῎', '῏' => '῏', 'ῐ' => 'ῐ', 'ῑ' => 'ῑ', 'ῒ' => 'ῒ', 'ΐ' => 'ΐ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'Ῐ' => 'Ῐ', 'Ῑ' => 'Ῑ', 'Ὶ' => 'Ὶ', 'Ί' => 'Ί', '῝' => '῝', '῞' => '῞', '῟' => '῟', 'ῠ' => 'ῠ', 'ῡ' => 'ῡ', 'ῢ' => 'ῢ', 'ΰ' => 'ΰ', 'ῤ' => 'ῤ', 'ῥ' => 'ῥ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'Ῠ' => 'Ῠ', 'Ῡ' => 'Ῡ', 'Ὺ' => 'Ὺ', 'Ύ' => 'Ύ', 'Ῥ' => 'Ῥ', '῭' => '῭', '΅' => '΅', '`' => '`', 'ῲ' => 'ῲ', 'ῳ' => 'ῳ', 'ῴ' => 'ῴ', 'ῶ' => 'ῶ', 'ῷ' => 'ῷ', 'Ὸ' => 'Ὸ', 'Ό' => 'Ό', 'Ὼ' => 'Ὼ', 'Ώ' => 'Ώ', 'ῼ' => 'ῼ', '´' => '´', ' ' => ' ', ' ' => ' ', 'Ω' => 'Ω', 'K' => 'K', 'Å' => 'Å', '↚' => '↚', '↛' => '↛', '↮' => '↮', '⇍' => '⇍', '⇎' => '⇎', '⇏' => '⇏', '∄' => '∄', '∉' => '∉', '∌' => '∌', '∤' => '∤', '∦' => '∦', '≁' => '≁', '≄' => '≄', '≇' => '≇', '≉' => '≉', '≠' => '≠', '≢' => '≢', '≭' => '≭', '≮' => '≮', '≯' => '≯', '≰' => '≰', '≱' => '≱', '≴' => '≴', '≵' => '≵', '≸' => '≸', '≹' => '≹', '⊀' => '⊀', '⊁' => '⊁', '⊄' => '⊄', '⊅' => '⊅', '⊈' => '⊈', '⊉' => '⊉', '⊬' => '⊬', '⊭' => '⊭', '⊮' => '⊮', '⊯' => '⊯', '⋠' => '⋠', '⋡' => '⋡', '⋢' => '⋢', '⋣' => '⋣', '⋪' => '⋪', '⋫' => '⋫', '⋬' => '⋬', '⋭' => '⋭', '〈' => '〈', '〉' => '〉', '⫝̸' => '⫝̸', 'が' => 'が', 'ぎ' => 'ぎ', 'ぐ' => 'ぐ', 'げ' => 'げ', 'ご' => 'ご', 'ざ' => 'ざ', 'じ' => 'じ', 'ず' => 'ず', 'ぜ' => 'ぜ', 'ぞ' => 'ぞ', 'だ' => 'だ', 'ぢ' => 'ぢ', 'づ' => 'づ', 'で' => 'で', 'ど' => 'ど', 'ば' => 'ば', 'ぱ' => 'ぱ', 'び' => 'び', 'ぴ' => 'ぴ', 'ぶ' => 'ぶ', 'ぷ' => 'ぷ', 'べ' => 'べ', 'ぺ' => 'ぺ', 'ぼ' => 'ぼ', 'ぽ' => 'ぽ', 'ゔ' => 'ゔ', 'ゞ' => 'ゞ', 'ガ' => 'ガ', 'ギ' => 'ギ', 'グ' => 'グ', 'ゲ' => 'ゲ', 'ゴ' => 'ゴ', 'ザ' => 'ザ', 'ジ' => 'ジ', 'ズ' => 'ズ', 'ゼ' => 'ゼ', 'ゾ' => 'ゾ', 'ダ' => 'ダ', 'ヂ' => 'ヂ', 'ヅ' => 'ヅ', 'デ' => 'デ', 'ド' => 'ド', 'バ' => 'バ', 'パ' => 'パ', 'ビ' => 'ビ', 'ピ' => 'ピ', 'ブ' => 'ブ', 'プ' => 'プ', 'ベ' => 'ベ', 'ペ' => 'ペ', 'ボ' => 'ボ', 'ポ' => 'ポ', 'ヴ' => 'ヴ', 'ヷ' => 'ヷ', 'ヸ' => 'ヸ', 'ヹ' => 'ヹ', 'ヺ' => 'ヺ', 'ヾ' => 'ヾ', '豈' => '豈', '更' => '更', '車' => '車', '賈' => '賈', '滑' => '滑', '串' => '串', '句' => '句', '龜' => '龜', '龜' => '龜', '契' => '契', '金' => '金', '喇' => '喇', '奈' => '奈', '懶' => '懶', '癩' => '癩', '羅' => '羅', '蘿' => '蘿', '螺' => '螺', '裸' => '裸', '邏' => '邏', '樂' => '樂', '洛' => '洛', '烙' => '烙', '珞' => '珞', '落' => '落', '酪' => '酪', '駱' => '駱', '亂' => '亂', '卵' => '卵', '欄' => '欄', '爛' => '爛', '蘭' => '蘭', '鸞' => '鸞', '嵐' => '嵐', '濫' => '濫', '藍' => '藍', '襤' => '襤', '拉' => '拉', '臘' => '臘', '蠟' => '蠟', '廊' => '廊', '朗' => '朗', '浪' => '浪', '狼' => '狼', '郎' => '郎', '來' => '來', '冷' => '冷', '勞' => '勞', '擄' => '擄', '櫓' => '櫓', '爐' => '爐', '盧' => '盧', '老' => '老', '蘆' => '蘆', '虜' => '虜', '路' => '路', '露' => '露', '魯' => '魯', '鷺' => '鷺', '碌' => '碌', '祿' => '祿', '綠' => '綠', '菉' => '菉', '錄' => '錄', '鹿' => '鹿', '論' => '論', '壟' => '壟', '弄' => '弄', '籠' => '籠', '聾' => '聾', '牢' => '牢', '磊' => '磊', '賂' => '賂', '雷' => '雷', '壘' => '壘', '屢' => '屢', '樓' => '樓', '淚' => '淚', '漏' => '漏', '累' => '累', '縷' => '縷', '陋' => '陋', '勒' => '勒', '肋' => '肋', '凜' => '凜', '凌' => '凌', '稜' => '稜', '綾' => '綾', '菱' => '菱', '陵' => '陵', '讀' => '讀', '拏' => '拏', '樂' => '樂', '諾' => '諾', '丹' => '丹', '寧' => '寧', '怒' => '怒', '率' => '率', '異' => '異', '北' => '北', '磻' => '磻', '便' => '便', '復' => '復', '不' => '不', '泌' => '泌', '數' => '數', '索' => '索', '參' => '參', '塞' => '塞', '省' => '省', '葉' => '葉', '說' => '說', '殺' => '殺', '辰' => '辰', '沈' => '沈', '拾' => '拾', '若' => '若', '掠' => '掠', '略' => '略', '亮' => '亮', '兩' => '兩', '凉' => '凉', '梁' => '梁', '糧' => '糧', '良' => '良', '諒' => '諒', '量' => '量', '勵' => '勵', '呂' => '呂', '女' => '女', '廬' => '廬', '旅' => '旅', '濾' => '濾', '礪' => '礪', '閭' => '閭', '驪' => '驪', '麗' => '麗', '黎' => '黎', '力' => '力', '曆' => '曆', '歷' => '歷', '轢' => '轢', '年' => '年', '憐' => '憐', '戀' => '戀', '撚' => '撚', '漣' => '漣', '煉' => '煉', '璉' => '璉', '秊' => '秊', '練' => '練', '聯' => '聯', '輦' => '輦', '蓮' => '蓮', '連' => '連', '鍊' => '鍊', '列' => '列', '劣' => '劣', '咽' => '咽', '烈' => '烈', '裂' => '裂', '說' => '說', '廉' => '廉', '念' => '念', '捻' => '捻', '殮' => '殮', '簾' => '簾', '獵' => '獵', '令' => '令', '囹' => '囹', '寧' => '寧', '嶺' => '嶺', '怜' => '怜', '玲' => '玲', '瑩' => '瑩', '羚' => '羚', '聆' => '聆', '鈴' => '鈴', '零' => '零', '靈' => '靈', '領' => '領', '例' => '例', '禮' => '禮', '醴' => '醴', '隸' => '隸', '惡' => '惡', '了' => '了', '僚' => '僚', '寮' => '寮', '尿' => '尿', '料' => '料', '樂' => '樂', '燎' => '燎', '療' => '療', '蓼' => '蓼', '遼' => '遼', '龍' => '龍', '暈' => '暈', '阮' => '阮', '劉' => '劉', '杻' => '杻', '柳' => '柳', '流' => '流', '溜' => '溜', '琉' => '琉', '留' => '留', '硫' => '硫', '紐' => '紐', '類' => '類', '六' => '六', '戮' => '戮', '陸' => '陸', '倫' => '倫', '崙' => '崙', '淪' => '淪', '輪' => '輪', '律' => '律', '慄' => '慄', '栗' => '栗', '率' => '率', '隆' => '隆', '利' => '利', '吏' => '吏', '履' => '履', '易' => '易', '李' => '李', '梨' => '梨', '泥' => '泥', '理' => '理', '痢' => '痢', '罹' => '罹', '裏' => '裏', '裡' => '裡', '里' => '里', '離' => '離', '匿' => '匿', '溺' => '溺', '吝' => '吝', '燐' => '燐', '璘' => '璘', '藺' => '藺', '隣' => '隣', '鱗' => '鱗', '麟' => '麟', '林' => '林', '淋' => '淋', '臨' => '臨', '立' => '立', '笠' => '笠', '粒' => '粒', '狀' => '狀', '炙' => '炙', '識' => '識', '什' => '什', '茶' => '茶', '刺' => '刺', '切' => '切', '度' => '度', '拓' => '拓', '糖' => '糖', '宅' => '宅', '洞' => '洞', '暴' => '暴', '輻' => '輻', '行' => '行', '降' => '降', '見' => '見', '廓' => '廓', '兀' => '兀', '嗀' => '嗀', '塚' => '塚', '晴' => '晴', '凞' => '凞', '猪' => '猪', '益' => '益', '礼' => '礼', '神' => '神', '祥' => '祥', '福' => '福', '靖' => '靖', '精' => '精', '羽' => '羽', '蘒' => '蘒', '諸' => '諸', '逸' => '逸', '都' => '都', '飯' => '飯', '飼' => '飼', '館' => '館', '鶴' => '鶴', '郞' => '郞', '隷' => '隷', '侮' => '侮', '僧' => '僧', '免' => '免', '勉' => '勉', '勤' => '勤', '卑' => '卑', '喝' => '喝', '嘆' => '嘆', '器' => '器', '塀' => '塀', '墨' => '墨', '層' => '層', '屮' => '屮', '悔' => '悔', '慨' => '慨', '憎' => '憎', '懲' => '懲', '敏' => '敏', '既' => '既', '暑' => '暑', '梅' => '梅', '海' => '海', '渚' => '渚', '漢' => '漢', '煮' => '煮', '爫' => '爫', '琢' => '琢', '碑' => '碑', '社' => '社', '祉' => '祉', '祈' => '祈', '祐' => '祐', '祖' => '祖', '祝' => '祝', '禍' => '禍', '禎' => '禎', '穀' => '穀', '突' => '突', '節' => '節', '練' => '練', '縉' => '縉', '繁' => '繁', '署' => '署', '者' => '者', '臭' => '臭', '艹' => '艹', '艹' => '艹', '著' => '著', '褐' => '褐', '視' => '視', '謁' => '謁', '謹' => '謹', '賓' => '賓', '贈' => '贈', '辶' => '辶', '逸' => '逸', '難' => '難', '響' => '響', '頻' => '頻', '恵' => '恵', '𤋮' => '𤋮', '舘' => '舘', '並' => '並', '况' => '况', '全' => '全', '侀' => '侀', '充' => '充', '冀' => '冀', '勇' => '勇', '勺' => '勺', '喝' => '喝', '啕' => '啕', '喙' => '喙', '嗢' => '嗢', '塚' => '塚', '墳' => '墳', '奄' => '奄', '奔' => '奔', '婢' => '婢', '嬨' => '嬨', '廒' => '廒', '廙' => '廙', '彩' => '彩', '徭' => '徭', '惘' => '惘', '慎' => '慎', '愈' => '愈', '憎' => '憎', '慠' => '慠', '懲' => '懲', '戴' => '戴', '揄' => '揄', '搜' => '搜', '摒' => '摒', '敖' => '敖', '晴' => '晴', '朗' => '朗', '望' => '望', '杖' => '杖', '歹' => '歹', '殺' => '殺', '流' => '流', '滛' => '滛', '滋' => '滋', '漢' => '漢', '瀞' => '瀞', '煮' => '煮', '瞧' => '瞧', '爵' => '爵', '犯' => '犯', '猪' => '猪', '瑱' => '瑱', '甆' => '甆', '画' => '画', '瘝' => '瘝', '瘟' => '瘟', '益' => '益', '盛' => '盛', '直' => '直', '睊' => '睊', '着' => '着', '磌' => '磌', '窱' => '窱', '節' => '節', '类' => '类', '絛' => '絛', '練' => '練', '缾' => '缾', '者' => '者', '荒' => '荒', '華' => '華', '蝹' => '蝹', '襁' => '襁', '覆' => '覆', '視' => '視', '調' => '調', '諸' => '諸', '請' => '請', '謁' => '謁', '諾' => '諾', '諭' => '諭', '謹' => '謹', '變' => '變', '贈' => '贈', '輸' => '輸', '遲' => '遲', '醙' => '醙', '鉶' => '鉶', '陼' => '陼', '難' => '難', '靖' => '靖', '韛' => '韛', '響' => '響', '頋' => '頋', '頻' => '頻', '鬒' => '鬒', '龜' => '龜', '𢡊' => '𢡊', '𢡄' => '𢡄', '𣏕' => '𣏕', '㮝' => '㮝', '䀘' => '䀘', '䀹' => '䀹', '𥉉' => '𥉉', '𥳐' => '𥳐', '𧻓' => '𧻓', '齃' => '齃', '龎' => '龎', 'יִ' => 'יִ', 'ײַ' => 'ײַ', 'שׁ' => 'שׁ', 'שׂ' => 'שׂ', 'שּׁ' => 'שּׁ', 'שּׂ' => 'שּׂ', 'אַ' => 'אַ', 'אָ' => 'אָ', 'אּ' => 'אּ', 'בּ' => 'בּ', 'גּ' => 'גּ', 'דּ' => 'דּ', 'הּ' => 'הּ', 'וּ' => 'וּ', 'זּ' => 'זּ', 'טּ' => 'טּ', 'יּ' => 'יּ', 'ךּ' => 'ךּ', 'כּ' => 'כּ', 'לּ' => 'לּ', 'מּ' => 'מּ', 'נּ' => 'נּ', 'סּ' => 'סּ', 'ףּ' => 'ףּ', 'פּ' => 'פּ', 'צּ' => 'צּ', 'קּ' => 'קּ', 'רּ' => 'רּ', 'שּ' => 'שּ', 'תּ' => 'תּ', 'וֹ' => 'וֹ', 'בֿ' => 'בֿ', 'כֿ' => 'כֿ', 'פֿ' => 'פֿ', '𑂚' => '𑂚', '𑂜' => '𑂜', '𑂫' => '𑂫', '𑄮' => '𑄮', '𑄯' => '𑄯', '𑍋' => '𑍋', '𑍌' => '𑍌', '𑒻' => '𑒻', '𑒼' => '𑒼', '𑒾' => '𑒾', '𑖺' => '𑖺', '𑖻' => '𑖻', '𑤸' => '𑤸', '𝅗𝅥' => '𝅗𝅥', '𝅘𝅥' => '𝅘𝅥', '𝅘𝅥𝅮' => '𝅘𝅥𝅮', '𝅘𝅥𝅯' => '𝅘𝅥𝅯', '𝅘𝅥𝅰' => '𝅘𝅥𝅰', '𝅘𝅥𝅱' => '𝅘𝅥𝅱', '𝅘𝅥𝅲' => '𝅘𝅥𝅲', '𝆹𝅥' => '𝆹𝅥', '𝆺𝅥' => '𝆺𝅥', '𝆹𝅥𝅮' => '𝆹𝅥𝅮', '𝆺𝅥𝅮' => '𝆺𝅥𝅮', '𝆹𝅥𝅯' => '𝆹𝅥𝅯', '𝆺𝅥𝅯' => '𝆺𝅥𝅯', '丽' => '丽', '丸' => '丸', '乁' => '乁', '𠄢' => '𠄢', '你' => '你', '侮' => '侮', '侻' => '侻', '倂' => '倂', '偺' => '偺', '備' => '備', '僧' => '僧', '像' => '像', '㒞' => '㒞', '𠘺' => '𠘺', '免' => '免', '兔' => '兔', '兤' => '兤', '具' => '具', '𠔜' => '𠔜', '㒹' => '㒹', '內' => '內', '再' => '再', '𠕋' => '𠕋', '冗' => '冗', '冤' => '冤', '仌' => '仌', '冬' => '冬', '况' => '况', '𩇟' => '𩇟', '凵' => '凵', '刃' => '刃', '㓟' => '㓟', '刻' => '刻', '剆' => '剆', '割' => '割', '剷' => '剷', '㔕' => '㔕', '勇' => '勇', '勉' => '勉', '勤' => '勤', '勺' => '勺', '包' => '包', '匆' => '匆', '北' => '北', '卉' => '卉', '卑' => '卑', '博' => '博', '即' => '即', '卽' => '卽', '卿' => '卿', '卿' => '卿', '卿' => '卿', '𠨬' => '𠨬', '灰' => '灰', '及' => '及', '叟' => '叟', '𠭣' => '𠭣', '叫' => '叫', '叱' => '叱', '吆' => '吆', '咞' => '咞', '吸' => '吸', '呈' => '呈', '周' => '周', '咢' => '咢', '哶' => '哶', '唐' => '唐', '啓' => '啓', '啣' => '啣', '善' => '善', '善' => '善', '喙' => '喙', '喫' => '喫', '喳' => '喳', '嗂' => '嗂', '圖' => '圖', '嘆' => '嘆', '圗' => '圗', '噑' => '噑', '噴' => '噴', '切' => '切', '壮' => '壮', '城' => '城', '埴' => '埴', '堍' => '堍', '型' => '型', '堲' => '堲', '報' => '報', '墬' => '墬', '𡓤' => '𡓤', '売' => '売', '壷' => '壷', '夆' => '夆', '多' => '多', '夢' => '夢', '奢' => '奢', '𡚨' => '𡚨', '𡛪' => '𡛪', '姬' => '姬', '娛' => '娛', '娧' => '娧', '姘' => '姘', '婦' => '婦', '㛮' => '㛮', '㛼' => '㛼', '嬈' => '嬈', '嬾' => '嬾', '嬾' => '嬾', '𡧈' => '𡧈', '寃' => '寃', '寘' => '寘', '寧' => '寧', '寳' => '寳', '𡬘' => '𡬘', '寿' => '寿', '将' => '将', '当' => '当', '尢' => '尢', '㞁' => '㞁', '屠' => '屠', '屮' => '屮', '峀' => '峀', '岍' => '岍', '𡷤' => '𡷤', '嵃' => '嵃', '𡷦' => '𡷦', '嵮' => '嵮', '嵫' => '嵫', '嵼' => '嵼', '巡' => '巡', '巢' => '巢', '㠯' => '㠯', '巽' => '巽', '帨' => '帨', '帽' => '帽', '幩' => '幩', '㡢' => '㡢', '𢆃' => '𢆃', '㡼' => '㡼', '庰' => '庰', '庳' => '庳', '庶' => '庶', '廊' => '廊', '𪎒' => '𪎒', '廾' => '廾', '𢌱' => '𢌱', '𢌱' => '𢌱', '舁' => '舁', '弢' => '弢', '弢' => '弢', '㣇' => '㣇', '𣊸' => '𣊸', '𦇚' => '𦇚', '形' => '形', '彫' => '彫', '㣣' => '㣣', '徚' => '徚', '忍' => '忍', '志' => '志', '忹' => '忹', '悁' => '悁', '㤺' => '㤺', '㤜' => '㤜', '悔' => '悔', '𢛔' => '𢛔', '惇' => '惇', '慈' => '慈', '慌' => '慌', '慎' => '慎', '慌' => '慌', '慺' => '慺', '憎' => '憎', '憲' => '憲', '憤' => '憤', '憯' => '憯', '懞' => '懞', '懲' => '懲', '懶' => '懶', '成' => '成', '戛' => '戛', '扝' => '扝', '抱' => '抱', '拔' => '拔', '捐' => '捐', '𢬌' => '𢬌', '挽' => '挽', '拼' => '拼', '捨' => '捨', '掃' => '掃', '揤' => '揤', '𢯱' => '𢯱', '搢' => '搢', '揅' => '揅', '掩' => '掩', '㨮' => '㨮', '摩' => '摩', '摾' => '摾', '撝' => '撝', '摷' => '摷', '㩬' => '㩬', '敏' => '敏', '敬' => '敬', '𣀊' => '𣀊', '旣' => '旣', '書' => '書', '晉' => '晉', '㬙' => '㬙', '暑' => '暑', '㬈' => '㬈', '㫤' => '㫤', '冒' => '冒', '冕' => '冕', '最' => '最', '暜' => '暜', '肭' => '肭', '䏙' => '䏙', '朗' => '朗', '望' => '望', '朡' => '朡', '杞' => '杞', '杓' => '杓', '𣏃' => '𣏃', '㭉' => '㭉', '柺' => '柺', '枅' => '枅', '桒' => '桒', '梅' => '梅', '𣑭' => '𣑭', '梎' => '梎', '栟' => '栟', '椔' => '椔', '㮝' => '㮝', '楂' => '楂', '榣' => '榣', '槪' => '槪', '檨' => '檨', '𣚣' => '𣚣', '櫛' => '櫛', '㰘' => '㰘', '次' => '次', '𣢧' => '𣢧', '歔' => '歔', '㱎' => '㱎', '歲' => '歲', '殟' => '殟', '殺' => '殺', '殻' => '殻', '𣪍' => '𣪍', '𡴋' => '𡴋', '𣫺' => '𣫺', '汎' => '汎', '𣲼' => '𣲼', '沿' => '沿', '泍' => '泍', '汧' => '汧', '洖' => '洖', '派' => '派', '海' => '海', '流' => '流', '浩' => '浩', '浸' => '浸', '涅' => '涅', '𣴞' => '𣴞', '洴' => '洴', '港' => '港', '湮' => '湮', '㴳' => '㴳', '滋' => '滋', '滇' => '滇', '𣻑' => '𣻑', '淹' => '淹', '潮' => '潮', '𣽞' => '𣽞', '𣾎' => '𣾎', '濆' => '濆', '瀹' => '瀹', '瀞' => '瀞', '瀛' => '瀛', '㶖' => '㶖', '灊' => '灊', '災' => '災', '灷' => '灷', '炭' => '炭', '𠔥' => '𠔥', '煅' => '煅', '𤉣' => '𤉣', '熜' => '熜', '𤎫' => '𤎫', '爨' => '爨', '爵' => '爵', '牐' => '牐', '𤘈' => '𤘈', '犀' => '犀', '犕' => '犕', '𤜵' => '𤜵', '𤠔' => '𤠔', '獺' => '獺', '王' => '王', '㺬' => '㺬', '玥' => '玥', '㺸' => '㺸', '㺸' => '㺸', '瑇' => '瑇', '瑜' => '瑜', '瑱' => '瑱', '璅' => '璅', '瓊' => '瓊', '㼛' => '㼛', '甤' => '甤', '𤰶' => '𤰶', '甾' => '甾', '𤲒' => '𤲒', '異' => '異', '𢆟' => '𢆟', '瘐' => '瘐', '𤾡' => '𤾡', '𤾸' => '𤾸', '𥁄' => '𥁄', '㿼' => '㿼', '䀈' => '䀈', '直' => '直', '𥃳' => '𥃳', '𥃲' => '𥃲', '𥄙' => '𥄙', '𥄳' => '𥄳', '眞' => '眞', '真' => '真', '真' => '真', '睊' => '睊', '䀹' => '䀹', '瞋' => '瞋', '䁆' => '䁆', '䂖' => '䂖', '𥐝' => '𥐝', '硎' => '硎', '碌' => '碌', '磌' => '磌', '䃣' => '䃣', '𥘦' => '𥘦', '祖' => '祖', '𥚚' => '𥚚', '𥛅' => '𥛅', '福' => '福', '秫' => '秫', '䄯' => '䄯', '穀' => '穀', '穊' => '穊', '穏' => '穏', '𥥼' => '𥥼', '𥪧' => '𥪧', '𥪧' => '𥪧', '竮' => '竮', '䈂' => '䈂', '𥮫' => '𥮫', '篆' => '篆', '築' => '築', '䈧' => '䈧', '𥲀' => '𥲀', '糒' => '糒', '䊠' => '䊠', '糨' => '糨', '糣' => '糣', '紀' => '紀', '𥾆' => '𥾆', '絣' => '絣', '䌁' => '䌁', '緇' => '緇', '縂' => '縂', '繅' => '繅', '䌴' => '䌴', '𦈨' => '𦈨', '𦉇' => '𦉇', '䍙' => '䍙', '𦋙' => '𦋙', '罺' => '罺', '𦌾' => '𦌾', '羕' => '羕', '翺' => '翺', '者' => '者', '𦓚' => '𦓚', '𦔣' => '𦔣', '聠' => '聠', '𦖨' => '𦖨', '聰' => '聰', '𣍟' => '𣍟', '䏕' => '䏕', '育' => '育', '脃' => '脃', '䐋' => '䐋', '脾' => '脾', '媵' => '媵', '𦞧' => '𦞧', '𦞵' => '𦞵', '𣎓' => '𣎓', '𣎜' => '𣎜', '舁' => '舁', '舄' => '舄', '辞' => '辞', '䑫' => '䑫', '芑' => '芑', '芋' => '芋', '芝' => '芝', '劳' => '劳', '花' => '花', '芳' => '芳', '芽' => '芽', '苦' => '苦', '𦬼' => '𦬼', '若' => '若', '茝' => '茝', '荣' => '荣', '莭' => '莭', '茣' => '茣', '莽' => '莽', '菧' => '菧', '著' => '著', '荓' => '荓', '菊' => '菊', '菌' => '菌', '菜' => '菜', '𦰶' => '𦰶', '𦵫' => '𦵫', '𦳕' => '𦳕', '䔫' => '䔫', '蓱' => '蓱', '蓳' => '蓳', '蔖' => '蔖', '𧏊' => '𧏊', '蕤' => '蕤', '𦼬' => '𦼬', '䕝' => '䕝', '䕡' => '䕡', '𦾱' => '𦾱', '𧃒' => '𧃒', '䕫' => '䕫', '虐' => '虐', '虜' => '虜', '虧' => '虧', '虩' => '虩', '蚩' => '蚩', '蚈' => '蚈', '蜎' => '蜎', '蛢' => '蛢', '蝹' => '蝹', '蜨' => '蜨', '蝫' => '蝫', '螆' => '螆', '䗗' => '䗗', '蟡' => '蟡', '蠁' => '蠁', '䗹' => '䗹', '衠' => '衠', '衣' => '衣', '𧙧' => '𧙧', '裗' => '裗', '裞' => '裞', '䘵' => '䘵', '裺' => '裺', '㒻' => '㒻', '𧢮' => '𧢮', '𧥦' => '𧥦', '䚾' => '䚾', '䛇' => '䛇', '誠' => '誠', '諭' => '諭', '變' => '變', '豕' => '豕', '𧲨' => '𧲨', '貫' => '貫', '賁' => '賁', '贛' => '贛', '起' => '起', '𧼯' => '𧼯', '𠠄' => '𠠄', '跋' => '跋', '趼' => '趼', '跰' => '跰', '𠣞' => '𠣞', '軔' => '軔', '輸' => '輸', '𨗒' => '𨗒', '𨗭' => '𨗭', '邔' => '邔', '郱' => '郱', '鄑' => '鄑', '𨜮' => '𨜮', '鄛' => '鄛', '鈸' => '鈸', '鋗' => '鋗', '鋘' => '鋘', '鉼' => '鉼', '鏹' => '鏹', '鐕' => '鐕', '𨯺' => '𨯺', '開' => '開', '䦕' => '䦕', '閷' => '閷', '𨵷' => '𨵷', '䧦' => '䧦', '雃' => '雃', '嶲' => '嶲', '霣' => '霣', '𩅅' => '𩅅', '𩈚' => '𩈚', '䩮' => '䩮', '䩶' => '䩶', '韠' => '韠', '𩐊' => '𩐊', '䪲' => '䪲', '𩒖' => '𩒖', '頋' => '頋', '頋' => '頋', '頩' => '頩', '𩖶' => '𩖶', '飢' => '飢', '䬳' => '䬳', '餩' => '餩', '馧' => '馧', '駂' => '駂', '駾' => '駾', '䯎' => '䯎', '𩬰' => '𩬰', '鬒' => '鬒', '鱀' => '鱀', '鳽' => '鳽', '䳎' => '䳎', '䳭' => '䳭', '鵧' => '鵧', '𪃎' => '𪃎', '䳸' => '䳸', '𪄅' => '𪄅', '𪈎' => '𪈎', '𪊑' => '𪊑', '麻' => '麻', '䵖' => '䵖', '黹' => '黹', '黾' => '黾', '鼅' => '鼅', '鼏' => '鼏', '鼖' => '鼖', '鼻' => '鼻', '𪘀' => '𪘀', ); PK2A#]���D5D5Lvendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.phpnu�[���<?php return array ( '̀' => 230, '́' => 230, '̂' => 230, '̃' => 230, '̄' => 230, '̅' => 230, '̆' => 230, '̇' => 230, '̈' => 230, '̉' => 230, '̊' => 230, '̋' => 230, '̌' => 230, '̍' => 230, '̎' => 230, '̏' => 230, '̐' => 230, '̑' => 230, '̒' => 230, '̓' => 230, '̔' => 230, '̕' => 232, '̖' => 220, '̗' => 220, '̘' => 220, '̙' => 220, '̚' => 232, '̛' => 216, '̜' => 220, '̝' => 220, '̞' => 220, '̟' => 220, '̠' => 220, '̡' => 202, '̢' => 202, '̣' => 220, '̤' => 220, '̥' => 220, '̦' => 220, '̧' => 202, '̨' => 202, '̩' => 220, '̪' => 220, '̫' => 220, '̬' => 220, '̭' => 220, '̮' => 220, '̯' => 220, '̰' => 220, '̱' => 220, '̲' => 220, '̳' => 220, '̴' => 1, '̵' => 1, '̶' => 1, '̷' => 1, '̸' => 1, '̹' => 220, '̺' => 220, '̻' => 220, '̼' => 220, '̽' => 230, '̾' => 230, '̿' => 230, '̀' => 230, '́' => 230, '͂' => 230, '̓' => 230, '̈́' => 230, 'ͅ' => 240, '͆' => 230, '͇' => 220, '͈' => 220, '͉' => 220, '͊' => 230, '͋' => 230, '͌' => 230, '͍' => 220, '͎' => 220, '͐' => 230, '͑' => 230, '͒' => 230, '͓' => 220, '͔' => 220, '͕' => 220, '͖' => 220, '͗' => 230, '͘' => 232, '͙' => 220, '͚' => 220, '͛' => 230, '͜' => 233, '͝' => 234, '͞' => 234, '͟' => 233, '͠' => 234, '͡' => 234, '͢' => 233, 'ͣ' => 230, 'ͤ' => 230, 'ͥ' => 230, 'ͦ' => 230, 'ͧ' => 230, 'ͨ' => 230, 'ͩ' => 230, 'ͪ' => 230, 'ͫ' => 230, 'ͬ' => 230, 'ͭ' => 230, 'ͮ' => 230, 'ͯ' => 230, '҃' => 230, '҄' => 230, '҅' => 230, '҆' => 230, '҇' => 230, '֑' => 220, '֒' => 230, '֓' => 230, '֔' => 230, '֕' => 230, '֖' => 220, '֗' => 230, '֘' => 230, '֙' => 230, '֚' => 222, '֛' => 220, '֜' => 230, '֝' => 230, '֞' => 230, '֟' => 230, '֠' => 230, '֡' => 230, '֢' => 220, '֣' => 220, '֤' => 220, '֥' => 220, '֦' => 220, '֧' => 220, '֨' => 230, '֩' => 230, '֪' => 220, '֫' => 230, '֬' => 230, '֭' => 222, '֮' => 228, '֯' => 230, 'ְ' => 10, 'ֱ' => 11, 'ֲ' => 12, 'ֳ' => 13, 'ִ' => 14, 'ֵ' => 15, 'ֶ' => 16, 'ַ' => 17, 'ָ' => 18, 'ֹ' => 19, 'ֺ' => 19, 'ֻ' => 20, 'ּ' => 21, 'ֽ' => 22, 'ֿ' => 23, 'ׁ' => 24, 'ׂ' => 25, 'ׄ' => 230, 'ׅ' => 220, 'ׇ' => 18, 'ؐ' => 230, 'ؑ' => 230, 'ؒ' => 230, 'ؓ' => 230, 'ؔ' => 230, 'ؕ' => 230, 'ؖ' => 230, 'ؗ' => 230, 'ؘ' => 30, 'ؙ' => 31, 'ؚ' => 32, 'ً' => 27, 'ٌ' => 28, 'ٍ' => 29, 'َ' => 30, 'ُ' => 31, 'ِ' => 32, 'ّ' => 33, 'ْ' => 34, 'ٓ' => 230, 'ٔ' => 230, 'ٕ' => 220, 'ٖ' => 220, 'ٗ' => 230, '٘' => 230, 'ٙ' => 230, 'ٚ' => 230, 'ٛ' => 230, 'ٜ' => 220, 'ٝ' => 230, 'ٞ' => 230, 'ٟ' => 220, 'ٰ' => 35, 'ۖ' => 230, 'ۗ' => 230, 'ۘ' => 230, 'ۙ' => 230, 'ۚ' => 230, 'ۛ' => 230, 'ۜ' => 230, '۟' => 230, '۠' => 230, 'ۡ' => 230, 'ۢ' => 230, 'ۣ' => 220, 'ۤ' => 230, 'ۧ' => 230, 'ۨ' => 230, '۪' => 220, '۫' => 230, '۬' => 230, 'ۭ' => 220, 'ܑ' => 36, 'ܰ' => 230, 'ܱ' => 220, 'ܲ' => 230, 'ܳ' => 230, 'ܴ' => 220, 'ܵ' => 230, 'ܶ' => 230, 'ܷ' => 220, 'ܸ' => 220, 'ܹ' => 220, 'ܺ' => 230, 'ܻ' => 220, 'ܼ' => 220, 'ܽ' => 230, 'ܾ' => 220, 'ܿ' => 230, '݀' => 230, '݁' => 230, '݂' => 220, '݃' => 230, '݄' => 220, '݅' => 230, '݆' => 220, '݇' => 230, '݈' => 220, '݉' => 230, '݊' => 230, '߫' => 230, '߬' => 230, '߭' => 230, '߮' => 230, '߯' => 230, '߰' => 230, '߱' => 230, '߲' => 220, '߳' => 230, '߽' => 220, 'ࠖ' => 230, 'ࠗ' => 230, '࠘' => 230, '࠙' => 230, 'ࠛ' => 230, 'ࠜ' => 230, 'ࠝ' => 230, 'ࠞ' => 230, 'ࠟ' => 230, 'ࠠ' => 230, 'ࠡ' => 230, 'ࠢ' => 230, 'ࠣ' => 230, 'ࠥ' => 230, 'ࠦ' => 230, 'ࠧ' => 230, 'ࠩ' => 230, 'ࠪ' => 230, 'ࠫ' => 230, 'ࠬ' => 230, '࠭' => 230, '࡙' => 220, '࡚' => 220, '࡛' => 220, '࣓' => 220, 'ࣔ' => 230, 'ࣕ' => 230, 'ࣖ' => 230, 'ࣗ' => 230, 'ࣘ' => 230, 'ࣙ' => 230, 'ࣚ' => 230, 'ࣛ' => 230, 'ࣜ' => 230, 'ࣝ' => 230, 'ࣞ' => 230, 'ࣟ' => 230, '࣠' => 230, '࣡' => 230, 'ࣣ' => 220, 'ࣤ' => 230, 'ࣥ' => 230, 'ࣦ' => 220, 'ࣧ' => 230, 'ࣨ' => 230, 'ࣩ' => 220, '࣪' => 230, '࣫' => 230, '࣬' => 230, '࣭' => 220, '࣮' => 220, '࣯' => 220, 'ࣰ' => 27, 'ࣱ' => 28, 'ࣲ' => 29, 'ࣳ' => 230, 'ࣴ' => 230, 'ࣵ' => 230, 'ࣶ' => 220, 'ࣷ' => 230, 'ࣸ' => 230, 'ࣹ' => 220, 'ࣺ' => 220, 'ࣻ' => 230, 'ࣼ' => 230, 'ࣽ' => 230, 'ࣾ' => 230, 'ࣿ' => 230, '़' => 7, '्' => 9, '॑' => 230, '॒' => 220, '॓' => 230, '॔' => 230, '়' => 7, '্' => 9, '৾' => 230, '਼' => 7, '੍' => 9, '઼' => 7, '્' => 9, '଼' => 7, '୍' => 9, '்' => 9, '్' => 9, 'ౕ' => 84, 'ౖ' => 91, '಼' => 7, '್' => 9, '഻' => 9, '഼' => 9, '്' => 9, '්' => 9, 'ุ' => 103, 'ู' => 103, 'ฺ' => 9, '่' => 107, '้' => 107, '๊' => 107, '๋' => 107, 'ຸ' => 118, 'ູ' => 118, '຺' => 9, '່' => 122, '້' => 122, '໊' => 122, '໋' => 122, '༘' => 220, '༙' => 220, '༵' => 220, '༷' => 220, '༹' => 216, 'ཱ' => 129, 'ི' => 130, 'ུ' => 132, 'ེ' => 130, 'ཻ' => 130, 'ོ' => 130, 'ཽ' => 130, 'ྀ' => 130, 'ྂ' => 230, 'ྃ' => 230, '྄' => 9, '྆' => 230, '྇' => 230, '࿆' => 220, '့' => 7, '္' => 9, '်' => 9, 'ႍ' => 220, '፝' => 230, '፞' => 230, '፟' => 230, '᜔' => 9, '᜴' => 9, '្' => 9, '៝' => 230, 'ᢩ' => 228, '᤹' => 222, '᤺' => 230, '᤻' => 220, 'ᨗ' => 230, 'ᨘ' => 220, '᩠' => 9, '᩵' => 230, '᩶' => 230, '᩷' => 230, '᩸' => 230, '᩹' => 230, '᩺' => 230, '᩻' => 230, '᩼' => 230, '᩿' => 220, '᪰' => 230, '᪱' => 230, '᪲' => 230, '᪳' => 230, '᪴' => 230, '᪵' => 220, '᪶' => 220, '᪷' => 220, '᪸' => 220, '᪹' => 220, '᪺' => 220, '᪻' => 230, '᪼' => 230, '᪽' => 220, 'ᪿ' => 220, 'ᫀ' => 220, '᬴' => 7, '᭄' => 9, '᭫' => 230, '᭬' => 220, '᭭' => 230, '᭮' => 230, '᭯' => 230, '᭰' => 230, '᭱' => 230, '᭲' => 230, '᭳' => 230, '᮪' => 9, '᮫' => 9, '᯦' => 7, '᯲' => 9, '᯳' => 9, '᰷' => 7, '᳐' => 230, '᳑' => 230, '᳒' => 230, '᳔' => 1, '᳕' => 220, '᳖' => 220, '᳗' => 220, '᳘' => 220, '᳙' => 220, '᳚' => 230, '᳛' => 230, '᳜' => 220, '᳝' => 220, '᳞' => 220, '᳟' => 220, '᳠' => 230, '᳢' => 1, '᳣' => 1, '᳤' => 1, '᳥' => 1, '᳦' => 1, '᳧' => 1, '᳨' => 1, '᳭' => 220, '᳴' => 230, '᳸' => 230, '᳹' => 230, '᷀' => 230, '᷁' => 230, '᷂' => 220, '᷃' => 230, '᷄' => 230, '᷅' => 230, '᷆' => 230, '᷇' => 230, '᷈' => 230, '᷉' => 230, '᷊' => 220, '᷋' => 230, '᷌' => 230, '᷍' => 234, '᷎' => 214, '᷏' => 220, '᷐' => 202, '᷑' => 230, '᷒' => 230, 'ᷓ' => 230, 'ᷔ' => 230, 'ᷕ' => 230, 'ᷖ' => 230, 'ᷗ' => 230, 'ᷘ' => 230, 'ᷙ' => 230, 'ᷚ' => 230, 'ᷛ' => 230, 'ᷜ' => 230, 'ᷝ' => 230, 'ᷞ' => 230, 'ᷟ' => 230, 'ᷠ' => 230, 'ᷡ' => 230, 'ᷢ' => 230, 'ᷣ' => 230, 'ᷤ' => 230, 'ᷥ' => 230, 'ᷦ' => 230, 'ᷧ' => 230, 'ᷨ' => 230, 'ᷩ' => 230, 'ᷪ' => 230, 'ᷫ' => 230, 'ᷬ' => 230, 'ᷭ' => 230, 'ᷮ' => 230, 'ᷯ' => 230, 'ᷰ' => 230, 'ᷱ' => 230, 'ᷲ' => 230, 'ᷳ' => 230, 'ᷴ' => 230, '᷵' => 230, '᷶' => 232, '᷷' => 228, '᷸' => 228, '᷹' => 220, '᷻' => 230, '᷼' => 233, '᷽' => 220, '᷾' => 230, '᷿' => 220, '⃐' => 230, '⃑' => 230, '⃒' => 1, '⃓' => 1, '⃔' => 230, '⃕' => 230, '⃖' => 230, '⃗' => 230, '⃘' => 1, '⃙' => 1, '⃚' => 1, '⃛' => 230, '⃜' => 230, '⃡' => 230, '⃥' => 1, '⃦' => 1, '⃧' => 230, '⃨' => 220, '⃩' => 230, '⃪' => 1, '⃫' => 1, '⃬' => 220, '⃭' => 220, '⃮' => 220, '⃯' => 220, '⃰' => 230, '⳯' => 230, '⳰' => 230, '⳱' => 230, '⵿' => 9, 'ⷠ' => 230, 'ⷡ' => 230, 'ⷢ' => 230, 'ⷣ' => 230, 'ⷤ' => 230, 'ⷥ' => 230, 'ⷦ' => 230, 'ⷧ' => 230, 'ⷨ' => 230, 'ⷩ' => 230, 'ⷪ' => 230, 'ⷫ' => 230, 'ⷬ' => 230, 'ⷭ' => 230, 'ⷮ' => 230, 'ⷯ' => 230, 'ⷰ' => 230, 'ⷱ' => 230, 'ⷲ' => 230, 'ⷳ' => 230, 'ⷴ' => 230, 'ⷵ' => 230, 'ⷶ' => 230, 'ⷷ' => 230, 'ⷸ' => 230, 'ⷹ' => 230, 'ⷺ' => 230, 'ⷻ' => 230, 'ⷼ' => 230, 'ⷽ' => 230, 'ⷾ' => 230, 'ⷿ' => 230, '〪' => 218, '〫' => 228, '〬' => 232, '〭' => 222, '〮' => 224, '〯' => 224, '゙' => 8, '゚' => 8, '꙯' => 230, 'ꙴ' => 230, 'ꙵ' => 230, 'ꙶ' => 230, 'ꙷ' => 230, 'ꙸ' => 230, 'ꙹ' => 230, 'ꙺ' => 230, 'ꙻ' => 230, '꙼' => 230, '꙽' => 230, 'ꚞ' => 230, 'ꚟ' => 230, '꛰' => 230, '꛱' => 230, '꠆' => 9, '꠬' => 9, '꣄' => 9, '꣠' => 230, '꣡' => 230, '꣢' => 230, '꣣' => 230, '꣤' => 230, '꣥' => 230, '꣦' => 230, '꣧' => 230, '꣨' => 230, '꣩' => 230, '꣪' => 230, '꣫' => 230, '꣬' => 230, '꣭' => 230, '꣮' => 230, '꣯' => 230, '꣰' => 230, '꣱' => 230, '꤫' => 220, '꤬' => 220, '꤭' => 220, '꥓' => 9, '꦳' => 7, '꧀' => 9, 'ꪰ' => 230, 'ꪲ' => 230, 'ꪳ' => 230, 'ꪴ' => 220, 'ꪷ' => 230, 'ꪸ' => 230, 'ꪾ' => 230, '꪿' => 230, '꫁' => 230, '꫶' => 9, '꯭' => 9, 'ﬞ' => 26, '︠' => 230, '︡' => 230, '︢' => 230, '︣' => 230, '︤' => 230, '︥' => 230, '︦' => 230, '︧' => 220, '︨' => 220, '︩' => 220, '︪' => 220, '︫' => 220, '︬' => 220, '︭' => 220, '︮' => 230, '︯' => 230, '𐇽' => 220, '𐋠' => 220, '𐍶' => 230, '𐍷' => 230, '𐍸' => 230, '𐍹' => 230, '𐍺' => 230, '𐨍' => 220, '𐨏' => 230, '𐨸' => 230, '𐨹' => 1, '𐨺' => 220, '𐨿' => 9, '𐫥' => 230, '𐫦' => 220, '𐴤' => 230, '𐴥' => 230, '𐴦' => 230, '𐴧' => 230, '𐺫' => 230, '𐺬' => 230, '𐽆' => 220, '𐽇' => 220, '𐽈' => 230, '𐽉' => 230, '𐽊' => 230, '𐽋' => 220, '𐽌' => 230, '𐽍' => 220, '𐽎' => 220, '𐽏' => 220, '𐽐' => 220, '𑁆' => 9, '𑁿' => 9, '𑂹' => 9, '𑂺' => 7, '𑄀' => 230, '𑄁' => 230, '𑄂' => 230, '𑄳' => 9, '𑄴' => 9, '𑅳' => 7, '𑇀' => 9, '𑇊' => 7, '𑈵' => 9, '𑈶' => 7, '𑋩' => 7, '𑋪' => 9, '𑌻' => 7, '𑌼' => 7, '𑍍' => 9, '𑍦' => 230, '𑍧' => 230, '𑍨' => 230, '𑍩' => 230, '𑍪' => 230, '𑍫' => 230, '𑍬' => 230, '𑍰' => 230, '𑍱' => 230, '𑍲' => 230, '𑍳' => 230, '𑍴' => 230, '𑑂' => 9, '𑑆' => 7, '𑑞' => 230, '𑓂' => 9, '𑓃' => 7, '𑖿' => 9, '𑗀' => 7, '𑘿' => 9, '𑚶' => 9, '𑚷' => 7, '𑜫' => 9, '𑠹' => 9, '𑠺' => 7, '𑤽' => 9, '𑤾' => 9, '𑥃' => 7, '𑧠' => 9, '𑨴' => 9, '𑩇' => 9, '𑪙' => 9, '𑰿' => 9, '𑵂' => 7, '𑵄' => 9, '𑵅' => 9, '𑶗' => 9, '𖫰' => 1, '𖫱' => 1, '𖫲' => 1, '𖫳' => 1, '𖫴' => 1, '𖬰' => 230, '𖬱' => 230, '𖬲' => 230, '𖬳' => 230, '𖬴' => 230, '𖬵' => 230, '𖬶' => 230, '𖿰' => 6, '𖿱' => 6, '𛲞' => 1, '𝅥' => 216, '𝅦' => 216, '𝅧' => 1, '𝅨' => 1, '𝅩' => 1, '𝅭' => 226, '𝅮' => 216, '𝅯' => 216, '𝅰' => 216, '𝅱' => 216, '𝅲' => 216, '𝅻' => 220, '𝅼' => 220, '𝅽' => 220, '𝅾' => 220, '𝅿' => 220, '𝆀' => 220, '𝆁' => 220, '𝆂' => 220, '𝆅' => 230, '𝆆' => 230, '𝆇' => 230, '𝆈' => 230, '𝆉' => 230, '𝆊' => 220, '𝆋' => 220, '𝆪' => 230, '𝆫' => 230, '𝆬' => 230, '𝆭' => 230, '𝉂' => 230, '𝉃' => 230, '𝉄' => 230, '𞀀' => 230, '𞀁' => 230, '𞀂' => 230, '𞀃' => 230, '𞀄' => 230, '𞀅' => 230, '𞀆' => 230, '𞀈' => 230, '𞀉' => 230, '𞀊' => 230, '𞀋' => 230, '𞀌' => 230, '𞀍' => 230, '𞀎' => 230, '𞀏' => 230, '𞀐' => 230, '𞀑' => 230, '𞀒' => 230, '𞀓' => 230, '𞀔' => 230, '𞀕' => 230, '𞀖' => 230, '𞀗' => 230, '𞀘' => 230, '𞀛' => 230, '𞀜' => 230, '𞀝' => 230, '𞀞' => 230, '𞀟' => 230, '𞀠' => 230, '𞀡' => 230, '𞀣' => 230, '𞀤' => 230, '𞀦' => 230, '𞀧' => 230, '𞀨' => 230, '𞀩' => 230, '𞀪' => 230, '𞄰' => 230, '𞄱' => 230, '𞄲' => 230, '𞄳' => 230, '𞄴' => 230, '𞄵' => 230, '𞄶' => 230, '𞋬' => 230, '𞋭' => 230, '𞋮' => 230, '𞋯' => 230, '𞣐' => 220, '𞣑' => 220, '𞣒' => 220, '𞣓' => 220, '𞣔' => 220, '𞣕' => 220, '𞣖' => 220, '𞥄' => 230, '𞥅' => 230, '𞥆' => 230, '𞥇' => 230, '𞥈' => 230, '𞥉' => 230, '𞥊' => 7, ); PK2A#]'�C��D�DRvendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.phpnu�[���<?php return array ( 'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'ÿ' => 'ÿ', 'Ā' => 'Ā', 'ā' => 'ā', 'Ă' => 'Ă', 'ă' => 'ă', 'Ą' => 'Ą', 'ą' => 'ą', 'Ć' => 'Ć', 'ć' => 'ć', 'Ĉ' => 'Ĉ', 'ĉ' => 'ĉ', 'Ċ' => 'Ċ', 'ċ' => 'ċ', 'Č' => 'Č', 'č' => 'č', 'Ď' => 'Ď', 'ď' => 'ď', 'Ē' => 'Ē', 'ē' => 'ē', 'Ĕ' => 'Ĕ', 'ĕ' => 'ĕ', 'Ė' => 'Ė', 'ė' => 'ė', 'Ę' => 'Ę', 'ę' => 'ę', 'Ě' => 'Ě', 'ě' => 'ě', 'Ĝ' => 'Ĝ', 'ĝ' => 'ĝ', 'Ğ' => 'Ğ', 'ğ' => 'ğ', 'Ġ' => 'Ġ', 'ġ' => 'ġ', 'Ģ' => 'Ģ', 'ģ' => 'ģ', 'Ĥ' => 'Ĥ', 'ĥ' => 'ĥ', 'Ĩ' => 'Ĩ', 'ĩ' => 'ĩ', 'Ī' => 'Ī', 'ī' => 'ī', 'Ĭ' => 'Ĭ', 'ĭ' => 'ĭ', 'Į' => 'Į', 'į' => 'į', 'İ' => 'İ', 'Ĵ' => 'Ĵ', 'ĵ' => 'ĵ', 'Ķ' => 'Ķ', 'ķ' => 'ķ', 'Ĺ' => 'Ĺ', 'ĺ' => 'ĺ', 'Ļ' => 'Ļ', 'ļ' => 'ļ', 'Ľ' => 'Ľ', 'ľ' => 'ľ', 'Ń' => 'Ń', 'ń' => 'ń', 'Ņ' => 'Ņ', 'ņ' => 'ņ', 'Ň' => 'Ň', 'ň' => 'ň', 'Ō' => 'Ō', 'ō' => 'ō', 'Ŏ' => 'Ŏ', 'ŏ' => 'ŏ', 'Ő' => 'Ő', 'ő' => 'ő', 'Ŕ' => 'Ŕ', 'ŕ' => 'ŕ', 'Ŗ' => 'Ŗ', 'ŗ' => 'ŗ', 'Ř' => 'Ř', 'ř' => 'ř', 'Ś' => 'Ś', 'ś' => 'ś', 'Ŝ' => 'Ŝ', 'ŝ' => 'ŝ', 'Ş' => 'Ş', 'ş' => 'ş', 'Š' => 'Š', 'š' => 'š', 'Ţ' => 'Ţ', 'ţ' => 'ţ', 'Ť' => 'Ť', 'ť' => 'ť', 'Ũ' => 'Ũ', 'ũ' => 'ũ', 'Ū' => 'Ū', 'ū' => 'ū', 'Ŭ' => 'Ŭ', 'ŭ' => 'ŭ', 'Ů' => 'Ů', 'ů' => 'ů', 'Ű' => 'Ű', 'ű' => 'ű', 'Ų' => 'Ų', 'ų' => 'ų', 'Ŵ' => 'Ŵ', 'ŵ' => 'ŵ', 'Ŷ' => 'Ŷ', 'ŷ' => 'ŷ', 'Ÿ' => 'Ÿ', 'Ź' => 'Ź', 'ź' => 'ź', 'Ż' => 'Ż', 'ż' => 'ż', 'Ž' => 'Ž', 'ž' => 'ž', 'Ơ' => 'Ơ', 'ơ' => 'ơ', 'Ư' => 'Ư', 'ư' => 'ư', 'Ǎ' => 'Ǎ', 'ǎ' => 'ǎ', 'Ǐ' => 'Ǐ', 'ǐ' => 'ǐ', 'Ǒ' => 'Ǒ', 'ǒ' => 'ǒ', 'Ǔ' => 'Ǔ', 'ǔ' => 'ǔ', 'Ǖ' => 'Ǖ', 'ǖ' => 'ǖ', 'Ǘ' => 'Ǘ', 'ǘ' => 'ǘ', 'Ǚ' => 'Ǚ', 'ǚ' => 'ǚ', 'Ǜ' => 'Ǜ', 'ǜ' => 'ǜ', 'Ǟ' => 'Ǟ', 'ǟ' => 'ǟ', 'Ǡ' => 'Ǡ', 'ǡ' => 'ǡ', 'Ǣ' => 'Ǣ', 'ǣ' => 'ǣ', 'Ǧ' => 'Ǧ', 'ǧ' => 'ǧ', 'Ǩ' => 'Ǩ', 'ǩ' => 'ǩ', 'Ǫ' => 'Ǫ', 'ǫ' => 'ǫ', 'Ǭ' => 'Ǭ', 'ǭ' => 'ǭ', 'Ǯ' => 'Ǯ', 'ǯ' => 'ǯ', 'ǰ' => 'ǰ', 'Ǵ' => 'Ǵ', 'ǵ' => 'ǵ', 'Ǹ' => 'Ǹ', 'ǹ' => 'ǹ', 'Ǻ' => 'Ǻ', 'ǻ' => 'ǻ', 'Ǽ' => 'Ǽ', 'ǽ' => 'ǽ', 'Ǿ' => 'Ǿ', 'ǿ' => 'ǿ', 'Ȁ' => 'Ȁ', 'ȁ' => 'ȁ', 'Ȃ' => 'Ȃ', 'ȃ' => 'ȃ', 'Ȅ' => 'Ȅ', 'ȅ' => 'ȅ', 'Ȇ' => 'Ȇ', 'ȇ' => 'ȇ', 'Ȉ' => 'Ȉ', 'ȉ' => 'ȉ', 'Ȋ' => 'Ȋ', 'ȋ' => 'ȋ', 'Ȍ' => 'Ȍ', 'ȍ' => 'ȍ', 'Ȏ' => 'Ȏ', 'ȏ' => 'ȏ', 'Ȑ' => 'Ȑ', 'ȑ' => 'ȑ', 'Ȓ' => 'Ȓ', 'ȓ' => 'ȓ', 'Ȕ' => 'Ȕ', 'ȕ' => 'ȕ', 'Ȗ' => 'Ȗ', 'ȗ' => 'ȗ', 'Ș' => 'Ș', 'ș' => 'ș', 'Ț' => 'Ț', 'ț' => 'ț', 'Ȟ' => 'Ȟ', 'ȟ' => 'ȟ', 'Ȧ' => 'Ȧ', 'ȧ' => 'ȧ', 'Ȩ' => 'Ȩ', 'ȩ' => 'ȩ', 'Ȫ' => 'Ȫ', 'ȫ' => 'ȫ', 'Ȭ' => 'Ȭ', 'ȭ' => 'ȭ', 'Ȯ' => 'Ȯ', 'ȯ' => 'ȯ', 'Ȱ' => 'Ȱ', 'ȱ' => 'ȱ', 'Ȳ' => 'Ȳ', 'ȳ' => 'ȳ', '΅' => '΅', 'Ά' => 'Ά', 'Έ' => 'Έ', 'Ή' => 'Ή', 'Ί' => 'Ί', 'Ό' => 'Ό', 'Ύ' => 'Ύ', 'Ώ' => 'Ώ', 'ΐ' => 'ΐ', 'Ϊ' => 'Ϊ', 'Ϋ' => 'Ϋ', 'ά' => 'ά', 'έ' => 'έ', 'ή' => 'ή', 'ί' => 'ί', 'ΰ' => 'ΰ', 'ϊ' => 'ϊ', 'ϋ' => 'ϋ', 'ό' => 'ό', 'ύ' => 'ύ', 'ώ' => 'ώ', 'ϓ' => 'ϓ', 'ϔ' => 'ϔ', 'Ѐ' => 'Ѐ', 'Ё' => 'Ё', 'Ѓ' => 'Ѓ', 'Ї' => 'Ї', 'Ќ' => 'Ќ', 'Ѝ' => 'Ѝ', 'Ў' => 'Ў', 'Й' => 'Й', 'й' => 'й', 'ѐ' => 'ѐ', 'ё' => 'ё', 'ѓ' => 'ѓ', 'ї' => 'ї', 'ќ' => 'ќ', 'ѝ' => 'ѝ', 'ў' => 'ў', 'Ѷ' => 'Ѷ', 'ѷ' => 'ѷ', 'Ӂ' => 'Ӂ', 'ӂ' => 'ӂ', 'Ӑ' => 'Ӑ', 'ӑ' => 'ӑ', 'Ӓ' => 'Ӓ', 'ӓ' => 'ӓ', 'Ӗ' => 'Ӗ', 'ӗ' => 'ӗ', 'Ӛ' => 'Ӛ', 'ӛ' => 'ӛ', 'Ӝ' => 'Ӝ', 'ӝ' => 'ӝ', 'Ӟ' => 'Ӟ', 'ӟ' => 'ӟ', 'Ӣ' => 'Ӣ', 'ӣ' => 'ӣ', 'Ӥ' => 'Ӥ', 'ӥ' => 'ӥ', 'Ӧ' => 'Ӧ', 'ӧ' => 'ӧ', 'Ӫ' => 'Ӫ', 'ӫ' => 'ӫ', 'Ӭ' => 'Ӭ', 'ӭ' => 'ӭ', 'Ӯ' => 'Ӯ', 'ӯ' => 'ӯ', 'Ӱ' => 'Ӱ', 'ӱ' => 'ӱ', 'Ӳ' => 'Ӳ', 'ӳ' => 'ӳ', 'Ӵ' => 'Ӵ', 'ӵ' => 'ӵ', 'Ӹ' => 'Ӹ', 'ӹ' => 'ӹ', 'آ' => 'آ', 'أ' => 'أ', 'ؤ' => 'ؤ', 'إ' => 'إ', 'ئ' => 'ئ', 'ۀ' => 'ۀ', 'ۂ' => 'ۂ', 'ۓ' => 'ۓ', 'ऩ' => 'ऩ', 'ऱ' => 'ऱ', 'ऴ' => 'ऴ', 'ো' => 'ো', 'ৌ' => 'ৌ', 'ୈ' => 'ୈ', 'ୋ' => 'ୋ', 'ୌ' => 'ୌ', 'ஔ' => 'ஔ', 'ொ' => 'ொ', 'ோ' => 'ோ', 'ௌ' => 'ௌ', 'ై' => 'ై', 'ೀ' => 'ೀ', 'ೇ' => 'ೇ', 'ೈ' => 'ೈ', 'ೊ' => 'ೊ', 'ೋ' => 'ೋ', 'ൊ' => 'ൊ', 'ോ' => 'ോ', 'ൌ' => 'ൌ', 'ේ' => 'ේ', 'ො' => 'ො', 'ෝ' => 'ෝ', 'ෞ' => 'ෞ', 'ဦ' => 'ဦ', 'ᬆ' => 'ᬆ', 'ᬈ' => 'ᬈ', 'ᬊ' => 'ᬊ', 'ᬌ' => 'ᬌ', 'ᬎ' => 'ᬎ', 'ᬒ' => 'ᬒ', 'ᬻ' => 'ᬻ', 'ᬽ' => 'ᬽ', 'ᭀ' => 'ᭀ', 'ᭁ' => 'ᭁ', 'ᭃ' => 'ᭃ', 'Ḁ' => 'Ḁ', 'ḁ' => 'ḁ', 'Ḃ' => 'Ḃ', 'ḃ' => 'ḃ', 'Ḅ' => 'Ḅ', 'ḅ' => 'ḅ', 'Ḇ' => 'Ḇ', 'ḇ' => 'ḇ', 'Ḉ' => 'Ḉ', 'ḉ' => 'ḉ', 'Ḋ' => 'Ḋ', 'ḋ' => 'ḋ', 'Ḍ' => 'Ḍ', 'ḍ' => 'ḍ', 'Ḏ' => 'Ḏ', 'ḏ' => 'ḏ', 'Ḑ' => 'Ḑ', 'ḑ' => 'ḑ', 'Ḓ' => 'Ḓ', 'ḓ' => 'ḓ', 'Ḕ' => 'Ḕ', 'ḕ' => 'ḕ', 'Ḗ' => 'Ḗ', 'ḗ' => 'ḗ', 'Ḙ' => 'Ḙ', 'ḙ' => 'ḙ', 'Ḛ' => 'Ḛ', 'ḛ' => 'ḛ', 'Ḝ' => 'Ḝ', 'ḝ' => 'ḝ', 'Ḟ' => 'Ḟ', 'ḟ' => 'ḟ', 'Ḡ' => 'Ḡ', 'ḡ' => 'ḡ', 'Ḣ' => 'Ḣ', 'ḣ' => 'ḣ', 'Ḥ' => 'Ḥ', 'ḥ' => 'ḥ', 'Ḧ' => 'Ḧ', 'ḧ' => 'ḧ', 'Ḩ' => 'Ḩ', 'ḩ' => 'ḩ', 'Ḫ' => 'Ḫ', 'ḫ' => 'ḫ', 'Ḭ' => 'Ḭ', 'ḭ' => 'ḭ', 'Ḯ' => 'Ḯ', 'ḯ' => 'ḯ', 'Ḱ' => 'Ḱ', 'ḱ' => 'ḱ', 'Ḳ' => 'Ḳ', 'ḳ' => 'ḳ', 'Ḵ' => 'Ḵ', 'ḵ' => 'ḵ', 'Ḷ' => 'Ḷ', 'ḷ' => 'ḷ', 'Ḹ' => 'Ḹ', 'ḹ' => 'ḹ', 'Ḻ' => 'Ḻ', 'ḻ' => 'ḻ', 'Ḽ' => 'Ḽ', 'ḽ' => 'ḽ', 'Ḿ' => 'Ḿ', 'ḿ' => 'ḿ', 'Ṁ' => 'Ṁ', 'ṁ' => 'ṁ', 'Ṃ' => 'Ṃ', 'ṃ' => 'ṃ', 'Ṅ' => 'Ṅ', 'ṅ' => 'ṅ', 'Ṇ' => 'Ṇ', 'ṇ' => 'ṇ', 'Ṉ' => 'Ṉ', 'ṉ' => 'ṉ', 'Ṋ' => 'Ṋ', 'ṋ' => 'ṋ', 'Ṍ' => 'Ṍ', 'ṍ' => 'ṍ', 'Ṏ' => 'Ṏ', 'ṏ' => 'ṏ', 'Ṑ' => 'Ṑ', 'ṑ' => 'ṑ', 'Ṓ' => 'Ṓ', 'ṓ' => 'ṓ', 'Ṕ' => 'Ṕ', 'ṕ' => 'ṕ', 'Ṗ' => 'Ṗ', 'ṗ' => 'ṗ', 'Ṙ' => 'Ṙ', 'ṙ' => 'ṙ', 'Ṛ' => 'Ṛ', 'ṛ' => 'ṛ', 'Ṝ' => 'Ṝ', 'ṝ' => 'ṝ', 'Ṟ' => 'Ṟ', 'ṟ' => 'ṟ', 'Ṡ' => 'Ṡ', 'ṡ' => 'ṡ', 'Ṣ' => 'Ṣ', 'ṣ' => 'ṣ', 'Ṥ' => 'Ṥ', 'ṥ' => 'ṥ', 'Ṧ' => 'Ṧ', 'ṧ' => 'ṧ', 'Ṩ' => 'Ṩ', 'ṩ' => 'ṩ', 'Ṫ' => 'Ṫ', 'ṫ' => 'ṫ', 'Ṭ' => 'Ṭ', 'ṭ' => 'ṭ', 'Ṯ' => 'Ṯ', 'ṯ' => 'ṯ', 'Ṱ' => 'Ṱ', 'ṱ' => 'ṱ', 'Ṳ' => 'Ṳ', 'ṳ' => 'ṳ', 'Ṵ' => 'Ṵ', 'ṵ' => 'ṵ', 'Ṷ' => 'Ṷ', 'ṷ' => 'ṷ', 'Ṹ' => 'Ṹ', 'ṹ' => 'ṹ', 'Ṻ' => 'Ṻ', 'ṻ' => 'ṻ', 'Ṽ' => 'Ṽ', 'ṽ' => 'ṽ', 'Ṿ' => 'Ṿ', 'ṿ' => 'ṿ', 'Ẁ' => 'Ẁ', 'ẁ' => 'ẁ', 'Ẃ' => 'Ẃ', 'ẃ' => 'ẃ', 'Ẅ' => 'Ẅ', 'ẅ' => 'ẅ', 'Ẇ' => 'Ẇ', 'ẇ' => 'ẇ', 'Ẉ' => 'Ẉ', 'ẉ' => 'ẉ', 'Ẋ' => 'Ẋ', 'ẋ' => 'ẋ', 'Ẍ' => 'Ẍ', 'ẍ' => 'ẍ', 'Ẏ' => 'Ẏ', 'ẏ' => 'ẏ', 'Ẑ' => 'Ẑ', 'ẑ' => 'ẑ', 'Ẓ' => 'Ẓ', 'ẓ' => 'ẓ', 'Ẕ' => 'Ẕ', 'ẕ' => 'ẕ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẛ' => 'ẛ', 'Ạ' => 'Ạ', 'ạ' => 'ạ', 'Ả' => 'Ả', 'ả' => 'ả', 'Ấ' => 'Ấ', 'ấ' => 'ấ', 'Ầ' => 'Ầ', 'ầ' => 'ầ', 'Ẩ' => 'Ẩ', 'ẩ' => 'ẩ', 'Ẫ' => 'Ẫ', 'ẫ' => 'ẫ', 'Ậ' => 'Ậ', 'ậ' => 'ậ', 'Ắ' => 'Ắ', 'ắ' => 'ắ', 'Ằ' => 'Ằ', 'ằ' => 'ằ', 'Ẳ' => 'Ẳ', 'ẳ' => 'ẳ', 'Ẵ' => 'Ẵ', 'ẵ' => 'ẵ', 'Ặ' => 'Ặ', 'ặ' => 'ặ', 'Ẹ' => 'Ẹ', 'ẹ' => 'ẹ', 'Ẻ' => 'Ẻ', 'ẻ' => 'ẻ', 'Ẽ' => 'Ẽ', 'ẽ' => 'ẽ', 'Ế' => 'Ế', 'ế' => 'ế', 'Ề' => 'Ề', 'ề' => 'ề', 'Ể' => 'Ể', 'ể' => 'ể', 'Ễ' => 'Ễ', 'ễ' => 'ễ', 'Ệ' => 'Ệ', 'ệ' => 'ệ', 'Ỉ' => 'Ỉ', 'ỉ' => 'ỉ', 'Ị' => 'Ị', 'ị' => 'ị', 'Ọ' => 'Ọ', 'ọ' => 'ọ', 'Ỏ' => 'Ỏ', 'ỏ' => 'ỏ', 'Ố' => 'Ố', 'ố' => 'ố', 'Ồ' => 'Ồ', 'ồ' => 'ồ', 'Ổ' => 'Ổ', 'ổ' => 'ổ', 'Ỗ' => 'Ỗ', 'ỗ' => 'ỗ', 'Ộ' => 'Ộ', 'ộ' => 'ộ', 'Ớ' => 'Ớ', 'ớ' => 'ớ', 'Ờ' => 'Ờ', 'ờ' => 'ờ', 'Ở' => 'Ở', 'ở' => 'ở', 'Ỡ' => 'Ỡ', 'ỡ' => 'ỡ', 'Ợ' => 'Ợ', 'ợ' => 'ợ', 'Ụ' => 'Ụ', 'ụ' => 'ụ', 'Ủ' => 'Ủ', 'ủ' => 'ủ', 'Ứ' => 'Ứ', 'ứ' => 'ứ', 'Ừ' => 'Ừ', 'ừ' => 'ừ', 'Ử' => 'Ử', 'ử' => 'ử', 'Ữ' => 'Ữ', 'ữ' => 'ữ', 'Ự' => 'Ự', 'ự' => 'ự', 'Ỳ' => 'Ỳ', 'ỳ' => 'ỳ', 'Ỵ' => 'Ỵ', 'ỵ' => 'ỵ', 'Ỷ' => 'Ỷ', 'ỷ' => 'ỷ', 'Ỹ' => 'Ỹ', 'ỹ' => 'ỹ', 'ἀ' => 'ἀ', 'ἁ' => 'ἁ', 'ἂ' => 'ἂ', 'ἃ' => 'ἃ', 'ἄ' => 'ἄ', 'ἅ' => 'ἅ', 'ἆ' => 'ἆ', 'ἇ' => 'ἇ', 'Ἀ' => 'Ἀ', 'Ἁ' => 'Ἁ', 'Ἂ' => 'Ἂ', 'Ἃ' => 'Ἃ', 'Ἄ' => 'Ἄ', 'Ἅ' => 'Ἅ', 'Ἆ' => 'Ἆ', 'Ἇ' => 'Ἇ', 'ἐ' => 'ἐ', 'ἑ' => 'ἑ', 'ἒ' => 'ἒ', 'ἓ' => 'ἓ', 'ἔ' => 'ἔ', 'ἕ' => 'ἕ', 'Ἐ' => 'Ἐ', 'Ἑ' => 'Ἑ', 'Ἒ' => 'Ἒ', 'Ἓ' => 'Ἓ', 'Ἔ' => 'Ἔ', 'Ἕ' => 'Ἕ', 'ἠ' => 'ἠ', 'ἡ' => 'ἡ', 'ἢ' => 'ἢ', 'ἣ' => 'ἣ', 'ἤ' => 'ἤ', 'ἥ' => 'ἥ', 'ἦ' => 'ἦ', 'ἧ' => 'ἧ', 'Ἠ' => 'Ἠ', 'Ἡ' => 'Ἡ', 'Ἢ' => 'Ἢ', 'Ἣ' => 'Ἣ', 'Ἤ' => 'Ἤ', 'Ἥ' => 'Ἥ', 'Ἦ' => 'Ἦ', 'Ἧ' => 'Ἧ', 'ἰ' => 'ἰ', 'ἱ' => 'ἱ', 'ἲ' => 'ἲ', 'ἳ' => 'ἳ', 'ἴ' => 'ἴ', 'ἵ' => 'ἵ', 'ἶ' => 'ἶ', 'ἷ' => 'ἷ', 'Ἰ' => 'Ἰ', 'Ἱ' => 'Ἱ', 'Ἲ' => 'Ἲ', 'Ἳ' => 'Ἳ', 'Ἴ' => 'Ἴ', 'Ἵ' => 'Ἵ', 'Ἶ' => 'Ἶ', 'Ἷ' => 'Ἷ', 'ὀ' => 'ὀ', 'ὁ' => 'ὁ', 'ὂ' => 'ὂ', 'ὃ' => 'ὃ', 'ὄ' => 'ὄ', 'ὅ' => 'ὅ', 'Ὀ' => 'Ὀ', 'Ὁ' => 'Ὁ', 'Ὂ' => 'Ὂ', 'Ὃ' => 'Ὃ', 'Ὄ' => 'Ὄ', 'Ὅ' => 'Ὅ', 'ὐ' => 'ὐ', 'ὑ' => 'ὑ', 'ὒ' => 'ὒ', 'ὓ' => 'ὓ', 'ὔ' => 'ὔ', 'ὕ' => 'ὕ', 'ὖ' => 'ὖ', 'ὗ' => 'ὗ', 'Ὑ' => 'Ὑ', 'Ὓ' => 'Ὓ', 'Ὕ' => 'Ὕ', 'Ὗ' => 'Ὗ', 'ὠ' => 'ὠ', 'ὡ' => 'ὡ', 'ὢ' => 'ὢ', 'ὣ' => 'ὣ', 'ὤ' => 'ὤ', 'ὥ' => 'ὥ', 'ὦ' => 'ὦ', 'ὧ' => 'ὧ', 'Ὠ' => 'Ὠ', 'Ὡ' => 'Ὡ', 'Ὢ' => 'Ὢ', 'Ὣ' => 'Ὣ', 'Ὤ' => 'Ὤ', 'Ὥ' => 'Ὥ', 'Ὦ' => 'Ὦ', 'Ὧ' => 'Ὧ', 'ὰ' => 'ὰ', 'ὲ' => 'ὲ', 'ὴ' => 'ὴ', 'ὶ' => 'ὶ', 'ὸ' => 'ὸ', 'ὺ' => 'ὺ', 'ὼ' => 'ὼ', 'ᾀ' => 'ᾀ', 'ᾁ' => 'ᾁ', 'ᾂ' => 'ᾂ', 'ᾃ' => 'ᾃ', 'ᾄ' => 'ᾄ', 'ᾅ' => 'ᾅ', 'ᾆ' => 'ᾆ', 'ᾇ' => 'ᾇ', 'ᾈ' => 'ᾈ', 'ᾉ' => 'ᾉ', 'ᾊ' => 'ᾊ', 'ᾋ' => 'ᾋ', 'ᾌ' => 'ᾌ', 'ᾍ' => 'ᾍ', 'ᾎ' => 'ᾎ', 'ᾏ' => 'ᾏ', 'ᾐ' => 'ᾐ', 'ᾑ' => 'ᾑ', 'ᾒ' => 'ᾒ', 'ᾓ' => 'ᾓ', 'ᾔ' => 'ᾔ', 'ᾕ' => 'ᾕ', 'ᾖ' => 'ᾖ', 'ᾗ' => 'ᾗ', 'ᾘ' => 'ᾘ', 'ᾙ' => 'ᾙ', 'ᾚ' => 'ᾚ', 'ᾛ' => 'ᾛ', 'ᾜ' => 'ᾜ', 'ᾝ' => 'ᾝ', 'ᾞ' => 'ᾞ', 'ᾟ' => 'ᾟ', 'ᾠ' => 'ᾠ', 'ᾡ' => 'ᾡ', 'ᾢ' => 'ᾢ', 'ᾣ' => 'ᾣ', 'ᾤ' => 'ᾤ', 'ᾥ' => 'ᾥ', 'ᾦ' => 'ᾦ', 'ᾧ' => 'ᾧ', 'ᾨ' => 'ᾨ', 'ᾩ' => 'ᾩ', 'ᾪ' => 'ᾪ', 'ᾫ' => 'ᾫ', 'ᾬ' => 'ᾬ', 'ᾭ' => 'ᾭ', 'ᾮ' => 'ᾮ', 'ᾯ' => 'ᾯ', 'ᾰ' => 'ᾰ', 'ᾱ' => 'ᾱ', 'ᾲ' => 'ᾲ', 'ᾳ' => 'ᾳ', 'ᾴ' => 'ᾴ', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾷ', 'Ᾰ' => 'Ᾰ', 'Ᾱ' => 'Ᾱ', 'Ὰ' => 'Ὰ', 'ᾼ' => 'ᾼ', '῁' => '῁', 'ῂ' => 'ῂ', 'ῃ' => 'ῃ', 'ῄ' => 'ῄ', 'ῆ' => 'ῆ', 'ῇ' => 'ῇ', 'Ὲ' => 'Ὲ', 'Ὴ' => 'Ὴ', 'ῌ' => 'ῌ', '῍' => '῍', '῎' => '῎', '῏' => '῏', 'ῐ' => 'ῐ', 'ῑ' => 'ῑ', 'ῒ' => 'ῒ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'Ῐ' => 'Ῐ', 'Ῑ' => 'Ῑ', 'Ὶ' => 'Ὶ', '῝' => '῝', '῞' => '῞', '῟' => '῟', 'ῠ' => 'ῠ', 'ῡ' => 'ῡ', 'ῢ' => 'ῢ', 'ῤ' => 'ῤ', 'ῥ' => 'ῥ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'Ῠ' => 'Ῠ', 'Ῡ' => 'Ῡ', 'Ὺ' => 'Ὺ', 'Ῥ' => 'Ῥ', '῭' => '῭', 'ῲ' => 'ῲ', 'ῳ' => 'ῳ', 'ῴ' => 'ῴ', 'ῶ' => 'ῶ', 'ῷ' => 'ῷ', 'Ὸ' => 'Ὸ', 'Ὼ' => 'Ὼ', 'ῼ' => 'ῼ', '↚' => '↚', '↛' => '↛', '↮' => '↮', '⇍' => '⇍', '⇎' => '⇎', '⇏' => '⇏', '∄' => '∄', '∉' => '∉', '∌' => '∌', '∤' => '∤', '∦' => '∦', '≁' => '≁', '≄' => '≄', '≇' => '≇', '≉' => '≉', '≠' => '≠', '≢' => '≢', '≭' => '≭', '≮' => '≮', '≯' => '≯', '≰' => '≰', '≱' => '≱', '≴' => '≴', '≵' => '≵', '≸' => '≸', '≹' => '≹', '⊀' => '⊀', '⊁' => '⊁', '⊄' => '⊄', '⊅' => '⊅', '⊈' => '⊈', '⊉' => '⊉', '⊬' => '⊬', '⊭' => '⊭', '⊮' => '⊮', '⊯' => '⊯', '⋠' => '⋠', '⋡' => '⋡', '⋢' => '⋢', '⋣' => '⋣', '⋪' => '⋪', '⋫' => '⋫', '⋬' => '⋬', '⋭' => '⋭', 'が' => 'が', 'ぎ' => 'ぎ', 'ぐ' => 'ぐ', 'げ' => 'げ', 'ご' => 'ご', 'ざ' => 'ざ', 'じ' => 'じ', 'ず' => 'ず', 'ぜ' => 'ぜ', 'ぞ' => 'ぞ', 'だ' => 'だ', 'ぢ' => 'ぢ', 'づ' => 'づ', 'で' => 'で', 'ど' => 'ど', 'ば' => 'ば', 'ぱ' => 'ぱ', 'び' => 'び', 'ぴ' => 'ぴ', 'ぶ' => 'ぶ', 'ぷ' => 'ぷ', 'べ' => 'べ', 'ぺ' => 'ぺ', 'ぼ' => 'ぼ', 'ぽ' => 'ぽ', 'ゔ' => 'ゔ', 'ゞ' => 'ゞ', 'ガ' => 'ガ', 'ギ' => 'ギ', 'グ' => 'グ', 'ゲ' => 'ゲ', 'ゴ' => 'ゴ', 'ザ' => 'ザ', 'ジ' => 'ジ', 'ズ' => 'ズ', 'ゼ' => 'ゼ', 'ゾ' => 'ゾ', 'ダ' => 'ダ', 'ヂ' => 'ヂ', 'ヅ' => 'ヅ', 'デ' => 'デ', 'ド' => 'ド', 'バ' => 'バ', 'パ' => 'パ', 'ビ' => 'ビ', 'ピ' => 'ピ', 'ブ' => 'ブ', 'プ' => 'プ', 'ベ' => 'ベ', 'ペ' => 'ペ', 'ボ' => 'ボ', 'ポ' => 'ポ', 'ヴ' => 'ヴ', 'ヷ' => 'ヷ', 'ヸ' => 'ヸ', 'ヹ' => 'ヹ', 'ヺ' => 'ヺ', 'ヾ' => 'ヾ', '𑂚' => '𑂚', '𑂜' => '𑂜', '𑂫' => '𑂫', '𑄮' => '𑄮', '𑄯' => '𑄯', '𑍋' => '𑍋', '𑍌' => '𑍌', '𑒻' => '𑒻', '𑒼' => '𑒼', '𑒾' => '𑒾', '𑖺' => '𑖺', '𑖻' => '𑖻', '𑤸' => '𑤸', ); PK2A#]���%��Fvendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.phpnu�[���<?php class Normalizer extends Symfony\Polyfill\Intl\Normalizer\Normalizer { /** * @deprecated since ICU 56 and removed in PHP 8 */ public const NONE = 2; public const FORM_D = 4; public const FORM_KD = 8; public const FORM_C = 16; public const FORM_KC = 32; public const NFD = 4; public const NFKD = 8; public const NFC = 16; public const NFKC = 32; } PK2A#]��H,,/vendor/symfony/polyfill-intl-normalizer/LICENSEnu�[���Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]#p���5vendor/symfony/polyfill-intl-normalizer/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Intl\Normalizer as p; if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!function_exists('normalizer_is_normalized')) { function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); } } if (!function_exists('normalizer_normalize')) { function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); } } PK2A#]��]��vendor/symfony/yaml/Escaper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; /** * Escaper encapsulates escaping rules for single and double-quoted * YAML strings. * * @author Matthew Lewinski <matthew@lewinski.org> * * @internal */ class Escaper { // Characters that would cause a dumped string to require double quoting. public const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\x7f|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; // Mapping arrays for escaping a double quoted string. The backslash is // first to ensure proper escaping because str_replace operates iteratively // on the input arrays. This ordering of the characters avoids the use of strtr, // which performs more slowly. private const ESCAPEES = ['\\', '\\\\', '\\"', '"', "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", "\x7f", "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", ]; private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"', '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', '\\x7f', '\\N', '\\_', '\\L', '\\P', ]; /** * Determines if a PHP value would require double quoting in YAML. * * @param string $value A PHP value */ public static function requiresDoubleQuoting(string $value): bool { return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); } /** * Escapes and surrounds a PHP value with double quotes. * * @param string $value A PHP value */ public static function escapeWithDoubleQuotes(string $value): string { return sprintf('"%s"', str_replace(self::ESCAPEES, self::ESCAPED, $value)); } /** * Determines if a PHP value would require single quoting in YAML. * * @param string $value A PHP value */ public static function requiresSingleQuoting(string $value): bool { // Determines if a PHP value is entirely composed of a value that would // require single quoting in YAML. if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) { return true; } // Determines if the PHP value contains any single characters that would // cause it to require single quoting in YAML. return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` \p{Zs}]/xu', $value); } /** * Escapes and surrounds a PHP value with single quotes. * * @param string $value A PHP value */ public static function escapeWithSingleQuotes(string $value): string { return sprintf("'%s'", str_replace('\'', '\'\'', $value)); } } PK2A#] t�qqvendor/symfony/yaml/Yaml.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\ParseException; /** * Yaml offers convenience methods to load and dump YAML. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class Yaml { public const DUMP_OBJECT = 1; public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; public const PARSE_OBJECT = 4; public const PARSE_OBJECT_FOR_MAP = 8; public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; public const PARSE_DATETIME = 32; public const DUMP_OBJECT_AS_MAP = 64; public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; public const PARSE_CONSTANT = 256; public const PARSE_CUSTOM_TAGS = 512; public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; public const DUMP_NULL_AS_TILDE = 2048; /** * Parses a YAML file into a PHP value. * * Usage: * * $array = Yaml::parseFile('config.yml'); * print_r($array); * * @param string $filename The path to the YAML file to be parsed * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior * * @return mixed * * @throws ParseException If the file could not be read or the YAML is not valid */ public static function parseFile(string $filename, int $flags = 0) { $yaml = new Parser(); return $yaml->parseFile($filename, $flags); } /** * Parses YAML into a PHP value. * * Usage: * <code> * $array = Yaml::parse(file_get_contents('config.yml')); * print_r($array); * </code> * * @param string $input A string containing YAML * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior * * @return mixed * * @throws ParseException If the YAML is not valid */ public static function parse(string $input, int $flags = 0) { $yaml = new Parser(); return $yaml->parse($input, $flags); } /** * Dumps a PHP value to a YAML string. * * The dump method, when supplied with an array, will do its best * to convert the array into friendly YAML. * * @param mixed $input The PHP value * @param int $inline The level where you switch to inline YAML * @param int $indent The amount of spaces to use for indentation of nested nodes * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string */ public static function dump($input, int $inline = 2, int $indent = 4, int $flags = 0): string { $yaml = new Dumper($indent); return $yaml->dump($input, $inline, 0, $flags); } } PK2A#]!8����vendor/symfony/yaml/Dumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Tag\TaggedValue; /** * Dumper dumps PHP variables to YAML strings. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class Dumper { /** * The amount of spaces to use for indentation of nested nodes. * * @var int */ protected $indentation; public function __construct(int $indentation = 4) { if ($indentation < 1) { throw new \InvalidArgumentException('The indentation must be greater than zero.'); } $this->indentation = $indentation; } /** * Dumps a PHP value to YAML. * * @param mixed $input The PHP value * @param int $inline The level where you switch to inline YAML * @param int $indent The level of indentation (used internally) * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string */ public function dump($input, int $inline = 0, int $indent = 0, int $flags = 0): string { $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; $dumpObjectAsInlineMap = true; if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { $dumpObjectAsInlineMap = empty((array) $input); } if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) { $output .= $prefix.Inline::dump($input, $flags); } elseif ($input instanceof TaggedValue) { $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); } else { $dumpAsMap = Inline::isHash($input); foreach ($input as $key => $value) { if ('' !== $output && "\n" !== $output[-1]) { $output .= "\n"; } if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && false !== strpos($value, "\n") && false === strpos($value, "\r")) { $blockIndentationIndicator = $this->getBlockIndentationIndicator($value); if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) { $blockChompingIndicator = '+'; } elseif ("\n" === $value[-1]) { $blockChompingIndicator = ''; } else { $blockChompingIndicator = '-'; } $output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator); foreach (explode("\n", $value) as $row) { if ('' === $row) { $output .= "\n"; } else { $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); } } continue; } if ($value instanceof TaggedValue) { $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag()); if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); $output .= sprintf(' |%s', $blockIndentationIndicator); foreach (explode("\n", $value->getValue()) as $row) { $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); } continue; } if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; } else { $output .= "\n"; $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); } continue; } $dumpObjectAsInlineMap = true; if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) { $dumpObjectAsInlineMap = empty((array) $value); } $willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value); $output .= sprintf('%s%s%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $willBeInlined ? ' ' : "\n", $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) ).($willBeInlined ? "\n" : ''); } } return $output; } private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string { $output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag()); if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); $output .= sprintf(' |%s', $blockIndentationIndicator); foreach (explode("\n", $value->getValue()) as $row) { $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); } return $output; } if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; } return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags); } private function getBlockIndentationIndicator(string $value): string { $lines = explode("\n", $value); // If the first line (that is neither empty nor contains only spaces) // starts with a space character, the spec requires a block indentation indicator // http://www.yaml.org/spec/1.2/spec.html#id2793979 foreach ($lines as $line) { if ('' !== trim($line, ' ')) { return (' ' === substr($line, 0, 1)) ? (string) $this->indentation : ''; } } return ''; } } PK2A#]n��%��'vendor/symfony/yaml/Tag/TaggedValue.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Tag; /** * @author Nicolas Grekas <p@tchwork.com> * @author Guilhem N. <egetick@gmail.com> */ final class TaggedValue { private $tag; private $value; public function __construct(string $tag, $value) { $this->tag = $tag; $this->value = $value; } public function getTag(): string { return $this->tag; } public function getValue() { return $this->value; } } PK2A#]�|�_�_�vendor/symfony/yaml/Parser.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Tag\TaggedValue; /** * Parser parses YAML strings to convert them to PHP arrays. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class Parser { public const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)'; public const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?'; public const REFERENCE_PATTERN = '#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u'; private $filename; private $offset = 0; private $numberOfParsedLines = 0; private $totalNumberOfLines; private $lines = []; private $currentLineNb = -1; private $currentLine = ''; private $refs = []; private $skippedLineNumbers = []; private $locallySkippedLineNumbers = []; private $refsBeingParsed = []; /** * Parses a YAML file into a PHP value. * * @param string $filename The path to the YAML file to be parsed * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * * @return mixed * * @throws ParseException If the file could not be read or the YAML is not valid */ public function parseFile(string $filename, int $flags = 0) { if (!is_file($filename)) { throw new ParseException(sprintf('File "%s" does not exist.', $filename)); } if (!is_readable($filename)) { throw new ParseException(sprintf('File "%s" cannot be read.', $filename)); } $this->filename = $filename; try { return $this->parse(file_get_contents($filename), $flags); } finally { $this->filename = null; } } /** * Parses a YAML string to a PHP value. * * @param string $value A YAML string * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * * @return mixed * * @throws ParseException If the YAML is not valid */ public function parse(string $value, int $flags = 0) { if (false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename); } $this->refs = []; $mbEncoding = null; if (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('UTF-8'); } try { $data = $this->doParse($value, $flags); } finally { if (null !== $mbEncoding) { mb_internal_encoding($mbEncoding); } $this->refsBeingParsed = []; $this->offset = 0; $this->lines = []; $this->currentLine = ''; $this->numberOfParsedLines = 0; $this->refs = []; $this->skippedLineNumbers = []; $this->locallySkippedLineNumbers = []; $this->totalNumberOfLines = null; } return $data; } private function doParse(string $value, int $flags) { $this->currentLineNb = -1; $this->currentLine = ''; $value = $this->cleanup($value); $this->lines = explode("\n", $value); $this->numberOfParsedLines = \count($this->lines); $this->locallySkippedLineNumbers = []; if (null === $this->totalNumberOfLines) { $this->totalNumberOfLines = $this->numberOfParsedLines; } if (!$this->moveToNextLine()) { return null; } $data = []; $context = null; $allowOverwrite = false; while ($this->isCurrentLineEmpty()) { if (!$this->moveToNextLine()) { return null; } } // Resolves the tag and returns if end of the document if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) { return new TaggedValue($tag, ''); } do { if ($this->isCurrentLineEmpty()) { continue; } // tab? if ("\t" === $this->currentLine[0]) { throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); $isRef = $mergeNode = false; if ('-' === $this->currentLine[0] && self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } $context = 'sequence'; if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) { throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // array if (isset($values['value']) && 0 === strpos(ltrim($values['value'], ' '), '-')) { // Inline first child $currentLineNumber = $this->getRealCurrentLineNb(); $sequenceIndentation = \strlen($values['leadspaces']) + 1; $sequenceYaml = substr($this->currentLine, $sequenceIndentation); $sequenceYaml .= "\n".$this->getNextEmbedBlock($sequenceIndentation, true); $data[] = $this->parseBlock($currentLineNumber, rtrim($sequenceYaml), $flags); } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true) ?? '', $flags); } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { $data[] = new TaggedValue( $subTag, $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags) ); } else { if ( isset($values['leadspaces']) && ( '!' === $values['value'][0] || self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches) ) ) { // this is a compact notation element, add to next block and parse $block = $values['value']; if ($this->isNextLineIndented()) { $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1); } $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); } else { $data[] = $this->parseValue($values['value'], $flags, $context); } } if ($isRef) { $this->refs[$isRef] = end($data); array_pop($this->refsBeingParsed); } } elseif ( self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(( |\t)++(?P<value>.+))?$#u', rtrim($this->currentLine), $values) && (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"])) ) { if ($context && 'sequence' == $context) { throw new ParseException('You cannot define a mapping item when in a sequence.', $this->currentLineNb + 1, $this->currentLine, $this->filename); } $context = 'mapping'; try { $key = Inline::parseScalar($values['key']); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } if (!\is_string($key) && !\is_int($key)) { throw new ParseException((is_numeric($key) ? 'Numeric' : 'Non-string').' keys are not supported. Quote your evaluable mapping keys instead.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // Convert float keys to strings, to avoid being converted to integers by PHP if (\is_float($key)) { $key = (string) $key; } if ('<<' === $key && (!isset($values['value']) || '&' !== $values['value'][0] || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) { $mergeNode = true; $allowOverwrite = true; if (isset($values['value'][0]) && '*' === $values['value'][0]) { $refName = substr(rtrim($values['value']), 1); if (!\array_key_exists($refName, $this->refs)) { if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } $refValue = $this->refs[$refName]; if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) { $refValue = (array) $refValue; } if (!\is_array($refValue)) { throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } $data += $refValue; // array union } else { if (isset($values['value']) && '' !== $values['value']) { $value = $values['value']; } else { $value = $this->getNextEmbedBlock(); } $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) { $parsed = (array) $parsed; } if (!\is_array($parsed)) { throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } if (isset($parsed[0])) { // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier // in the sequence override keys specified in later mapping nodes. foreach ($parsed as $parsedItem) { if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) { $parsedItem = (array) $parsedItem; } if (!\is_array($parsedItem)) { throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename); } $data += $parsedItem; // array union } } else { // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the // current mapping, unless the key already exists in it. $data += $parsed; // array union } } } elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } $subTag = null; if ($mergeNode) { // Merge keys } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { // hash // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. if ($allowOverwrite || !isset($data[$key])) { if (null !== $subTag) { $data[$key] = new TaggedValue($subTag, ''); } else { $data[$key] = null; } } else { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); } } else { // remember the parsed line number here in case we need it to provide some contexts in error messages below $realCurrentLineNbKey = $this->getRealCurrentLineNb(); $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); if ('<<' === $key) { $this->refs[$refMatches['ref']] = $value; if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) { $value = (array) $value; } $data += $value; } elseif ($allowOverwrite || !isset($data[$key])) { // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. if (null !== $subTag) { $data[$key] = new TaggedValue($subTag, $value); } else { $data[$key] = $value; } } else { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine); } } } else { $value = $this->parseValue(rtrim($values['value']), $flags, $context); // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. if ($allowOverwrite || !isset($data[$key])) { $data[$key] = $value; } else { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); } } if ($isRef) { $this->refs[$isRef] = $data[$key]; array_pop($this->refsBeingParsed); } } elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) { if (null !== $context) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } try { return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } } elseif ('{' === $this->currentLine[0]) { if (null !== $context) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } try { $parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs); while ($this->moveToNextLine()) { if (!$this->isCurrentLineEmpty()) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } return $parsedMapping; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } } elseif ('[' === $this->currentLine[0]) { if (null !== $context) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } try { $parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs); while ($this->moveToNextLine()) { if (!$this->isCurrentLineEmpty()) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } return $parsedSequence; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } } else { // multiple documents are not supported if ('---' === $this->currentLine) { throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename); } if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) { throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } // 1-liner optionally followed by newline(s) if (\is_string($value) && $this->lines[0] === trim($value)) { try { $value = Inline::parse($this->lines[0], $flags, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } return $value; } // try to parse the value as a multi-line string as a last resort if (0 === $this->currentLineNb) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; $value = ''; foreach ($this->lines as $line) { $trimmedLine = trim($line); if ('#' === ($trimmedLine[0] ?? '')) { continue; } // If the indentation is not consistent at offset 0, it is to be considered as a ParseError if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } if (false !== strpos($line, ': ')) { throw new ParseException('Mapping values are not allowed in multi-line blocks.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } if ('' === $trimmedLine) { $value .= "\n"; } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { $value .= ' '; } if ('' !== $trimmedLine && '\\' === substr($line, -1)) { $value .= ltrim(substr($line, 0, -1)); } elseif ('' !== $trimmedLine) { $value .= $trimmedLine; } if ('' === $trimmedLine) { $previousLineWasNewline = true; $previousLineWasTerminatedWithBackslash = false; } elseif ('\\' === substr($line, -1)) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = true; } else { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; } } try { return Inline::parse(trim($value)); } catch (ParseException $e) { // fall-through to the ParseException thrown below } } throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } while ($this->moveToNextLine()); if (null !== $tag) { $data = new TaggedValue($tag, $data); } if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && 'mapping' === $context && !\is_object($data)) { $object = new \stdClass(); foreach ($data as $key => $value) { $object->$key = $value; } $data = $object; } return empty($data) ? null : $data; } private function parseBlock(int $offset, string $yaml, int $flags) { $skippedLineNumbers = $this->skippedLineNumbers; foreach ($this->locallySkippedLineNumbers as $lineNumber) { if ($lineNumber < $offset) { continue; } $skippedLineNumbers[] = $lineNumber; } $parser = new self(); $parser->offset = $offset; $parser->totalNumberOfLines = $this->totalNumberOfLines; $parser->skippedLineNumbers = $skippedLineNumbers; $parser->refs = &$this->refs; $parser->refsBeingParsed = $this->refsBeingParsed; return $parser->doParse($yaml, $flags); } /** * Returns the current line number (takes the offset into account). * * @internal */ public function getRealCurrentLineNb(): int { $realCurrentLineNumber = $this->currentLineNb + $this->offset; foreach ($this->skippedLineNumbers as $skippedLineNumber) { if ($skippedLineNumber > $realCurrentLineNumber) { break; } ++$realCurrentLineNumber; } return $realCurrentLineNumber; } /** * Returns the current line indentation. */ private function getCurrentLineIndentation(): int { if (' ' !== ($this->currentLine[0] ?? '')) { return 0; } return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' ')); } /** * Returns the next embed block of YAML. * * @param int|null $indentation The indent level at which the block is to be read, or null for default * @param bool $inSequence True if the enclosing data structure is a sequence * * @throws ParseException When indentation problem are detected */ private function getNextEmbedBlock(int $indentation = null, bool $inSequence = false): string { $oldLineIndentation = $this->getCurrentLineIndentation(); if (!$this->moveToNextLine()) { return ''; } if (null === $indentation) { $newIndent = null; $movements = 0; do { $EOF = false; // empty and comment-like lines do not influence the indentation depth if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { $EOF = !$this->moveToNextLine(); if (!$EOF) { ++$movements; } } else { $newIndent = $this->getCurrentLineIndentation(); } } while (!$EOF && null === $newIndent); for ($i = 0; $i < $movements; ++$i) { $this->moveToPreviousLine(); } $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } else { $newIndent = $indentation; } $data = []; if ($this->getCurrentLineIndentation() >= $newIndent) { $data[] = substr($this->currentLine, $newIndent ?? 0); } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { $data[] = $this->currentLine; } else { $this->moveToPreviousLine(); return ''; } if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { // the previous line contained a dash but no item content, this line is a sequence item with the same indentation // and therefore no nested list or mapping $this->moveToPreviousLine(); return ''; } $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); $isItComment = $this->isCurrentLineComment(); while ($this->moveToNextLine()) { if ($isItComment && !$isItUnindentedCollection) { $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); $isItComment = $this->isCurrentLineComment(); } $indent = $this->getCurrentLineIndentation(); if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { $this->moveToPreviousLine(); break; } if ($this->isCurrentLineBlank()) { $data[] = substr($this->currentLine, $newIndent); continue; } if ($indent >= $newIndent) { $data[] = substr($this->currentLine, $newIndent); } elseif ($this->isCurrentLineComment()) { $data[] = $this->currentLine; } elseif (0 == $indent) { $this->moveToPreviousLine(); break; } else { throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } } return implode("\n", $data); } private function hasMoreLines(): bool { return (\count($this->lines) - 1) > $this->currentLineNb; } /** * Moves the parser to the next line. */ private function moveToNextLine(): bool { if ($this->currentLineNb >= $this->numberOfParsedLines - 1) { return false; } $this->currentLine = $this->lines[++$this->currentLineNb]; return true; } /** * Moves the parser to the previous line. */ private function moveToPreviousLine(): bool { if ($this->currentLineNb < 1) { return false; } $this->currentLine = $this->lines[--$this->currentLineNb]; return true; } /** * Parses a YAML value. * * @param string $value A YAML value * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * @param string $context The parser context (either sequence or mapping) * * @return mixed * * @throws ParseException When reference does not exist */ private function parseValue(string $value, int $flags, string $context) { if (0 === strpos($value, '*')) { if (false !== $pos = strpos($value, '#')) { $value = substr($value, 1, $pos - 2); } else { $value = substr($value, 1); } if (!\array_key_exists($value, $this->refs)) { if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } return $this->refs[$value]; } if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { $modifiers = $matches['modifiers'] ?? ''; $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers)); if ('' !== $matches['tag'] && '!' !== $matches['tag']) { if ('!!binary' === $matches['tag']) { return Inline::evaluateBinaryScalar($data); } return new TaggedValue(substr($matches['tag'], 1), $data); } return $data; } try { if ('' !== $value && '{' === $value[0]) { $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs); } elseif ('' !== $value && '[' === $value[0]) { $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs); } switch ($value[0] ?? '') { case '"': case "'": $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); $parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs); if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A', '', substr($this->currentLine, $cursor))) { throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor))); } return $parsedValue; default: $lines = []; while ($this->moveToNextLine()) { // unquoted strings end before the first unindented line if (0 === $this->getCurrentLineIndentation()) { $this->moveToPreviousLine(); break; } $lines[] = trim($this->currentLine); } for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { if ('' === $lines[$i]) { $value .= "\n"; $previousLineBlank = true; } elseif ($previousLineBlank) { $value .= $lines[$i]; $previousLineBlank = false; } else { $value .= ' '.$lines[$i]; $previousLineBlank = false; } } Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); $parsedValue = Inline::parse($value, $flags, $this->refs); if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); } return $parsedValue; } } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } } /** * Parses a block scalar. * * @param string $style The style indicator that was used to begin this block scalar (| or >) * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) * @param int $indentation The indentation indicator that was used to begin this block scalar */ private function parseBlockScalar(string $style, string $chomping = '', int $indentation = 0): string { $notEOF = $this->moveToNextLine(); if (!$notEOF) { return ''; } $isCurrentLineBlank = $this->isCurrentLineBlank(); $blockLines = []; // leading blank lines are consumed before determining indentation while ($notEOF && $isCurrentLineBlank) { // newline only if not EOF if ($notEOF = $this->moveToNextLine()) { $blockLines[] = ''; $isCurrentLineBlank = $this->isCurrentLineBlank(); } } // determine indentation if not specified if (0 === $indentation) { $currentLineLength = \strlen($this->currentLine); for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) { ++$indentation; } } if ($indentation > 0) { $pattern = sprintf('/^ {%d}(.*)$/', $indentation); while ( $notEOF && ( $isCurrentLineBlank || self::preg_match($pattern, $this->currentLine, $matches) ) ) { if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) { $blockLines[] = substr($this->currentLine, $indentation); } elseif ($isCurrentLineBlank) { $blockLines[] = ''; } else { $blockLines[] = $matches[1]; } // newline only if not EOF if ($notEOF = $this->moveToNextLine()) { $isCurrentLineBlank = $this->isCurrentLineBlank(); } } } elseif ($notEOF) { $blockLines[] = ''; } if ($notEOF) { $blockLines[] = ''; $this->moveToPreviousLine(); } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { $blockLines[] = ''; } // folded style if ('>' === $style) { $text = ''; $previousLineIndented = false; $previousLineBlank = false; for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) { if ('' === $blockLines[$i]) { $text .= "\n"; $previousLineIndented = false; $previousLineBlank = true; } elseif (' ' === $blockLines[$i][0]) { $text .= "\n".$blockLines[$i]; $previousLineIndented = true; $previousLineBlank = false; } elseif ($previousLineIndented) { $text .= "\n".$blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } elseif ($previousLineBlank || 0 === $i) { $text .= $blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } else { $text .= ' '.$blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } } } else { $text = implode("\n", $blockLines); } // deal with trailing newlines if ('' === $chomping) { $text = preg_replace('/\n+$/', "\n", $text); } elseif ('-' === $chomping) { $text = preg_replace('/\n+$/', '', $text); } return $text; } /** * Returns true if the next line is indented. */ private function isNextLineIndented(): bool { $currentIndentation = $this->getCurrentLineIndentation(); $movements = 0; do { $EOF = !$this->moveToNextLine(); if (!$EOF) { ++$movements; } } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); if ($EOF) { return false; } $ret = $this->getCurrentLineIndentation() > $currentIndentation; for ($i = 0; $i < $movements; ++$i) { $this->moveToPreviousLine(); } return $ret; } /** * Returns true if the current line is blank or if it is a comment line. */ private function isCurrentLineEmpty(): bool { return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); } /** * Returns true if the current line is blank. */ private function isCurrentLineBlank(): bool { return '' === $this->currentLine || '' === trim($this->currentLine, ' '); } /** * Returns true if the current line is a comment line. */ private function isCurrentLineComment(): bool { // checking explicitly the first char of the trim is faster than loops or strpos $ltrimmedLine = '' !== $this->currentLine && ' ' === $this->currentLine[0] ? ltrim($this->currentLine, ' ') : $this->currentLine; return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; } private function isCurrentLineLastLineInDocument(): bool { return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); } /** * Cleanups a YAML string to be parsed. * * @param string $value The input YAML string */ private function cleanup(string $value): string { $value = str_replace(["\r\n", "\r"], "\n", $value); // strip YAML header $count = 0; $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); $this->offset += $count; // remove leading comments $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); if (1 === $count) { // items have been removed, update the offset $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; } // remove start of the document marker (---) $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); if (1 === $count) { // items have been removed, update the offset $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; // remove end of the document marker (...) $value = preg_replace('#\.\.\.\s*$#', '', $value); } return $value; } /** * Returns true if the next line starts unindented collection. */ private function isNextLineUnIndentedCollection(): bool { $currentIndentation = $this->getCurrentLineIndentation(); $movements = 0; do { $EOF = !$this->moveToNextLine(); if (!$EOF) { ++$movements; } } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); if ($EOF) { return false; } $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); for ($i = 0; $i < $movements; ++$i) { $this->moveToPreviousLine(); } return $ret; } /** * Returns true if the string is un-indented collection item. */ private function isStringUnIndentedCollectionItem(): bool { return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } /** * A local wrapper for "preg_match" which will throw a ParseException if there * is an internal error in the PCRE engine. * * This avoids us needing to check for "false" every time PCRE is used * in the YAML engine * * @throws ParseException on a PCRE internal error * * @see preg_last_error() * * @internal */ public static function preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int { if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { switch (preg_last_error()) { case \PREG_INTERNAL_ERROR: $error = 'Internal PCRE error.'; break; case \PREG_BACKTRACK_LIMIT_ERROR: $error = 'pcre.backtrack_limit reached.'; break; case \PREG_RECURSION_LIMIT_ERROR: $error = 'pcre.recursion_limit reached.'; break; case \PREG_BAD_UTF8_ERROR: $error = 'Malformed UTF-8 data.'; break; case \PREG_BAD_UTF8_OFFSET_ERROR: $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; break; default: $error = 'Error.'; } throw new ParseException($error); } return $ret; } /** * Trim the tag on top of the value. * * Prevent values such as "!foo {quz: bar}" to be considered as * a mapping block. */ private function trimTag(string $value): string { if ('!' === $value[0]) { return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); } return $value; } private function getLineTag(string $value, int $flags, bool $nextLineCheck = true): ?string { if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) { return null; } if ($nextLineCheck && !$this->isNextLineIndented()) { return null; } $tag = substr($matches['tag'], 1); // Built-in tags if ($tag && '!' === $tag[0]) { throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename); } if (Yaml::PARSE_CUSTOM_TAGS & $flags) { return $tag; } throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename); } private function lexInlineQuotedString(int &$cursor = 0): string { $quotation = $this->currentLine[$cursor]; $value = $quotation; ++$cursor; $previousLineWasNewline = true; $previousLineWasTerminatedWithBackslash = false; $lineNumber = 0; do { if (++$lineNumber > 1) { $cursor += strspn($this->currentLine, ' ', $cursor); } if ($this->isCurrentLineBlank()) { $value .= "\n"; } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { $value .= ' '; } for (; \strlen($this->currentLine) > $cursor; ++$cursor) { switch ($this->currentLine[$cursor]) { case '\\': if ("'" === $quotation) { $value .= '\\'; } elseif (isset($this->currentLine[++$cursor])) { $value .= '\\'.$this->currentLine[$cursor]; } break; case $quotation: ++$cursor; if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) { $value .= "''"; break; } return $value.$quotation; default: $value .= $this->currentLine[$cursor]; } } if ($this->isCurrentLineBlank()) { $previousLineWasNewline = true; $previousLineWasTerminatedWithBackslash = false; } elseif ('\\' === $this->currentLine[-1]) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = true; } else { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; } if ($this->hasMoreLines()) { $cursor = 0; } } while ($this->moveToNextLine()); throw new ParseException('Malformed inline YAML string.'); } private function lexUnquotedString(int &$cursor): string { $offset = $cursor; $cursor += strcspn($this->currentLine, '[]{},: ', $cursor); if ($cursor === $offset) { throw new ParseException('Malformed unquoted YAML string.'); } return substr($this->currentLine, $offset, $cursor - $offset); } private function lexInlineMapping(int &$cursor = 0): string { return $this->lexInlineStructure($cursor, '}'); } private function lexInlineSequence(int &$cursor = 0): string { return $this->lexInlineStructure($cursor, ']'); } private function lexInlineStructure(int &$cursor, string $closingTag): string { $value = $this->currentLine[$cursor]; ++$cursor; do { $this->consumeWhitespaces($cursor); while (isset($this->currentLine[$cursor])) { switch ($this->currentLine[$cursor]) { case '"': case "'": $value .= $this->lexInlineQuotedString($cursor); break; case ':': case ',': $value .= $this->currentLine[$cursor]; ++$cursor; break; case '{': $value .= $this->lexInlineMapping($cursor); break; case '[': $value .= $this->lexInlineSequence($cursor); break; case $closingTag: $value .= $this->currentLine[$cursor]; ++$cursor; return $value; case '#': break 2; default: $value .= $this->lexUnquotedString($cursor); } if ($this->consumeWhitespaces($cursor)) { $value .= ' '; } } if ($this->hasMoreLines()) { $cursor = 0; } } while ($this->moveToNextLine()); throw new ParseException('Malformed inline YAML string.'); } private function consumeWhitespaces(int &$cursor): bool { $whitespacesConsumed = 0; do { $whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor); $whitespacesConsumed += $whitespaceOnlyTokenLength; $cursor += $whitespaceOnlyTokenLength; if (isset($this->currentLine[$cursor])) { return 0 < $whitespacesConsumed; } if ($this->hasMoreLines()) { $cursor = 0; } } while ($this->moveToNextLine()); return 0 < $whitespacesConsumed; } } PK2A#]���U,,vendor/symfony/yaml/LICENSEnu�[���Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]����+vendor/symfony/yaml/Resources/bin/yaml-lintnu�[���#!/usr/bin/env php <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== \PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } /** * Runs the Yaml lint command. * * @author Jan Schädlich <jan.schaedlich@sensiolabs.de> */ use Symfony\Component\Console\Application; use Symfony\Component\Yaml\Command\LintCommand; function includeIfExists(string $file): bool { return file_exists($file) && include $file; } if ( !includeIfExists(__DIR__ . '/../../../../autoload.php') && !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') ) { fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); exit(1); } if (!class_exists(Application::class)) { fwrite(STDERR, 'You need the "symfony/console" component in order to run the Yaml linter.'.PHP_EOL); exit(1); } (new Application())->add($command = new LintCommand()) ->getApplication() ->setDefaultCommand($command->getName(), true) ->run() ; PK2A#]�&��'�'+vendor/symfony/yaml/Command/LintCommand.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Command; use Symfony\Component\Console\CI\GithubActionReporter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Yaml; /** * Validates YAML files syntax and outputs encountered errors. * * @author Grégoire Pineau <lyrixx@lyrixx.info> * @author Robin Chalas <robin.chalas@gmail.com> */ class LintCommand extends Command { protected static $defaultName = 'lint:yaml'; protected static $defaultDescription = 'Lint a YAML file and outputs encountered errors'; private $parser; private $format; private $displayCorrectFiles; private $directoryIteratorProvider; private $isReadableProvider; public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null) { parent::__construct($name); $this->directoryIteratorProvider = $directoryIteratorProvider; $this->isReadableProvider = $isReadableProvider; } /** * {@inheritdoc} */ protected function configure() { $this ->setDescription(self::$defaultDescription) ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format') ->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude') ->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null) ->setHelp(<<<EOF The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT the first encountered syntax error. You can validates YAML contents passed from STDIN: <info>cat filename | php %command.full_name% -</info> You can also validate the syntax of a file: <info>php %command.full_name% filename</info> Or of a whole directory: <info>php %command.full_name% dirname</info> <info>php %command.full_name% dirname --format=json</info> You can also exclude one or more specific files: <info>php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml"</info> EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); $filenames = (array) $input->getArgument('filename'); $excludes = $input->getOption('exclude'); $this->format = $input->getOption('format'); $flags = $input->getOption('parse-tags'); if ('github' === $this->format && !class_exists(GithubActionReporter::class)) { throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.'); } if (null === $this->format) { // Autodetect format according to CI environment $this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt'; } $flags = $flags ? Yaml::PARSE_CUSTOM_TAGS : 0; $this->displayCorrectFiles = $output->isVerbose(); if (['-'] === $filenames) { return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]); } if (!$filenames) { throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); } $filesInfo = []; foreach ($filenames as $filename) { if (!$this->isReadable($filename)) { throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); } foreach ($this->getFiles($filename) as $file) { if (!\in_array($file->getPathname(), $excludes, true)) { $filesInfo[] = $this->validate(file_get_contents($file), $flags, $file); } } } return $this->display($io, $filesInfo); } private function validate(string $content, int $flags, string $file = null) { $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { if (\E_USER_DEPRECATED === $level) { throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1); } return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; }); try { $this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags); } catch (ParseException $e) { return ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage()]; } finally { restore_error_handler(); } return ['file' => $file, 'valid' => true]; } private function display(SymfonyStyle $io, array $files): int { switch ($this->format) { case 'txt': return $this->displayTxt($io, $files); case 'json': return $this->displayJson($io, $files); case 'github': return $this->displayTxt($io, $files, true); default: throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); } } private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false): int { $countFiles = \count($filesInfo); $erroredFiles = 0; $suggestTagOption = false; if ($errorAsGithubAnnotations) { $githubReporter = new GithubActionReporter($io); } foreach ($filesInfo as $info) { if ($info['valid'] && $this->displayCorrectFiles) { $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); } elseif (!$info['valid']) { ++$erroredFiles; $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); $io->text(sprintf('<error> >> %s</error>', $info['message'])); if (false !== strpos($info['message'], 'PARSE_CUSTOM_TAGS')) { $suggestTagOption = true; } if ($errorAsGithubAnnotations) { $githubReporter->error($info['message'], $info['file'] ?? 'php://stdin', $info['line']); } } } if (0 === $erroredFiles) { $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles)); } else { $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : '')); } return min($erroredFiles, 1); } private function displayJson(SymfonyStyle $io, array $filesInfo): int { $errors = 0; array_walk($filesInfo, function (&$v) use (&$errors) { $v['file'] = (string) $v['file']; if (!$v['valid']) { ++$errors; } if (isset($v['message']) && false !== strpos($v['message'], 'PARSE_CUSTOM_TAGS')) { $v['message'] .= ' Use the --parse-tags option if you want parse custom tags.'; } }); $io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); return min($errors, 1); } private function getFiles(string $fileOrDirectory): iterable { if (is_file($fileOrDirectory)) { yield new \SplFileInfo($fileOrDirectory); return; } foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { if (!\in_array($file->getExtension(), ['yml', 'yaml'])) { continue; } yield $file; } } private function getParser(): Parser { if (!$this->parser) { $this->parser = new Parser(); } return $this->parser; } private function getDirectoryIterator(string $directory): iterable { $default = function ($directory) { return new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY ); }; if (null !== $this->directoryIteratorProvider) { return ($this->directoryIteratorProvider)($directory, $default); } return $default($directory); } private function isReadable(string $fileOrDirectory): bool { $default = function ($fileOrDirectory) { return is_readable($fileOrDirectory); }; if (null !== $this->isReadableProvider) { return ($this->isReadableProvider)($fileOrDirectory, $default); } return $default($fileOrDirectory); } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues(['txt', 'json', 'github']); } } } PK2A#]�,���!vendor/symfony/yaml/Unescaper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\ParseException; /** * Unescaper encapsulates unescaping rules for single and double-quoted * YAML strings. * * @author Matthew Lewinski <matthew@lewinski.org> * * @internal */ class Unescaper { /** * Regex fragment that matches an escaped character in a double quoted string. */ public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; /** * Unescapes a single quoted string. * * @param string $value A single quoted string */ public function unescapeSingleQuotedString(string $value): string { return str_replace('\'\'', '\'', $value); } /** * Unescapes a double quoted string. * * @param string $value A double quoted string */ public function unescapeDoubleQuotedString(string $value): string { $callback = function ($match) { return $this->unescapeCharacter($match[0]); }; // evaluate the string return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); } /** * Unescapes a character that was found in a double-quoted string. * * @param string $value An escaped character */ private function unescapeCharacter(string $value): string { switch ($value[1]) { case '0': return "\x0"; case 'a': return "\x7"; case 'b': return "\x8"; case 't': return "\t"; case "\t": return "\t"; case 'n': return "\n"; case 'v': return "\xB"; case 'f': return "\xC"; case 'r': return "\r"; case 'e': return "\x1B"; case ' ': return ' '; case '"': return '"'; case '/': return '/'; case '\\': return '\\'; case 'N': // U+0085 NEXT LINE return "\xC2\x85"; case '_': // U+00A0 NO-BREAK SPACE return "\xC2\xA0"; case 'L': // U+2028 LINE SEPARATOR return "\xE2\x80\xA8"; case 'P': // U+2029 PARAGRAPH SEPARATOR return "\xE2\x80\xA9"; case 'x': return self::utf8chr(hexdec(substr($value, 2, 2))); case 'u': return self::utf8chr(hexdec(substr($value, 2, 4))); case 'U': return self::utf8chr(hexdec(substr($value, 2, 8))); default: throw new ParseException(sprintf('Found unknown escape character "%s".', $value)); } } /** * Get the UTF-8 character for the given code point. */ private static function utf8chr(int $c): string { if (0x80 > $c %= 0x200000) { return \chr($c); } if (0x800 > $c) { return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F); } if (0x10000 > $c) { return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); } return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); } } PK2A#]Tu��=�=�vendor/symfony/yaml/Inline.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\DumpException; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Tag\TaggedValue; /** * Inline implements a YAML parser/dumper for the YAML inline syntax. * * @author Fabien Potencier <fabien@symfony.com> * * @internal */ class Inline { public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; public static $parsedLineNumber = -1; public static $parsedFilename; private static $exceptionOnInvalidType = false; private static $objectSupport = false; private static $objectForMap = false; private static $constantSupport = false; public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null) { self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags); self::$parsedFilename = $parsedFilename; if (null !== $parsedLineNumber) { self::$parsedLineNumber = $parsedLineNumber; } } /** * Converts a YAML string to a PHP value. * * @param string|null $value A YAML string * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior * @param array $references Mapping of variable names to values * * @return mixed * * @throws ParseException */ public static function parse(string $value = null, int $flags = 0, array &$references = []) { self::initialize($flags); $value = trim($value); if ('' === $value) { return ''; } if (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); } try { $i = 0; $tag = self::parseTag($value, $i, $flags); switch ($value[$i]) { case '[': $result = self::parseSequence($value, $flags, $i, $references); ++$i; break; case '{': $result = self::parseMapping($value, $flags, $i, $references); ++$i; break; default: $result = self::parseScalar($value, $flags, null, $i, true, $references); } // some comments are allowed at the end if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) { throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } if (null !== $tag && '' !== $tag) { return new TaggedValue($tag, $result); } return $result; } finally { if (isset($mbEncoding)) { mb_internal_encoding($mbEncoding); } } } /** * Dumps a given PHP variable to a YAML string. * * @param mixed $value The PHP variable to convert * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string * * @throws DumpException When trying to dump PHP resource */ public static function dump($value, int $flags = 0): string { switch (true) { case \is_resource($value): if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); } return self::dumpNull($flags); case $value instanceof \DateTimeInterface: return $value->format('c'); case $value instanceof \UnitEnum: return sprintf('!php/const %s::%s', \get_class($value), $value->name); case \is_object($value): if ($value instanceof TaggedValue) { return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags); } if (Yaml::DUMP_OBJECT & $flags) { return '!php/object '.self::dump(serialize($value)); } if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { $output = []; foreach ($value as $key => $val) { $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); } return sprintf('{ %s }', implode(', ', $output)); } if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { throw new DumpException('Object support when dumping a YAML file has been disabled.'); } return self::dumpNull($flags); case \is_array($value): return self::dumpArray($value, $flags); case null === $value: return self::dumpNull($flags); case true === $value: return 'true'; case false === $value: return 'false'; case \is_int($value): return $value; case is_numeric($value) && false === strpbrk($value, "\f\n\r\t\v"): $locale = setlocale(\LC_NUMERIC, 0); if (false !== $locale) { setlocale(\LC_NUMERIC, 'C'); } if (\is_float($value)) { $repr = (string) $value; if (is_infinite($value)) { $repr = str_ireplace('INF', '.Inf', $repr); } elseif (floor($value) == $value && $repr == $value) { // Preserve float data type since storing a whole number will result in integer value. if (false === strpos($repr, 'E')) { $repr = $repr.'.0'; } } } else { $repr = \is_string($value) ? "'$value'" : (string) $value; } if (false !== $locale) { setlocale(\LC_NUMERIC, $locale); } return $repr; case '' == $value: return "''"; case self::isBinaryString($value): return '!!binary '.base64_encode($value); case Escaper::requiresDoubleQuoting($value): return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value): case Parser::preg_match(self::getHexRegex(), $value): case Parser::preg_match(self::getTimestampRegex(), $value): return Escaper::escapeWithSingleQuotes($value); default: return $value; } } /** * Check if given array is hash or just normal indexed array. * * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check */ public static function isHash($value): bool { if ($value instanceof \stdClass || $value instanceof \ArrayObject) { return true; } $expectedKey = 0; foreach ($value as $key => $val) { if ($key !== $expectedKey++) { return true; } } return false; } /** * Dumps a PHP array to a YAML string. * * @param array $value The PHP array to dump * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string */ private static function dumpArray(array $value, int $flags): string { // array if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) { $output = []; foreach ($value as $val) { $output[] = self::dump($val, $flags); } return sprintf('[%s]', implode(', ', $output)); } // hash $output = []; foreach ($value as $key => $val) { $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); } return sprintf('{ %s }', implode(', ', $output)); } private static function dumpNull(int $flags): string { if (Yaml::DUMP_NULL_AS_TILDE & $flags) { return '~'; } return 'null'; } /** * Parses a YAML scalar. * * @return mixed * * @throws ParseException When malformed inline YAML string is parsed */ public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [], bool &$isQuoted = null) { if (\in_array($scalar[$i], ['"', "'"], true)) { // quoted scalar $isQuoted = true; $output = self::parseQuotedScalar($scalar, $i); if (null !== $delimiters) { $tmp = ltrim(substr($scalar, $i), " \n"); if ('' === $tmp) { throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (!\in_array($tmp[0], $delimiters)) { throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } } } else { // "normal" string $isQuoted = false; if (!$delimiters) { $output = substr($scalar, $i); $i += \strlen($output); // remove comments if (Parser::preg_match('/[ \t]+#/', $output, $match, \PREG_OFFSET_CAPTURE)) { $output = substr($output, 0, $match[0][1]); } } elseif (Parser::preg_match('/^(.*?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { $output = $match[1]; $i += \strlen($output); $output = trim($output); } else { throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename); } // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) { throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename); } if ($evaluate) { $output = self::evaluateScalar($output, $flags, $references, $isQuoted); } } return $output; } /** * Parses a YAML quoted scalar. * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseQuotedScalar(string $scalar, int &$i = 0): string { if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: "%s".', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } $output = substr($match[0], 1, -1); $unescaper = new Unescaper(); if ('"' == $scalar[$i]) { $output = $unescaper->unescapeDoubleQuotedString($output); } else { $output = $unescaper->unescapeSingleQuotedString($output); } $i += \strlen($match[0]); return $output; } /** * Parses a YAML sequence. * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array { $output = []; $len = \strlen($sequence); ++$i; // [foo, bar, ...] while ($i < $len) { if (']' === $sequence[$i]) { return $output; } if (',' === $sequence[$i] || ' ' === $sequence[$i]) { ++$i; continue; } $tag = self::parseTag($sequence, $i, $flags); switch ($sequence[$i]) { case '[': // nested sequence $value = self::parseSequence($sequence, $flags, $i, $references); break; case '{': // nested mapping $value = self::parseMapping($sequence, $flags, $i, $references); break; default: $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted); // the value can be an array if a reference has been resolved to an array var if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { // embedded mapping? try { $pos = 0; $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); } catch (\InvalidArgumentException $e) { // no, it's not } } if (!$isQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { $references[$matches['ref']] = $matches['value']; $value = $matches['value']; } --$i; } if (null !== $tag && '' !== $tag) { $value = new TaggedValue($tag, $value); } $output[] = $value; ++$i; } throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename); } /** * Parses a YAML mapping. * * @return array|\stdClass * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = []) { $output = []; $len = \strlen($mapping); ++$i; $allowOverwrite = false; // {foo: bar, bar:foo, ...} while ($i < $len) { switch ($mapping[$i]) { case ' ': case ',': case "\n": ++$i; continue 2; case '}': if (self::$objectForMap) { return (object) $output; } return $output; } // key $offsetBeforeKeyParsing = $i; $isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true); $key = self::parseScalar($mapping, $flags, [':', ' '], $i, false); if ($offsetBeforeKeyParsing === $i) { throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping); } if ('!php/const' === $key) { $key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false); $key = self::evaluateScalar($key, $flags); } if (false === $i = strpos($mapping, ':', $i)) { break; } if (!$isKeyQuoted) { $evaluatedKey = self::evaluateScalar($key, $flags, $references); if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) { throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping); } } if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}', "\n"], true))) { throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping); } if ('<<' === $key) { $allowOverwrite = true; } while ($i < $len) { if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) { ++$i; continue; } $tag = self::parseTag($mapping, $i, $flags); switch ($mapping[$i]) { case '[': // nested sequence $value = self::parseSequence($mapping, $flags, $i, $references); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { foreach ($value as $parsedValue) { $output += $parsedValue; } } elseif ($allowOverwrite || !isset($output[$key])) { if (null !== $tag) { $output[$key] = new TaggedValue($tag, $value); } else { $output[$key] = $value; } } elseif (isset($output[$key])) { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } break; case '{': // nested mapping $value = self::parseMapping($mapping, $flags, $i, $references); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { $output += $value; } elseif ($allowOverwrite || !isset($output[$key])) { if (null !== $tag) { $output[$key] = new TaggedValue($tag, $value); } else { $output[$key] = $value; } } elseif (isset($output[$key])) { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } break; default: $value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { $output += $value; } elseif ($allowOverwrite || !isset($output[$key])) { if (!$isValueQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { $references[$matches['ref']] = $matches['value']; $value = $matches['value']; } if (null !== $tag) { $output[$key] = new TaggedValue($tag, $value); } else { $output[$key] = $value; } } elseif (isset($output[$key])) { throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); } --$i; } ++$i; continue 2; } } throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename); } /** * Evaluates scalars and replaces magic values. * * @return mixed * * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved */ private static function evaluateScalar(string $scalar, int $flags, array &$references = [], bool &$isQuotedString = null) { $isQuotedString = false; $scalar = trim($scalar); if (0 === strpos($scalar, '*')) { if (false !== $pos = strpos($scalar, '#')) { $value = substr($scalar, 1, $pos - 2); } else { $value = substr($scalar, 1); } // an unquoted * if (false === $value || '' === $value) { throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); } if (!\array_key_exists($value, $references)) { throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } return $references[$value]; } $scalarLower = strtolower($scalar); switch (true) { case 'null' === $scalarLower: case '' === $scalar: case '~' === $scalar: return null; case 'true' === $scalarLower: return true; case 'false' === $scalarLower: return false; case '!' === $scalar[0]: switch (true) { case 0 === strpos($scalar, '!!str '): $s = (string) substr($scalar, 6); if (\in_array($s[0] ?? '', ['"', "'"], true)) { $isQuotedString = true; $s = self::parseQuotedScalar($s); } return $s; case 0 === strpos($scalar, '! '): return substr($scalar, 2); case 0 === strpos($scalar, '!php/object'): if (self::$objectSupport) { if (!isset($scalar[12])) { trigger_deprecation('symfony/yaml', '5.1', 'Using the !php/object tag without a value is deprecated.'); return false; } return unserialize(self::parseScalar(substr($scalar, 12))); } if (self::$exceptionOnInvalidType) { throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return null; case 0 === strpos($scalar, '!php/const'): if (self::$constantSupport) { if (!isset($scalar[11])) { trigger_deprecation('symfony/yaml', '5.1', 'Using the !php/const tag without a value is deprecated.'); return ''; } $i = 0; if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) { return \constant($const); } throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (self::$exceptionOnInvalidType) { throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return null; case 0 === strpos($scalar, '!!float '): return (float) substr($scalar, 8); case 0 === strpos($scalar, '!!binary '): return self::evaluateBinaryScalar(substr($scalar, 9)); } throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); case preg_match('/^(?:\+|-)?0o(?P<value>[0-7_]++)$/', $scalar, $matches): $value = str_replace('_', '', $matches['value']); if ('-' === $scalar[0]) { return -octdec($value); } return octdec($value); case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]): if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { $scalar = str_replace('_', '', $scalar); } switch (true) { case ctype_digit($scalar): if (preg_match('/^0[0-7]+$/', $scalar)) { trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '0o'.substr($scalar, 1)); return octdec($scalar); } $cast = (int) $scalar; return ($scalar === (string) $cast) ? $cast : $scalar; case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): if (preg_match('/^-0[0-7]+$/', $scalar)) { trigger_deprecation('symfony/yaml', '5.1', 'Support for parsing numbers prefixed with 0 as octal numbers. They will be parsed as strings as of 6.0. Use "%s" to represent the octal number.', '-0o'.substr($scalar, 2)); return -octdec(substr($scalar, 1)); } $cast = (int) $scalar; return ($scalar === (string) $cast) ? $cast : $scalar; case is_numeric($scalar): case Parser::preg_match(self::getHexRegex(), $scalar): $scalar = str_replace('_', '', $scalar); return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; case '.inf' === $scalarLower: case '.nan' === $scalarLower: return -log(0); case '-.inf' === $scalarLower: return log(0); case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): return (float) str_replace('_', '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. $time = new \DateTime($scalar, new \DateTimeZone('UTC')); if (Yaml::PARSE_DATETIME & $flags) { return $time; } try { if (false !== $scalar = $time->getTimestamp()) { return $scalar; } } catch (\ValueError $e) { // no-op } return $time->format('U'); } } return (string) $scalar; } private static function parseTag(string $value, int &$i, int $flags): ?string { if ('!' !== $value[$i]) { return null; } $tagLength = strcspn($value, " \t\n[]{},", $i + 1); $tag = substr($value, $i + 1, $tagLength); $nextOffset = $i + $tagLength + 1; $nextOffset += strspn($value, ' ', $nextOffset); if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], true))) { throw new ParseException('Using the unquoted scalar value "!" is not supported. You must quote it.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); } // Is followed by a scalar and is a built-in tag if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) { // Manage in {@link self::evaluateScalar()} return null; } $i = $nextOffset; // Built-in tags if ('' !== $tag && '!' === $tag[0]) { throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } if ('' !== $tag && !isset($value[$i])) { throw new ParseException(sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) { return $tag; } throw new ParseException(sprintf('Tags support is not enabled. Enable the "Yaml::PARSE_CUSTOM_TAGS" flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); } public static function evaluateBinaryScalar(string $scalar): string { $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); if (0 !== (\strlen($parsedBinaryData) % 4)) { throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return base64_decode($parsedBinaryData, true); } private static function isBinaryString(string $value): bool { return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); } /** * Gets a regex that matches a YAML date. * * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 */ private static function getTimestampRegex(): string { return <<<EOF ~^ (?P<year>[0-9][0-9][0-9][0-9]) -(?P<month>[0-9][0-9]?) -(?P<day>[0-9][0-9]?) (?:(?:[Tt]|[ \t]+) (?P<hour>[0-9][0-9]?) :(?P<minute>[0-9][0-9]) :(?P<second>[0-9][0-9]) (?:\.(?P<fraction>[0-9]*))? (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) (?::(?P<tz_minute>[0-9][0-9]))?))?)? $~x EOF; } /** * Gets a regex that matches a YAML number in hexadecimal notation. */ private static function getHexRegex(): string { return '~^0x[0-9a-f_]++$~i'; } } PK2A#]�BU�jj0vendor/symfony/yaml/Exception/ParseException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during parsing. * * @author Fabien Potencier <fabien@symfony.com> */ class ParseException extends RuntimeException { private $parsedFile; private $parsedLine; private $snippet; private $rawMessage; /** * @param string $message The error message * @param int $parsedLine The line where the error occurred * @param string|null $snippet The snippet of code near the problem * @param string|null $parsedFile The file name where the error occurred */ public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Throwable $previous = null) { $this->parsedFile = $parsedFile; $this->parsedLine = $parsedLine; $this->snippet = $snippet; $this->rawMessage = $message; $this->updateRepr(); parent::__construct($this->message, 0, $previous); } /** * Gets the snippet of code near the error. * * @return string */ public function getSnippet() { return $this->snippet; } /** * Sets the snippet of code near the error. */ public function setSnippet(string $snippet) { $this->snippet = $snippet; $this->updateRepr(); } /** * Gets the filename where the error occurred. * * This method returns null if a string is parsed. * * @return string */ public function getParsedFile() { return $this->parsedFile; } /** * Sets the filename where the error occurred. */ public function setParsedFile(string $parsedFile) { $this->parsedFile = $parsedFile; $this->updateRepr(); } /** * Gets the line where the error occurred. * * @return int */ public function getParsedLine() { return $this->parsedLine; } /** * Sets the line where the error occurred. */ public function setParsedLine(int $parsedLine) { $this->parsedLine = $parsedLine; $this->updateRepr(); } private function updateRepr() { $this->message = $this->rawMessage; $dot = false; if ('.' === substr($this->message, -1)) { $this->message = substr($this->message, 0, -1); $dot = true; } if (null !== $this->parsedFile) { $this->message .= sprintf(' in %s', json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); } if ($this->parsedLine >= 0) { $this->message .= sprintf(' at line %d', $this->parsedLine); } if ($this->snippet) { $this->message .= sprintf(' (near "%s")', $this->snippet); } if ($dot) { $this->message .= '.'; } } } PK2A#]B9���4vendor/symfony/yaml/Exception/ExceptionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception interface for all exceptions thrown by the component. * * @author Fabien Potencier <fabien@symfony.com> */ interface ExceptionInterface extends \Throwable { } PK2A#]�_q���2vendor/symfony/yaml/Exception/RuntimeException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during parsing. * * @author Romain Neutron <imprec@gmail.com> */ class RuntimeException extends \RuntimeException implements ExceptionInterface { } PK2A#]���/vendor/symfony/yaml/Exception/DumpException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during dumping. * * @author Fabien Potencier <fabien@symfony.com> */ class DumpException extends RuntimeException { } PK2A#]MK�<��;vendor/symfony/polyfill-php80/Resources/stubs/Attribute.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ #[Attribute(Attribute::TARGET_CLASS)] final class Attribute { public const TARGET_CLASS = 1; public const TARGET_FUNCTION = 2; public const TARGET_METHOD = 4; public const TARGET_PROPERTY = 8; public const TARGET_CLASS_CONSTANT = 16; public const TARGET_PARAMETER = 32; public const TARGET_ALL = 63; public const IS_REPEATABLE = 64; /** @var int */ public $flags; public function __construct(int $flags = self::TARGET_ALL) { $this->flags = $flags; } } PK2A#]t]\ڌ�<vendor/symfony/polyfill-php80/Resources/stubs/Stringable.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80000) { interface Stringable { /** * @return string */ public function __toString(); } } PK2A#]g���>><vendor/symfony/polyfill-php80/Resources/stubs/ValueError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80000) { class ValueError extends Error { } } PK2A#]ֈ+GGEvendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80000) { class UnhandledMatchError extends Error { } } PK2A#]=7T8ww:vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) { class PhpToken extends Symfony\Polyfill\Php80\PhpToken { } } PK2A#]cH��� � 'vendor/symfony/polyfill-php80/Php80.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php80; /** * @author Ion Bazan <ion.bazan@gmail.com> * @author Nico Oelgart <nicoswd@gmail.com> * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Php80 { public static function fdiv(float $dividend, float $divisor): float { return @($dividend / $divisor); } public static function get_debug_type($value): string { switch (true) { case null === $value: return 'null'; case \is_bool($value): return 'bool'; case \is_string($value): return 'string'; case \is_array($value): return 'array'; case \is_int($value): return 'int'; case \is_float($value): return 'float'; case \is_object($value): break; case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; default: if (null === $type = @get_resource_type($value)) { return 'unknown'; } if ('Unknown' === $type) { $type = 'closed'; } return "resource ($type)"; } $class = \get_class($value); if (false === strpos($class, '@')) { return $class; } return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; } public static function get_resource_id($res): int { if (!\is_resource($res) && null === @get_resource_type($res)) { throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); } return (int) $res; } public static function preg_last_error_msg(): string { switch (preg_last_error()) { case \PREG_INTERNAL_ERROR: return 'Internal error'; case \PREG_BAD_UTF8_ERROR: return 'Malformed UTF-8 characters, possibly incorrectly encoded'; case \PREG_BAD_UTF8_OFFSET_ERROR: return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; case \PREG_BACKTRACK_LIMIT_ERROR: return 'Backtrack limit exhausted'; case \PREG_RECURSION_LIMIT_ERROR: return 'Recursion limit exhausted'; case \PREG_JIT_STACKLIMIT_ERROR: return 'JIT stack limit exhausted'; case \PREG_NO_ERROR: return 'No error'; default: return 'Unknown error'; } } public static function str_contains(string $haystack, string $needle): bool { return '' === $needle || false !== strpos($haystack, $needle); } public static function str_starts_with(string $haystack, string $needle): bool { return 0 === strncmp($haystack, $needle, \strlen($needle)); } public static function str_ends_with(string $haystack, string $needle): bool { if ('' === $needle || $needle === $haystack) { return true; } if ('' === $haystack) { return false; } $needleLength = \strlen($needle); return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); } } PK2A#]]f��*vendor/symfony/polyfill-php80/PhpToken.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php80; /** * @author Fedonyuk Anton <info@ensostudio.ru> * * @internal */ class PhpToken implements \Stringable { /** * @var int */ public $id; /** * @var string */ public $text; /** * @var int */ public $line; /** * @var int */ public $pos; public function __construct(int $id, string $text, int $line = -1, int $position = -1) { $this->id = $id; $this->text = $text; $this->line = $line; $this->pos = $position; } public function getTokenName(): ?string { if ('UNKNOWN' === $name = token_name($this->id)) { $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; } return $name; } /** * @param int|string|array $kind */ public function is($kind): bool { foreach ((array) $kind as $value) { if (\in_array($value, [$this->id, $this->text], true)) { return true; } } return false; } public function isIgnorable(): bool { return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); } public function __toString(): string { return (string) $this->text; } /** * @return static[] */ public static function tokenize(string $code, int $flags = 0): array { $line = 1; $position = 0; $tokens = token_get_all($code, $flags); foreach ($tokens as $index => $token) { if (\is_string($token)) { $id = \ord($token); $text = $token; } else { [$id, $text, $line] = $token; } $tokens[$index] = new static($id, $text, $line, $position); $position += \strlen($text); } return $tokens; } } PK2A#]K�,,%vendor/symfony/polyfill-php80/LICENSEnu�[���Copyright (c) 2020-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK2A#]�.��+vendor/symfony/polyfill-php80/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php80 as p; if (\PHP_VERSION_ID >= 80000) { return; } if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); } if (!function_exists('fdiv')) { function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } } if (!function_exists('preg_last_error_msg')) { function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } } if (!function_exists('str_contains')) { function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } } if (!function_exists('str_starts_with')) { function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } } if (!function_exists('str_ends_with')) { function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } } if (!function_exists('get_debug_type')) { function get_debug_type($value): string { return p\Php80::get_debug_type($value); } } if (!function_exists('get_resource_id')) { function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } } PK2A#]!=�NV!V!2vendor/symfony/console/Command/CompleteCommand.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Completion\Output\BashCompletionOutput; use Symfony\Component\Console\Completion\Output\CompletionOutputInterface; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** * Responsible for providing the values to the shell completion. * * @author Wouter de Jong <wouter@wouterj.nl> */ final class CompleteCommand extends Command { protected static $defaultName = '|_complete'; protected static $defaultDescription = 'Internal command to provide shell completion suggestions'; private $completionOutputs; private $isDebug = false; /** * @param array<string, class-string<CompletionOutputInterface>> $completionOutputs A list of additional completion outputs, with shell name as key and FQCN as value */ public function __construct(array $completionOutputs = []) { // must be set before the parent constructor, as the property value is used in configure() $this->completionOutputs = $completionOutputs + ['bash' => BashCompletionOutput::class]; parent::__construct(); } protected function configure(): void { $this ->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")') ->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)') ->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)') ->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'The version of the completion script') ; } protected function initialize(InputInterface $input, OutputInterface $output) { $this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOLEAN); } protected function execute(InputInterface $input, OutputInterface $output): int { try { // uncomment when a bugfix or BC break has been introduced in the shell completion scripts // $version = $input->getOption('symfony'); // if ($version && version_compare($version, 'x.y', '>=')) { // $message = sprintf('Completion script version is not supported ("%s" given, ">=x.y" required).', $version); // $this->log($message); // $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.'); // return 126; // } $shell = $input->getOption('shell'); if (!$shell) { throw new \RuntimeException('The "--shell" option must be set.'); } if (!$completionOutput = $this->completionOutputs[$shell] ?? false) { throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs)))); } $completionInput = $this->createCompletionInput($input); $suggestions = new CompletionSuggestions(); $this->log([ '', '<comment>'.date('Y-m-d H:i:s').'</>', '<info>Input:</> <comment>("|" indicates the cursor position)</>', ' '.(string) $completionInput, '<info>Command:</>', ' '.(string) implode(' ', $_SERVER['argv']), '<info>Messages:</>', ]); $command = $this->findCommand($completionInput, $output); if (null === $command) { $this->log(' No command found, completing using the Application class.'); $this->getApplication()->complete($completionInput, $suggestions); } elseif ( $completionInput->mustSuggestArgumentValuesFor('command') && $command->getName() !== $completionInput->getCompletionValue() && !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true) ) { $this->log(' No command found, completing using the Application class.'); // expand shortcut names ("cache:cl<TAB>") into their full name ("cache:clear") $suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases()))); } else { $command->mergeApplicationDefinition(); $completionInput->bind($command->getDefinition()); if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) { $this->log(' Completing option names for the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> command.'); $suggestions->suggestOptions($command->getDefinition()->getOptions()); } else { $this->log([ ' Completing using the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> class.', ' Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>', ]); if (null !== $compval = $completionInput->getCompletionValue()) { $this->log(' Current value: <comment>'.$compval.'</>'); } $command->complete($completionInput, $suggestions); } } /** @var CompletionOutputInterface $completionOutput */ $completionOutput = new $completionOutput(); $this->log('<info>Suggestions:</>'); if ($options = $suggestions->getOptionSuggestions()) { $this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options))); } elseif ($values = $suggestions->getValueSuggestions()) { $this->log(' '.implode(' ', $values)); } else { $this->log(' <comment>No suggestions were provided</>'); } $completionOutput->write($suggestions, $output); } catch (\Throwable $e) { $this->log([ '<error>Error!</error>', (string) $e, ]); if ($output->isDebug()) { throw $e; } return 2; } return 0; } private function createCompletionInput(InputInterface $input): CompletionInput { $currentIndex = $input->getOption('current'); if (!$currentIndex || !ctype_digit($currentIndex)) { throw new \RuntimeException('The "--current" option must be set and it must be an integer.'); } $completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex); try { $completionInput->bind($this->getApplication()->getDefinition()); } catch (ExceptionInterface $e) { } return $completionInput; } private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command { try { $inputName = $completionInput->getFirstArgument(); if (null === $inputName) { return null; } return $this->getApplication()->find($inputName); } catch (CommandNotFoundException $e) { } return null; } private function log($messages): void { if (!$this->isDebug) { return; } $commandName = basename($_SERVER['argv'][0]); file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND); } } PK2A#]��͚((.vendor/symfony/console/Command/HelpCommand.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Descriptor\ApplicationDescription; use Symfony\Component\Console\Helper\DescriptorHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** * HelpCommand displays the help for a given command. * * @author Fabien Potencier <fabien@symfony.com> */ class HelpCommand extends Command { private $command; /** * {@inheritdoc} */ protected function configure() { $this->ignoreValidationErrors(); $this ->setName('help') ->setDefinition([ new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), ]) ->setDescription('Display help for a command') ->setHelp(<<<'EOF' The <info>%command.name%</info> command displays help for a given command: <info>%command.full_name% list</info> You can also output the help in other formats by using the <comment>--format</comment> option: <info>%command.full_name% --format=xml list</info> To display the list of available commands, please use the <info>list</info> command. EOF ) ; } public function setCommand(Command $command) { $this->command = $command; } /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { if (null === $this->command) { $this->command = $this->getApplication()->find($input->getArgument('command_name')); } $helper = new DescriptorHelper(); $helper->describe($output, $this->command, [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), ]); $this->command = null; return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('command_name')) { $descriptor = new ApplicationDescription($this->getApplication()); $suggestions->suggestValues(array_keys($descriptor->getCommands())); return; } if ($input->mustSuggestOptionValuesFor('format')) { $helper = new DescriptorHelper(); $suggestions->suggestValues($helper->getFormats()); } } } PK2A#]�?�� .vendor/symfony/console/Command/LazyCommand.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Command; use Symfony\Component\Console\Application; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * @author Nicolas Grekas <p@tchwork.com> */ final class LazyCommand extends Command { private $command; private $isEnabled; public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true) { $this->setName($name) ->setAliases($aliases) ->setHidden($isHidden) ->setDescription($description); $this->command = $commandFactory; $this->isEnabled = $isEnabled; } public function ignoreValidationErrors(): void { $this->getCommand()->ignoreValidationErrors(); } public function setApplication(Application $application = null): void { if ($this->command instanceof parent) { $this->command->setApplication($application); } parent::setApplication($application); } public function setHelperSet(HelperSet $helperSet): void { if ($this->command instanceof parent) { $this->command->setHelperSet($helperSet); } parent::setHelperSet($helperSet); } public function isEnabled(): bool { return $this->isEnabled ?? $this->getCommand()->isEnabled(); } public function run(InputInterface $input, OutputInterface $output): int { return $this->getCommand()->run($input, $output); } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { $this->getCommand()->complete($input, $suggestions); } /** * @return $this */ public function setCode(callable $code): self { $this->getCommand()->setCode($code); return $this; } /** * @internal */ public function mergeApplicationDefinition(bool $mergeArgs = true): void { $this->getCommand()->mergeApplicationDefinition($mergeArgs); } /** * @return $this */ public function setDefinition($definition): self { $this->getCommand()->setDefinition($definition); return $this; } public function getDefinition(): InputDefinition { return $this->getCommand()->getDefinition(); } public function getNativeDefinition(): InputDefinition { return $this->getCommand()->getNativeDefinition(); } /** * @return $this */ public function addArgument(string $name, int $mode = null, string $description = '', $default = null): self { $this->getCommand()->addArgument($name, $mode, $description, $default); return $this; } /** * @return $this */ public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null): self { $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default); return $this; } /** * @return $this */ public function setProcessTitle(string $title): self { $this->getCommand()->setProcessTitle($title); return $this; } /** * @return $this */ public function setHelp(string $help): self { $this->getCommand()->setHelp($help); return $this; } public function getHelp(): string { return $this->getCommand()->getHelp(); } public function getProcessedHelp(): string { return $this->getCommand()->getProcessedHelp(); } public function getSynopsis(bool $short = false): string { return $this->getCommand()->getSynopsis($short); } /** * @return $this */ public function addUsage(string $usage): self { $this->getCommand()->addUsage($usage); return $this; } public function getUsages(): array { return $this->getCommand()->getUsages(); } /** * @return mixed */ public function getHelper(string $name) { return $this->getCommand()->getHelper($name); } public function getCommand(): parent { if (!$this->command instanceof \Closure) { return $this->command; } $command = $this->command = ($this->command)(); $command->setApplication($this->getApplication()); if (null !== $this->getHelperSet()) { $command->setHelperSet($this->getHelperSet()); } $command->setName($this->getName()) ->setAliases($this->getAliases()) ->setHidden($this->isHidden()) ->setDescription($this->getDescription()); // Will throw if the command is not correctly initialized. $command->getDefinition(); return $command; } } PK2A#]Vn�tPP*vendor/symfony/console/Command/Command.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Command; use Symfony\Component\Console\Application; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** * Base class for all commands. * * @author Fabien Potencier <fabien@symfony.com> */ class Command { // see https://tldp.org/LDP/abs/html/exitcodes.html public const SUCCESS = 0; public const FAILURE = 1; public const INVALID = 2; /** * @var string|null The default command name */ protected static $defaultName; /** * @var string|null The default command description */ protected static $defaultDescription; private $application; private $name; private $processTitle; private $aliases = []; private $definition; private $hidden = false; private $help = ''; private $description = ''; private $fullDefinition; private $ignoreValidationErrors = false; private $code; private $synopsis = []; private $usages = []; private $helperSet; /** * @return string|null */ public static function getDefaultName() { $class = static::class; if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { return $attribute[0]->newInstance()->name; } $r = new \ReflectionProperty($class, 'defaultName'); return $class === $r->class ? static::$defaultName : null; } public static function getDefaultDescription(): ?string { $class = static::class; if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { return $attribute[0]->newInstance()->description; } $r = new \ReflectionProperty($class, 'defaultDescription'); return $class === $r->class ? static::$defaultDescription : null; } /** * @param string|null $name The name of the command; passing null means it must be set in configure() * * @throws LogicException When the command name is empty */ public function __construct(string $name = null) { $this->definition = new InputDefinition(); if (null === $name && null !== $name = static::getDefaultName()) { $aliases = explode('|', $name); if ('' === $name = array_shift($aliases)) { $this->setHidden(true); $name = array_shift($aliases); } $this->setAliases($aliases); } if (null !== $name) { $this->setName($name); } if ('' === $this->description) { $this->setDescription(static::getDefaultDescription() ?? ''); } $this->configure(); } /** * Ignores validation errors. * * This is mainly useful for the help command. */ public function ignoreValidationErrors() { $this->ignoreValidationErrors = true; } public function setApplication(Application $application = null) { $this->application = $application; if ($application) { $this->setHelperSet($application->getHelperSet()); } else { $this->helperSet = null; } $this->fullDefinition = null; } public function setHelperSet(HelperSet $helperSet) { $this->helperSet = $helperSet; } /** * Gets the helper set. * * @return HelperSet|null */ public function getHelperSet() { return $this->helperSet; } /** * Gets the application instance for this command. * * @return Application|null */ public function getApplication() { return $this->application; } /** * Checks whether the command is enabled or not in the current environment. * * Override this to check for x or y and return false if the command cannot * run properly under the current conditions. * * @return bool */ public function isEnabled() { return true; } /** * Configures the current command. */ protected function configure() { } /** * Executes the current command. * * This method is not abstract because you can use this class * as a concrete class. In this case, instead of defining the * execute() method, you set the code to execute by passing * a Closure to the setCode() method. * * @return int 0 if everything went fine, or an exit code * * @throws LogicException When this abstract method is not implemented * * @see setCode() */ protected function execute(InputInterface $input, OutputInterface $output) { throw new LogicException('You must override the execute() method in the concrete command class.'