Merge pull request #4 from rhymix/develop

Sync with origin
This commit is contained in:
YJSoft 2016-03-28 15:09:17 +09:00
commit 9330bb443a
611 changed files with 53504 additions and 10759 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
indent_style = tab
indent_size = 4

View file

@ -1,6 +1,5 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
@ -11,14 +10,14 @@ before_script:
- npm install grunt grunt-cli grunt-contrib-jshint grunt-contrib-csslint grunt-phplint --save-dev
- mysql -u root -e "CREATE DATABASE rhymix"
- mysql -u root -e "SET PASSWORD FOR 'travis'@'localhost' = PASSWORD('travis')"
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi
- if [[ $TRAVIS_PHP_VERSION == "5.3" ]]; then touch codecept.phar; fi
- if [[ $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi
- if [[ $TRAVIS_PHP_VERSION == "5.4" ]]; then wget http://codeception.com/releases/2.0.16/codecept.phar; fi
- if [[ ! -f codecept.phar ]]; then wget http://codeception.com/codecept.phar; fi
- if [[ ! -f codecept.phar ]]; then wget http://codeception.com/releases/2.1.6/codecept.phar; fi
script:
- if [[ -f codecept.phar ]]; then php codecept.phar build; fi
- if [[ $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
- if [[ -s codecept.phar ]]; then php codecept.phar build; fi
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "5.4" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
- grunt lint
notifications:
email: false

View file

@ -58,6 +58,8 @@ PHP, HTML, XML, CSS, JS 등 모든 텍스트 파일의 문자셋은 BOM이 없
들여쓰기는 1개의 탭으로 합니다.
단, 탭 대신 공백을 사용하는 파일에서는 일관성 유지를 위해 4칸의 공백을 사용할 수 있습니다.
들여쓴 줄들 사이의 빈 줄도 들여씁니다. (에디터에서 후행 공백을 제거하지 않도록 설정하십시오.)
PHP 코드만으로 이루어진 파일은 맨 끝에 `?>` 태그를 사용하지 않습니다.
### 공백 및 줄바꿈 규칙
@ -155,5 +157,8 @@ Rhymix에서 정한 `error_reporting` 설정 하에서 어떤 에러도 발생
문자열과 문자열, 정수와 정수를 비교할 때는 가능하면 `==` 대신 `===`을 사용합니다.
실제 자료형이 다를 가능성이 있는 경우 `intval()`, `strval()` 등의 함수와 함께 사용합니다.
PHP 5.4 이상에서 지원하는 간단한 배열 문법(`[1, 2, 3]`)을 사용할 수 있으나,
복잡한 구조의 배열을 선언할 때는 이 문법이 오히려 가독성을 해칠 수 있으니 주의하시기 바랍니다.
여기에서 규정하지 않은 내용은 [PSR-1](http://www.php-fig.org/psr/psr-1/)과
[PSR-2](http://www.php-fig.org/psr/psr-2/)를 따릅니다.

View file

@ -32,18 +32,23 @@ Rhymix는 개발자와 사용자가 서로의 권리와 책임을 존중하는
### 설치 환경
- PHP 5.3.3 이상 (PHP 5.5.9 이상 권장, PHP 7 지원)
- MySQL 4.1.13 이상 (MySQL 5.0.7 이상 또는 MariaDB 권장)
- CUBRID 또는 MS SQL을 DB로 사용할 수도 있으나, 권장하지는 않습니다.
Rhymix를 사용하려면 아래의 조건을 충족하는 웹호스팅이나 서버를 마련하셔야 합니다.
- PHP 5.5.9 이상 (PHP 7 권장)
- MySQL 5.0.7 이상 (MariaDB 권장)
- 필수 PHP 모듈
- curl
- gd
- iconv 또는 mbstring
- json
- mcrypt 또는 openssl
- xml 및 simplexml
- simplexml
- php.ini에서 session.auto_start = Off로 설정되어 있어야 합니다.
- 설치 폴더 또는 files 폴더에 쓰기 권한이 주어져야 합니다.
- MySQL/MariaDB 외에도 아래의 DB를 사용할 수 있습니다.
- CUBRID 9.0 이상
- Microsoft SQL Server 2008 이상
### 개발 참여

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@
/**
* Manages Context such as request arguments/environment variables
* It has dual method structure, easy-to use methods which can be called as self::methodname(),and methods called with static object.
*
* @author NAVER (developers@xpressengine.com)
*/
@ -394,11 +393,11 @@ class Context
}
if (strpos($current_url, 'xn--') !== false)
{
$current_url = self::decodeIdna($current_url);
$current_url = Rhymix\Framework\URL::decodeIdna($current_url);
}
if (strpos($request_uri, 'xn--') !== false)
{
$request_uri = self::decodeIdna($request_uri);
$request_uri = Rhymix\Framework\URL::decodeIdna($request_uri);
}
self::set('current_url', $current_url);
self::set('request_uri', $request_uri);
@ -449,6 +448,12 @@ class Context
*/
public static function close()
{
// Save debugging information.
if (!DisplayHandler::$debug_printed)
{
DisplayHandler::getDebugInfo();
}
// Check session status and close it if open.
if (self::checkSessionStatus())
{
@ -574,8 +579,8 @@ class Context
$db_info->sitelock_title = $config['lock']['title'];
$db_info->sitelock_message = $config['lock']['message'];
$db_info->sitelock_whitelist = count($config['lock']['allow']) ? $config['lock']['allow'] : array('127.0.0.1');
$db_info->embed_white_iframe = $config['embedfilter']['iframe'];
$db_info->embed_white_object = $config['embedfilter']['object'];
$db_info->embed_white_iframe = $config['mediafilter']['iframe'] ?: $config['embedfilter']['iframe'];
$db_info->embed_white_object = $config['mediafilter']['object'] ?: $config['embedfilter']['object'];
$db_info->use_mobile_view = $config['use_mobile_view'] ? 'Y' : 'N';
$db_info->use_prepared_statements = $config['use_prepared_statements'] ? 'Y' : 'N';
$db_info->use_rewrite = $config['use_rewrite'] ? 'Y' : 'N';
@ -686,7 +691,7 @@ class Context
public function checkSSO()
{
// pass if it's not GET request or XE is not yet installed
if(!config('use_sso') || isCrawler())
if(!config('use_sso') || Rhymix\Framework\UA::isRobot())
{
return TRUE;
}
@ -712,61 +717,66 @@ class Context
$current_site = self::getRequestUri();
// Step 1: if the current site is not the default site, send SSO validation request to the default site
if($default_url !== $current_site && !self::get('SSOID') && $_COOKIE['sso'] !== md5($current_site))
if($default_url !== $current_site && !self::get('sso_response') && $_COOKIE['sso'] !== md5($current_site))
{
// Set sso cookie to prevent multiple simultaneous SSO validation requests
setcookie('sso', md5($current_site), 0, '/');
// Redirect to the default site
$redirect_url = sprintf('%s?return_url=%s', $default_url, urlencode(base64_encode($current_site)));
$sso_request = Rhymix\Framework\Security::encrypt(Rhymix\Framework\URL::getCurrentURL());
$redirect_url = $default_url . '?sso_request=' . urlencode($sso_request);
header('Location:' . $redirect_url);
return FALSE;
return false;
}
// Step 2: receive and process SSO validation request at the default site
if($default_url === $current_site && self::get('return_url'))
if($default_url === $current_site && self::get('sso_request'))
{
// Get the URL of the origin site
$url = base64_decode(self::get('return_url'));
$url_info = parse_url($url);
$sso_request = Rhymix\Framework\Security::decrypt(self::get('sso_request'));
if (!$sso_request || !preg_match('!^https?://!', $sso_request))
{
self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400);
return false;
}
// Check that the origin site is a valid site in this XE installation (to prevent open redirect vuln)
if(!getModel('module')->getSiteInfoByDomain(rtrim($url, '/'))->site_srl)
{
htmlHeader();
echo self::getLang("msg_invalid_request");
htmlFooter();
return FALSE;
self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400);
return false;
}
// Redirect back to the origin site
$url_info['query'] .= ($url_info['query'] ? '&' : '') . 'SSOID=' . session_id();
$redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : ''));
header('Location:' . $redirect_url);
return FALSE;
$sso_response = Rhymix\Framework\Security::encrypt(session_id());
header('Location: ' . Rhymix\Framework\URL::modifyURL($sso_request, array('sso_response' => $sso_response)));
return false;
}
// Step 3: back at the origin site, set session ID to be the same as the default site
if($default_url !== $current_site && self::get('SSOID'))
if($default_url !== $current_site && self::get('sso_response'))
{
// Check that the session ID was given by the default site (to prevent session fixation CSRF)
// Check SSO response
$sso_response = Rhymix\Framework\Security::decrypt(self::get('sso_response'));
if ($sso_response === false)
{
self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400);
return false;
}
// Check that the response was given by the default site (to prevent session fixation CSRF)
if(isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $default_url) !== 0)
{
htmlHeader();
echo self::getLang("msg_invalid_request");
htmlFooter();
return FALSE;
self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400);
return false;
}
// Set session ID
setcookie(session_name(), self::get('SSOID'));
setcookie(session_name(), $sso_response);
// Finally, redirect to the originally requested URL
$url_info = parse_url(self::getRequestUrl());
$url_info['query'] = preg_replace('/(^|\b)SSOID=([^&?]+)/', '', $url_info['query']);
$redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : ''));
header('Location:' . $redirect_url);
return FALSE;
header('Location: ' . Rhymix\Framework\URL::getCurrentURL(array('sso_response' => null)));
return false;
}
// If none of the conditions above apply, proceed normally
@ -1058,7 +1068,11 @@ class Context
*/
public static function convertEncodingStr($str)
{
if(!$str) return null;
if (!$str || utf8_check($str))
{
return $str;
}
$obj = new stdClass;
$obj->str = $str;
$obj = self::convertEncoding($obj);
@ -1073,15 +1087,7 @@ class Context
*/
public static function encodeIdna($domain)
{
if(function_exists('idn_to_ascii'))
{
return idn_to_ascii($domain);
}
else
{
$encoder = new TrueBV\Punycode();
return $encoder->encode($domain);
}
return Rhymix\Framework\URL::encodeIdna($domain);
}
/**
@ -1092,15 +1098,7 @@ class Context
*/
public static function decodeIdna($domain)
{
if(function_exists('idn_to_utf8'))
{
return idn_to_utf8($domain);
}
else
{
$decoder = new TrueBV\Punycode();
return $decoder->decode($domain);
}
return Rhymix\Framework\URL::decodeIdna($domain);
}
/**
@ -1283,11 +1281,15 @@ class Context
}
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
if(Security::detectingXEE($xml))
if(!Rhymix\Framework\Security::checkXEE($xml))
{
header("HTTP/1.0 400 Bad Request");
exit;
}
if(function_exists('libxml_disable_entity_loader'))
{
libxml_disable_entity_loader(true);
}
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml);
@ -1363,10 +1365,9 @@ class Context
* @see Cast variables, such as _srl, page, and cpage, into interger
* @param string $key Variable key
* @param string $val Variable value
* @param string $do_stripslashes Whether to strip slashes
* @return mixed filtered value. Type are string or array
*/
public function _filterRequestVar($key, $val, $do_stripslashes = 1)
public function _filterRequestVar($key, $val)
{
if(!($isArray = is_array($val)))
{
@ -1392,19 +1393,6 @@ class Context
else
{
$result[$k] = $v;
if($do_stripslashes && version_compare(PHP_VERSION, '5.4.0', '<') && get_magic_quotes_gpc())
{
if (is_array($result[$k]))
{
array_walk_recursive($result[$k], function(&$val) { $val = stripslashes($val); });
}
else
{
$result[$k] = stripslashes($result[$k]);
}
}
if(is_array($result[$k]))
{
array_walk_recursive($result[$k], function(&$val) { $val = trim($val); });
@ -1490,13 +1478,9 @@ class Context
}
// Allow if the current user is in the list of allowed IPs.
$allowed_list = config('lock.allow');
foreach ($allowed_list as $allowed_ip)
if (Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow')))
{
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $allowed_ip))
{
return;
}
return;
}
// Set headers and constants for backward compatibility.

View file

@ -24,7 +24,6 @@ class DB
*/
protected static $priority_dbms = array(
'mysqli' => 6,
'mysql' => 4,
'cubrid' => 2,
'mssql' => 1
);
@ -271,9 +270,13 @@ class DB
// after creating instance of class, check is supported
foreach ($supported_list as $db_type)
{
if (strncasecmp($db_type, 'mysql', 5) === 0 && strtolower($db_type) !== 'mysqli')
{
continue;
}
$class_name = sprintf("DB%s%s", strtoupper(substr($db_type, 0, 1)), strtolower(substr($db_type, 1)));
$class_file = sprintf(_XE_PATH_ . "classes/db/%s.class.php", $class_name);
if(!file_exists($class_file) || stripos($class_file, '_innodb') !== false)
if (!file_exists($class_file))
{
continue;
}

View file

@ -81,9 +81,14 @@ class DBCubrid extends DB
define('__CUBRID_VERSION__', $cubrid_version);
}
if(__CUBRID_VERSION__ >= '8.4.0')
cubrid_set_autocommit($result, CUBRID_AUTOCOMMIT_TRUE);
if(version_compare(__CUBRID_VERSION__, '9.0', '<'))
{
$this->setError(-1, 'Rhymix requires CUBRID 9.0 or later. Current CUBRID version is ' . __CUBRID_VERSION__);
return;
}
cubrid_set_autocommit($result, CUBRID_AUTOCOMMIT_TRUE);
return $result;
}

View file

@ -71,6 +71,16 @@ class DBMssql extends DB
$this->setError(-1, 'database connect fail' . PHP_EOL . $errors);
return;
}
$server_info = sqlsrv_server_info($result);
$server_version = $server_info['SQLServerVersion'];
if ($server_version && version_compare($server_version, '10', '<'))
{
$this->setError(-1, 'Rhymix requires Microsoft SQL Server 2008 or later. Current version is ' . $server_version);
return;
}
return $result;
}

View file

@ -67,7 +67,8 @@ class DBMysql extends DB
$result = @mysql_connect($connection['host'], $connection['user'], $connection['pass']);
if(!$result)
{
exit('Unable to connect to DB.');
$this->setError(-1, 'Unable to connect to DB.');
return;
}
if(mysql_error())
@ -76,10 +77,10 @@ class DBMysql extends DB
return;
}
// Error appears if the version is lower than 4.1.13
if(version_compare(mysql_get_server_info($result), '4.1.13', '<'))
// Error appears if the version is lower than 5.0.7
if(version_compare(mysql_get_server_info($result), '5.0.7', '<'))
{
$this->setError(-1, 'Rhymix requires MySQL 4.1.13 or later. Current MySQL version is ' . mysql_get_server_info());
$this->setError(-1, 'Rhymix requires MySQL 5.0.7 or later. Current MySQL version is ' . mysql_get_server_info());
return;
}
@ -164,7 +165,8 @@ class DBMysql extends DB
{
if(!$connection)
{
exit('Rhymix cannot handle DB connection.');
$this->setError(-1, 'Unable to connect to DB.');
return false;
}
// Run the query statement
$result = @mysql_query($query, $connection);

View file

@ -80,8 +80,8 @@ class DBMysqli extends DBMysql
{
if ($connection === null)
{
debug_print_backtrace();
exit;
$this->setError(-1, 'Unable to connect to DB.');
return false;
}
if($this->use_prepared_statements == 'Y')
{

View file

@ -12,6 +12,7 @@
class DisplayHandler extends Handler
{
public static $response_size = 0;
public static $debug_printed = 0;
var $content_size = 0; // /< The size of displaying contents
var $gz_enabled = FALSE; // / <a flog variable whether to call contents after compressing by gzip
var $handler = NULL;
@ -136,16 +137,34 @@ class DisplayHandler extends Handler
*
* @return string
*/
public function getDebugInfo(&$output)
public function getDebugInfo(&$output = null)
{
// Check if debugging information has already been printed.
if (self::$debug_printed)
{
return;
}
else
{
self::$debug_printed = 1;
}
// Check if debugging is enabled for this request.
if (!config('debug.enabled') || !Rhymix\Framework\Debug::isEnabledForCurrentUser())
{
return;
}
// Do not display debugging information if there is no output.
$display_type = config('debug.display_type');
if ($output === null && $display_type !== 'file')
{
return;
}
// Print debug information.
switch ($display_type = config('debug.display_type'))
switch ($display_type)
{
case 'panel':
$data = Rhymix\Framework\Debug::getDebugData();

View file

@ -8,7 +8,6 @@
*/
class FileHandler
{
/**
* Changes path of target file, directory into absolute path
*
@ -17,12 +16,7 @@ class FileHandler
*/
public static function getRealPath($source)
{
if(strlen($source) >= 2 && substr_compare($source, './', 0, 2) === 0)
{
return _XE_PATH_ . substr($source, 2);
}
return $source;
return (strncmp($source, './', 2) === 0) ? (\RX_BASEDIR . substr($source, 2)) : $source;
}
/**
@ -33,64 +27,11 @@ class FileHandler
* @param string $source_dir Path of source directory
* @param string $target_dir Path of target dir
* @param string $filter Regex to filter files. If file matches this regex, the file is not copied.
* @param string $type If set as 'force'. Even if the file exists in target, the file is copied.
* @return void
*/
public static function copyDir($source_dir, $target_dir, $filter = null, $type = null)
public static function copyDir($source_dir, $target_dir, $filter = null)
{
$source_dir = self::getRealPath($source_dir);
$target_dir = self::getRealPath($target_dir);
if(!is_dir($source_dir))
{
return FALSE;
}
// generate when no target exists
self::makeDir($target_dir);
if(substr($source_dir, -1) != DIRECTORY_SEPARATOR)
{
$source_dir .= DIRECTORY_SEPARATOR;
}
if(substr($target_dir, -1) != DIRECTORY_SEPARATOR)
{
$target_dir .= DIRECTORY_SEPARATOR;
}
$oDir = dir($source_dir);
while($file = $oDir->read())
{
if($file{0} == '.')
{
continue;
}
if($filter && preg_match($filter, $file))
{
continue;
}
if(is_dir($source_dir . $file))
{
self::copyDir($source_dir . $file, $target_dir . $file, $type);
}
else
{
if($type == 'force')
{
@unlink($target_dir . $file);
}
else
{
if(!file_exists($target_dir . $file))
{
@copy($source_dir . $file, $target_dir . $file);
}
}
}
}
$oDir->close();
return Rhymix\Framework\Storage::copyDirectory(self::getRealPath($source_dir), self::getRealPath($target_dir), $filter);
}
/**
@ -104,18 +45,7 @@ class FileHandler
public static function copyFile($source, $target, $force = 'Y')
{
setlocale(LC_CTYPE, 'en_US.UTF8', 'ko_KR.UTF8');
$source = self::getRealPath($source);
$target_dir = self::getRealPath(dirname($target));
$target = basename($target);
self::makeDir($target_dir);
if($force == 'Y')
{
@unlink($target_dir . DIRECTORY_SEPARATOR . $target);
}
@copy($source, $target_dir . DIRECTORY_SEPARATOR . $target);
return Rhymix\Framework\Storage::copy(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -126,12 +56,7 @@ class FileHandler
*/
public static function readFile($filename)
{
if(($filename = self::exists($filename)) === FALSE || filesize($filename) < 1)
{
return;
}
return @file_get_contents($filename);
return Rhymix\Framework\Storage::read(self::getRealPath($filename));
}
/**
@ -144,22 +69,7 @@ class FileHandler
*/
public static function writeFile($filename, $buff, $mode = "w")
{
$filename = self::getRealPath($filename);
$pathinfo = pathinfo($filename);
self::makeDir($pathinfo['dirname']);
$flags = 0;
if(strtolower($mode) == 'a')
{
$flags = FILE_APPEND;
}
@file_put_contents($filename, $buff, $flags|LOCK_EX);
@chmod($filename, 0644);
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return Rhymix\Framework\Storage::write(self::getRealPath($filename), $buff, $mode);
}
/**
@ -170,16 +80,7 @@ class FileHandler
*/
public static function removeFile($filename)
{
if(($filename = self::exists($filename)) === false)
{
return false;
}
$status = @unlink($filename);
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $status;
return Rhymix\Framework\Storage::delete(self::getRealPath($filename));
}
/**
@ -193,7 +94,7 @@ class FileHandler
*/
public static function rename($source, $target)
{
return @rename(self::getRealPath($source), self::getRealPath($target));
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -205,12 +106,7 @@ class FileHandler
*/
public static function moveFile($source, $target)
{
if(($source = self::exists($source)) !== FALSE)
{
self::removeFile($target);
return self::rename($source, $target);
}
return FALSE;
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -224,7 +120,7 @@ class FileHandler
*/
public static function moveDir($source_dir, $target_dir)
{
self::rename($source_dir, $target_dir);
return Rhymix\Framework\Storage::move(self::getRealPath($source_dir), self::getRealPath($target_dir));
}
/**
@ -240,45 +136,31 @@ class FileHandler
*/
public static function readDir($path, $filter = '', $to_lower = FALSE, $concat_prefix = FALSE)
{
$path = self::getRealPath($path);
$list = Rhymix\Framework\Storage::readDirectory(self::getRealPath($path), $concat_prefix, true, false);
if (!$list)
{
return array();
}
$output = array();
if(substr($path, -1) != '/')
foreach ($list as $filename)
{
$path .= '/';
}
if(!is_dir($path))
{
return $output;
}
$files = scandir($path);
foreach($files as $file)
{
if($file{0} == '.' || ($filter && !preg_match($filter, $file)))
$filename = str_replace(array('/\\', '//'), '/', $filename);
$basename = $concat_prefix ? basename($filename) : $filename;
if ($basename[0] === '.' || ($filter && !preg_match($filter, $basename)))
{
continue;
}
if($to_lower)
if ($to_lower)
{
$file = strtolower($file);
$filename = strtolower($filename);
}
if($filter)
{
$file = preg_replace($filter, '$1', $file);
$filename = preg_replace($filter, '$1', $filename);
}
if($concat_prefix)
{
$file = sprintf('%s%s', str_replace(_XE_PATH_, '', $path), $file);
}
$output[] = str_replace(array('/\\', '//'), '/', $file);
$output[] = $filename;
}
return $output;
}
@ -292,16 +174,12 @@ class FileHandler
*/
public static function makeDir($path_string)
{
if(self::exists($path_string) !== FALSE)
if (!ini_get('safe_mode'))
{
return TRUE;
}
if(!ini_get('safe_mode'))
{
@mkdir($path_string, 0755, TRUE);
@chmod($path_string, 0755);
$path = self::getRealPath($path_string);
return Rhymix\Framework\Storage::isDirectory($path) || Rhymix\Framework\Storage::createDirectory($path);
}
// if safe_mode is on, use FTP
else
{
@ -372,37 +250,7 @@ class FileHandler
*/
public static function removeDir($path)
{
if(($path = self::isDir($path)) === FALSE)
{
return;
}
if(self::isDir($path))
{
$files = array_diff(scandir($path), array('..', '.'));
foreach($files as $file)
{
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
if(is_dir($target))
{
self::removeDir($target);
}
else
{
unlink($target);
}
}
rmdir($path);
}
else
{
unlink($path);
}
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path));
}
/**
@ -413,27 +261,14 @@ class FileHandler
*/
public static function removeBlankDir($path)
{
if(($path = self::isDir($path)) === FALSE)
$path = self::getRealPath($path);
if (Rhymix\Framework\Storage::isEmptyDirectory($path))
{
return;
return Rhymix\Framework\Storage::deleteDirectory($path);
}
$files = array_diff(scandir($path), array('..', '.'));
if(count($files) < 1)
else
{
rmdir($path);
return;
}
foreach($files as $file)
{
if(($target = self::isDir($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
self::removeBlankDir($target);
return false;
}
}
@ -447,37 +282,7 @@ class FileHandler
*/
public static function removeFilesInDir($path)
{
if(($path = self::getRealPath($path)) === FALSE)
{
return;
}
if(is_dir($path))
{
$files = array_diff(scandir($path), array('..', '.'));
foreach($files as $file)
{
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
if(is_dir($target))
{
self::removeFilesInDir($target);
}
else
{
unlink($target);
}
}
}
else
{
if(self::exists($path)) unlink($path);
}
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path), false);
}
/**
@ -584,7 +389,14 @@ class FileHandler
if($response->success)
{
return $response->body;
if (isset($request_config['filename']))
{
return true;
}
else
{
return $response->body;
}
}
else
{
@ -611,13 +423,9 @@ class FileHandler
*/
public static function getRemoteFile($url, $target_filename, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array(), $cookies = array(), $post_data = array(), $request_config = array())
{
if(!($body = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers,$cookies,$post_data,$request_config)))
{
return FALSE;
}
self::writeFile($target_filename, $body);
return TRUE;
$request_config['filename'] = $target_filename;
$success = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers, $cookies, $post_data, $request_config);
return $success ? true : false;
}
/**
@ -658,12 +466,17 @@ class FileHandler
$channels = 6; //for png
}
$memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR);
$availableMemory = self::returnBytes(ini_get('memory_limit')) - memory_get_usage();
$memoryLimit = self::returnBytes(ini_get('memory_limit'));
if($memoryLimit < 0)
{
return true;
}
$availableMemory = $memoryLimit - memory_get_usage();
if($availableMemory < $memoryNeeded)
{
return FALSE;
return false;
}
return TRUE;
return true;
}
/**
@ -882,19 +695,13 @@ class FileHandler
*/
public static function readIniFile($filename)
{
if(($filename = self::exists($filename)) === FALSE)
if(!Rhymix\Framework\Storage::isReadable($filename))
{
return FALSE;
}
$arr = parse_ini_file($filename, TRUE);
if(is_array($arr) && count($arr) > 0)
{
return $arr;
}
else
{
return array();
return false;
}
$arr = parse_ini_file($filename, true);
return is_array($arr) ? $arr : array();
}
/**
@ -966,11 +773,9 @@ class FileHandler
*/
public static function openFile($filename, $mode)
{
$pathinfo = pathinfo($filename);
self::makeDir($pathinfo['dirname']);
require_once("FileObject.class.php");
return new FileObject($filename, $mode);
$filename = self::getRealPath($filename);
Rhymix\Framework\Storage::createDirectory(dirname($filename));
return new FileObject($filename, $mode);
}
/**
@ -981,7 +786,7 @@ class FileHandler
*/
public static function hasContent($filename)
{
return (is_readable($filename) && (filesize($filename) > 0));
return Rhymix\Framework\Storage::getSize(self::getRealPath($filename)) > 0;
}
/**
@ -993,7 +798,7 @@ class FileHandler
public static function exists($filename)
{
$filename = self::getRealPath($filename);
return file_exists($filename) ? $filename : FALSE;
return Rhymix\Framework\Storage::exists($filename) ? $filename : false;
}
/**
@ -1005,7 +810,7 @@ class FileHandler
public static function isDir($path)
{
$path = self::getRealPath($path);
return is_dir($path) ? $path : FALSE;
return Rhymix\Framework\Storage::isDirectory($path) ? $path : false;
}
/**
@ -1017,22 +822,7 @@ class FileHandler
public static function isWritableDir($path)
{
$path = self::getRealPath($path);
if(is_dir($path)==FALSE)
{
return FALSE;
}
$checkFile = $path . '/_CheckWritableDir';
$fp = fopen($checkFile, 'w');
if(!is_resource($fp))
{
return FALSE;
}
fclose($fp);
self::removeFile($checkFile);
return TRUE;
return Rhymix\Framework\Storage::isDirectory($path) && Rhymix\Framework\Storage::isWritable($path);
}
}

View file

@ -460,81 +460,7 @@ class Mail
*/
function returnMIMEType($filename)
{
preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix);
switch(strtolower($fileSuffix[1]))
{
case "js" :
return "application/x-javascript";
case "json" :
return "application/json";
case "jpg" :
case "jpeg" :
case "jpe" :
return "image/jpg";
case "png" :
case "gif" :
case "bmp" :
case "tiff" :
return "image/" . strtolower($fileSuffix[1]);
case "css" :
return "text/css";
case "xml" :
return "application/xml";
case "doc" :
case "docx" :
return "application/msword";
case "xls" :
case "xlt" :
case "xlm" :
case "xld" :
case "xla" :
case "xlc" :
case "xlw" :
case "xll" :
return "application/vnd.ms-excel";
case "ppt" :
case "pps" :
return "application/vnd.ms-powerpoint";
case "rtf" :
return "application/rtf";
case "pdf" :
return "application/pdf";
case "html" :
case "htm" :
case "php" :
return "text/html";
case "txt" :
return "text/plain";
case "mpeg" :
case "mpg" :
case "mpe" :
return "video/mpeg";
case "mp3" :
return "audio/mpeg3";
case "wav" :
return "audio/wav";
case "aiff" :
case "aif" :
return "audio/aiff";
case "avi" :
return "video/msvideo";
case "wmv" :
return "video/x-ms-wmv";
case "mov" :
return "video/quicktime";
case "zip" :
return "application/zip";
case "tar" :
return "application/x-tar";
case "swf" :
return "application/x-shockwave-flash";
default :
if(function_exists("mime_content_type"))
{
$fileSuffix = mime_content_type($filename);
}
return "unknown/" . trim($fileSuffix[0], ".");
}
return Rhymix\Framework\MIME::getTypeByFilename($filename);
}
}

View file

@ -55,9 +55,6 @@ class Mobile
return $this->ismobile = false;
}
$xe_web_path = Context::pathToUrl(_XE_PATH_);
// default setting. if there is cookie for a device, XE do not have to check if it is mobile or not and it will enhance performace of the server.
$this->ismobile = FALSE;
$m = Context::get('m');
@ -87,28 +84,14 @@ class Mobile
}
else
{
$this->ismobile = FALSE;
setcookie("mobile", FALSE, 0, $xe_web_path);
setcookie("user-agent", FALSE, 0, $xe_web_path);
if(!self::isMobilePadCheckByAgent() && self::isMobileCheckByAgent())
{
$this->ismobile = TRUE;
}
setcookie("mobile", FALSE, 0, RX_BASEURL);
setcookie("user-agent", FALSE, 0, RX_BASEURL);
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
}
}
else
{
if(self::isMobilePadCheckByAgent())
{
$this->ismobile = FALSE;
}
else
{
if(self::isMobileCheckByAgent())
{
$this->ismobile = TRUE;
}
}
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
}
if($this->ismobile !== NULL)
@ -118,18 +101,18 @@ class Mobile
if($_COOKIE['mobile'] != 'true')
{
$_COOKIE['mobile'] = 'true';
setcookie("mobile", 'true', 0, $xe_web_path);
setcookie("mobile", 'true', 0, RX_BASEURL);
}
}
elseif(isset($_COOKIE['mobile']) && $_COOKIE['mobile'] != 'false')
{
$_COOKIE['mobile'] = 'false';
setcookie("mobile", 'false', 0, $xe_web_path);
setcookie("mobile", 'false', 0, RX_BASEURL);
}
if(isset($_COOKIE['mobile']) && $_COOKIE['user-agent'] != md5($_SERVER['HTTP_USER_AGENT']))
{
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, $xe_web_path);
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, RX_BASEURL);
}
}
@ -143,31 +126,7 @@ class Mobile
*/
public static function isMobileCheckByAgent()
{
static $UACheck;
if(isset($UACheck))
{
return $UACheck;
}
$oMobile = Mobile::getInstance();
$mobileAgent = array('iPod', 'iPhone', 'Android', 'BlackBerry', 'SymbianOS', 'Bada', 'Tizen', 'Kindle', 'Wii', 'SCH-', 'SPH-', 'CANU-', 'Windows Phone', 'Windows CE', 'POLARIS', 'Palm', 'Dorothy Browser', 'Mobile', 'Opera Mobi', 'Opera Mini', 'Minimo', 'AvantGo', 'NetFront', 'Nokia', 'LGPlayer', 'SonyEricsson', 'HTC');
if($oMobile->isMobilePadCheckByAgent())
{
$UACheck = TRUE;
return TRUE;
}
foreach($mobileAgent as $agent)
{
if(stripos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
{
$UACheck = TRUE;
return TRUE;
}
}
$UACheck = FALSE;
return FALSE;
return Rhymix\Framework\UA::isMobile();
}
/**
@ -177,45 +136,7 @@ class Mobile
*/
public static function isMobilePadCheckByAgent()
{
static $UACheck;
if(isset($UACheck))
{
return $UACheck;
}
$padAgent = array('iPad', 'Android', 'webOS', 'hp-tablet', 'PlayBook');
// Android with 'Mobile' string is not a tablet-like device, and 'Andoroid' without 'Mobile' string is a tablet-like device.
// $exceptionAgent[0] contains exception agents for all exceptions.
$exceptionAgent = array(0 => array('Opera Mini', 'Opera Mobi'), 'Android' => 'Mobile');
foreach($padAgent as $agent)
{
if(strpos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
{
if(!isset($exceptionAgent[$agent]))
{
$UACheck = TRUE;
return TRUE;
}
elseif(strpos($_SERVER['HTTP_USER_AGENT'], $exceptionAgent[$agent]) === FALSE)
{
// If the agent is the Android, that can be either tablet and mobile phone.
foreach($exceptionAgent[0] as $val)
{
if(strpos($_SERVER['HTTP_USER_AGENT'], $val) !== FALSE)
{
$UACheck = FALSE;
return FALSE;
}
}
$UACheck = TRUE;
return TRUE;
}
}
}
$UACheck = FALSE;
return FALSE;
return Rhymix\Framework\UA::isTablet();
}
/**

View file

@ -96,10 +96,13 @@ class ModuleHandler extends Handler
{
if(Context::get('_use_ssl') == 'optional' && Context::isExistsSSLAction($this->act) && !RX_SSL)
{
if(Context::get('_https_port')!=null) {
header('location:https://' . $_SERVER['HTTP_HOST'] . ':' . Context::get('_https_port') . $_SERVER['REQUEST_URI']);
} else {
header('location:https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
if(Context::get('_https_port') != null)
{
header('location: https://' . $_SERVER['HTTP_HOST'] . ':' . Context::get('_https_port') . $_SERVER['REQUEST_URI']);
}
else
{
header('location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
}
return;
}
@ -165,12 +168,21 @@ class ModuleHandler extends Handler
// Get module's information based on document_srl, if it's specified
if($this->document_srl)
{
$module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl);
// If the document does not exist, remove document_srl
if(!$module_info)
{
unset($this->document_srl);
if(Context::getRequestMethod() == 'GET')
{
$this->error = 'The document does not exist';
$this->httpStatusCode = '404';
return true;
}
else
{
unset($this->document_srl);
}
}
else
{
@ -178,19 +190,17 @@ class ModuleHandler extends Handler
// if mids are not matching, set it as the document's mid
if(!$this->mid || ($this->mid != $module_info->mid))
{
if(Context::getRequestMethod() == 'GET')
{
$this->mid = $module_info->mid;
header('location:' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $this->mid, 'document_srl', $this->document_srl));
return FALSE;
Context::setCacheControl(0);
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301);
return false;
}
else
{
$this->mid = $module_info->mid;
Context::set('mid', $this->mid);
}
}
// if requested module is different from one of the document, remove the module information retrieved based on the document number
if($this->module && $module_info->module != $this->module)
@ -198,7 +208,6 @@ class ModuleHandler extends Handler
unset($module_info);
}
}
}
// If module_info is not set yet, and there exists mid information, get module information based on the mid
@ -211,9 +220,10 @@ class ModuleHandler extends Handler
// redirect, if module_site_srl and site_srl are different
if(!$this->module && !$module_info && $site_module_info->site_srl == 0 && $site_module_info->module_site_srl > 0)
{
Context::setCacheControl(0);
$site_info = $oModuleModel->getSiteInfo($site_module_info->module_site_srl);
header("location:" . getNotEncodedSiteUrl($site_info->domain, 'mid', $site_module_info->mid));
return FALSE;
header('location: ' . getNotEncodedSiteUrl($site_info->domain, 'mid', $site_module_info->mid), true, 301);
return false;
}
// If module_info is not set still, and $module does not exist, find the default module
@ -228,7 +238,7 @@ class ModuleHandler extends Handler
}
// redirect, if site_srl of module_info is different from one of site's module_info
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !isCrawler())
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !Rhymix\Framework\UA::isRobot())
{
// If the module is of virtual site
if($module_info->site_srl)
@ -241,10 +251,20 @@ class ModuleHandler extends Handler
{
$redirect_url = getNotEncodedSiteUrl(Context::getDefaultUrl(), 'mid', Context::get('mid'), 'document_srl', Context::get('document_srl'), 'module_srl', Context::get('module_srl'), 'entry', Context::get('entry'));
}
header("Location: $redirect_url");
return FALSE;
Context::setCacheControl(0);
header("Location: $redirect_url", true, 301);
return false;
}
// redirect, if site start module
if(isset($_GET['mid']) && $_GET['mid'] === $site_module_info->mid && !$this->act && !$this->module && !$this->document_srl)
{
Context::setCacheControl(0);
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain), true, 301);
return false;
}
// If module info was set, retrieve variables from the module information
if($module_info)
{
@ -290,6 +310,7 @@ class ModuleHandler extends Handler
{
$this->error = 'msg_module_is_not_exists';
$this->httpStatusCode = '404';
return true;
}
// If mid exists, set mid into context
@ -303,13 +324,13 @@ class ModuleHandler extends Handler
if(!$output->toBool())
{
$this->error = $output->getMessage();
return TRUE;
return true;
}
// Set current module info into context
Context::set('current_module_info', $this->module_info);
return TRUE;
return true;
}
/**
@ -465,7 +486,6 @@ class ModuleHandler extends Handler
// If there is no such action in the module object
if(!isset($xml_info->action->{$this->act}) || !method_exists($oModule, $this->act))
{
if(!Context::isInstalled())
{
self::_setInputErrorToContext();
@ -760,10 +780,6 @@ class ModuleHandler extends Handler
}
self::_setInputValueToSession();
}
else
{
}
if($error != 0)
{
@ -884,7 +900,7 @@ class ModuleHandler extends Handler
if($_SESSION['XE_VALIDATOR_RETURN_URL'])
{
header('location:' . $_SESSION['XE_VALIDATOR_RETURN_URL']);
header('location: ' . $_SESSION['XE_VALIDATOR_RETURN_URL']);
return;
}
@ -939,13 +955,11 @@ class ModuleHandler extends Handler
if($layout_srl && !$oModule->getLayoutFile())
{
// If layout_srl exists, get information of the layout, and set the location of layout_path/ layout_file
$oLayoutModel = getModel('layout');
$layout_info = $oLayoutModel->getLayout($layout_srl);
if($layout_info)
{
// Input extra_vars into $layout_info
if($layout_info->extra_var_count)
{
@ -1283,6 +1297,8 @@ class ModuleHandler extends Handler
$statusMessageList = array(
'100' => 'Continue',
'101' => 'Switching Protocols',
'102' => 'Processing',
'103' => 'Checkpoint',
'200' => 'OK',
'201' => 'Created',
'202' => 'Accepted',
@ -1290,13 +1306,18 @@ class ModuleHandler extends Handler
'204' => 'No Content',
'205' => 'Reset Content',
'206' => 'Partial Content',
'207' => 'Multi-Status',
'208' => 'Already Reported',
'226' => 'IM Used',
'300' => 'Multiple Choices',
'301' => 'Moved Permanently',
'302' => 'Found',
'303' => 'See Other',
'304' => 'Not Modified',
'305' => 'Use Proxy',
'306' => 'Switch Proxy',
'307' => 'Temporary Redirect',
'308' => 'Permanent Redirect',
'400' => 'Bad Request',
'401' => 'Unauthorized',
'402' => 'Payment Required',
@ -1310,19 +1331,39 @@ class ModuleHandler extends Handler
'410' => 'Gone',
'411' => 'Length Required',
'412' => 'Precondition Failed',
'413' => 'Request Entity Too Large',
'414' => 'Request-URI Too Long',
'413' => 'Payload Too Large',
'414' => 'URI Too Long',
'415' => 'Unsupported Media Type',
'416' => 'Requested Range Not Satisfiable',
'416' => 'Range Not Satisfiable',
'417' => 'Expectation Failed',
'418' => 'I\'m a teapot',
'420' => 'Enhance Your Calm',
'421' => 'Misdirected Request',
'422' => 'Unprocessable Entity',
'423' => 'Locked',
'424' => 'Failed Dependency',
'425' => 'Unordered Collection',
'426' => 'Upgrade Required',
'428' => 'Precondition Required',
'429' => 'Too Many Requests',
'431' => 'Request Header Fields Too Large',
'444' => 'No Response',
'449' => 'Retry With',
'451' => 'Unavailable For Legal Reasons',
'500' => 'Internal Server Error',
'501' => 'Not Implemented',
'502' => 'Bad Gateway',
'503' => 'Service Unavailable',
'504' => 'Gateway Timeout',
'505' => 'HTTP Version Not Supported',
'506' => 'Variant Also Negotiates',
'507' => 'Insufficient Storage',
'508' => 'Loop Detected',
'509' => 'Bandwidth Limit Exceeded',
'510' => 'Not Extended',
'511' => 'Network Authentication Required',
);
$statusMessage = $statusMessageList[$code];
$statusMessage = $statusMessageList[strval($code)];
if(!$statusMessage)
{
$statusMessage = 'OK';

View file

@ -3,313 +3,78 @@
class EmbedFilter
{
/**
* allow script access list
* Deprecated properties
* @var array
*/
var $allowscriptaccessList = array();
/**
* allow script access key
* @var int
*/
var $allowscriptaccessKey = 0;
var $whiteUrlList = array();
var $whiteIframeUrlList = array();
var $mimeTypeList = array();
var $extList = array();
var $parser = NULL;
/**
* @constructor
* @return void
*/
function __construct()
{
$this->_makeWhiteDomainList();
}
public $whiteUrlList = array();
public $whiteIframeUrlList = array();
public $mimeTypeList = array();
public $extList = array();
/**
* Return EmbedFilter object
* This method for singleton
*
* @return EmbedFilter
*/
function getInstance()
{
if(!isset($GLOBALS['__EMBEDFILTER_INSTANCE__']))
{
$GLOBALS['__EMBEDFILTER_INSTANCE__'] = new EmbedFilter();
}
return $GLOBALS['__EMBEDFILTER_INSTANCE__'];
return new self();
}
public function getWhiteUrlList()
{
return $this->whiteUrlList;
return Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist();
}
public function getWhiteIframeUrlList()
{
return $this->whiteIframeUrlList;
return Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist();
}
/**
* Check the content.
* @return void
*/
function check(&$content)
{
$content = preg_replace_callback('/<(object|param|embed)[^>]*/is', array($this, '_checkAllowScriptAccess'), $content);
$content = preg_replace_callback('/<object[^>]*>/is', array($this, '_addAllowScriptAccess'), $content);
$this->checkObjectTag($content);
$this->checkEmbedTag($content);
$this->checkParamTag($content);
}
/**
* Check iframe tag in the content.
* @return void
*/
function checkIframeTag(&$content)
{
// check in Purifier class
return;
}
/**
* Check object tag in the content.
* @return void
*/
function checkObjectTag(&$content)
{
$content = preg_replace_callback('/<\s*object\s*[^>]+(?:\/?>?)/is', function($m) {
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
foreach ($html->find('object') as $element)
{
if ($element->data && !$this->isWhiteDomain($element->data))
{
return escape($m[0], false);
}
if ($element->type && !$this->isWhiteMimetype($element->type))
{
return escape($m[0], false);
}
}
return $m[0];
}, $content);
}
/**
* Check embed tag in the content.
* @return void
*/
function checkEmbedTag(&$content)
{
$content = preg_replace_callback('/<\s*embed\s*[^>]+(?:\/?>?)/is', function($m) {
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
foreach ($html->find('embed') as $element)
{
if ($element->src && !$this->isWhiteDomain($element->src))
{
return escape($m[0], false);
}
if ($element->type && !$this->isWhiteMimetype($element->type))
{
return escape($m[0], false);
}
}
return $m[0];
}, $content);
}
/**
* Check param tag in the content.
* @return void
*/
function checkParamTag(&$content)
{
$content = preg_replace_callback('/<\s*param\s*[^>]+(?:\/?>?)/is', function($m) {
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
foreach ($html->find('param') as $element)
{
foreach (array('movie', 'src', 'href', 'url', 'source') as $attr)
{
if ($element->$attr && !$this->isWhiteDomain($element->$attr))
{
return escape($m[0], false);
}
}
}
return $m[0];
}, $content);
}
/**
* Check white domain in object data attribute or embed src attribute.
* @return string
*/
function isWhiteDomain($urlAttribute)
{
if(is_array($this->whiteUrlList))
{
foreach($this->whiteUrlList AS $key => $value)
{
if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute))
{
return TRUE;
}
}
}
return FALSE;
return Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist($urlAttribute);
}
/**
* Check white domain in iframe src attribute.
* @return string
*/
function isWhiteIframeDomain($urlAttribute)
{
if(is_array($this->whiteIframeUrlList))
{
foreach($this->whiteIframeUrlList AS $key => $value)
{
if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute))
{
return TRUE;
}
}
}
return FALSE;
return Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist($urlAttribute);
}
/**
* Check white mime type in object type attribute or embed type attribute.
* @return string
*/
function isWhiteMimetype($mimeType)
{
if(isset($this->mimeTypeList[$mimeType]))
{
return TRUE;
}
return FALSE;
return true;
}
function isWhiteExt($ext)
{
if(isset($this->extList[$ext]))
{
return TRUE;
}
return FALSE;
return true;
}
function _checkAllowScriptAccess($m)
function check(&$content)
{
if($m[1] == 'object')
{
$this->allowscriptaccessList[] = 1;
}
if($m[1] == 'param')
{
if(stripos($m[0], 'allowscriptaccess'))
{
$m[0] = '<param name="allowscriptaccess" value="never"';
if(substr($m[0], -1) == '/')
{
$m[0] .= '/';
}
$this->allowscriptaccessList[count($this->allowscriptaccessList) - 1]--;
}
}
else if($m[1] == 'embed')
{
if(stripos($m[0], 'allowscriptaccess'))
{
$m[0] = preg_replace('/always|samedomain/i', 'never', $m[0]);
}
else
{
$m[0] = preg_replace('/\<embed/i', '<embed allowscriptaccess="never"', $m[0]);
}
}
return $m[0];
// This functionality has been moved to the HTMLFilter class.
}
function _addAllowScriptAccess($m)
function checkIframeTag(&$content)
{
if($this->allowscriptaccessList[$this->allowscriptaccessKey] == 1)
{
$m[0] = $m[0] . '<param name="allowscriptaccess" value="never"></param>';
}
$this->allowscriptaccessKey++;
return $m[0];
// This functionality has been moved to the HTMLFilter class.
}
/**
* Make white domain list cache file from xml config file.
* @param $whitelist array
* @return void
*/
function _makeWhiteDomainList($whitelist = NULL)
function checkObjectTag(&$content)
{
$whiteUrlDefaultList = (include RX_BASEDIR . 'common/defaults/whitelist.php');
$this->extList = $whiteUrlDefaultList['extensions'];
$this->mimeTypeList = $whiteUrlDefaultList['mime'];
$this->whiteUrlList = array();
$this->whiteIframeUrlList = array();
if($whitelist !== NULL)
{
if(!is_array($whitelist) || !isset($whitelist['object']) || !isset($whitelist['iframe']))
{
$whitelist = array(
'object' => isset($whitelist->object) ? $whitelist->object : array(),
'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(),
);
}
foreach ($whitelist['object'] as $prefix)
{
$this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
}
foreach ($whitelist['iframe'] as $prefix)
{
$this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
}
}
else
{
foreach ($whiteUrlDefaultList['object'] as $prefix)
{
$this->whiteUrlList[] = $prefix;
}
foreach ($whiteUrlDefaultList['iframe'] as $prefix)
{
$this->whiteIframeUrlList[] = $prefix;
}
if ($embedfilter_object = config('embedfilter.object'))
{
foreach ($embedfilter_object as $prefix)
{
$this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
}
}
if ($embedfilter_iframe = config('embedfilter.iframe'))
{
foreach ($embedfilter_iframe as $prefix)
{
$this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
}
}
}
$this->whiteUrlList = array_unique($this->whiteUrlList);
$this->whiteIframeUrlList = array_unique($this->whiteIframeUrlList);
natcasesort($this->whiteUrlList);
natcasesort($this->whiteIframeUrlList);
// This functionality has been moved to the HTMLFilter class.
}
function checkEmbedTag(&$content)
{
// This functionality has been moved to the HTMLFilter class.
}
function checkParamTag(&$content)
{
// This functionality has been moved to the HTMLFilter class.
}
}
/* End of file : EmbedFilter.class.php */

View file

@ -6,26 +6,12 @@ class IpFilter
public function filter($ip_list, $ip = NULL)
{
if(!$ip) $ip = $_SERVER['REMOTE_ADDR'];
foreach($ip_list as $filter)
{
if(Rhymix\Framework\IpFilter::inRange($ip, $filter))
{
return true;
}
}
return false;
return Rhymix\Framework\Filters\IpFilter::inRanges($ip, $ip_list);
}
public function validate($ip_list = array())
{
foreach($ip_list as $filter)
{
if(!Rhymix\Framework\IpFilter::validateRange($filter))
{
return false;
}
}
return true;
return Rhymix\Framework\Filters\IpFilter::validateRanges($ip_list);
}
}

View file

@ -1,458 +1,79 @@
<?php
/* Copyright (C) NAVER <http://www.navercorp.com> */
/**
* This class can be used to hash passwords using various algorithms and check their validity.
* It is fully compatible with previous defaults, while also supporting bcrypt and pbkdf2.
*
* @file Password.class.php
* @author Kijin Sung (kijin@kijinsung.com)
* @package /classes/security
* @version 1.1
*/
class Password
{
/**
* @brief Custom algorithms are stored here
* @var array
*/
protected static $_custom = array();
/**
* @brief Register a custom algorithm for password checking
* @param string $name The name of the algorithm
* @param string $regexp The regular expression to detect the algorithm
* @param callable $callback The function to call to regenerate the hash
* @return void
*/
public static function registerCustomAlgorithm($name, $regexp, $callback)
{
self::$_custom[$name] = array('regexp' => $regexp, 'callback' => $callback);
Rhymix\Framework\Password::addAlgorithm($name, $regexp, $callback);
}
/**
* @brief Return the list of hashing algorithms supported by this server
* @return array
*/
public function getSupportedAlgorithms()
{
$retval = array();
if(function_exists('hash_hmac') && in_array('sha256', hash_algos()))
{
$retval['pbkdf2'] = 'pbkdf2';
}
if(version_compare(PHP_VERSION, '5.3.7', '>=') && defined('CRYPT_BLOWFISH'))
{
$retval['bcrypt'] = 'bcrypt';
}
$retval['md5'] = 'md5';
return $retval;
return Rhymix\Framework\Password::getSupportedAlgorithms();
}
/**
* @brief Return the best hashing algorithm supported by this server
* @return string
*/
public function getBestAlgorithm()
{
$algos = $this->getSupportedAlgorithms();
return key($algos);
return Rhymix\Framework\Password::getBestSupportedAlgorithm();
}
/**
* @brief Return the currently selected hashing algorithm
* @return string
*/
public function getCurrentlySelectedAlgorithm()
{
if(function_exists('getModel'))
{
$config = getModel('member')->getMemberConfig();
$algorithm = $config->password_hashing_algorithm;
if(strval($algorithm) === '')
{
$algorithm = 'md5'; // Historical default for XE
}
}
else
{
$algorithm = 'md5';
}
return $algorithm;
return Rhymix\Framework\Password::getDefaultAlgorithm();
}
/**
* @brief Return the currently configured work factor for bcrypt and other adjustable algorithms
* @return int
*/
public function getWorkFactor()
{
if(function_exists('getModel'))
{
$config = getModel('member')->getMemberConfig();
$work_factor = $config->password_hashing_work_factor;
if(!$work_factor || $work_factor < 4 || $work_factor > 31)
{
$work_factor = 8; // Reasonable default
}
}
else
{
$work_factor = 8;
}
return $work_factor;
return Rhymix\Framework\Password::getWorkFactor();
}
/**
* @brief Create a hash using the specified algorithm
* @param string $password The password
* @param string $algorithm The algorithm (optional)
* @return string
*/
public function createHash($password, $algorithm = null)
{
if($algorithm === null)
{
$algorithm = $this->getCurrentlySelectedAlgorithm();
}
if(!array_key_exists($algorithm, $this->getSupportedAlgorithms()))
{
return false;
}
$password = trim($password);
switch($algorithm)
{
case 'md5':
return md5($password);
case 'pbkdf2':
$iterations = pow(2, $this->getWorkFactor() + 5);
$salt = $this->createSecureSalt(12, 'alnum');
$hash = base64_encode($this->pbkdf2($password, $salt, 'sha256', $iterations, 24));
return 'sha256:'.sprintf('%07d', $iterations).':'.$salt.':'.$hash;
case 'bcrypt':
return $this->bcrypt($password);
default:
return false;
}
return Rhymix\Framework\Password::hashPassword($password, $algorithm);
}
/**
* @brief Check if a password matches a hash
* @param string $password The password
* @param string $hash The hash
* @param string $algorithm The algorithm (optional)
* @return bool
*/
public function checkPassword($password, $hash, $algorithm = null)
{
if($algorithm === null)
{
$algorithm = $this->checkAlgorithm($hash);
}
$password = trim($password);
switch($algorithm)
{
case 'md5':
return md5($password) === $hash || md5(sha1(md5($password))) === $hash;
case 'mysql_old_password':
return (class_exists('Context') && substr(Context::getDBType(), 0, 5) === 'mysql') ?
DB::getInstance()->isValidOldPassword($password, $hash) : false;
case 'mysql_password':
return $hash[0] === '*' && substr($hash, 1) === strtoupper(sha1(sha1($password, true)));
case 'pbkdf2':
$hash = explode(':', $hash);
$hash[3] = base64_decode($hash[3]);
$hash_to_compare = $this->pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
return $this->strcmpConstantTime($hash_to_compare, $hash[3]);
case 'bcrypt':
$hash_to_compare = $this->bcrypt($password, $hash);
return $this->strcmpConstantTime($hash_to_compare, $hash);
default:
if($algorithm && isset(self::$_custom[$algorithm]))
{
$hash_callback = self::$_custom[$algorithm]['callback'];
$hash_to_compare = $hash_callback($password, $hash);
return $this->strcmpConstantTime($hash_to_compare, $hash);
}
if(in_array($algorithm, hash_algos()))
{
return $this->strcmpConstantTime(hash($algorithm, $password), $hash);
}
return false;
}
return Rhymix\Framework\Password::checkPassword($password, $hash, $algorithm);
}
/**
* @brief Check the algorithm used to create a hash
* @param string $hash The hash
* @return string
*/
function checkAlgorithm($hash)
{
foreach(self::$_custom as $name => $definition)
{
if(preg_match($definition['regexp'], $hash))
{
return $name;
}
}
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
{
return 'bcrypt';
}
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
{
return 'pbkdf2';
}
elseif(strlen($hash) === 32 && ctype_xdigit($hash))
{
return 'md5';
}
elseif(strlen($hash) === 40 && ctype_xdigit($hash))
{
return 'sha1';
}
elseif(strlen($hash) === 64 && ctype_xdigit($hash))
{
return 'sha256';
}
elseif(strlen($hash) === 96 && ctype_xdigit($hash))
{
return 'sha384';
}
elseif(strlen($hash) === 128 && ctype_xdigit($hash))
{
return 'sha512';
}
elseif(strlen($hash) === 16 && ctype_xdigit($hash))
{
return 'mysql_old_password';
}
elseif(strlen($hash) === 41 && $hash[0] === '*')
{
return 'mysql_password';
}
else
{
return false;
}
$algos = Rhymix\Framework\Password::checkAlgorithm($hash);
return count($algos) ? $algos[0] : false;
}
/**
* @brief Check the work factor of a hash
* @param string $hash The hash
* @return int
*/
function checkWorkFactor($hash)
{
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
{
return intval($matches[1], 10);
}
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
{
return max(0, round(log($matches[1], 2)) - 5);
}
else
{
return false;
}
return Rhymix\Framework\Password::checkWorkFactor($hash);
}
/**
* @brief Generate a cryptographically secure random string to use as a salt
* @param int $length The number of bytes to return
* @param string $format hex or alnum
* @return string
*/
public function createSecureSalt($length, $format = 'hex')
{
// Find out how many bytes of entropy we really need
switch($format)
{
case 'hex':
$entropy_required_bytes = ceil($length / 2);
break;
case 'alnum':
case 'printable':
$entropy_required_bytes = ceil($length * 3 / 4);
break;
default:
$entropy_required_bytes = $length;
}
// Cap entropy to 256 bits from any one source, because anything more is meaningless
$entropy_capped_bytes = min(32, $entropy_required_bytes);
// Find and use the most secure way to generate a random string
$entropy = false;
$is_windows = (defined('PHP_OS') && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
if(function_exists('random_bytes')) // PHP 7
{
$entropy = random_bytes($entropy_capped_bytes);
}
elseif(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION, '5.4', '>=')))
{
$entropy = openssl_random_pseudo_bytes($entropy_capped_bytes);
}
elseif(function_exists('mcrypt_create_iv') && (!$is_windows || version_compare(PHP_VERSION, '5.3.7', '>=')))
{
$entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_DEV_URANDOM);
}
elseif(function_exists('mcrypt_create_iv') && $is_windows)
{
$entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_RAND);
}
elseif(!$is_windows && @is_readable('/dev/urandom'))
{
$fp = fopen('/dev/urandom', 'rb');
if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM
{
stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom
}
$entropy = fread($fp, $entropy_capped_bytes);
fclose($fp);
}
// Use built-in source of entropy if an error occurs while using other functions
if($entropy === false || strlen($entropy) < $entropy_capped_bytes)
{
$entropy = '';
for($i = 0; $i < $entropy_capped_bytes; $i += 2)
{
$entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535));
}
}
// Mixing (see RFC 4086 section 5)
$output = '';
for($i = 0; $i < $entropy_required_bytes; $i += 32)
{
$output .= hash('sha256', $entropy . $i . rand(), true);
}
// Encode and return the random string
switch($format)
{
case 'hex':
return substr(bin2hex($output), 0, $length);
case 'binary':
return substr($output, 0, $length);
case 'printable':
$salt = '';
for($i = 0; $i < $length; $i++)
{
$salt .= chr(33 + (crc32(sha1($i . $output)) % 94));
}
return $salt;
case 'alnum':
default:
$salt = substr(base64_encode($output), 0, $length);
$replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9);
return strtr($salt, '+/=', $replacements);
}
return Rhymix\Framework\Security::getRandom($length, $format);
}
/**
* @brief Generate a temporary password using the secure salt generator
* @param int $length The number of bytes to return
* @return string
*/
public function createTemporaryPassword($length = 16)
{
while(true)
{
$source = base64_encode($this->createSecureSalt(64, 'binary'));
$source = strtr($source, 'iIoOjl10/', '@#$%&*-!?');
$source_length = strlen($source);
for($i = 0; $i < $source_length - $length; $i++)
{
$candidate = substr($source, $i, $length);
if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) &&
preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate))
{
return $candidate;
}
}
}
return Rhymix\Framework\Password::getRandomPassword($length);
}
/**
* @brief Generate the PBKDF2 hash of a string using a salt
* @param string $password The password
* @param string $salt The salt
* @param string $algorithm The algorithm (optional, default is sha256)
* @param int $iterations Iteration count (optional, default is 8192)
* @param int $length The length of the hash (optional, default is 32)
* @return string
*/
public function pbkdf2($password, $salt, $algorithm = 'sha256', $iterations = 8192, $length = 24)
{
if(function_exists('hash_pbkdf2'))
{
return hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true);
}
else
{
$output = '';
$block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash
for($i = 1; $i <= $block_count; $i++)
{
$last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian
$last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration
for($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations
{
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
return substr($output, 0, $length);
}
$hash = Rhymix\Framework\Security::pbkdf2($password, $salt, $algorithm, $iterations, $length);
$hash = explode(':', $hash);
return base64_decode($hash[3]);
}
/**
* @brief Generate the bcrypt hash of a string using a salt
* @param string $password The password
* @param string $salt The salt (optional, auto-generated if empty)
* @return string
*/
public function bcrypt($password, $salt = null)
{
if($salt === null)
{
$salt = '$2y$'.sprintf('%02d', $this->getWorkFactor()).'$'.$this->createSecureSalt(22, 'alnum');
}
return crypt($password, $salt);
return Rhymix\Framework\Security::bcrypt($password, $salt);
}
/**
* @brief Compare two strings in constant time
* @param string $a The first string
* @param string $b The second string
* @return bool
*/
function strcmpConstantTime($a, $b)
{
$diff = strlen($a) ^ strlen($b);
$maxlen = min(strlen($a), strlen($b));
for($i = 0; $i < $maxlen; $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
return Rhymix\Framework\Security::compareStrings($a, $b);
}
}
/* End of file : Password.class.php */

View file

@ -3,161 +3,14 @@
class Purifier
{
private $_cacheDir;
private $_htmlPurifier;
private $_config;
private $_def;
public function __construct()
public static function getInstance()
{
$this->_checkCacheDir();
$this->_setConfig();
return new self();
}
public function getInstance()
{
if(!isset($GLOBALS['__PURIFIER_INSTANCE__']))
{
$GLOBALS['__PURIFIER_INSTANCE__'] = new Purifier();
}
return $GLOBALS['__PURIFIER_INSTANCE__'];
}
private function _setConfig()
{
$this->_config = HTMLPurifier_Config::createDefault();
$this->_config->set('HTML.TidyLevel', 'light');
$this->_config->set('Output.FlashCompat', TRUE);
$this->_config->set('HTML.SafeObject', TRUE);
$this->_config->set('HTML.SafeEmbed', TRUE);
$this->_config->set('HTML.SafeIframe', TRUE);
$this->_config->set('URI.SafeIframeRegexp', $this->_getWhiteDomainRegexp());
$this->_config->set('Cache.SerializerPath', $this->_cacheDir);
$this->_config->set('Attr.AllowedFrameTargets', array('_blank'));
//$allowdClasses = array('emoticon');
//$this->_config->set('Attr.AllowedClasses', $allowdClasses);
$this->_def = $this->_config->getHTMLDefinition(TRUE);
}
private function _setDefinition(&$content)
{
// add attribute for edit component
$editComponentAttrs = $this->_searchEditComponent($content);
if(is_array($editComponentAttrs))
{
foreach($editComponentAttrs AS $k => $v)
{
$this->_def->addAttribute('img', $v, 'CDATA');
$this->_def->addAttribute('div', $v, 'CDATA');
}
}
// add attribute for widget component
$widgetAttrs = $this->_searchWidget($content);
if(is_array($widgetAttrs))
{
foreach($widgetAttrs AS $k => $v)
{
$this->_def->addAttribute('img', $v, 'CDATA');
}
}
}
/**
* Search attribute of edit component tag
* @param string $content
* @return array
*/
private function _searchEditComponent($content)
{
preg_match_all('!<(?:(div)|img)([^>]*)editor_component=([^>]*)>(?(1)(.*?)</div>)!is', $content, $m);
$attributeList = array();
if(is_array($m[2]))
{
foreach($m[2] as $key => $value)
{
unset($script, $m2);
$script = " {$m[2][$key]} editor_component={$m[3][$key]}";
if(preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $script, $m2))
{
foreach($m2[1] as $value2)
{
//SECISSUE check style attr
if($value2 == 'style')
{
continue;
}
$attributeList[] = $value2;
}
}
}
}
return array_unique($attributeList);
}
/**
* Search edit component tag
* @param string $content
* @return array
*/
private function _searchWidget(&$content)
{
preg_match_all('!<(?:(div)|img)([^>]*)class="zbxe_widget_output"([^>]*)>(?(1)(.*?)</div>)!is', $content, $m);
$attributeList = array();
if(is_array($m[3]))
{
$content = str_replace('<img class="zbxe_widget_output"', '<img src="" class="zbxe_widget_output"', $content);
foreach($m[3] as $key => $value)
{
if (preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $m[3][$key], $m2))
{
foreach($m2[1] as $value2)
{
//SECISSUE check style attr
if($value2 == 'style')
{
continue;
}
$attributeList[] = $value2;
}
}
}
}
return array_unique($attributeList);
}
private function _getWhiteDomainRegexp()
{
$oEmbedFilter = EmbedFilter::getInstance();
$whiteIframeUrlList = $oEmbedFilter->getWhiteIframeUrlList();
$whiteDomains = array();
foreach($whiteIframeUrlList as $domain)
{
$whiteDomains[] = preg_quote($domain, '%');
}
return '%^https?://(' . implode('|', $whiteDomains) . ')%';
}
private function _checkCacheDir()
{
// check htmlpurifier cache directory
$this->_cacheDir = _XE_PATH_ . 'files/cache/htmlpurifier';
FileHandler::makeDir($this->_cacheDir);
}
public function purify(&$content)
{
$this->_setDefinition($content);
$this->_htmlPurifier = new HTMLPurifier($this->_config);
$content = $this->_htmlPurifier->purify($content);
$content = Rhymix\Framework\Filters\HTMLFilter::clean($content);
}
}

View file

@ -15,14 +15,14 @@ class Security
* Action target variable. If this value is null, the method will use Context variables
* @var mixed
*/
var $_targetVar = NULL;
public $_targetVar = NULL;
/**
* @constructor
* @param mixed $var Target context
* @return void
*/
function __construct($var = NULL)
public function __construct($var = NULL)
{
$this->_targetVar = $var;
}
@ -34,7 +34,7 @@ class Security
* separate the owner(object or array) and the item(property or element) using a dot(.)
* @return mixed
*/
function encodeHTML(/* , $varName1, $varName2, ... */)
public function encodeHTML(/* , $varName1, $varName2, ... */)
{
$varNames = func_get_args();
if(count($varNames) < 0)
@ -109,7 +109,7 @@ class Security
* @param array $name
* @return mixed
*/
function _encodeHTML($var, $name = array())
protected function _encodeHTML($var, $name = array())
{
if(is_string($var))
{
@ -183,46 +183,9 @@ class Security
* @param string $xml
* @return bool
*/
static function detectingXEE($xml)
public static function detectingXEE($xml)
{
if(!$xml) return FALSE;
if(strpos($xml, '<!ENTITY') !== FALSE)
{
return TRUE;
}
// Strip XML declaration.
$header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xml, 0, 100), 1);
$xml = trim(substr_replace($xml, $header, 0, 100));
if($xml == '')
{
return TRUE;
}
// Strip DTD.
$header = preg_replace('/^<!DOCTYPE[^>]*+>/i', '', substr($xml, 0, 200), 1);
$xml = trim(substr_replace($xml, $header, 0, 200));
if($xml == '')
{
return TRUE;
}
// Confirm the XML now starts with a valid root tag. A root tag can end in [> \t\r\n]
$root_tag = substr($xml, 0, strcspn(substr($xml, 0, 20), "> \t\r\n"));
// Reject a second DTD.
if(strtoupper($root_tag) == '<!DOCTYPE')
{
return TRUE;
}
if(!in_array($root_tag, array('<methodCall', '<methodResponse', '<fault')))
{
return TRUE;
}
return FALSE;
return !Rhymix\Framework\Security::checkXEE($xml);
}
}
/* End of file : Security.class.php */

View file

@ -3,40 +3,9 @@
class UploadFileFilter
{
private static $_block_list = array ('exec', 'system', 'passthru', 'show_source', 'phpinfo', 'fopen', 'file_get_contents', 'file_put_contents', 'fwrite', 'proc_open', 'popen');
public function check($file)
{
// TODO: 기능개선후 enable
return TRUE; // disable
if (! $file || ! FileHandler::exists($file)) return TRUE;
return self::_check ( $file );
}
private function _check($file)
{
if (! ($fp = fopen ( $file, 'r' ))) return FALSE;
$has_php_tag = FALSE;
while ( ! feof ( $fp ) )
{
$content = fread ( $fp, 8192 );
if (FALSE === $has_php_tag) $has_php_tag = strpos ( $content, '<?' );
foreach ( self::$_block_list as $v )
{
if (FALSE !== $has_php_tag && FALSE !== strpos ( $content, $v ))
{
fclose ( $fp );
return FALSE;
}
}
}
fclose ( $fp );
return TRUE;
return true;
}
}

View file

@ -3,7 +3,7 @@
/**
* RX_VERSION is the version number of the Rhymix CMS.
*/
define('RX_VERSION', '1.8.17');
define('RX_VERSION', '1.8.18');
/**
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.
@ -53,8 +53,8 @@ else
*/
if (isset($_SERVER['HTTP_CF_CONNECTING_IP']))
{
include_once __DIR__ . '/framework/ipfilter.php';
Rhymix\Framework\IpFilter::getCloudFlareRealIP();
include_once __DIR__ . '/framework/filters/ipfilter.php';
Rhymix\Framework\Filters\IpFilter::getCloudFlareRealIP();
}
if (isset($_SERVER['REMOTE_ADDR']) && preg_match('/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/', $_SERVER['REMOTE_ADDR'], $matches))
{
@ -124,7 +124,7 @@ define('__XE_VERSION_ALPHA__', false);
define('__XE_VERSION_BETA__', false);
define('__XE_VERSION_RC__', false);
define('__XE_VERSION_STABLE__', true);
define('__XE_MIN_PHP_VERSION__', '5.3.3');
define('__XE_MIN_PHP_VERSION__', '5.5.9');
define('__XE_RECOMMEND_PHP_VERSION__', '5.5.9');
define('__ZBXE__', true);
define('__ZBXE_VERSION__', RX_VERSION);

View file

@ -97,7 +97,7 @@ return array(
'display_to' => 'admin',
'allow' => array(),
),
'embedfilter' => array(
'mediafilter' => array(
'iframe' => array(),
'object' => array(),
),

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
<?php
/**
* Source: https://ip.kisa.or.kr/ip_cate_stat/stat_05_05.act
* Last Updated: 2016-03-18
*/
return array (
0 =>
array (
0 => '2001000000000000',
1 => '2001000fffffffff',
),
1 =>
array (
0 => '2001022000000000',
1 => '20010220ffffffff',
),
2 =>
array (
0 => '2001023000000000',
1 => '20010230ffffffff',
),
3 =>
array (
0 => '2001027000000000',
1 => '20010270ffffffff',
),
4 =>
array (
0 => '2001028000000000',
1 => '20010280ffffffff',
),
5 =>
array (
0 => '2001029000000000',
1 => '20010290ffffffff',
),
6 =>
array (
0 => '200102b000000000',
1 => '200102b0ffffffff',
),
7 =>
array (
0 => '200102b800000000',
1 => '200102b8ffffffff',
),
8 =>
array (
0 => '200102d800000000',
1 => '200102d8ffffffff',
),
9 =>
array (
0 => '2001032000000000',
1 => '20010320ffffffff',
),
10 =>
array (
0 => '2001033000000000',
1 => '20010330ffffffff',
),
11 =>
array (
0 => '2001037800000000',
1 => '20010378ffffffff',
),
12 =>
array (
0 => '2001039000000000',
1 => '20010390ffffffff',
),
13 =>
array (
0 => '200103a800000000',
1 => '200103a8ffffffff',
),
14 =>
array (
0 => '200107fa00000002',
1 => '200107fa00000002',
),
15 =>
array (
0 => '200107fa00080000',
1 => '200107fa0008ffff',
),
16 =>
array (
0 => '20010c4800000000',
1 => '20010c48ffffffff',
),
17 =>
array (
0 => '20010c9800000000',
1 => '20010c98ffffffff',
),
18 =>
array (
0 => '20010cf000000000',
1 => '20010cf0ffffffff',
),
19 =>
array (
0 => '20010d3800000000',
1 => '20010d38ffffffff',
),
20 =>
array (
0 => '20010dc500000000',
1 => '20010dc5ffffffff',
),
21 =>
array (
0 => '20010dcc00000000',
1 => '20010dccffffffff',
),
22 =>
array (
0 => '20010ea000000000',
1 => '20010ea0ffffffff',
),
23 =>
array (
0 => '20010ea800000000',
1 => '20010ea8ffffffff',
),
24 =>
array (
0 => '20010eb800000000',
1 => '20010eb8ffffffff',
),
25 =>
array (
0 => '20010ed000000000',
1 => '20010ed0ffffffff',
),
26 =>
array (
0 => '20010ee800000000',
1 => '20010ee8ffffffff',
),
27 =>
array (
0 => '20010ef000000000',
1 => '20010ef0ffffffff',
),
28 =>
array (
0 => '20010ef800000000',
1 => '20010ef8ffffffff',
),
29 =>
array (
0 => '20010f2800000000',
1 => '20010f28ffffffff',
),
30 =>
array (
0 => '20010f4800000000',
1 => '20010f48ffffffff',
),
31 =>
array (
0 => '24033e0000000000',
1 => '24033e00ffffffff',
),
32 =>
array (
0 => '2001443000000000',
1 => '20014430ffffffff',
),
33 =>
array (
0 => '2400000000000000',
1 => '24000fffffffffff',
),
34 =>
array (
0 => '2400180000000000',
1 => '24001800ffffffff',
),
35 =>
array (
0 => '2400330000000000',
1 => '24003300ffffffff',
),
36 =>
array (
0 => '2400478000000000',
1 => '24004780ffffffff',
),
37 =>
array (
0 => '2400498000000000',
1 => '24004980ffffffff',
),
38 =>
array (
0 => '24009f8000000000',
1 => '24009f80ffffffff',
),
39 =>
array (
0 => '2400a58000000000',
1 => '2400a580ffffffff',
),
40 =>
array (
0 => '2400ab0000000000',
1 => '2400ab00ffffffff',
),
41 =>
array (
0 => '2400cf0000000000',
1 => '2400cf00ffffffff',
),
42 =>
array (
0 => '2400fd8000000000',
1 => '2400fd80ffffffff',
),
43 =>
array (
0 => '2401270000000000',
1 => '24012700ffffffff',
),
44 =>
array (
0 => '2401400000000000',
1 => '24014000ffffffff',
),
45 =>
array (
0 => '2401a00000000000',
1 => '2401a000ffffffff',
),
46 =>
array (
0 => '2401a80000000000',
1 => '2401a800ffffffff',
),
47 =>
array (
0 => '2401c50000000000',
1 => '2401c500ffffffff',
),
48 =>
array (
0 => '2402000000000000',
1 => '240200ffffffffff',
),
49 =>
array (
0 => '24021a0000000000',
1 => '24021a00ffffffff',
),
50 =>
array (
0 => '2402310000000000',
1 => '24023100ffffffff',
),
51 =>
array (
0 => '2402580000000000',
1 => '24025800ffffffff',
),
52 =>
array (
0 => '2402610000000000',
1 => '24026100ffffffff',
),
53 =>
array (
0 => '2402700000000000',
1 => '24027000ffffffff',
),
54 =>
array (
0 => '2402be0000000000',
1 => '2402be00ffffffff',
),
55 =>
array (
0 => '2402de0000000000',
1 => '2402de00ffffffff',
),
56 =>
array (
0 => '2402f40000000000',
1 => '2402f400ffffffff',
),
57 =>
array (
0 => '2403370000000000',
1 => '24033700ffffffff',
),
58 =>
array (
0 => '2403630000000000',
1 => '24036300ffffffff',
),
59 =>
array (
0 => '2403650000000000',
1 => '24036500ffffffff',
),
60 =>
array (
0 => '2404000000000000',
1 => '2404000fffffffff',
),
61 =>
array (
0 => '2404080000000000',
1 => '24040800ffffffff',
),
62 =>
array (
0 => '2404230000000000',
1 => '24042300ffffffff',
),
63 =>
array (
0 => '2404460000000000',
1 => '24044600ffffffff',
),
64 =>
array (
0 => '2405350000000000',
1 => '24053500ffffffff',
),
65 =>
array (
0 => '2405430000000000',
1 => '24054300ffffffff',
),
66 =>
array (
0 => '2405580000000000',
1 => '24055800ffffffff',
),
67 =>
array (
0 => '20010e6000000000',
1 => '20010e60ffffffff',
),
68 =>
array (
0 => '20010e7000000000',
1 => '20010e70ffffffff',
),
69 =>
array (
0 => '20010e7800000000',
1 => '20010e78ffffffff',
),
70 =>
array (
0 => '20010e9800000000',
1 => '20010e98ffffffff',
),
71 =>
array (
0 => '24009e8000000000',
1 => '24009e80ffffffff',
),
72 =>
array (
0 => '2400e18000000000',
1 => '2400e180ffffffff',
),
73 =>
array (
0 => '2401e20000000000',
1 => '2401e200ffffffff',
),
74 =>
array (
0 => '24053d0000000000',
1 => '24053d00ffffffff',
),
75 =>
array (
0 => '24055f0000000000',
1 => '24055f00ffffffff',
),
76 =>
array (
0 => '24057b0000000000',
1 => '24057b00ffffffff',
),
77 =>
array (
0 => '2405860000000000',
1 => '24058600ffffffff',
),
78 =>
array (
0 => '2405950000000000',
1 => '24059500ffffffff',
),
79 =>
array (
0 => '2405c00000000000',
1 => '2405c000ffffffff',
),
80 =>
array (
0 => '2406400000000000',
1 => '24064000ffffffff',
),
81 =>
array (
0 => '2406590000000000',
1 => '24065900ffffffff',
),
82 =>
array (
0 => '2406660000000000',
1 => '24066600ffffffff',
),
83 =>
array (
0 => '2406680000000000',
1 => '24066800ffffffff',
),
84 =>
array (
0 => '24066a0000000000',
1 => '24066a00ffffffff',
),
85 =>
array (
0 => '2406ad0000000000',
1 => '2406ad00ffffffff',
),
86 =>
array (
0 => '2406b00000000000',
1 => '2406b000ffffffff',
),
87 =>
array (
0 => '2406d00000000000',
1 => '2406d000ffffffff',
),
88 =>
array (
0 => '2406d70000000000',
1 => '2406d700ffffffff',
),
89 =>
array (
0 => '24070b0000000000',
1 => '24070b00ffffffff',
),
90 =>
array (
0 => '2407200000000000',
1 => '24072000ffffffff',
),
91 =>
array (
0 => '2407350000000000',
1 => '24073500ffffffff',
),
92 =>
array (
0 => '2407650000000000',
1 => '24076500ffffffff',
),
93 =>
array (
0 => '2407670000000000',
1 => '24076700ffffffff',
),
94 =>
array (
0 => '2407910000000000',
1 => '24079100ffffffff',
),
95 =>
array (
0 => '2407b20000000000',
1 => '2407b200ffffffff',
),
96 =>
array (
0 => '2407b80000000000',
1 => '2407b800ffffffff',
),
97 =>
array (
0 => '2407c00000000000',
1 => '2407c000ffffffff',
),
98 =>
array (
0 => '2407c70000000000',
1 => '2407c700ffffffff',
),
);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,132 @@
<?php
namespace Rhymix\Framework;
/**
* The calendar class.
*/
class Calendar
{
/**
* This method returns the English name of a month, e.g. 9 = 'September'.
*
* @param int $month_number
* @param bool $long_format (optional, default is true)
* @return string
*/
public static function getMonthName($month_number, $long_format = true)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return date($long_format ? 'F' : 'M', mktime(0, 0, 0, $month_number, 1));
}
/**
* This method returns the day on which a month begins.
*
* 0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday.
* If you do not specify a year, the current year is assumed.
*
* @param int $month_number
* @param int $year (optional)
* @return int
*/
public static function getMonthStartDayOfWeek($month_number, $year = null)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return (int)date('w', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
}
/**
* This method returns the number of days in a month, e.g. February 2016 has 29 days.
*
* If you do not specify a year, the current year is assumed.
* You must specify a year to get the number of days in February.
*
* @param int $month_number
* @param int $year (optional)
* @return int
*/
public static function getMonthDays($month_number, $year = null)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return (int)date('t', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
}
/**
* This method returns a complete calendar for a month.
*
* The return value is an array with six members, each representing a week.
* Each week is an array with seven members, each representing a day.
* 6 weeks are returned. Empty cells are represented by nulls.
*
* If you do not specify a year, the current year is assumed.
*
* @param int $month_number
* @param int $year (optional)
* @param int $start_dow (optional)
* @return array
*/
public static function getMonthCalendar($month_number, $year = null, $start_dow = 0)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
if (!is_between($start_dow, 0, 6))
{
return false;
}
if (!$year || !is_between($year, 1000, 9999))
{
$year = date('Y');
}
$start = self::getMonthStartDayOfWeek($month_number, $year);
$count = self::getMonthDays($month_number, $year);
$initial_blank_cells = (7 + $start - $start_dow) % 7;
$final_blank_cells = 42 - $count - $initial_blank_cells;
$temp = array();
for ($i = 0; $i < $initial_blank_cells; $i++)
{
$temp[] = null;
}
for ($i = 0; $i < $count; $i++)
{
$temp[] = $i + 1;
}
for ($i = 0; $i < $final_blank_cells; $i++)
{
$temp[] = null;
}
$return = array();
for ($i = 0; $i < 6; $i++)
{
$week = array();
for ($j = 0; $j < 7; $j++)
{
$week[] = array_shift($temp);
}
$return[] = $week;
}
return $return;
}
}

View file

@ -28,13 +28,13 @@ class Config
*/
public static function init()
{
if (file_exists(RX_BASEDIR . self::$config_filename))
if (file_exists(\RX_BASEDIR . self::$config_filename))
{
self::$_config = (include RX_BASEDIR . self::$config_filename);
self::$_config = (include \RX_BASEDIR . self::$config_filename);
}
else
{
if (self::$_config = Compat\ConfigParser::convert())
if (self::$_config = Parsers\ConfigParser::convert())
{
self::save();
}
@ -59,7 +59,7 @@ class Config
*/
public static function getDefaults()
{
return (include RX_BASEDIR . self::$default_config_filename);
return (include \RX_BASEDIR . self::$default_config_filename);
}
/**
@ -135,7 +135,7 @@ class Config
// Save the main config file.
$buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n";
$result = \FileHandler::writeFile(RX_BASEDIR . self::$config_filename, $buff) ? true : false;
$result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false;
//if (!$result) return false;
// Save XE-compatible config files.
@ -144,9 +144,9 @@ class Config
$db_info_without_ftp = clone $db_info;
unset($db_info_without_ftp->ftp_info);
$buff = '<?php' . "\n" . '$db_info = ' . self::serialize($db_info_without_ftp) . ';' . "\n";
\FileHandler::writeFile(RX_BASEDIR . self::$old_db_config_filename, $buff);
Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff);
$buff = '<?php' . "\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n";
\FileHandler::writeFile(RX_BASEDIR . self::$old_ftp_config_filename, $buff);
Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff);
return true;
}

View file

@ -145,7 +145,7 @@ class Debug
self::$_entries[] = $entry;
// Add the entry to the error log.
if (self::$write_to_error_log)
if (self::$write_to_error_log && self::isEnabledForCurrentUser())
{
$log_entry = str_replace("\0", '', sprintf('Rhymix Debug: %s in %s on line %d',
var_export($message, true), $entry->file, $entry->line));
@ -173,8 +173,8 @@ class Debug
// Rewrite the error message with relative paths.
$message = str_replace(array(
' called in ' . RX_BASEDIR,
' defined in ' . RX_BASEDIR,
' called in ' . \RX_BASEDIR,
' defined in ' . \RX_BASEDIR,
), array(
' called in ',
' defined in ',
@ -397,6 +397,12 @@ class Debug
*/
public static function displayErrorScreen($message)
{
// Do not display error screen in CLI.
if (php_sapi_name() === 'cli')
{
return;
}
// Disable output buffering.
while (ob_get_level())
{
@ -445,13 +451,17 @@ class Debug
return $cache = true;
case 'ip':
$allowed_ip = Config::get('debug.allow');
foreach ($allowed_ip as $range)
if (Filters\IpFilter::inRanges(\RX_CLIENT_IP, Config::get('debug.allow')))
{
if (IpFilter::inRange(RX_CLIENT_IP, $range))
{
return $cache = true;
}
return $cache = true;
}
if (\RX_CLIENT_IP === '127.0.0.1' || \RX_CLIENT_IP === '::1')
{
return $cache = true;
}
if (\RX_CLIENT_IP === $_SERVER['SERVER_ADDR'] || \RX_CLIENT_IP === $_SERVER['LOCAL_ADDR'])
{
return $cache = true;
}
return $cache = false;
@ -475,7 +485,7 @@ class Debug
{
// Collect debug information.
$data = (object)array(
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', RX_TIME),
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', \RX_TIME),
'url' => getCurrentPageUrl(),
'request' => (object)array(
'method' => $_SERVER['REQUEST_METHOD'] . ($_SERVER['REQUEST_METHOD'] !== \Context::getRequestMethod() ? (' (' . \Context::getRequestMethod() . ')') : ''),

View file

@ -0,0 +1,88 @@
<?php
namespace Rhymix\Framework\Filters;
/**
* The filename filter class.
*/
class FilenameFilter
{
/**
* Remove illegal and dangerous characters from a filename.
*
* @param string $filename
* @return string
*/
public static function clean($filename)
{
// Replace dangerous characters with safe alternatives, maintaining meaning as much as possible.
$illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '@', '#', '$', '%', '^', '&', '*', '?');
$replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '_', '_', '_', '', '');
$filename = str_replace($illegal, $replace, $filename);
// Remove control characters.
$filename = preg_replace('/([\\x00-\\x1f\\x7f\\xff]+)/u', '', $filename);
// Standardize whitespace characters.
$filename = trim(preg_replace('/[\\pZ\\pC]+/u', ' ', $filename));
// Remove excess spaces and replacement characters.
$filename = trim($filename, ' .-_');
$filename = preg_replace('/__+/', '_', $filename);
// Change .php files to .phps to make them non-executable.
if (strtolower(substr($filename, strlen($filename) - 4)) === '.php')
{
$filename = substr($filename, 0, strlen($filename) - 4) . '.phps';
}
// Truncate filenames over 127 chars long, or extensions over 16 chars long.
if (mb_strlen($filename, 'UTF-8') > 127)
{
$extension = strrchr($filename, '.');
if (mb_strlen($extension, 'UTF-8') > 16) $extension = mb_substr($extension, 0, 16);
$filename = mb_substr($filename, 0, 127 - mb_strlen($extension)) . $extension;
}
return $filename;
}
/**
* Clean a path to remove ./, ../, trailing slashes, etc.
*
* @param string $path
* @return string
*/
public static function cleanPath($path)
{
// Convert relative paths to absolute paths.
if (!preg_match('@^(?:/|[a-z]:[\\\\/]|\\\\|https?:)@i', $path))
{
$path = \RX_BASEDIR . $path;
}
// Convert backslashes to forward slashes.
$path = str_replace('\\', '/', $path);
// Remove querystrings and URL fragments.
if (($querystring = strpbrk($path, '?#')) !== false)
{
$path = substr($path, 0, -1 * strlen($querystring));
}
// Remove single dots, three or more dots, and duplicate slashes.
$path = preg_replace(array(
'@(?<!^|^http:|^https:)/{2,}@',
'@/(?:(?:\.|\.{3,})/)+@',
), '/', $path);
// Remove double dots and the preceding directory.
while (preg_match('@/(?!\.\.)[^/]+/\.\.(?:/|$)@', $path, $matches))
{
$path = str_replace($matches[0], '/', $path);
}
// Trim trailing slashes.
return rtrim($path, '/');
}
}

View file

@ -0,0 +1,501 @@
<?php
namespace Rhymix\Framework\Filters;
use Rhymix\Framework\Security;
use Rhymix\Framework\Storage;
/**
* The HTML filter class.
*/
class HTMLFilter
{
/**
* HTMLPurifier instance is cached here.
*/
protected static $_htmlpurifier;
/**
* Pre-processing and post-processing filters are stored here.
*/
protected static $_preproc = array();
protected static $_postproc = array();
/**
* Prepend a pre-processing filter.
*
* @param callable $callback
* @return void
*/
public static function prependPreFilter($callback)
{
array_unshift(self::$_preproc, $callback);
}
/**
* Append a pre-processing filter.
*
* @param callable $callback
* @return void
*/
public static function appendPreFilter($callback)
{
self::$_preproc[] = $callback;
}
/**
* Prepend a post-processing filter.
*
* @param callable $callback
* @return void
*/
public static function prependPostFilter($callback)
{
array_unshift(self::$_postproc, $callback);
}
/**
* Append a post-processing filter.
*
* @param callable $callback
* @return void
*/
public static function appendPostFilter($callback)
{
self::$_postproc[] = $callback;
}
/**
* Filter HTML content to block XSS attacks.
*
* @param string $input
* @return string
*/
public static function clean($input)
{
foreach (self::$_preproc as $callback)
{
$input = $callback($input);
}
$input = self::_preprocess($input);
$output = self::getHTMLPurifier()->purify($input);
$output = self::_postprocess($output);
foreach (self::$_postproc as $callback)
{
$output = $callback($output);
}
return $output;
}
/**
* Get an instance of HTMLPurifier.
*
* @return object
*/
public static function getHTMLPurifier()
{
// Create an instance with reasonable defaults.
if (self::$_htmlpurifier === null)
{
// Get the default configuration.
$config = \HTMLPurifier_Config::createDefault();
// Customize the default configuration.
$config->set('Attr.AllowedFrameTargets', array('_blank'));
$config->set('Attr.DefaultImageAlt', '');
$config->set('Attr.EnableID', false);
$config->set('AutoFormat.AutoParagraph', false);
$config->set('AutoFormat.DisplayLinkURI', false);
$config->set('AutoFormat.Linkify', false);
$config->set('Core.Encoding', 'UTF-8');
$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
$config->set('HTML.FlashAllowFullScreen', true);
$config->set('HTML.MaxImgLength', null);
$config->set('CSS.MaxImgLength', null);
$config->set('CSS.Proprietary', true);
$config->set('Output.FlashCompat', true);
$config->set('Output.Newline', "\n");
$config->set('URI.MakeAbsolute', false);
// Allow embedding of external multimedia content.
$config->set('HTML.SafeEmbed', true);
$config->set('HTML.SafeIframe', true);
$config->set('HTML.SafeObject', true);
$config->set('URI.SafeIframeRegexp', MediaFilter::getIframeWhitelistRegex());
// Set the serializer path.
$config->set('Cache.SerializerPath', \RX_BASEDIR . 'files/cache/htmlpurifier');
Storage::createDirectory(\RX_BASEDIR . 'files/cache/htmlpurifier');
// Modify the HTML definition to support editor components and widgets.
$def = $config->getHTMLDefinition(true);
$def->addAttribute('img', 'editor_component', 'Text');
$def->addAttribute('div', 'editor_component', 'Text');
$def->addAttribute('img', 'rx_encoded_properties', 'Text');
$def->addAttribute('div', 'rx_encoded_properties', 'Text');
// Support HTML5 and CSS3.
self::_supportHTML5($config);
self::_supportCSS3($config);
// Cache our instance of HTMLPurifier.
self::$_htmlpurifier = new \HTMLPurifier($config);
}
// Return the cached instance.
return self::$_htmlpurifier;
}
/**
* Patch HTMLPurifier to support some HTML5 tags and attributes.
*
* These changes are based on https://github.com/xemlock/htmlpurifier-html5
* but modified to support even more tags and attributes.
*
* @param object $config
* @return void
*/
protected static function _supportHTML5($config)
{
// Get the HTML definition.
$def = $config->getHTMLDefinition(true);
// Add various block-level tags.
$def->addElement('header', 'Block', 'Flow', 'Common');
$def->addElement('footer', 'Block', 'Flow', 'Common');
$def->addElement('nav', 'Block', 'Flow', 'Common');
$def->addElement('main', 'Block', 'Flow', 'Common');
$def->addElement('section', 'Block', 'Flow', 'Common');
$def->addElement('article', 'Block', 'Flow', 'Common');
$def->addElement('aside', 'Block', 'Flow', 'Common');
// Add various inline tags.
$def->addElement('s', 'Inline', 'Inline', 'Common');
$def->addElement('sub', 'Inline', 'Inline', 'Common');
$def->addElement('sup', 'Inline', 'Inline', 'Common');
$def->addElement('mark', 'Inline', 'Inline', 'Common');
$def->addElement('wbr', 'Inline', 'Empty', 'Core');
// Support figures.
$def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
$def->addElement('figcaption', 'Inline', 'Flow', 'Common');
// Support insertions and deletions.
$def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text'));
$def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text'));
// Support the <time> tag.
$time = $def->addElement('time', 'Inline', 'Inline', 'Common', array('datetime' => 'Text', 'pubdate' => 'Bool'));
$time->excludes = array('time' => true);
// Suppport <audio> and <video> tags. DO NOT ALLOW AUTOPLAY.
$def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array(
'src' => 'URI',
'type' => 'Text',
'preload' => 'Enum#auto,metadata,none',
'controls' => 'Bool',
'muted' => 'Bool',
'loop' => 'Bool',
));
$def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array(
'src' => 'URI',
'type' => 'Text',
'width' => 'Length',
'height' => 'Length',
'poster' => 'URI',
'preload' => 'Enum#auto,metadata,none',
'controls' => 'Bool',
'muted' => 'Bool',
'loop' => 'Bool',
));
$def->addElement('source', 'Block', 'Empty', 'Common', array(
'src' => 'URI',
'media' => 'Text',
'type' => 'Text',
));
$def->addElement('track', 'Block', 'Empty', 'Common', array(
'src' => 'URI',
'srclang' => 'Text',
'label' => 'Text',
'kind' => 'Enum#captions,chapters,descriptions,metadata,subtitles',
'default' => 'Bool',
));
// Support additional properties.
$def->addAttribute('img', 'srcset', 'Text');
$def->addAttribute('iframe', 'allowfullscreen', 'Bool');
}
/**
* Patch HTMLPurifier to support more CSS2 and some CSS3 properties.
*
* These changes are based on:
* - https://github.com/mattiaswelander/htmlpurifier
*
* @param object $config
* @return void
*/
protected static function _supportCSS3($config)
{
// Initialize $info.
$info = array();
// min-width, max-width, etc.
$info['min-width'] = $info['max-width'] = $info['min-height'] =
$info['max-height'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(0),
new \HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')),
));
// border-radius, etc.
$border_radius = $info['border-top-left-radius'] =
$info['border-top-right-radius'] = $info['border-bottom-left-radius'] =
$info['border-bottom-right-radius'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(0),
new \HTMLPurifier_AttrDef_CSS_Percentage(true),
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
));
$info['border-radius'] = new \HTMLPurifier_AttrDef_CSS_Multiple($border_radius);
// word-break word-wrap, etc.
$info['word-break'] = new \HTMLPurifier_AttrDef_Enum(array(
'normal', 'break-all', 'keep-all', 'initial', 'inherit',
));
$info['word-wrap'] = new \HTMLPurifier_AttrDef_Enum(array(
'normal', 'break-word', 'initial', 'inherit',
));
$info['text-overflow'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_Enum(array('clip', 'ellipsis', 'initial', 'inherit')),
));
// text-shadow
$info['text-shadow'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(),
new \HTMLPurifier_AttrDef_CSS_Color(),
))),
new \HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')),
));
// box-shadow and box-sizing
$info['box-shadow'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(),
new \HTMLPurifier_AttrDef_CSS_Percentage(),
new \HTMLPurifier_AttrDef_CSS_Color(),
new \HTMLPurifier_AttrDef_Enum(array('none', 'inset', 'initial', 'inherit')),
)));
$info['box-sizing'] = new \HTMLPurifier_AttrDef_Enum(array(
'content-box', 'border-box', 'initial', 'inherit',
));
// outline
$info['outline-color'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Color(),
new \HTMLPurifier_AttrDef_Enum(array('invert', 'initial', 'inherit')),
));
$info['outline-offset'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(),
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
));
$info['outline-style'] = new \HTMLPurifier_AttrDef_Enum(array(
'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'initial', 'inherit',
));
$info['outline-width'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(),
new \HTMLPurifier_AttrDef_Enum(array('medium', 'thin', 'thick', 'initial', 'inherit')),
));
$info['outline'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
$info['outline-color'], $info['outline-style'], $info['outline-width'],
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
)));
// flexbox
$info['display'] = new \HTMLPurifier_AttrDef_Enum(array(
'block', 'flex', '-webkit-flex', 'inline', 'inline-block', 'inline-flex', '-webkit-inline-flex', 'inline-table',
'list-item', 'run-in', 'compact', 'marker', 'table', 'table-row-group', 'table-header-group', 'table-footer-group',
'table-row', 'table-column-group', 'table-column', 'table-cell', 'table-caption',
'none', 'initial', 'inherit',
));
$info['order'] = new \HTMLPurifier_AttrDef_CSS_Number();
$info['align-content'] = $info['justify-content'] = new \HTMLPurifier_AttrDef_Enum(array(
'stretch', 'center', 'flex-start', 'flex-end', 'space-between', 'space-around', 'initial', 'inherit',
));
$info['align-items'] = $info['align-self'] = new \HTMLPurifier_AttrDef_Enum(array(
'stretch', 'center', 'flex-start', 'flex-end', 'baseline', 'initial', 'inherit',
));
$info['flex-basis'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
new \HTMLPurifier_AttrDef_CSS_Length(),
new \HTMLPurifier_AttrDef_CSS_Percentage(),
new \HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit')),
));
$info['flex-direction'] = new \HTMLPurifier_AttrDef_Enum(array(
'row', 'row-reverse', 'column', 'column-reverse', 'initial', 'inherit',
));
$info['flex-wrap'] = new \HTMLPurifier_AttrDef_Enum(array(
'nowrap', 'wrap', 'wrap-reverse', 'initial', 'inherit',
));
$info['flex-flow'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
$info['flex-direction'], $info['flex-wrap'],
)));
$info['flex-grow'] = new \HTMLPurifier_AttrDef_CSS_Number();
$info['flex-shrink'] = new \HTMLPurifier_AttrDef_CSS_Number();
$info['flex'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
$info['flex-grow'], $info['flex-shrink'], $info['flex-basis'],
new \HTMLPurifier_AttrDef_Enum(array('auto', 'none', 'initial', 'inherit')),
)));
// misc
$info['caption-side'] = new \HTMLPurifier_AttrDef_Enum(array(
'top', 'bottom', 'initial', 'inherit',
));
$info['empty-cells'] = new \HTMLPurifier_AttrDef_Enum(array(
'show', 'hide', 'initial', 'inherit',
));
$info['hanging-punctuation'] = new \HTMLPurifier_AttrDef_Enum(array(
'none', 'first', 'last', 'allow-end', 'force-end', 'initial', 'inherit',
));
$info['overflow'] = $info['overflow-x'] = $info['overflow-y'] = new \HTMLPurifier_AttrDef_Enum(array(
'visible', 'hidden', 'scroll', 'auto', 'initial', 'inherit',
));
$info['resize'] = new \HTMLPurifier_AttrDef_Enum(array(
'none', 'both', 'horizontal', 'vertical', 'initial', 'inherit',
));
// Wrap all new properties with a decorator that handles !important.
$allow_important = $config->get('CSS.AllowImportant');
$css_definition = $config->getCSSDefinition();
foreach ($info as $key => $val)
{
$css_definition->info[$key] = new \HTMLPurifier_AttrDef_CSS_ImportantDecorator($val, $allow_important);
}
}
/**
* Rhymix-specific preprocessing method.
*
* @param string $content
* @return string
*/
protected static function _preprocess($content)
{
// Encode widget and editor component properties so that they are not removed by HTMLPurifier.
$content = self::_encodeWidgetsAndEditorComponents($content);
return $content;
}
/**
* Rhymix-specific postprocessing method.
*
* @param string $content
* @return string
*/
protected static function _postprocess($content)
{
// Define acts to allow and deny.
$allow_acts = array('procFileDownload');
$deny_acts = array('dispMemberLogout', 'dispLayoutPreview');
// Remove tags not supported in Rhymix. Some of these may also have been removed by HTMLPurifier.
$content = preg_replace_callback('!</?(?:html|body|head|title|meta|base|link|script|style|applet)\b[^>]*>!i', function($matches) {
return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8');
}, $content);
// Remove object and embed URLs that are not allowed.
$whitelist = MediaFilter::getObjectWhitelistRegex();
$content = preg_replace_callback('!<(object|embed|param|audio|video|source|track)([^>]+)>!i', function($matches) use($whitelist) {
return preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($whitelist) {
if (in_array($attr[1], array('data', 'src', 'href', 'url', 'movie', 'source')))
{
$url = trim(htmlspecialchars_decode($attr[2]));
if (preg_match('!^(https?:)?//!i', $url) && !preg_match($whitelist, $url))
{
return $attr[1] . '=""';
}
}
return $attr[0];
}, $matches[0]);
}, $content);
// Remove link URLs that may be CSRF attempts.
$content = preg_replace_callback('!\b(src|href|data|value)="([^"]+)"!i', function($matches) use($allow_acts, $deny_acts) {
$url = preg_replace('!\s+!', '', htmlspecialchars_decode(rawurldecode($matches[2])));
if (preg_match('!\bact=((disp|proc)[^&]+)!i', $url, $urlmatches))
{
$act = $urlmatches[1];
if (!in_array($act, $allow_acts) && (in_array($act, $deny_acts) || $urlmatches[2] === 'proc'))
{
return $matches[1] . '=""';
}
}
return $matches[0];
}, $content);
// Restore widget and editor component properties.
$content = self::_decodeWidgetsAndEditorComponents($content);
return $content;
}
/**
* Encode widgets and editor components before processing.
*
* @param string $content
* @return string
*/
protected static function _encodeWidgetsAndEditorComponents($content)
{
return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) {
$tag = strtolower($match[1]);
$attrs = array();
$html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) {
$attrkey = strtolower($attr[1]);
if ($tag === 'img' && preg_match('/^(?:width|height|src|alt|ismap|usemap)$/', $attrkey))
{
return $attr[0];
}
if (preg_match('/^(?:on|data-|(?:accesskey|class|contextmenu|contenteditable|dir|draggable|dropzone|editor_component|hidden|id|lang|name|style|tabindex|title)$)/', $attrkey))
{
return $attr[0];
}
$attrs[$attrkey] = htmlspecialchars_decode($attr[2]);
return '';
}, $match[0]);
if ($tag === 'img' && !preg_match('/\ssrc="/', $html))
{
$html = substr($html, 0, 4) . ' src=""' . substr($html, 4);
}
$encoded_properties = Security::encrypt(json_encode($attrs));
return substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4);
}, $content);
}
/**
* Decode widgets and editor components after processing.
*
* @param string $content
* @return string
*/
protected static function _decodeWidgetsAndEditorComponents($content)
{
return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) {
$attrs = array();
$decoded_properties = Security::decrypt($match[4]);
if (!$decoded_properties)
{
return str_replace($match[3], '', $match[0]);
}
$decoded_properties = json_decode($decoded_properties);
if (!$decoded_properties)
{
return str_replace($match[3], '', $match[0]);
}
foreach ($decoded_properties as $key => $val)
{
$attrs[] = $key . '="' . htmlspecialchars($val) . '"';
}
return str_replace($match[3], ' ' . implode(' ', $attrs), $match[0]);
}, $content);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Rhymix\Framework;
namespace Rhymix\Framework\Filters;
/**
* The IP filter class.
@ -66,6 +66,25 @@ class IpFilter
}
}
/**
* Check whether the given IP address belongs to a set of ranges.
*
* @param string $ip
* @param array $ranges
* @return bool
*/
public static function inRanges($ip, array $ranges)
{
foreach ($ranges as $range)
{
if (self::inRange($ip, $range))
{
return true;
}
}
return false;
}
/**
* Check whether a range definition is valid.
*
@ -91,6 +110,24 @@ class IpFilter
return false;
}
/**
* Check whether a set of range definitions is valid.
*
* @param array $ranges
* @return bool
*/
public static function validateRanges(array $ranges)
{
foreach ($ranges as $range)
{
if (!self::validateRange($range))
{
return false;
}
}
return true;
}
/**
* Get real IP from CloudFlare headers.
*
@ -103,7 +140,7 @@ class IpFilter
return false;
}
$cloudflare_ranges = (include RX_BASEDIR . 'common/defaults/cloudflare.php');
$cloudflare_ranges = (include \RX_BASEDIR . 'common/defaults/cloudflare.php');
foreach ($cloudflare_ranges as $cloudflare_range)
{
if (self::inRange($_SERVER['REMOTE_ADDR'], $cloudflare_range))

View file

@ -0,0 +1,254 @@
<?php
namespace Rhymix\Framework\Filters;
use Rhymix\Framework\Config;
/**
* The media filter class.
*/
class MediaFilter
{
/**
* Whitelists are cached here.
*/
protected static $_iframe_whitelist;
protected static $_object_whitelist;
/**
* Add a prefix to the iframe whitelist.
*
* @param string $prefix
* @parsm bool $permanently
* @return void
*/
public static function addIframePrefix($prefix, $permanently = false)
{
if (!count(self::$_iframe_whitelist))
{
self::_loadWhitelists();
}
$prefix = self::formatPrefix($prefix);
if (!in_array($prefix, self::$_iframe_whitelist))
{
self::$_iframe_whitelist[] = $prefix;
natcasesort(self::$_iframe_whitelist);
if ($permanently)
{
Config::set('mediafilter.iframe', self::$_iframe_whitelist);
Config::save();
}
}
}
/**
* Add a prefix to the object whitelist.
*
* @param string $prefix
* @parsm bool $permanently
* @return void
*/
public static function addObjectPrefix($prefix, $permanently = false)
{
if (!count(self::$_object_whitelist))
{
self::_loadWhitelists();
}
$prefix = self::formatPrefix($prefix);
if (!in_array($prefix, self::$_object_whitelist))
{
self::$_object_whitelist[] = $prefix;
natcasesort(self::$_object_whitelist);
if ($permanently)
{
Config::set('mediafilter.object', self::$_object_whitelist);
Config::save();
}
}
}
/**
* Format a prefix for standardization.
*
* @param string $prefix
* @return string
*/
public static function formatPrefix($prefix)
{
$prefix = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
if (strpos($prefix, '/') === false)
{
$prefix .= '/';
}
return $prefix;
}
/**
* Get the iframe whitelist.
*
* @return string
*/
public static function getIframeWhitelist()
{
if (!count(self::$_iframe_whitelist))
{
self::_loadWhitelists();
}
return self::$_iframe_whitelist;
}
/**
* Get the iframe whitelist as a regular expression.
*
* @return string
*/
public static function getIframeWhitelistRegex()
{
if (!count(self::$_iframe_whitelist))
{
self::_loadWhitelists();
}
$result = array();
foreach(self::$_iframe_whitelist as $domain)
{
$result[] = str_replace('\*\.', '[a-z0-9-]+\.', preg_quote($domain, '%'));
}
return '%^https?://(' . implode('|', $result) . ')%';
}
/**
* Get the object whitelist.
*
* @return string
*/
public static function getObjectWhitelist()
{
if (!count(self::$_object_whitelist))
{
self::_loadWhitelists();
}
return self::$_object_whitelist;
}
/**
* Get the object whitelist as a regular expression.
*
* @return string
*/
public static function getObjectWhitelistRegex()
{
if (!count(self::$_object_whitelist))
{
self::_loadWhitelists();
}
$result = array();
foreach(self::$_object_whitelist as $domain)
{
$result[] = str_replace('\*\.', '[a-z0-9-]+\.', preg_quote($domain, '%'));
}
return '%^https?://(' . implode('|', $result) . ')%';
}
/**
* Check if a URL matches the iframe whitelist.
*
* @param string $url
* @return bool
*/
public static function matchIframeWhitelist($url)
{
return preg_match(self::getIframeWhitelistRegex(), $url) ? true : false;
}
/**
* Check if a URL matches the iframe whitelist.
*
* @param string $url
* @return bool
*/
public static function matchObjectWhitelist($url)
{
return preg_match(self::getObjectWhitelistRegex(), $url) ? true : false;
}
/**
* Remove embedded media from HTML content.
*
* @param string $input
* @param string $replacement
* @return string
*/
public static function removeEmbeddedMedia($input, $replacement = '')
{
$input = preg_replace('!<object[^>]*>(.*?</object>)?!is', $replacement, $input);
$input = preg_replace('!<embed[^>]*>(.*?</embed>)?!is', $replacement, $input);
$input = preg_replace('!<img[^>]*editor_component="multimedia_link"[^>]*>(.*?</img>)?!is', $replacement, $input);
return $input;
}
/**
* Load whitelists.
*
* @param array $custom_whitelist
* @return void
*/
protected static function _loadWhitelists($custom_whitelist = array())
{
$default_whitelist = (include \RX_BASEDIR . 'common/defaults/whitelist.php');
self::$_object_whitelist = array();
self::$_iframe_whitelist = array();
if(count($custom_whitelist))
{
if(!is_array($custom_whitelist) || !isset($custom_whitelist['iframe']) || !isset($custom_whitelist['object']))
{
$whitelist = array(
'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(),
'object' => isset($whitelist->object) ? $whitelist->object : array(),
);
}
foreach ($custom_whitelist['iframe'] as $prefix)
{
self::$_iframe_whitelist[] = self::formatPrefix($prefix);
}
foreach ($custom_whitelist['object'] as $prefix)
{
self::$_object_whitelist[] = self::formatPrefix($prefix);
}
}
else
{
foreach ($default_whitelist['iframe'] as $prefix)
{
self::$_iframe_whitelist[] = $prefix;
}
foreach ($default_whitelist['object'] as $prefix)
{
self::$_object_whitelist[] = $prefix;
}
if ($iframe_whitelist = config('mediafilter.iframe') ?: config('embedfilter.iframe'))
{
foreach ($iframe_whitelist as $prefix)
{
self::$_iframe_whitelist[] = self::formatPrefix($prefix);
}
}
if ($object_whitelist = config('mediafilter.object') ?: config('embedfilter.object'))
{
foreach ($object_whitelist as $prefix)
{
self::$_object_whitelist[] = self::formatPrefix($prefix);
}
}
}
self::$_object_whitelist = array_unique(self::$_object_whitelist);
self::$_iframe_whitelist = array_unique(self::$_iframe_whitelist);
natcasesort(self::$_object_whitelist);
natcasesort(self::$_iframe_whitelist);
}
}

View file

@ -0,0 +1,311 @@
<?php
namespace Rhymix\Framework;
/**
* The formatter class.
*/
class Formatter
{
/**
* Options for text to HTML conversion.
*/
const TEXT_NEWLINE_AS_P = 1;
const TEXT_DOUBLE_NEWLINE_AS_P = 2;
/**
* Options for Markdown to HTML conversion.
*/
const MD_NEWLINE_AS_BR = 16;
const MD_ENABLE_EXTRA = 128;
/**
* Convert plain text to HTML.
*
* @param string $text
* @param int $options (optional)
* @return string
*/
public static function text2html($text, $options = 0)
{
// This option uses <p> instead of <br> to separate lines.
if ($options & self::TEXT_NEWLINE_AS_P)
{
$lines = array_map('trim', explode("\n", escape(trim($text))));
$result = '';
foreach ($lines as $line)
{
$result .= "<p>$line</p>\n";
}
return $result;
}
// This option uses <br> to separate lines and <p> to separate paragraphs.
if ($options & self::TEXT_DOUBLE_NEWLINE_AS_P)
{
$lines = preg_replace('!(<br />)+\s*$!', '', nl2br(escape(trim($text))));
$lines = preg_split('!(<br />\s*)+<br />!', $lines);
foreach ($lines as $line)
{
$result .= "<p>\n" . trim($line) . "\n</p>\n";
}
return $result;
}
// The default is to use <br> always.
return nl2br(escape(trim($text))) . "<br />\n";
}
/**
* Convert HTML to plain text.
*
* @param string $html
* @return string
*/
public static function html2text($html)
{
// Add line breaks after <br> and <p> tags.
$html = preg_replace('!<br[^>]*>\s*!i', "\n", $html);
$html = preg_replace('!<p\b[^>]*>\s*!i', '', $html);
$html = preg_replace('!</p[^>]*>\s*!i', "\n\n", $html);
// Encode links and images to preserve essential information.
$html = preg_replace_callback('!<a\b[^>]*href="([^>"]+)"[^>]*>([^<]*)</a>!i', function($matches) {
return trim($matches[2] . ' &lt;' . $matches[1] . '&gt;');
}, $html);
$html = preg_replace_callback('!<img\b[^>]*src="([^>"]+)"[^>]*>!i', function($matches) {
$title = preg_match('!title="([^>"]+)"!i', $matches[0], $m) ? $m[1] : null;
$title = $title ?: (preg_match('!alt="([^>"]+)"!i', $matches[0], $m) ? $m[1] : 'IMAGE');
return trim('[' . $title . '] &lt;' . $matches[1] . '&gt;');
}, $html);
// Strip all other HTML.
$text = html_entity_decode(strip_tags($html));
unset($html);
// Normalize whitespace and return.
$text = str_replace("\r\n", "\n", $text);
$text = preg_replace('/\n(?:\s*\n)+/', "\n\n", $text);
return trim($text) . "\n";
}
/**
* Convert Markdown to HTML.
*
* @param string $markdown
* @param int $options (optional)
* @return string
*/
public static function markdown2html($markdown, $options = 0)
{
if ($options & self::MD_NEWLINE_AS_BR)
{
$markdown = preg_replace('/(?<!\n)\n(?![\n\*\#\-])/', " \n", $markdown);
}
if ($options & self::MD_ENABLE_EXTRA)
{
$class_name = '\\Michelf\\MarkdownExtra';
}
else
{
$class_name = '\\Michelf\\Markdown';
}
$html = $class_name::defaultTransform($markdown);
return Filters\HTMLFilter::clean($html);
}
/**
* Convert HTML to Markdown.
*
* @param string $html
* @return string
*/
public static function html2markdown($html)
{
$converter = new \League\HTMLToMarkdown\HtmlConverter();
$converter->getConfig()->setOption('bold_style', '**');
$converter->getConfig()->setOption('italic_style', '_');
$converter->getConfig()->setOption('strip_tags', true);
return trim($converter->convert($html)) . "\n";
}
/**
* Convert BBCode to HTML.
*
* @param string $bbcode
* @return string
*/
public static function bbcode($bbcode)
{
$parser = new \JBBCode\Parser;
$parser->addCodeDefinitionSet(new \JBBCode\DefaultCodeDefinitionSet());
$builder = new \JBBCode\CodeDefinitionBuilder('quote', '<blockquote>{param}</blockquote>');
$parser->addCodeDefinition($builder->build());
$builder = new \JBBCode\CodeDefinitionBuilder('code', '<pre><code>{param}</code></pre>');
$builder->setParseContent(false);
$parser->addCodeDefinition($builder->build());
$parser->parse($bbcode);
$html = $parser->getAsHtml();
return Filters\HTMLFilter::clean($html);
}
/**
* Apply smart quotes and other stylistic enhancements to HTML.
*
* @param string $html
* @return string
*/
public static function applySmartQuotes($html)
{
return \Michelf\SmartyPants::defaultTransform($html, 'qbBdDiew');
}
/**
* Compile LESS into CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @param array $variables (optional)
* @parsm bool $minify (optional)
* @return bool
*/
public static function compileLESS($source_filename, $target_filename, $variables = array(), $minify = false)
{
// Get the cleaned and concatenated content.
$content = self::_concatenate($source_filename, $target_filename);
// Compile!
try
{
$less_compiler = new \lessc;
$less_compiler->setFormatter($minify ? 'compressed' : 'lessjs');
$less_compiler->setImportDir(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
if ($variables)
{
$less_compiler->setVariables($variables);
}
$content = '@charset "UTF-8";' . "\n" . $less_compiler->compile($content) . "\n";
$result = true;
}
catch (\Exception $e)
{
$content = '/*' . "\n" . 'Error while compiling LESS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
$result = false;
}
// Save the result to the target file.
Storage::write($target_filename, $content);
return $result;
}
/**
* Compile SCSS into CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @param array $variables (optional)
* @parsm bool $minify (optional)
* @return bool
*/
public static function compileSCSS($source_filename, $target_filename, $variables = array(), $minify = false)
{
// Get the cleaned and concatenated content.
$content = self::_concatenate($source_filename, $target_filename);
// Compile!
try
{
$scss_compiler = new \Leafo\ScssPhp\Compiler;
$scss_compiler->setFormatter($minify ? '\Leafo\ScssPhp\Formatter\Crunched' : '\Leafo\ScssPhp\Formatter\Expanded');
$scss_compiler->setImportPaths(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
if ($variables)
{
$scss_compiler->setVariables($variables);
}
$content = '@charset "UTF-8";' . "\n" . $scss_compiler->compile($content) . "\n";
$result = true;
}
catch (\Exception $e)
{
$content = '/*' . "\n" . 'Error while compiling SCSS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
$result = false;
}
// Save the result to the target file.
Storage::write($target_filename, $content);
return $result;
}
/**
* Minify CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @return bool
*/
public static function minifyCSS($source_filename, $target_filename)
{
$minifier = new \MatthiasMullie\Minify\CSS($source_filename);
$content = $minifier->execute($target_filename);
Storage::write($target_filename, $content);
return strlen($content) ? true : false;
}
/**
* Minify JS.
*
* @param string|array $source_filename
* @param string $target_filename
* @return bool
*/
public static function minifyJS($source_filename, $target_filename)
{
$minifier = new \MatthiasMullie\Minify\JS($source_filename);
$content = $minifier->execute($target_filename);
Storage::write($target_filename, $content);
return strlen($content) ? true : false;
}
/**
* CSS concatenation subroutine for compileLESS() and compileSCSS().
*
* @param string|array $source_filename
* @param string $target_filename
* @return string
*/
protected static function _concatenate($source_filename, $target_filename)
{
$result = '';
if (!is_array($source_filename))
{
$source_filename = array($source_filename);
}
foreach ($source_filename as $filename)
{
$content = utf8_clean(file_get_contents($filename));
$path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename);
$content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) {
$url = trim($matches[1], '\'"');
if (!strlen($url) || $url[0] === '/')
{
return $matches[0];
}
else
{
return 'url("' . escape_dqstr($path_converter->convert($url)) . '")';
}
}, $content);
unset($path_converter);
$result .= trim($content) . "\n\n";
}
return $result;
}
}

78
common/framework/i18n.php Normal file
View file

@ -0,0 +1,78 @@
<?php
namespace Rhymix\Framework;
/**
* The i18n (internationalization) class.
*/
class i18n
{
/**
* Constants for sorting.
*/
const SORT_CODE_2 = 2;
const SORT_CODE_3 = 3;
const SORT_CODE_NUMERIC = 4;
const SORT_CCTLD = 5;
const SORT_NAME_ENGLISH = 6;
const SORT_NAME_KOREAN = 7;
const SORT_NAME_NATIVE = 8;
/**
* Get the list of all countries.
*
* @param int $sort_by
* @return array
*/
public static function listCountries($sort_by = self::SORT_NAME_ENGLISH)
{
$countries = (include \RX_BASEDIR . 'common/defaults/countries.php');
$result = array();
foreach ($countries as $country)
{
$result[$country['iso_3166_1_alpha3']] = (object)$country;
}
switch ($sort_by)
{
case self::SORT_CODE_2:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_alpha2, $b->iso_3166_1_alpha2);
});
break;
case self::SORT_CODE_3:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_alpha3, $b->iso_3166_1_alpha3);
});
break;
case self::SORT_CODE_NUMERIC:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_numeric, $b->iso_3166_1_numeric);
});
break;
case self::SORT_CCTLD:
uasort($result, function($a, $b) {
return strcmp($a->cctld, $b->cctld);
});
break;
case self::SORT_NAME_ENGLISH:
uasort($result, function($a, $b) {
return strcmp($a->name_english, $b->name_english);
});
break;
case self::SORT_NAME_KOREAN:
uasort($result, function($a, $b) {
return strcmp($a->name_korean, $b->name_korean);
});
break;
case self::SORT_NAME_NATIVE:
uasort($result, function($a, $b) {
return strcmp($a->name_native, $b->name_native);
});
break;
}
return $result;
}
}

453
common/framework/korea.php Normal file
View file

@ -0,0 +1,453 @@
<?php
namespace Rhymix\Framework;
/**
* Class for validating Korea-specific information.
*/
class Korea
{
/**
* Format a phone number.
*
* @param string $num
* @return string
*/
public static function formatPhoneNumber($num)
{
// Remove all non-numbers.
$num = preg_replace('/[^0-9]/', '', $num);
// Remove the country code.
if (strncmp($num, '82', 2) === 0)
{
$num = substr($num, 2);
if (strncmp($num, '0', 1) !== 0)
{
$num = '0' . $num;
}
}
// Apply different format based on the number of digits.
switch (strlen($num))
{
case 8:
return substr($num, 0, 4) . '-' . substr($num, 4);
case 9:
return substr($num, 0, 2) . '-' . substr($num, 2, 3) . '-' . substr($num, 5);
case 10:
if (substr($num, 0, 2) === '02')
{
return substr($num, 0, 2) . '-' . substr($num, 2, 4) . '-' . substr($num, 6);
}
else
{
return substr($num, 0, 3) . '-' . substr($num, 3, 3) . '-' . substr($num, 6);
}
default:
if (substr($num, 0, 4) === '0303' || substr($num, 0, 3) === '050')
{
return substr($num, 0, 4) . '-' . substr($num, 4, 3) . '-' . substr($num, 7);
}
else
{
return substr($num, 0, 3) . '-' . substr($num, 3, 4) . '-' . substr($num, 7);
}
}
}
/**
* Check if a Korean phone number contains a valid area code and the correct number of digits.
*
* @param string $num
* @return bool
*/
public static function isValidPhoneNumber($num)
{
$num = str_replace('-', '', self::formatPhoneNumber($num));
if (preg_match('/^1[0-9]{7}$/', $num))
{
return true;
}
if (preg_match('/^02[2-9][0-9]{6,7}$/', $num))
{
return true;
}
if (preg_match('/^0[13-8][0-9][2-9][0-9]{6,7}$/', $num))
{
return true;
}
return false;
}
/**
* Check if a Korean phone number is a mobile phone number.
*
* @param string $num
* @return bool
*/
public static function isValidMobilePhoneNumber($num)
{
$num = str_replace('-', '', self::formatPhoneNumber($num));
$len = strlen($num);
return preg_match('/^01[016789][2-9][0-9]{6,7}$/', $num) ? true : false;
}
/**
* Check if the given string is a valid resident registration number (주민등록번호)
* or foreigner registration number (외국인등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidJuminNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Return false if the date of birth is in the future.
if (in_array((int)($code[6]), array(3, 4, 7, 8)) && intval(substr($code, 0, 6), 10) > date('ymd'))
{
return false;
}
// Calculate the checksum.
$sum = 0;
for ($i = 0; $i < 12; $i++)
{
$sum += $code[$i] * (($i % 8) + 2);
}
$checksum = (11 - ($sum % 11)) % 10;
if (in_array((int)($code[6]), array(1, 2, 3, 4, 9, 0)))
{
return $checksum === (int)($code[12]);
}
else
{
if (substr($code, 7, 2) % 2 !== 0)
{
return false;
}
else
{
return (($checksum + 2) % 10) === (int)($code[12]);
}
}
}
/**
* Check if the given string is a valid corporation registration number (법인등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidCorporationNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Calculate the checksum.
$sum = 0;
for ($i = 0; $i < 12; $i++)
{
$sum += $code[$i] * (($i % 2) + 1);
}
$checksum = (10 - ($sum % 10)) % 10;
// Check the 7th and 13th digits.
if ($code[6] !== '0')
{
return false;
}
return $checksum === (int)($code[12]);
}
/**
* Check if the given string is a valid business registration number (사업자등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidBusinessNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Calculate the checksum.
$sum = 0;
$sum += $code[0] + ($code[1] * 3) + ($code[2] * 7);
$sum += $code[3] + ($code[4] * 3) + ($code[5] * 7);
$sum += $code[6] + ($code[7] * 3) + ($code[8] * 5);
$sum += floor(($code[8] * 5) / 10);
$checksum = (10 - ($sum % 10)) % 10;
// Check the last digit.
return $checksum === (int)($code[9]);
}
/**
* Check if the given IP address is Korean.
*
* This method may return incorrect results if the IP allocation databases
* (korea.ipv4.php, korea.ipv6.php) are out of date.
*
* @param string $ip
* @return bool
*/
public static function isKoreanIP($ip)
{
// Extract the IPv4 address from an "IPv4-mapped IPv6" address.
if (preg_match('/::ffff:(?:0+:)?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/', $ip, $matches)) $ip = $matches[1];
// Return false if the IP address is not in the right format.
if (!filter_var($ip, \FILTER_VALIDATE_IP)) return false;
// Check IPv4.
if (filter_var($ip, \FILTER_VALIDATE_IP, array('flags' => \FILTER_FLAG_IPV4)))
{
// Convert to integer.
$ipnum = sprintf('%u', ip2long($ip));
// Treat local addresses as Korean.
if ($ipnum >= 167772160 && $ipnum <= 184549375) return true; // 10.0.0.0/8
if ($ipnum >= 2130706432 && $ipnum <= 2147483647) return true; // 127.0.0.0/8
if ($ipnum >= 3232235520 && $ipnum <= 3232301055) return true; // 192.168.0.0/16
if ($ipnum >= 2886729728 && $ipnum <= 2887778303) return true; // 172.16.0.0/20
// Check the IPv4 allocation database.
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv4.php');
foreach ($ranges as $range)
{
if ($ipnum >= $range[0] && $ipnum <= $range[1]) return true;
}
return false;
}
// Check IPv6.
elseif (function_exists('inet_pton'))
{
// Convert to hexadecimal format.
$ipbin = strtolower(bin2hex(inet_pton($ip)));
// Treat local addresses as Korean.
if ($ipbin == '00000000000000000000000000000001') return true; // ::1
if (preg_match('/^f(?:[cd]|e80{13})/', $ipbin)) return true; // fc00::/8, fd00::/8, fe80::/64
// Check the IPv6 allocation database.
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv6.php');
foreach ($ranges as $range)
{
if (strncmp($ipbin, $range[0], 16) >= 0 && strncmp($ipbin, $range[1], 16) <= 0) return true;
}
return false;
}
return false;
}
/**
* Check if the given email address is hosted by a Korean portal site.
*
* This can be used to tell which recipients may subscribe to the KISA RBL (kisarbl.or.kr).
* If the domain is not found, this method returns false.
*
* @param string $domain
* @param bool $clear_cache (optional)
* @return bool
*/
public static function isKoreanEmailAddress($email_address, $clear_cache = false)
{
// Clear the cache if requested.
if ($clear_cache)
{
self::$_domain_cache = array();
}
// Get the domain from the email address.
if ($pos = strpos($email_address, '@'))
{
$domain = substr($email_address, $pos + 1);
}
else
{
$domain = $email_address;
}
$domain = rtrim(strtolower($domain), '.');
// Return cached result if available.
if (array_key_exists($domain, self::$_domain_cache))
{
return self::$_domain_cache[$domain];
}
// Shortcut for known domains.
if (in_array($domain, self::$known_korean))
{
return self::$_domain_cache[$domain] = true;
}
if (in_array($domain, self::$known_foreign))
{
return self::$_domain_cache[$domain] = false;
}
// For unknown domains, check the MX record.
$mx = self::_getDNSRecords($domain, \DNS_MX);
$i = 0;
foreach ($mx as $mx)
{
$mx = rtrim($mx, '.');
foreach (self::$known_korean as $portal)
{
if ($mx === $portal || ends_with('.' . $portal, $mx))
{
return self::$_domain_cache[$domain] = true;
}
}
foreach (self::$known_foreign as $portal)
{
if ($mx === $portal || ends_with('.' . $portal, $mx))
{
return self::$_domain_cache[$domain] = false;
}
}
foreach (self::_getDNSRecords($domain, \DNS_A) as $mx_ip)
{
return self::$_domain_cache[$domain] = self::isKoreanIP($mx_ip);
}
if (++$i > 2)
{
break;
}
}
return self::$_domain_cache[$domain] = false;
}
/**
* Get the DNS records of a domain.
*
* @param string $domain
* @param int $type
* @return array
*/
protected static function _getDNSRecords($domain, $type)
{
$records = dns_get_record($domain, $type);
if (!$records)
{
return array();
}
$result = array();
foreach ($records as $record)
{
if (isset($record['pri']) && isset($record['target']))
{
$result[intval($record['pri'])] = $record['target'];
}
elseif (isset($record['target']))
{
$result[] = $record['target'];
}
elseif (isset($record['ip']) || isset($record['ipv6']))
{
$result[] = isset($record['ip']) ? $record['ip'] : $record['ipv6'];
}
elseif (isset($record['txt']))
{
$result[] = $record['txt'];
}
}
ksort($result);
return $result;
}
/**
* Prevent multiple lookups for the same domain.
*/
protected static $_domain_cache = array();
/**
* Domains known to be Korean and subscribed to the KISA RBL.
*/
public static $known_korean = array(
'hanmail.net',
'hanmail2.net',
'daum.net',
'paran.com',
'tistory.com',
'naver.com',
'navercorp.com',
'nate.com',
'cyworld.com',
'dreamwiz.com',
'korea.com',
'dreamx.com',
'chol.com',
'chollian.net',
'hanmir.com',
'hitel.com',
'freechal.com',
'empas.com',
'empal.com',
'hanafos.com',
);
/**
* Domains known to be foreign.
*/
public static $known_foreign = array(
'gmail.com',
'googlemail.com',
'google.com',
'yahoo.com',
'yahoo.co.kr',
'hotmail.com',
'hotmail.co.kr',
'live.com',
'outlook.com',
'msn.com',
'me.com',
'mac.com',
'icloud.com',
'facebook.com',
'aol.com',
'gmx.com',
'mail.com',
'fastmail.com',
'fastmail.fm',
'runbox.com',
'inbox.com',
'lycos.com',
'zoho.com',
);
}

View file

@ -75,19 +75,19 @@ class Lang
if ($name === 'common')
{
$this->loadDirectory(RX_BASEDIR . 'common/lang', 'common');
$this->loadDirectory(\RX_BASEDIR . 'common/lang', 'common');
}
elseif (file_exists(RX_BASEDIR . "plugins/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "plugins/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "plugins/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "plugins/$name/lang", $name);
}
elseif (file_exists(RX_BASEDIR . "modules/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "modules/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "modules/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "modules/$name/lang", $name);
}
elseif (file_exists(RX_BASEDIR . "addons/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "addons/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "addons/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "addons/$name/lang", $name);
}
}
@ -108,70 +108,52 @@ class Lang
return true;
}
// Load the language file.
$lang = $this->getPluginLang($dir);
// Initialize variables.
$filename = null;
$lang = new \stdClass;
$result = true;
// Load the default language file.
// Find a suitable language file in the given directory.
if (file_exists($dir . '/' . $this->_language . '.php'))
{
$filename = $dir . '/' . $this->_language . '.php';
}
elseif (($hyphen = strpos($this->_language, '-')) !== false && file_exists($dir . '/' . substr($this->_language, 0, $hyphen) . '.php'))
{
$filename = $dir . '/' . substr($this->_language, 0, $hyphen) . '.php';
}
elseif (file_exists("$dir/lang.xml"))
{
$filename = Parsers\LangParser::compileXMLtoPHP("$dir/lang.xml", $this->_language === 'ja' ? 'jp' : $this->_language);
}
elseif (file_exists($dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php'))
{
$filename = $dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php';
}
// Load the language file.
if ($filename)
{
include $filename;
array_unshift($this->_search_priority, $plugin_name);
$result = true;
}
else
{
$result = false;
}
// Mark this directory and plugin as loaded.
$this->_loaded_directories[$dir] = true;
$this->_loaded_plugins[$plugin_name] = $lang;
// Load the same directory in the default language, too.
if ($this->_language !== 'en')
{
self::getInstance('en')->loadDirectory($dir, $plugin_name);
}
if (!empty($lang))
{
$this->_loaded_directories[$dir] = true;
$this->_loaded_plugins[$plugin_name] = $lang;
array_unshift($this->_search_priority, $plugin_name);
return true;
}
else
{
$this->_loaded_directories[$dir] = true;
$this->_loaded_plugins[$plugin_name] = new \stdClass;
return false;
}
}
/**
* Get the language file from plugin.
*
* @param string $dir
* @param string $language
* @return object
*/
public function getPluginLang($dir, $language = null)
{
if (!$language)
{
$language = $this->_language;
}
if (file_exists($dir . '/' . $language . '.php'))
{
$filename = $dir . '/' . $language . '.php';
}
elseif (($hyphen = strpos($language, '-')) !== false && file_exists($dir . '/' . substr($language, 0, $hyphen) . '.php'))
{
$filename = $dir . '/' . substr($language, 0, $hyphen) . '.php';
}
elseif (file_exists("$dir/lang.xml"))
{
$filename = Compat\LangParser::compileXMLtoPHP("$dir/lang.xml", $language === 'ja' ? 'jp' : $language);
}
elseif (file_exists($dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php'))
{
$filename = $dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php';
}
if (!$filename)
{
return new \stdClass;
}
$lang = new \stdClass;
include $filename;
return $lang;
return $result;
}
/**
@ -181,7 +163,7 @@ class Lang
*/
public static function getSupportedList()
{
return (include RX_BASEDIR . 'common/defaults/lang.php');
return (include \RX_BASEDIR . 'common/defaults/lang.php');
}
/**
@ -194,7 +176,19 @@ class Lang
{
$args = func_get_args();
array_shift($args);
return $this->__call($key, $args);
if (count($args) === 1 && is_array($args[0]))
{
$args = $args[0];
}
// Get the translation.
$translation = $this->__get($key);
// If there are no arguments, return the translation.
if (!count($args)) return $translation;
// If there are arguments, interpolate them into the translation and return the result.
return vsprintf($translation, $args);
}
/**
@ -209,6 +203,24 @@ class Lang
$this->__set($key, $value);
}
/**
* Fallback method for getting the default translation.
*
* @param string $key
* @return string
*/
public function getFromDefaultLang($key)
{
if ($this->_language === 'en')
{
return $key;
}
else
{
return self::getInstance('en')->__get($key);
}
}
/**
* Magic method for translations without arguments.
*
@ -217,84 +229,59 @@ class Lang
*/
public function __get($key)
{
// Get default language
if ($this->_language !== 'en')
// Load a dot-separated key (prefixed by plugin name).
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2)
{
$lang_en = self::getInstance('en')->{$key};
}
// Separate the plugin name from the key.
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key, 2)) && count($keys) === 2)
{
list($plugin_name, $lang_key) = $keys;
// Attempt to load the plugin.
$plugin_name = array_shift($keys);
if (!isset($this->_loaded_plugins[$plugin_name]))
{
$this->loadPlugin($plugin_name);
}
if (isset($this->_loaded_plugins[$plugin_name]->{$lang_key}))
if (!isset($this->_loaded_plugins[$plugin_name]))
{
$lang = $this->_loaded_plugins[$plugin_name]->{$lang_key};
if (is_array($lang) && is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
{
return $lang_en;
}
return $lang;
return $this->getFromDefaultLang($key);
}
}
else
{
// Search custom translations first.
if (isset($this->_loaded_plugins['_custom_']->{$key}))
// Find the given key.
$lang = $this->_loaded_plugins[$plugin_name];
foreach ($keys as $subkey)
{
$lang = $this->_loaded_plugins['_custom_']->{$key};
if (is_array($lang))
if (is_object($lang) && isset($lang->{$subkey}))
{
if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
{
return new \ArrayObject($lang_en, 3);
}
return new \ArrayObject($lang, 3);
$lang = $lang->{$subkey};
}
elseif (is_array($lang) && isset($lang[$subkey]))
{
$lang = $lang[$subkey];
}
else
{
return $lang;
}
}
// Search other plugins.
foreach ($this->_search_priority as $plugin_name)
{
if (isset($this->_loaded_plugins[$plugin_name]->{$key}))
{
$lang = $this->_loaded_plugins[$plugin_name]->{$key};
if (is_array($lang))
{
if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
{
return new \ArrayObject($lang_en, 3);
}
return new \ArrayObject($lang, 3);
}
else
{
return $lang;
}
return $this->getFromDefaultLang($key);
}
}
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
}
// Search other language.
if (isset($lang_en))
// Search custom translations first.
if (isset($this->_loaded_plugins['_custom_']->{$key}))
{
return $lang_en;
$lang = $this->_loaded_plugins['_custom_']->{$key};
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
}
// If no translation is found, return the key.
return $key;
// Search other plugins.
foreach ($this->_search_priority as $plugin_name)
{
if (isset($this->_loaded_plugins[$plugin_name]->{$key}))
{
$lang = $this->_loaded_plugins[$plugin_name]->{$key};
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
}
}
// If no translation is found, return the default language.
return $this->getFromDefaultLang($key);
}
/**
@ -306,6 +293,73 @@ class Lang
*/
public function __set($key, $value)
{
// Set a dot-separated key (prefixed by plugin name).
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2)
{
// Attempt to load the plugin.
$plugin_name = array_shift($keys);
if (!isset($this->_loaded_plugins[$plugin_name]))
{
$this->loadPlugin($plugin_name);
}
if (!isset($this->_loaded_plugins[$plugin_name]))
{
return false;
}
// Set the given key.
$count = count($keys);
$lang = $this->_loaded_plugins[$plugin_name];
foreach ($keys as $i => $subkey)
{
if (is_object($lang) && isset($lang->{$subkey}))
{
if ($i === $count - 1)
{
$lang->{$subkey} = $value;
break;
}
elseif (is_array($lang->{$subkey}))
{
$lang = &$lang->{$subkey};
}
else
{
return false;
}
}
elseif (is_array($lang) && isset($lang[$subkey]))
{
if ($i === $count - 1)
{
$lang[$subkey] = $value;
break;
}
elseif (is_array($lang[$subkey]))
{
$lang = &$lang[$subkey];
}
else
{
return false;
}
}
else
{
if (is_object($lang))
{
$lang->{$subkey} = $value;
}
else
{
$lang[$subkey] = $value;
}
break;
}
}
}
// Set a regular key.
$this->_loaded_plugins['_custom_']->{$key} = $value;
}
@ -335,13 +389,7 @@ class Lang
*/
public function __unset($key)
{
foreach ($this->_loaded_plugins as $plugin_name => $translations)
{
if (isset($translations->{$key}))
{
unset($translations->{$key});
}
}
$this->set($key, null);
}
/**
@ -353,16 +401,6 @@ class Lang
*/
public function __call($key, $args = array())
{
// Remove a colon from the beginning of the string.
if ($key !== '' && $key[0] === ':') $key = substr($key, 1);
// Find the translation.
$translation = $this->__get($key);
// If there are no arguments, return the translation.
if (!count($args)) return $translation;
// If there are arguments, interpolate them into the translation and return the result.
return vsprintf($translation, $args);
return $this->get($key, $args);
}
}

154
common/framework/mime.php Normal file
View file

@ -0,0 +1,154 @@
<?php
namespace Rhymix\Framework;
/**
* The MIME class.
*/
class MIME
{
/**
* Get the MIME type for the given extension.
*
* @param string $extension
* @return string
*/
public static function getTypeByExtension($extension)
{
$extension = strtolower($extension);
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
}
/**
* Get the MIME type for the given filename.
*
* @param string $filename
* @return string
*/
public static function getTypeByFilename($filename)
{
$extension = strrchr($filename, '.');
if ($extension === false) return self::$_default;
$extension = strtolower(substr($extension, 1));
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
}
/**
* Get the most common extension for the given MIME type.
*
* @param string $type
* @return string|false
*/
public static function getExtensionByType($type)
{
foreach (self::$_types as $extension => $mime)
{
if (!strncasecmp($type, $mime, strlen($type))) return $extension;
}
return false;
}
/**
* The default MIME type for unknown extensions.
*/
protected static $_default = 'application/octet-stream';
/**
* The list of known MIME types.
*/
protected static $_types = array(
// Text-based document formats.
'html' => 'text/html',
'htm' => 'text/html',
'shtml' => 'text/html',
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => 'text/plain',
'md' => 'text/markdown',
'markdown' => 'text/markdown',
'rtf' => 'text/rtf',
'xml' => 'text/xml',
'xsl' => 'text/xml',
'css' => 'text/css',
'csv' => 'text/csv',
// Binary document formats.
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odb' => 'application/vnd.oasis.opendocument.database',
'pdf' => 'application/pdf',
// Images.
'bmp' => 'image/bmp',
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'svg' => 'image/svg+xml',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'ico' => 'image/vnd.microsoft.icon',
// Audio.
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'ra' => 'audio/x-realaudio',
'wav' => 'audio/x-wav',
'ogg' => 'audio/ogg',
// Video.
'avi' => 'video/x-msvideo',
'flv' => 'video/x-flv',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'mp4' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'movie' => 'video/x-sgi-movie',
'rv' => 'video/vnd.rn-realvideo',
'dvi' => 'application/x-dvi',
// Other multimedia file formats.
'psd' => 'application/x-photoshop',
'swf' => 'application/x-shockwave-flash',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'mif' => 'application/vnd.mif',
'xul' => 'application/vnd.mozilla.xul+xml',
// Source code formats.
'phps' => 'application/x-httpd-php-source',
'js' => 'application/x-javascript',
// Archives.
'bz2' => 'application/x-bzip',
'gz' => 'application/x-gzip',
'tar' => 'application/x-tar',
'tgz' => 'application/x-tar',
'gtar' => 'application/x-gtar',
'rar' => 'application/x-rar-compressed',
'zip' => 'application/x-zip',
// RFC822 email message.
'eml' => 'message/rfc822',
);
}

View file

@ -0,0 +1,153 @@
<?php
namespace Rhymix\Framework;
/**
* The pagination class.
*/
class Pagination
{
/**
* Count style constants.
*/
const COUNT_STYLE_NORMAL = 1;
const COUNT_STYLE_CONTINUOUS = 2;
/**
* Calculate the number of pages.
*
* @param int $total_items
* @param int $items_per_page
* @param int $minimum (optional)
* @return int
*/
public static function countPages($total_items, $items_per_page, $minimum = 1)
{
if (!$items_per_page)
{
return (int)$minimum;
}
else
{
return (int)max($minimum, ceil($total_items / $items_per_page));
}
}
/**
* Create HTML for pagination.
*
* @param string $base_url ($PAGE will be replaced with the page number)
* @param int $current_page
* @param int $total_pages
* @param int $count (optional)
*/
public static function createLinks($base_url, $total_pages, $current_page, $count = 10, $count_style = self::COUNT_STYLE_NORMAL)
{
// Only integers are allowed here.
$current_page = (int)$current_page;
$total_pages = (int)$total_pages;
$count = (int)$count;
// Determine the range of pages to show.
if ($count_style === self::COUNT_STYLE_NORMAL)
{
$last_shown = ceil($current_page / $count) * $count;
$first_shown = max(1, $last_shown - $count + 1);
if ($last_shown > $total_pages)
{
$last_shown = $total_pages;
}
}
else
{
$first_shown = $current_page - floor(($count - 1) / 2);
if ($first_shown < 1)
{
$first_shown = 1;
}
$last_shown = $first_shown + $count - 1;
if ($last_shown > $total_pages)
{
$last_shown = $total_pages;
$first_shown = max(1, $last_shown - $count + 1);
}
}
// Open the <div> tag.
$return = array('<div class="pagination">');
// Compose the link to the first page.
if ($first_shown > 1)
{
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', 1, $base_url);
}
else
{
$target_url = $base_url . 1;
}
$return[] = self::_composeLink($target_url, '<span class="arrow">&laquo;</span> <span class="page_number first_page">1</span>');
$return[] = '<span class="ellipsis">...</span>';
}
// Compose links for each page.
for ($page = $first_shown; $page <= $last_shown; $page++)
{
if ($page == $current_page)
{
$opening_span = '<span class="page_number current_page">';
}
else
{
$opening_span = '<span class="page_number">';
}
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', $page, $base_url);
}
else
{
$target_url = $base_url . $page;
}
$return[] = self::_composeLink($target_url, $opening_span . $page . '</span>');
}
// Compose the link to the last page.
if ($last_shown < $total_pages)
{
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', $total_pages, $base_url);
}
else
{
$target_url = $base_url . $total_pages;
}
$return[] = '<span class="ellipsis">...</span>';
$return[] = self::_composeLink($target_url, '<span class="page_number last_page">' . $total_pages . '</span> <span class="arrow">&raquo;</span>');
}
// Close the <div> tag.
$return[] = '</div>';
// Return the completed HTML.
return implode(' ', $return);
}
/**
* Link creation subroutine.
*
* @param string $target_url
* @param string $content
* @return string
*/
protected static function _composeLink($target_url, $content)
{
return '<a href="' . escape($target_url) . '">' . $content . '</a>';
}
}

View file

@ -1,9 +1,10 @@
<?php
namespace Rhymix\Framework\Compat;
namespace Rhymix\Framework\Parsers;
use Rhymix\Framework\Config;
use Rhymix\Framework\DateTime;
use Rhymix\Framework\Security;
/**
* Config parser class for XE compatibility.
@ -18,9 +19,9 @@ class ConfigParser
public static function convert()
{
// Load DB info file.
if (file_exists(RX_BASEDIR . Config::$old_db_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_db_config_filename))
{
include RX_BASEDIR . Config::$old_db_config_filename;
include \RX_BASEDIR . Config::$old_db_config_filename;
}
else
{
@ -28,16 +29,16 @@ class ConfigParser
}
// Load FTP info file.
if (file_exists(RX_BASEDIR . Config::$old_ftp_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_ftp_config_filename))
{
include RX_BASEDIR . Config::$old_ftp_config_filename;
include \RX_BASEDIR . Config::$old_ftp_config_filename;
}
// Load selected language file.
if (file_exists(RX_BASEDIR . Config::$old_lang_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_lang_config_filename))
{
$lang_selected = array();
$lang_selected_raw = file_get_contents(RX_BASEDIR . Config::$old_lang_config_filename);
$lang_selected_raw = file_get_contents(\RX_BASEDIR . Config::$old_lang_config_filename);
$lang_selected_raw = array_map('trim', explode("\n", $lang_selected_raw));
foreach ($lang_selected_raw as $lang_selected_item)
{
@ -58,7 +59,7 @@ class ConfigParser
}
// Load defaults for the new configuration.
$config = (include RX_BASEDIR . Config::$default_config_filename);
$config = (include \RX_BASEDIR . Config::$default_config_filename);
// Convert database configuration.
if (!isset($db_info->master_db))
@ -151,9 +152,9 @@ class ConfigParser
}
// Create new crypto keys.
$config['crypto']['encryption_key'] = \Password::createSecureSalt(64, 'alnum');
$config['crypto']['authentication_key'] = \Password::createSecureSalt(64, 'alnum');
$config['crypto']['session_key'] = \Password::createSecureSalt(64, 'alnum');
$config['crypto']['encryption_key'] = Security::getRandom(64, 'alnum');
$config['crypto']['authentication_key'] = Security::getRandom(64, 'alnum');
$config['crypto']['session_key'] = Security::getRandom(64, 'alnum');
// Convert language configuration.
if (isset($db_info->lang_type))
@ -183,7 +184,7 @@ class ConfigParser
{
$default_url = \Context::decodeIdna($default_url);
}
$config['url']['default'] = $default_url ?: (RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
$config['url']['default'] = $default_url ?: (\RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
$config['url']['http_port'] = $db_info->http_port ?: null;
$config['url']['https_port'] = $db_info->https_port ?: null;
$config['url']['ssl'] = $db_info->use_ssl ?: 'none';
@ -216,14 +217,14 @@ class ConfigParser
}
$config['lock']['allow'] = array_values($db_info->sitelock_whitelist);
// Convert embed filter configuration.
// Convert media filter configuration.
if (is_array($db_info->embed_white_iframe))
{
$whitelist = array_unique(array_map(function($item) {
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
}, $db_info->embed_white_iframe));
natcasesort($whitelist);
$config['embedfilter']['iframe'] = $whitelist;
$config['mediafilter']['iframe'] = $whitelist;
}
if (is_array($db_info->embed_white_object))
{
@ -231,7 +232,7 @@ class ConfigParser
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
}, $db_info->embed_white_object));
natcasesort($whitelist);
$config['embedfilter']['object'] = $whitelist;
$config['mediafilter']['object'] = $whitelist;
}
// Convert miscellaneous configuration.

View file

@ -1,8 +1,9 @@
<?php
namespace Rhymix\Framework\Compat;
namespace Rhymix\Framework\Parsers;
use Rhymix\Framework\Lang;
use Rhymix\Framework\Storage;
/**
* Lang parser class for XE compatibility.
@ -32,7 +33,7 @@ class LangParser
foreach ($files as $filename)
{
$new_filename = preg_replace('/\.lang\.php$/', '.php', str_replace('jp.lang', 'ja.lang', $filename));
\FileHandler::rename($filename, $new_filename);
Storage::move($filename, $new_filename);
}
}
}
@ -49,7 +50,7 @@ class LangParser
// Check if the cache file already exists.
if ($output_filename === null)
{
$output_filename = RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
$output_filename = \RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
if (file_exists($output_filename) && filemtime($output_filename) > filemtime($filename))
{
return $output_filename;
@ -60,7 +61,7 @@ class LangParser
$xml = @simplexml_load_file($filename);
if ($xml === false)
{
\FileHandler::writeFile($output_filename, '');
Storage::write($output_filename, '');
return false;
}
@ -95,7 +96,7 @@ class LangParser
$buff .= '$lang->' . $key . ' = ' . var_export($value, true) . ";\n";
}
}
\FileHandler::writeFile($output_filename, $buff);
Storage::write($output_filename, $buff);
return $output_filename;
}

View file

@ -0,0 +1,480 @@
<?php
namespace Rhymix\Framework;
/**
* The password class.
*/
class Password
{
/**
* Regular expressions to detect various hashing algorithms.
*/
protected static $_algorithm_callbacks = array();
protected static $_algorithm_signatures = array(
'bcrypt' => '/^\$2[a-z]\$[0-9]{2}\$/',
'pbkdf2' => '/^[a-z0-9]+:[0-9]+:/',
'md5' => '/^[0-9a-f]{32}$/',
'md5,sha1,md5' => '/^[0-9a-f]{32}$/',
'sha1' => '/^[0-9a-f]{40}$/',
'sha256' => '/^[0-9a-f]{64}$/',
'sha384' => '/^[0-9a-f]{96}$/',
'sha512' => '/^[0-9a-f]{128}$/',
'ripemd160' => '/^[0-9a-f]{40}$/',
'whirlpool' => '/^[0-9a-f]{128}$/',
'mssql_pwdencrypt' => '/^0x0100[0-9A-F]{48}$/',
'mysql_old_password' => '/^[0-9a-f]{16}$/',
'mysql_new_password' => '/^\*[0-9A-F]{40}$/',
'portable' => '/^\$P\$/',
'drupal' => '/^\$S\$/',
'joomla' => '/^[0-9a-f]{32}:[0-9a-zA-Z\.\+\/\=]{32}$/',
'kimsqrb' => '/\$[1-4]\$[0-9]{14}$/',
'crypt' => '/^([0-9a-zA-Z\.\/]{13}$|_[0-9a-zA-Z\.\/]{19}$|\$[156]\$)/',
);
/**
* Add a custom algorithm.
*
* @param string $name
* @param string $signature
* @param callable $callback
* @return void
*/
public static function addAlgorithm($name, $signature, $callback)
{
self::$_algorithm_signatures[$name] = $signature;
self::$_algorithm_callbacks[$name] = $callback;
}
/**
* Check if the given sequence of algorithms is valid.
*
* @param array|string $algos
* @return bool
*/
public static function isValidAlgorithm($algos)
{
$hash_algos = hash_algos();
$algos = is_array($algos) ? $algos : explode(',', $algos);
foreach ($algos as $algo)
{
if (array_key_exists($algo, self::$_algorithm_signatures))
{
continue;
}
if (in_array($algo, $hash_algos))
{
continue;
}
return false;
}
return true;
}
/**
* Get the list of hashing algorithms supported by this server.
*
* @return array
*/
public static function getSupportedAlgorithms()
{
$retval = array();
if (defined('\CRYPT_BLOWFISH'))
{
$retval['bcrypt'] = 'bcrypt';
}
if (in_array('sha512', hash_algos()))
{
$retval['pbkdf2'] = 'pbkdf2';
}
$retval['sha512'] = 'sha512';
$retval['sha256'] = 'sha256';
$retval['sha1'] = 'sha1';
$retval['md5'] = 'md5';
return $retval;
}
/**
* Get the best hashing algorithm supported by this server.
*
* @return string
*/
public static function getBestSupportedAlgorithm()
{
$algos = self::getSupportedAlgorithms();
return key($algos);
}
/**
* Get the current default hashing algorithm.
*
* @return string
*/
public static function getDefaultAlgorithm()
{
if (function_exists('getModel'))
{
$config = getModel('member')->getMemberConfig();
$algorithm = $config->password_hashing_algorithm;
if (strval($algorithm) === '')
{
$algorithm = 'md5';
}
}
else
{
$algorithm = 'md5';
}
return $algorithm;
}
/**
* Get the currently configured work factor for bcrypt and other adjustable algorithms.
*
* @return int
*/
public static function getWorkFactor()
{
if (function_exists('getModel'))
{
$config = getModel('member')->getMemberConfig();
$work_factor = $config->password_hashing_work_factor;
if (!$work_factor || $work_factor < 4 || $work_factor > 31)
{
$work_factor = 9;
}
}
else
{
$work_factor = 9;
}
return $work_factor;
}
/**
* Generate a reasonably strong random password.
*
* @param int $length
* @return string
*/
public static function getRandomPassword($length = 16)
{
while(true)
{
$source = base64_encode(Security::getRandom(64, 'binary'));
$source = strtr($source, 'iIoOjl10/', '@#$%&*-!?');
$source_length = strlen($source);
for($i = 0; $i < $source_length - $length; $i++)
{
$candidate = substr($source, $i, $length);
if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) &&
preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate))
{
return $candidate;
}
}
}
}
/**
* Hash a password.
*
* To use multiple algorithms in series, provide them as an array.
* Salted algorithms such as bcrypt, pbkdf2, or portable must be used last.
* On error, false will be returned.
*
* @param string $password
* @param string|array $algos (optional)
* @param string $salt (optional)
* @return string|false
*/
public static function hashPassword($password, $algos = null, $salt = null)
{
// If the algorithm is null, use the default algorithm.
if ($algos === null)
{
$algos = self::getDefaultAlgorithm();
}
// Initialize the chain of hashes.
$algos = array_map('strtolower', array_map('trim', is_array($algos) ? $algos : explode(',', $algos)));
$hashchain = preg_replace('/\\s+/', ' ', trim($password));
// Apply the given algorithms one by one.
foreach ($algos as $algo)
{
switch ($algo)
{
// bcrypt (must be used last)
case 'bcrypt':
$hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor());
if ($hashchain[0] === '*') return false;
return $hashchain;
// PBKDF2 (must be used last)
case 'pbkdf2':
if ($salt === null)
{
$salt = Security::getRandom(12, 'alnum');
$hash_algorithm = 'sha512';
$iterations = intval(pow(2, self::getWorkFactor() + 5)) ?: 16384;
$key_length = 24;
}
else
{
$parts = explode(':', $salt);
$salt = $parts[2];
$hash_algorithm = $parts[0];
$iterations = intval($parts[1], 10);
$key_length = strlen(base64_decode($parts[3]));
}
return self::pbkdf2($hashchain, $salt, $hash_algorithm, $iterations, $key_length);
// phpass portable algorithm (must be used last)
case 'portable':
$phpass = new \Hautelook\Phpass\PasswordHash(self::getWorkFactor(), true);
if ($salt === null)
{
$hashchain = $phpass->HashPassword($hashchain);
return $hashchain;
}
else
{
$match = $phpass->CheckPassword($hashchain, $salt);
return $match ? $salt : false;
}
// Drupal's SHA-512 based algorithm (must be used last)
case 'drupal':
$hashchain = \VendorPass::drupal($password, $salt);
return $hashchain;
// Joomla's MD5 based algorithm (must be used last)
case 'joomla':
$hashchain = \VendorPass::joomla($password, $salt);
return $hashchain;
// KimsQ Rb algorithms (must be used last)
case 'kimsqrb':
$hashchain = \VendorPass::kimsqrb($password, $salt);
return $hashchain;
// crypt() function (must be used last)
case 'crypt':
if ($salt === null) $salt = Security::getRandom(2, 'alnum');
$hashchain = crypt($hashchain, $salt);
return $hashchain;
// MS SQL's PWDENCRYPT() function (must be used last)
case 'mssql_pwdencrypt':
$hashchain = \VendorPass::mssql_pwdencrypt($hashchain, $salt);
return $hashchain;
// MySQL's old PASSWORD() function.
case 'mysql_old_password':
$hashchain = \VendorPass::mysql_old_password($hashchain);
break;
// MySQL's new PASSWORD() function.
case 'mysql_new_password':
$hashchain = \VendorPass::mysql_new_password($hashchain);
break;
// A dummy algorithm that does nothing.
case 'null':
break;
// All other algorithms will be passed to hash() or treated as a function name.
default:
if (isset(self::$_algorithm_callbacks[$algo]))
{
$callback = self::$_algorithm_callbacks[$algo];
$hashchain = $callback($hashchain, $salt);
}
elseif (in_array($algo, hash_algos()))
{
$hashchain = hash($algo, $hashchain);
}
elseif (function_exists($algo))
{
$hashchain = $algo($hashchain, $salt);
}
else
{
return false;
}
}
}
return $hashchain;
}
/**
* Check a password against a hash.
*
* This method returns true if the password is correct, and false otherwise.
* If the algorithm is not specified, it will be guessed from the format of the hash.
*
* @param string $password
* @param string $hash
* @param array|string $algos
* @return bool
*/
public static function checkPassword($password, $hash, $algos = null)
{
if ($algos === null)
{
$algos = self::checkAlgorithm($hash);
foreach ($algos as $algo)
{
if (Security::compareStrings($hash, self::hashPassword($password, $algo, $hash)))
{
return true;
}
}
return false;
}
else
{
return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash));
}
}
/**
* Guess which algorithm(s) were used to generate the given hash.
*
* If there are multiple possibilities, all of them will be returned in an array.
*
* @param string $hash
* @return array
*/
public static function checkAlgorithm($hash)
{
$candidates = array();
foreach (self::$_algorithm_signatures as $name => $signature)
{
if (preg_match($signature, $hash)) $candidates[] = $name;
}
return $candidates;
}
/**
* Check the work factor of a hash.
*
* @param string $hash
* @return int
*/
public static function checkWorkFactor($hash)
{
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
{
return intval($matches[1], 10);
}
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
{
return max(0, round(log($matches[1], 2)) - 5);
}
else
{
return 0;
}
}
/**
* Generate the bcrypt hash of a string.
*
* @param string $password
* @param string $salt (optional)
* @param int $work_factor (optional)
* @return string
*/
public static function bcrypt($password, $salt = null, $work_factor = 10)
{
if ($salt === null)
{
$salt = '$2y$' . sprintf('%02d', $work_factor) . '$' . Security::getRandom(22, 'alnum');
}
return crypt($password, $salt);
}
/**
* Generate the PBKDF2 hash of a string.
*
* @param string $password
* @param string $salt (optional)
* @param string $algorithm (optional)
* @param int $iterations (optional)
* @param int $length (optional)
* @return string
*/
public static function pbkdf2($password, $salt = null, $algorithm = 'sha512', $iterations = 16384, $length = 24)
{
if ($salt === null)
{
$salt = Security::getRandom(12, 'alnum');
}
if (function_exists('hash_pbkdf2'))
{
$hash = hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true);
}
else
{
$output = '';
$block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash
for ($i = 1; $i <= $block_count; $i++)
{
$last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian
$last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration
for ($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations
{
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
$hash = substr($output, 0, $length);
}
return $algorithm . ':' . sprintf('%07d', $iterations) . ':' . $salt . ':' . base64_encode($hash);
}
/**
* Count the amount of entropy that a password contains.
*
* @param string $password
* @return int
*/
public static function countEntropyBits($password)
{
// An empty string has no entropy.
if ($password === '') return 0;
// Common character sets and the number of possible mutations.
static $entropy_per_char = array(
'/^[0-9]+$/' => 10,
'/^[a-z]+$/' => 26,
'/^[A-Z]+$/' => 26,
'/^[a-z0-9]+$/' => 36,
'/^[A-Z0-9]+$/' => 36,
'/^[a-zA-Z]+$/' => 52,
'/^[a-zA-Z0-9]+$/' => 62,
'/^[a-zA-Z0-9_-]+$/' => 64,
'/^[\\x20-\\x7e]+$/' => 95,
'/^[\\x00-\\x7f]+$/' => 128,
);
foreach ($entropy_per_char as $regex => $entropy)
{
if (preg_match($regex, $password))
{
return log(pow($entropy, strlen($password)), 2);
}
}
return strlen($password) * 8;
}
}

View file

@ -0,0 +1,339 @@
<?php
namespace Rhymix\Framework;
/**
* The security class.
*/
class Security
{
/**
* Sanitize a variable.
*
* @param string $input
* @param string $type
* @return string|false
*/
public static function sanitize($input, $type)
{
switch ($type)
{
// Escape HTML special characters.
case 'escape':
if (!utf8_check($input)) return false;
return escape($input);
// Strip all HTML tags.
case 'strip':
if (!utf8_check($input)) return false;
return escape(strip_tags($input));
// Clean up HTML content to prevent XSS attacks.
case 'html':
if (!utf8_check($input)) return false;
return Filters\HTMLFilter::clean($input);
// Clean up the input to be used as a safe filename.
case 'filename':
if (!utf8_check($input)) return false;
return Filters\FilenameFilter::clean($input);
// Unknown filters return false.
default: return false;
}
}
/**
* Encrypt a string using AES.
*
* @param string $plaintext
* @param string $key (optional)
* @param bool $force_compat (optional)
* @return string|false
*/
public static function encrypt($plaintext, $key = null, $force_compat = false)
{
// Get the encryption key.
$key = $key ?: config('crypto.encryption_key');
$key = substr(hash('sha256', $key, true), 0, 16);
// Use defuse/php-encryption if possible.
if (!$force_compat && function_exists('openssl_encrypt'))
{
try
{
return base64_encode(\Crypto::Encrypt($plaintext, $key));
}
catch (\Exception $e)
{
return false;
}
}
// Otherwise, use the CryptoCompat class.
return base64_encode(\CryptoCompat::encrypt($plaintext, $key));
}
/**
* Decrypt a string using AES.
*
* @param string $plaintext
* @param string $key (optional)
* @param bool $force_compat (optional)
* @return string|false
*/
public static function decrypt($ciphertext, $key = null, $force_compat = false)
{
// Get the encryption key.
$key = $key ?: config('crypto.encryption_key');
$key = substr(hash('sha256', $key, true), 0, 16);
// Check whether the ciphertext is valid.
$ciphertext = @base64_decode($ciphertext);
if (strlen($ciphertext) < 48)
{
return false;
}
// Use defuse/php-encryption if possible.
if (!$force_compat && function_exists('openssl_decrypt'))
{
try
{
return \Crypto::Decrypt($ciphertext, $key);
}
catch (\Exception $e)
{
return false;
}
}
// Otherwise, use the CryptoCompat class.
return \CryptoCompat::decrypt($ciphertext, $key);
}
/**
* Generate a cryptographically secure random string.
*
* @param int $length
* @param string $format
* @return string
*/
public function getRandom($length = 32, $format = 'alnum')
{
// Find out how many bytes of entropy we really need.
switch($format)
{
case 'binary':
$entropy_required_bytes = $length;
break;
case 'hex':
$entropy_required_bytes = ceil($length / 2);
break;
case 'alnum':
case 'printable':
default:
$entropy_required_bytes = ceil($length * 3 / 4);
break;
}
// Cap entropy to 256 bits from any one source, because anything more is meaningless.
$entropy_capped_bytes = min(32, $entropy_required_bytes);
// Find and use the most secure way to generate a random string.
$entropy = false;
$is_windows = (defined('\PHP_OS') && strtoupper(substr(\PHP_OS, 0, 3)) === 'WIN');
if(function_exists('random_bytes')) // PHP 7
{
$entropy = random_bytes($entropy_capped_bytes);
}
elseif(function_exists('openssl_random_pseudo_bytes'))
{
$entropy = openssl_random_pseudo_bytes($entropy_capped_bytes);
}
elseif(function_exists('mcrypt_create_iv') && !$is_windows)
{
$entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_DEV_URANDOM);
}
elseif(function_exists('mcrypt_create_iv') && $is_windows)
{
$entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_RAND);
}
elseif(!$is_windows && @is_readable('/dev/urandom'))
{
$fp = fopen('/dev/urandom', 'rb');
if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM.
{
stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom.
}
$entropy = fread($fp, $entropy_capped_bytes);
fclose($fp);
}
// Use built-in source of entropy if an error occurs while using other functions.
if($entropy === false || strlen($entropy) < $entropy_capped_bytes)
{
$entropy = '';
for($i = 0; $i < $entropy_capped_bytes; $i += 2)
{
$entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535));
}
}
// Mixing (see RFC 4086 section 5)
$output = '';
for($i = 0; $i < $entropy_required_bytes; $i += 32)
{
$output .= hash('sha256', $entropy . $i . rand(), true);
}
// Encode and return the random string.
switch($format)
{
case 'binary':
return substr($output, 0, $length);
case 'printable':
$salt = '';
for($i = 0; $i < $length; $i++)
{
$salt .= chr(33 + (crc32(sha1($i . $output)) % 94));
}
return $salt;
case 'hex':
return substr(bin2hex($output), 0, $length);
case 'alnum':
default:
$salt = substr(base64_encode($output), 0, $length);
$replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9);
return strtr($salt, '+/=', $replacements);
}
}
/**
* Generate a cryptographically secure random number between $min and $max.
*
* @param int $min
* @param int $max
* @return int
*/
public static function getRandomNumber($min = 0, $max = 0x7fffffff)
{
if (function_exists('random_int'))
{
return random_int($min, $max);
}
else
{
$bytes_required = min(4, ceil(log($max - $min, 2) / 8) + 1);
$bytes = self::getRandom($bytes_required, 'binary');
$offset = abs(hexdec(bin2hex($bytes)) % ($max - $min + 1));
return intval($min + $offset);
}
}
/**
* Generate a random UUID.
*
* @return string
*/
public static function getRandomUUID()
{
$randpool = self::getRandom(16, 'binary');
$randpool[6] = chr(ord($randpool[6]) & 0x0f | 0x40);
$randpool[8] = chr(ord($randpool[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($randpool), 4));
}
/**
* Compare two strings in constant time.
*
* @param string $a
* @param string $b
* @return bool
*/
public static function compareStrings($a, $b)
{
$diff = strlen($a) ^ strlen($b);
$maxlen = min(strlen($a), strlen($b));
for($i = 0; $i < $maxlen; $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/**
* Check if the current request seems to be a CSRF attack.
*
* This method returns true if the request seems to be innocent,
* and false if it seems to be a CSRF attack.
*
* @param string $referer (optional)
* @return bool
*/
public static function checkCSRF($referer = null)
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST')
{
return false;
}
if (!$referer)
{
$referer = strval($_SERVER['HTTP_REFERER']);
if ($referer === '')
{
return true;
}
}
return URL::isInternalURL($referer);
}
/**
* Check if the current request seems to be an XEE attack.
*
* This method returns true if the request seems to be innocent,
* and false if it seems to be an XEE attack.
* This is the opposite of XE's Security::detectXEE() method.
*
* @param string $xml (optional)
* @return bool
*/
public static function checkXEE($xml = null)
{
// Stop if there is no XML content.
if (!$xml)
{
return true;
}
// Reject entity tags.
if (strpos($xml, '<!ENTITY') !== false)
{
return false;
}
// Check if there is no content after the xml tag.
$header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xml, 0, 100), 1);
if (($xml = trim(substr_replace($xml, $header, 0, 100))) === '')
{
return false;
}
// Check if there is no content after the DTD.
$header = preg_replace('/^<!DOCTYPE[^>]*+>/i', '', substr($xml, 0, 200), 1);
if (($xml = trim(substr_replace($xml, $header, 0, 200))) === '')
{
return false;
}
// Check that the root tag is valid.
if (!preg_match('/^<(methodCall|methodResponse|fault)/', $xml))
{
return false;
}
return true;
}
}

View file

@ -0,0 +1,547 @@
<?php
namespace Rhymix\Framework;
/**
* The storage class.
*/
class Storage
{
/**
* Check if a path really exists.
*
* @param string $path
* @return bool
*/
public static function exists($path)
{
$path = rtrim($path, '/\\');
clearstatcache(true, $path);
return @file_exists($path);
}
/**
* Check if the given path is a file.
*
* @param string $path
* @return bool
*/
public static function isFile($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_file($path);
}
/**
* Check if the given path is an empty file.
*
* @param string $path
* @return bool
*/
public static function isEmptyFile($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_file($path) && (@filesize($path) == 0);
}
/**
* Check if the given path is a directory.
*
* @param string $path
* @return bool
*/
public static function isDirectory($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_dir($path);
}
/**
* Check if the given path is an empty directory.
*
* @param string $path
* @return bool
*/
public static function isEmptyDirectory($path)
{
$path = rtrim($path, '/\\');
if (!self::isDirectory($path))
{
return false;
}
$iterator = new \FilesystemIterator($path, \FilesystemIterator::SKIP_DOTS);
return (iterator_count($iterator)) === 0 ? true : false;
}
/**
* Check if the given path is a symbolic link.
*
* @param string $path
* @return bool
*/
public static function isSymlink($path)
{
$path = rtrim($path, '/\\');
return @is_link($path);
}
/**
* Check if the given path is a valid symbolic link.
*
* @param string $path
* @return bool
*/
public static function isValidSymlink($path)
{
$path = rtrim($path, '/\\');
return @is_link($path) && ($target = @readlink($path)) !== false && self::exists($target);
}
/**
* Check if the given path is readable.
*
* @param string $path
* @return bool
*/
public static function isReadable($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_readable($path);
}
/**
* Check if the given path is writable.
*
* @param string $path
* @return bool
*/
public static function isWritable($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_writable($path);
}
/**
* Get the size of a file.
*
* This method returns the size of a file, or false on error.
*
* @param string $filename
* @return int|false
*/
public static function getSize($filename)
{
$filename = rtrim($filename, '/\\');
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
{
return @filesize($filename);
}
else
{
return false;
}
}
/**
* Get the content of a file.
*
* This method returns the content if it exists and is readable.
* If $stream is true, it will return the content as a stream instead of
* loading the entire content in memory. This may be useful for large files.
* If the file cannot be opened, this method returns false.
*
* @param string $filename
* @param bool $stream (optional)
* @return string|resource|false
*/
public static function read($filename, $stream = false)
{
$filename = rtrim($filename, '/\\');
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
{
if ($stream)
{
return @fopen($filename, 'r');
}
else
{
return @file_get_contents($filename);
}
}
else
{
return false;
}
}
/**
* Read PHP data from a file, formatted for easy retrieval.
*
* This method returns the data on success and false on failure.
*
* @param string $filename
* @return mixed
*/
public static function readPHPData($filename)
{
$filename = rtrim($filename, '/\\');
if (@is_file($filename) && @is_readable($filename))
{
return @include $filename;
}
else
{
return false;
}
}
/**
* Write $content to a file.
*
* If $content is a stream, this method will copy it to the target file
* without loading the entire content in memory. This may be useful for large files.
* This method returns true on success and false on failure.
*
* @param string $filename
* @param string|resource $content
* @param string $mode (optional)
* @param int $perms (optional)
* @return string|false
*/
public static function write($filename, $content, $mode = 'w', $perms = null)
{
$filename = rtrim($filename, '/\\');
$destination_dir = dirname($filename);
if (!self::exists($destination_dir))
{
$mkdir_success = self::createDirectory($destination_dir);
if (!$mkdir_success)
{
return false;
}
}
if ($fp = fopen($filename, $mode))
{
flock($fp, \LOCK_EX);
if (is_resource($content))
{
$result = stream_copy_to_stream($content, $fp) ? true : false;
}
else
{
$result = fwrite($fp, $content) ? true : false;
}
fflush($fp);
flock($fp, \LOCK_UN);
fclose($fp);
}
else
{
return false;
}
@chmod($filename, ($perms === null ? (0666 & ~umask()) : $perms));
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $result;
}
/**
* Write PHP data to a file, formatted for easy retrieval.
*
* This method returns true on success and false on failure.
* Resources and anonymous functions cannot be saved.
*
* @param string $filename
* @param mixed $data
* @return string|false
*/
public static function writePHPData($filename, $data)
{
return self::write($filename, '<' . '?php return unserialize(' . var_export(serialize($data), true) . ');');
}
/**
* Copy $source to $destination.
*
* This method returns true on success and false on failure.
* If the destination permissions are not given, they will be copied from the source.
*
* @param string $source
* @param string $destination
* @param int $destination_perms
* @return bool
*/
public static function copy($source, $destination, $destination_perms = null)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::exists($source))
{
return false;
}
$destination_dir = dirname($destination);
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
{
return false;
}
elseif (self::isDirectory($destination))
{
$destination = $destination . '/' . basename($source);
}
$copy_success = @copy($source, $destination);
if (!$copy_success)
{
return false;
}
if ($destination_perms === null)
{
@chmod($destination, 0777 & @fileperms($source));
}
else
{
@chmod($destination, $destination_perms);
}
return true;
}
/**
* Move $source to $destination.
*
* This method returns true on success and false on failure.
*
* @param string $source
* @param string $destination
* @return bool
*/
public static function move($source, $destination)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::exists($source))
{
return false;
}
$destination_dir = dirname($destination);
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
{
return false;
}
elseif (self::isDirectory($destination))
{
$destination = $destination . '/' . basename($source);
}
$result = @rename($source, $destination);
if (function_exists('opcache_invalidate') && substr($source, -4) === '.php')
{
@opcache_invalidate($source, true);
}
return $result;
}
/**
* Delete a file.
*
* This method returns true if the file exists and has been successfully
* deleted, and false on any kind of failure.
*
* @param string $filename
* @return bool
*/
public static function delete($filename)
{
$filename = rtrim($filename, '/\\');
$result = @self::exists($filename) && @is_file($filename) && @unlink($filename);
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $result;
}
/**
* Create a directory.
*
* @param string $dirname
* @return bool
*/
public static function createDirectory($dirname, $mode = null)
{
$dirname = rtrim($dirname, '/\\');
if ($mode === null)
{
$mode = 0777 & ~umask();
}
return @mkdir($dirname, $mode, true);
}
/**
* Read the list of files in a directory.
*
* @param string $dirname
* @param bool $full_path (optional)
* @param bool $skip_dotfiles (optional)
* @param bool $skip_subdirs (optional)
* @return array|false
*/
public static function readDirectory($dirname, $full_path = true, $skip_dotfiles = true, $skip_subdirs = true)
{
$dirname = rtrim($dirname, '/\\');
if (!self::isDirectory($dirname))
{
return false;
}
try
{
$iterator = new \FilesystemIterator($dirname, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS);
}
catch (\UnexpectedValueException $e)
{
return false;
}
$result = array();
foreach ($iterator as $fileinfo)
{
if (!$skip_subdirs || !is_dir($fileinfo))
{
$basename = basename($fileinfo);
if (!$skip_dotfiles || $basename[0] !== '.')
{
$result[] = $full_path ? $fileinfo : $basename;
}
}
}
sort($result);
return $result;
}
/**
* Copy a directory recursively.
*
* @param string $source
* @param string $destination
* @param string $exclude_regexp (optional)
* @return bool
*/
public static function copyDirectory($source, $destination, $exclude_regexp = null)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::isDirectory($source))
{
return false;
}
if (!self::isDirectory($destination) && !self::createDirectory($destination))
{
return false;
}
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, $rdi_options), $rii_options);
foreach ($iterator as $path)
{
$path_source = $path->getPathname();
if (strpos($path_source, $source) !== 0)
{
continue;
}
if ($exclude_regexp && preg_match($exclude_regexp, $path_source))
{
continue;
}
$path_destination = $destination . substr($path_source, strlen($source));
if ($path->isDir())
{
$status = self::isDirectory($path_destination) || self::createDirectory($path_destination, $path->getPerms());
if (!$status)
{
return false;
}
}
else
{
$status = self::copy($path_source, $path_destination, $path->getPerms());
if (!$status)
{
return false;
}
}
}
return true;
}
/**
* Move a directory.
*
* @param string $source
* @param string $destination
* @return bool
*/
public static function moveDirectory($source, $destination)
{
return self::move($source, $destination);
}
/**
* Delete a directory recursively.
*
* @param string $dirname
* @param bool $delete_self (optional)
* @return bool
*/
public static function deleteDirectory($dirname, $delete_self = true)
{
$dirname = rtrim($dirname, '/\\');
if (!self::isDirectory($dirname))
{
return false;
}
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dirname, $rdi_options), $rii_options);
foreach ($iterator as $path)
{
if ($path->isDir())
{
if (!@rmdir($path->getPathname()))
{
return false;
}
}
else
{
if (!@unlink($path->getPathname()))
{
return false;
}
}
}
if ($delete_self)
{
return @rmdir($dirname);
}
else
{
return true;
}
}
}

109
common/framework/timer.php Normal file
View file

@ -0,0 +1,109 @@
<?php
namespace Rhymix\Framework;
/**
* The timer class.
*/
class Timer
{
/**
* Timestamps are stored here.
*/
protected static $_timestamps = array();
/**
* Start a timer.
*
* This method returns the current microtime.
*
* @param string $name (optional)
* @return float
*/
public static function start($name = null)
{
$timestamp = microtime(true);
if ($name === null)
{
$name = 'anon-timer-' . $timestamp;
}
self::$_timestamps[$name] = $timestamp;
return $timestamp;
}
/**
* Stop a timer and return the elapsed time.
*
* If the name is not given, the most recently started timer will be stopped.
* If no timer has been started, this method returns false.
*
* @param string $name (optional)
* @return float|false
*/
public static function stop($name = null)
{
$timestamp = microtime(true);
$started_timestamp = 0;
if ($name === null)
{
if (count(self::$_timestamps))
{
$started_timestamp = array_pop(self::$_timestamps);
}
else
{
return false;
}
}
elseif (array_key_exists($name, self::$_timestamps))
{
$started_timestamp = self::$_timestamps[$name];
unset(self::$_timestamps[$name]);
}
else
{
return false;
}
return $timestamp - $started_timestamp;
}
/**
* Stop a timer and return the elapsed time in a human-readable format.
*
* If the name is not given, the most recently started timer will be stopped.
* If no timer has been started, this method returns false.
*
* @param string $name (optional)
* @return string|false
*/
public static function stopFormat($name = null)
{
$result = self::stop($name);
if ($result === false) return $result;
return number_format($result * 1000, 1, '.', ',') . 'ms';
}
/**
* This method returns how much time has elapsed since Rhymix startup.
*
* @return float
*/
public static function sinceStartup()
{
return microtime(true) - \RX_MICROTIME;
}
/**
* This method returns how much time has elapsed since startup in a human-readable format.
*
* @return string
*/
public static function sinceStartupFormat()
{
return number_format((microtime(true) - \RX_MICROTIME) * 1000, 1, '.', ',') . 'ms';
}
}

240
common/framework/ua.php Normal file
View file

@ -0,0 +1,240 @@
<?php
namespace Rhymix\Framework;
/**
* The user-agent class.
*/
class UA
{
/**
* Cache to prevent multiple lookups.
*/
protected static $_mobile_cache = array();
protected static $_tablet_cache = array();
protected static $_robot_cache = array();
/**
* Check whether the current visitor is using a mobile device.
*
* @param string $ua (optional)
* @return bool
*/
public static function isMobile($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
if ($ua === null)
{
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
$using_header = true;
}
else
{
$using_header = false;
}
// If the User-Agent header is missing, it's probably not a mobile browser.
if (is_null($ua))
{
return false;
}
// Look for headers that are only used in mobile browsers.
if ($using_header && (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])))
{
return true;
}
// Look up the cache.
if (isset(self::$_mobile_cache[$ua]))
{
return self::$_mobile_cache[$ua];
}
// Look for the 'mobile' keyword and common mobile platform names.
if (preg_match('/android|ip(hone|ad|od)|blackberry|nokia|palm|mobile/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// Look for common non-mobile OS names.
if (preg_match('/windows|linux|os [x9]|bsd/i', $ua))
{
return self::$_mobile_cache[$ua] = false;
}
// Look for other platform, manufacturer, and device names that are known to be mobile.
if (preg_match('/kindle|opera (mini|mobi)|polaris|netfront|fennec|motorola|symbianos|webos/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
if (preg_match('/s[pgc]h-|lgtelecom|sonyericsson|alcatel|vodafone|maemo|minimo|bada/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// If we're here, it's probably not a mobile device.
return self::$_mobile_cache[$ua] = false;
}
/**
* Check whether the current visitor is using a tablet.
*
* @param string $ua (optional)
* @return bool
*/
public static function isTablet($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a tablet.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_tablet_cache[$ua]))
{
return self::$_tablet_cache[$ua];
}
// Check if the user-agent is mobile.
if (!self::isMobile($ua))
{
return self::$_tablet_cache[$ua] = false;
}
// Check for Android tablets without the 'mobile' keyword.
if (stripos($ua, 'android') !== false && stripos($ua, 'mobile') === false)
{
return self::$_tablet_cache[$ua] = true;
}
// Check for common tablet identifiers.
if (preg_match('/tablet|pad\b|tab\b|\bgt-\d+|kindle|nook|playbook|webos|xoom/i', $ua))
{
return self::$_tablet_cache[$ua] = true;
}
// If we're here, it's probably not a tablet.
return self::$_tablet_cache[$ua] = false;
}
/**
* Check whether the current visitor is a robot.
*
* @param string $ua (optional)
* @return bool
*/
public static function isRobot($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a robot.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_robot_cache[$ua]))
{
return self::$_robot_cache[$ua];
}
// Look for common search engine names and the 'bot' keyword.
if (preg_match('/bot|slurp|facebook(externalhit|scraper)|ia_archiver|ask jeeves|teoma|baidu|daumoa|lycos|pingdom/i', $ua))
{
return self::$_robot_cache[$ua] = true;
}
// If we're here, it's probably not a robot.
return self::$_robot_cache[$ua] = false;
}
/**
* This method parses the User-Agent string to guess what kind of browser it is.
*
* @param string $ua (optional)
* @return object
*/
public static function getBrowserInfo($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// Initialize the result.
$result = (object)array(
'browser' => null,
'version' => null,
'os' => null,
'is_mobile' => null,
'is_tablet' => null,
);
if (is_null($ua))
{
return $result;
}
// Try to guess the OS.
if (preg_match('#(Windows|Android|Linux|iOS|OS X|Macintosh)#i', $ua, $matches))
{
if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false)
{
$matches[1] = 'Android';
}
if ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false)
{
$matches[1] = 'OS X';
}
$result->os = $matches[1];
}
// Fill in miscellaneous fields.
$result->is_mobile = self::isMobile($ua);
$result->is_tablet = self::isTablet($ua);
// Try to match some of the most common browsers.
if (preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches) && strpos($ua, 'Chrome') === false)
{
$result->browser = 'Android';
$result->version = $matches[1];
return $result;
}
if (preg_match('#Edge/([0-9]+\\.)#', $ua, $matches))
{
$result->browser = 'Edge';
$result->version = $matches[1] . '0';
return $result;
}
if (preg_match('#Trident/([0-9]+)\\.[0-9]+#', $ua, $matches))
{
$result->browser = 'IE';
$result->version = ($matches[1] + 4) . '.0';
return $result;
}
if (preg_match('#(MSIE|Chrome|Firefox|Safari)[ /:]([0-9]+\\.[0-9]+)#', $ua, $matches))
{
$result->browser = $matches[1] === 'MSIE' ? 'IE' : $matches[1];
$result->version = $matches[2];
return $result;
}
if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Opera';
$result->version = $matches[1];
return $result;
}
if (preg_match('#(?:Konqueror|KHTML)/([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Konqueror';
$result->version = $matches[1];
return $result;
}
return $result;
}
}

220
common/framework/url.php Normal file
View file

@ -0,0 +1,220 @@
<?php
namespace Rhymix\Framework;
/**
* The URL class.
*/
class URL
{
/**
* Get the current URL.
*
* If $changes are given, they will be appended to the current URL as a query string.
* To delete an existing query string, set its value to null.
*
* @param array $changes
* @return string
*/
public static function getCurrentURL(array $changes = array())
{
$url = self::getCurrentDomainURL(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/');
if (count($changes))
{
return self::modifyURL($url, $changes);
}
else
{
return self::getCanonicalURL($url);
}
}
/**
* Get the current domain.
*
* @param string $path
* @return string
*/
public static function getCurrentDomainURL($path = '/')
{
$proto = \RX_SSL ? 'https://' : 'http://';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $proto . $host . '/' . ltrim($path, '/');
}
/**
* Convert a URL to its canonical format.
*
* @param string $url
* @return string
*/
public static function getCanonicalURL($url)
{
if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url))
{
$url = self::getCurrentDomainURL(\RX_BASEURL . ltrim($url, './'));
}
return preg_replace_callback('#^(https?:|)//([^/]+)#i', function($matches) {
if ($matches[1] === '') $matches[1] = \RX_SSL ? 'https:' : 'http:';
return $matches[1] . '//' . self::decodeIdna($matches[2]);
}, $url);
}
/**
* Get the domain from a URL.
*
* @param string $url
* @return string|false
*/
public static function getDomainFromURL($url)
{
$domain = @parse_url($url, \PHP_URL_HOST);
if ($domain === false || $domain === null)
{
return false;
}
else
{
return self::decodeIdna($domain);
}
}
/**
* Check if a URL is internal to this site.
*
* @param string $url
* @return bool
*/
public static function isInternalURL($url)
{
$domain = self::getDomainFromURL($url);
if ($domain === false)
{
return true;
}
if ($domain === self::getDomainFromURL('http://' . $_SERVER['HTTP_HOST']))
{
return true;
}
if ($domain === self::getDomainFromURL(\Context::getDefaultUrl()))
{
return true;
}
return false;
}
/**
* Modify a URL.
*
* If $changes are given, they will be appended to the current URL as a query string.
* To delete an existing query string, set its value to null.
*
* @param string $url
* @param array $changes
* @return string
*/
public static function modifyURL($url, array $changes = array())
{
$url = parse_url(self::getCanonicalURL($url));
$prefix = sprintf('%s://%s%s%s', $url['scheme'], $url['host'], ($url['port'] ? (':' . $url['port']) : ''), $url['path']);
parse_str($url['query'], $args);
$changes = array_merge($args, $changes);
$changes = array_filter($changes, function($val) { return $val !== null; });
if (count($changes))
{
return $prefix . '?' . http_build_query($changes);
}
else
{
return $prefix;
}
}
/**
* Convert a server-side path to a URL.
*
* This method returns false if the path cannot be converted to a URL,
* e.g. if the path is outside of the document root.
*
* @param string $path
* @return string|false
*/
public static function fromServerPath($path)
{
$cleanpath = Filters\FilenameFilter::cleanPath($path);
if (substr($path, -1) === '/')
{
$cleanpath .= '/';
}
$root = Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT']);
if ($cleanpath === $root)
{
return self::getCurrentDomainURL('/');
}
if (starts_with($root . '/', $cleanpath))
{
return self::getCurrentDomainURL(substr($cleanpath, strlen($root)));
}
return false;
}
/**
* Convert a URL to a server-side path.
*
* This method returns false if the URL cannot be converted to a server-side path,
* e.g. if the URL belongs to an external domain.
*
* @param string $url
* @return string
*/
public static function toServerPath($url)
{
$url = self::getCanonicalURL($url);
if (!self::isInternalURL($url))
{
return false;
}
return Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT'] . parse_url($url, \PHP_URL_PATH));
}
/**
* Encode UTF-8 domain into IDNA (punycode)
*
* @param string $domain
* @return string
*/
public static function encodeIdna($domain)
{
if (function_exists('idn_to_ascii'))
{
return idn_to_ascii($domain);
}
else
{
$encoder = new \TrueBV\Punycode();
return $encoder->encode($domain);
}
}
/**
* Convert IDNA (punycode) domain into UTF-8
*
* @param string $domain
* @return string
*/
public static function decodeIdna($domain)
{
if (function_exists('idn_to_utf8'))
{
return idn_to_utf8($domain);
}
else
{
$decoder = new \TrueBV\Punycode();
return $decoder->decode($domain);
}
}
}

View file

@ -133,6 +133,19 @@ function class_basename($class)
return basename(str_replace('\\', '/', is_object($class) ? get_class($class) : $class));
}
/**
* Clean a path to remove ./, ../, trailing slashes, etc.
*
* This function is an alias to Rhymix\Framework\Filters\FilenameFilter::cleanPath().
*
* @param string $path
* @return string
*/
function clean_path($path)
{
return Rhymix\Framework\Filters\FilenameFilter::cleanPath($path);
}
/**
* This function is a shortcut to htmlspecialchars().
*
@ -238,7 +251,7 @@ function starts_with($needle, $haystack, $case_sensitive = true)
}
else
{
!strncasecmp($needle, $haystack, strlen($needle));
return !strncasecmp($needle, $haystack, strlen($needle));
}
}
@ -337,6 +350,34 @@ function base64_decode_urlsafe($str)
return @base64_decode(str_pad(strtr($str, '-_', '+/'), ceil(strlen($str) / 4) * 4, '=', STR_PAD_RIGHT));
}
/**
* Convert a server-side path to a URL.
*
* This function is an alias to Rhymix\Framework\URL::fromServerPath().
* It returns false if the path cannot be converted.
*
* @param string $path
* @return string|false
*/
function path2url($path)
{
return Rhymix\Framework\URL::fromServerPath($path);
}
/**
* Convert a URL to a server-side path.
*
* This function is an alias to Rhymix\Framework\URL::toServerPath().
* It returns false if the URL cannot be converted.
*
* @param string $url
* @return string|false
*/
function url2path($url)
{
return Rhymix\Framework\URL::toServerPath($url);
}
/**
* Convert hexadecimal color codes to an array of R, G, B values.
* This function can handle both 6-digit and 3-digit notations, optionally prefixed with '#'.
@ -452,8 +493,11 @@ if (!function_exists('hex2bin'))
*/
function tobool($input)
{
if (preg_match('/^(1|[ty].*|on|oui|si|vrai|aye)$/i', $input)) return true;
if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false;
if (is_scalar($input))
{
if (preg_match('/^(1|[ty].*|on|ok.*oui|si|vrai|aye)$/i', $input)) return true;
if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false;
}
return (bool)$input;
}
@ -475,6 +519,27 @@ function utf8_check($str)
}
}
/**
* Remove BOM and invalid UTF-8 sequences from file content.
*
* @param string $str
* @return string
*/
function utf8_clean($str)
{
if (strlen($str) >= 3 && substr($str, 0, 3) === "\xEF\xBB\xBF")
{
$str = substr($str, 3);
}
if (!utf8_check($str))
{
$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
}
return $str;
}
/**
* Encode UTF-8 characters outside of the Basic Multilingual Plane in the &#xxxxxx format.
* This allows emoticons and other characters to be stored in MySQL without utf8mb4 support.

View file

@ -284,7 +284,7 @@ jQuery(function($) {
uri = uri.replace(re, toReplace);
}
var bUseSSL = !!window.enforce_ssl;
var bUseSSL = !!window.enforce_ssl || (location.protocol == 'https:');
if (!bUseSSL && isArray(window.ssl_actions) && (act=uri.getQuery('act'))) {
for (var i=0,c=ssl_actions.length; i < c; i++) {
if (ssl_actions[i] === act) {
@ -381,13 +381,11 @@ function sendMailTo(to) {
* @brief url이동 (Rhymix 개선된 버전)
*/
function redirect(url) {
if (url === window.location.href || url.indexOf(window.location.href.replace(/#.+$/, "") + "#") === 0)
{
if (url === window.location.href || url.indexOf(window.location.href.replace(/#.+$/, "") + "#") === 0 ||
url === window.location.pathname || url.indexOf(window.location.pathname.replace(/#.+$/, "") + "#") === 0) {
window.location.href = url;
window.location.reload();
}
else
{
} else {
window.location.href = url;
}
}

View file

@ -103,7 +103,7 @@
if(result.error == 0) {
if(/\.(jpe?g|png|gif)$/i.test(result.source_filename)) {
temp_code += '<img src="' + window.request_uri + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />';
temp_code += '<img src="' + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />';
temp_code += "\r\n<p><br></p>\r\n";
}
@ -244,10 +244,10 @@
if(!fileinfo) return;
if(/\.(jpe?g|png|gif)$/i.test(fileinfo.source_filename)) {
temp_code += '<img src="' + window.request_uri + fileinfo.download_url + '" alt="' + fileinfo.source_filename + '" editor_component="image_link" data-file-srl="' + fileinfo.file_srl + '" />';
temp_code += '<img src="' + fileinfo.download_url + '" alt="' + fileinfo.source_filename + '" editor_component="image_link" data-file-srl="' + fileinfo.file_srl + '" />';
temp_code += "\r\n<p><br></p>\r\n";
} else {
temp_code += '<a href="' + window.request_uri + fileinfo.download_url + '" data-file-srl="' + fileinfo.file_srl + '">' + fileinfo.source_filename + "</a>\n";
temp_code += '<a href="' + fileinfo.download_url + '" data-file-srl="' + fileinfo.file_srl + '">' + fileinfo.source_filename + "</a>\n";
}
});
@ -379,6 +379,3 @@
return u;
};
})(jQuery);

View file

@ -12,6 +12,7 @@ jQuery(function($){
$.getScript(request_uri + "./common/js/plugins/spectrum/i18n/jquery.spectrum-"+ xe.current_lang.replace("jp", "ja").toLowerCase() +".js", function() {
var settings = {
showInput: true,
allowEmpty:true,
showInitial: true,
showPalette: true,
showSelectionPalette: true,

View file

@ -0,0 +1,37 @@
/* Afrikaans initialisation for the jQuery UI date picker plugin. */
/* Written by Renier Pretorius. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.af = {
closeText: "Selekteer",
prevText: "Vorige",
nextText: "Volgende",
currentText: "Vandag",
monthNames: [ "Januarie","Februarie","Maart","April","Mei","Junie",
"Julie","Augustus","September","Oktober","November","Desember" ],
monthNamesShort: [ "Jan", "Feb", "Mrt", "Apr", "Mei", "Jun",
"Jul", "Aug", "Sep", "Okt", "Nov", "Des" ],
dayNames: [ "Sondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrydag", "Saterdag" ],
dayNamesShort: [ "Son", "Maa", "Din", "Woe", "Don", "Vry", "Sat" ],
dayNamesMin: [ "So","Ma","Di","Wo","Do","Vr","Sa" ],
weekHeader: "Wk",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.af );
return datepicker.regional.af;
} ) );

View file

@ -0,0 +1,37 @@
/* Algerian Arabic Translation for jQuery UI date picker plugin. (can be used for Tunisia)*/
/* Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "ar-DZ" ] = {
closeText: "إغلاق",
prevText: "&#x3C;السابق",
nextText: "التالي&#x3E;",
currentText: "اليوم",
monthNames: [ "جانفي", "فيفري", "مارس", "أفريل", "ماي", "جوان",
"جويلية", "أوت", "سبتمبر","أكتوبر", "نوفمبر", "ديسمبر" ],
monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ],
dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
dayNamesShort: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
dayNamesMin: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
weekHeader: "أسبوع",
dateFormat: "dd/mm/yy",
firstDay: 6,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "ar-DZ" ] );
return datepicker.regional[ "ar-DZ" ];
} ) );

View file

@ -0,0 +1,38 @@
/* Arabic Translation for jQuery UI date picker plugin. */
/* Used in most of Arab countries, primarily in Bahrain, Kuwait, Oman, Qatar, Saudi Arabia and the United Arab Emirates, Egypt, Sudan and Yemen. */
/* Written by Mohammed Alshehri -- m@dralshehri.com */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ar = {
closeText: "إغلاق",
prevText: "&#x3C;السابق",
nextText: "التالي&#x3E;",
currentText: "اليوم",
monthNames: [ "يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو",
"يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر" ],
monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ],
dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
dayNamesShort: [ "أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت" ],
dayNamesMin: [ "ح", "ن", "ث", "ر", "خ", "ج", "س" ],
weekHeader: "أسبوع",
dateFormat: "dd/mm/yy",
firstDay: 0,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.ar );
return datepicker.regional.ar;
} ) );

View file

@ -0,0 +1,37 @@
/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Jamil Najafov (necefov33@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.az = {
closeText: "Bağla",
prevText: "&#x3C;Geri",
nextText: "İrəli&#x3E;",
currentText: "Bugün",
monthNames: [ "Yanvar","Fevral","Mart","Aprel","May","İyun",
"İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr" ],
monthNamesShort: [ "Yan","Fev","Mar","Apr","May","İyun",
"İyul","Avq","Sen","Okt","Noy","Dek" ],
dayNames: [ "Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə" ],
dayNamesShort: [ "B","Be","Ça","Ç","Ca","C","Ş" ],
dayNamesMin: [ "B","B","Ç","С","Ç","C","Ş" ],
weekHeader: "Hf",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.az );
return datepicker.regional.az;
} ) );

View file

@ -0,0 +1,37 @@
/* Belarusian initialisation for the jQuery UI date picker plugin. */
/* Written by Pavel Selitskas <p.selitskas@gmail.com> */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.be = {
closeText: "Зачыніць",
prevText: "&larr;Папяр.",
nextText: "Наст.&rarr;",
currentText: "Сёньня",
monthNames: [ "Студзень","Люты","Сакавік","Красавік","Травень","Чэрвень",
"Ліпень","Жнівень","Верасень","Кастрычнік","Лістапад","Сьнежань" ],
monthNamesShort: [ "Сту","Лют","Сак","Кра","Тра","Чэр",
"Ліп","Жні","Вер","Кас","Ліс","Сьн" ],
dayNames: [ "нядзеля","панядзелак","аўторак","серада","чацьвер","пятніца","субота" ],
dayNamesShort: [ "ндз","пнд","аўт","срд","чцв","птн","сбт" ],
dayNamesMin: [ "Нд","Пн","Аў","Ср","Чц","Пт","Сб" ],
weekHeader: "Тд",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.be );
return datepicker.regional.be;
} ) );

View file

@ -0,0 +1,38 @@
/* Bulgarian initialisation for the jQuery UI date picker plugin. */
/* Written by Stoyan Kyosev (http://svest.org). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.bg = {
closeText: "затвори",
prevText: "&#x3C;назад",
nextText: "напред&#x3E;",
nextBigText: "&#x3E;&#x3E;",
currentText: "днес",
monthNames: [ "Януари","Февруари","Март","Април","Май","Юни",
"Юли","Август","Септември","Октомври","Ноември","Декември" ],
monthNamesShort: [ "Яну","Фев","Мар","Апр","Май","Юни",
"Юли","Авг","Сеп","Окт","Нов","Дек" ],
dayNames: [ "Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота" ],
dayNamesShort: [ "Нед","Пон","Вто","Сря","Чет","Пет","Съб" ],
dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Съ" ],
weekHeader: "Wk",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.bg );
return datepicker.regional.bg;
} ) );

View file

@ -0,0 +1,37 @@
/* Bosnian i18n for the jQuery UI date picker plugin. */
/* Written by Kenan Konjo. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.bs = {
closeText: "Zatvori",
prevText: "&#x3C;",
nextText: "&#x3E;",
currentText: "Danas",
monthNames: [ "Januar","Februar","Mart","April","Maj","Juni",
"Juli","August","Septembar","Oktobar","Novembar","Decembar" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
"Jul","Aug","Sep","Okt","Nov","Dec" ],
dayNames: [ "Nedelja","Ponedeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ],
dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ],
dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ],
weekHeader: "Wk",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.bs );
return datepicker.regional.bs;
} ) );

View file

@ -0,0 +1,37 @@
/* Inicialització en català per a l'extensió 'UI date picker' per jQuery. */
/* Writers: (joan.leon@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ca = {
closeText: "Tanca",
prevText: "Anterior",
nextText: "Següent",
currentText: "Avui",
monthNames: [ "gener","febrer","març","abril","maig","juny",
"juliol","agost","setembre","octubre","novembre","desembre" ],
monthNamesShort: [ "gen","feb","març","abr","maig","juny",
"jul","ag","set","oct","nov","des" ],
dayNames: [ "diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte" ],
dayNamesShort: [ "dg","dl","dt","dc","dj","dv","ds" ],
dayNamesMin: [ "dg","dl","dt","dc","dj","dv","ds" ],
weekHeader: "Set",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.ca );
return datepicker.regional.ca;
} ) );

View file

@ -0,0 +1,37 @@
/* Czech initialisation for the jQuery UI date picker plugin. */
/* Written by Tomas Muller (tomas@tomas-muller.net). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.cs = {
closeText: "Zavřít",
prevText: "&#x3C;Dříve",
nextText: "Později&#x3E;",
currentText: "Nyní",
monthNames: [ "leden","únor","březen","duben","květen","červen",
"červenec","srpen","září","říjen","listopad","prosinec" ],
monthNamesShort: [ "led","úno","bře","dub","kvě","čer",
"čvc","srp","zář","říj","lis","pro" ],
dayNames: [ "neděle", "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota" ],
dayNamesShort: [ "ne", "po", "út", "st", "čt", "pá", "so" ],
dayNamesMin: [ "ne","po","út","st","čt","pá","so" ],
weekHeader: "Týd",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.cs );
return datepicker.regional.cs;
} ) );

View file

@ -0,0 +1,37 @@
/* Welsh/UK initialisation for the jQuery UI date picker plugin. */
/* Written by William Griffiths. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "cy-GB" ] = {
closeText: "Done",
prevText: "Prev",
nextText: "Next",
currentText: "Today",
monthNames: [ "Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin",
"Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr" ],
monthNamesShort: [ "Ion", "Chw", "Maw", "Ebr", "Mai", "Meh",
"Gor", "Aws", "Med", "Hyd", "Tac", "Rha" ],
dayNames: [ "Dydd Sul", "Dydd Llun", "Dydd Mawrth", "Dydd Mercher", "Dydd Iau", "Dydd Gwener", "Dydd Sadwrn" ],
dayNamesShort: [ "Sul", "Llu", "Maw", "Mer", "Iau", "Gwe", "Sad" ],
dayNamesMin: [ "Su","Ll","Ma","Me","Ia","Gw","Sa" ],
weekHeader: "Wy",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "cy-GB" ] );
return datepicker.regional[ "cy-GB" ];
} ) );

View file

@ -0,0 +1,37 @@
/* Danish initialisation for the jQuery UI date picker plugin. */
/* Written by Jan Christensen ( deletestuff@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.da = {
closeText: "Luk",
prevText: "&#x3C;Forrige",
nextText: "Næste&#x3E;",
currentText: "Idag",
monthNames: [ "Januar","Februar","Marts","April","Maj","Juni",
"Juli","August","September","Oktober","November","December" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
"Jul","Aug","Sep","Okt","Nov","Dec" ],
dayNames: [ "Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag" ],
dayNamesShort: [ "Søn","Man","Tir","Ons","Tor","Fre","Lør" ],
dayNamesMin: [ "Sø","Ma","Ti","On","To","Fr","Lø" ],
weekHeader: "Uge",
dateFormat: "dd-mm-yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.da );
return datepicker.regional.da;
} ) );

View file

@ -0,0 +1,37 @@
/* German initialisation for the jQuery UI date picker plugin. */
/* Written by Milian Wolff (mail@milianw.de). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.de = {
closeText: "Schließen",
prevText: "&#x3C;Zurück",
nextText: "Vor&#x3E;",
currentText: "Heute",
monthNames: [ "Januar","Februar","März","April","Mai","Juni",
"Juli","August","September","Oktober","November","Dezember" ],
monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun",
"Jul","Aug","Sep","Okt","Nov","Dez" ],
dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ],
dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
weekHeader: "KW",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.de );
return datepicker.regional.de;
} ) );

View file

@ -0,0 +1,37 @@
/* Greek (el) initialisation for the jQuery UI date picker plugin. */
/* Written by Alex Cicovic (http://www.alexcicovic.com) */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.el = {
closeText: "Κλείσιμο",
prevText: "Προηγούμενος",
nextText: "Επόμενος",
currentText: "Σήμερα",
monthNames: [ "Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος",
"Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος" ],
monthNamesShort: [ "Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν",
"Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ" ],
dayNames: [ "Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο" ],
dayNamesShort: [ "Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ" ],
dayNamesMin: [ "Κυ","Δε","Τρ","Τε","Πε","Πα","Σα" ],
weekHeader: "Εβδ",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.el );
return datepicker.regional.el;
} ) );

View file

@ -0,0 +1,37 @@
/* English/Australia initialisation for the jQuery UI date picker plugin. */
/* Based on the en-GB initialisation. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "en-AU" ] = {
closeText: "Done",
prevText: "Prev",
nextText: "Next",
currentText: "Today",
monthNames: [ "January","February","March","April","May","June",
"July","August","September","October","November","December" ],
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
weekHeader: "Wk",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "en-AU" ] );
return datepicker.regional[ "en-AU" ];
} ) );

View file

@ -0,0 +1,37 @@
/* English/UK initialisation for the jQuery UI date picker plugin. */
/* Written by Stuart. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "en-GB" ] = {
closeText: "Done",
prevText: "Prev",
nextText: "Next",
currentText: "Today",
monthNames: [ "January","February","March","April","May","June",
"July","August","September","October","November","December" ],
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
weekHeader: "Wk",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "en-GB" ] );
return datepicker.regional[ "en-GB" ];
} ) );

View file

@ -0,0 +1,37 @@
/* English/New Zealand initialisation for the jQuery UI date picker plugin. */
/* Based on the en-GB initialisation. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "en-NZ" ] = {
closeText: "Done",
prevText: "Prev",
nextText: "Next",
currentText: "Today",
monthNames: [ "January","February","March","April","May","June",
"July","August","September","October","November","December" ],
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
weekHeader: "Wk",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "en-NZ" ] );
return datepicker.regional[ "en-NZ" ];
} ) );

View file

@ -0,0 +1,37 @@
/* Esperanto initialisation for the jQuery UI date picker plugin. */
/* Written by Olivier M. (olivierweb@ifrance.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.eo = {
closeText: "Fermi",
prevText: "&#x3C;Anta",
nextText: "Sekv&#x3E;",
currentText: "Nuna",
monthNames: [ "Januaro","Februaro","Marto","Aprilo","Majo","Junio",
"Julio","Aŭgusto","Septembro","Oktobro","Novembro","Decembro" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
"Jul","Aŭg","Sep","Okt","Nov","Dec" ],
dayNames: [ "Dimanĉo","Lundo","Mardo","Merkredo","Ĵaŭdo","Vendredo","Sabato" ],
dayNamesShort: [ "Dim","Lun","Mar","Mer","Ĵaŭ","Ven","Sab" ],
dayNamesMin: [ "Di","Lu","Ma","Me","Ĵa","Ve","Sa" ],
weekHeader: "Sb",
dateFormat: "dd/mm/yy",
firstDay: 0,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.eo );
return datepicker.regional.eo;
} ) );

View file

@ -0,0 +1,37 @@
/* Inicialización en español para la extensión 'UI date picker' para jQuery. */
/* Traducido por Vester (xvester@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.es = {
closeText: "Cerrar",
prevText: "&#x3C;Ant",
nextText: "Sig&#x3E;",
currentText: "Hoy",
monthNames: [ "enero","febrero","marzo","abril","mayo","junio",
"julio","agosto","septiembre","octubre","noviembre","diciembre" ],
monthNamesShort: [ "ene","feb","mar","abr","may","jun",
"jul","ago","sep","oct","nov","dic" ],
dayNames: [ "domingo","lunes","martes","miércoles","jueves","viernes","sábado" ],
dayNamesShort: [ "dom","lun","mar","mié","jue","vie","sáb" ],
dayNamesMin: [ "D","L","M","X","J","V","S" ],
weekHeader: "Sm",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.es );
return datepicker.regional.es;
} ) );

View file

@ -0,0 +1,37 @@
/* Estonian initialisation for the jQuery UI date picker plugin. */
/* Written by Mart Sõmermaa (mrts.pydev at gmail com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.et = {
closeText: "Sulge",
prevText: "Eelnev",
nextText: "Järgnev",
currentText: "Täna",
monthNames: [ "Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni",
"Juuli","August","September","Oktoober","November","Detsember" ],
monthNamesShort: [ "Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni",
"Juuli", "Aug", "Sept", "Okt", "Nov", "Dets" ],
dayNames: [ "Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev" ],
dayNamesShort: [ "Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup" ],
dayNamesMin: [ "P","E","T","K","N","R","L" ],
weekHeader: "näd",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.et );
return datepicker.regional.et;
} ) );

View file

@ -0,0 +1,36 @@
/* Karrikas-ek itzulia (karrikas@karrikas.com) */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.eu = {
closeText: "Egina",
prevText: "&#x3C;Aur",
nextText: "Hur&#x3E;",
currentText: "Gaur",
monthNames: [ "urtarrila","otsaila","martxoa","apirila","maiatza","ekaina",
"uztaila","abuztua","iraila","urria","azaroa","abendua" ],
monthNamesShort: [ "urt.","ots.","mar.","api.","mai.","eka.",
"uzt.","abu.","ira.","urr.","aza.","abe." ],
dayNames: [ "igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata" ],
dayNamesShort: [ "ig.","al.","ar.","az.","og.","ol.","lr." ],
dayNamesMin: [ "ig","al","ar","az","og","ol","lr" ],
weekHeader: "As",
dateFormat: "yy-mm-dd",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.eu );
return datepicker.regional.eu;
} ) );

View file

@ -0,0 +1,73 @@
/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */
/* Javad Mowlanezhad -- jmowla@gmail.com */
/* Jalali calendar should supported soon! (Its implemented but I have to test it) */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.fa = {
closeText: "بستن",
prevText: "&#x3C;قبلی",
nextText: "بعدی&#x3E;",
currentText: "امروز",
monthNames: [
"ژانویه",
"فوریه",
"مارس",
"آوریل",
"مه",
"ژوئن",
"ژوئیه",
"اوت",
"سپتامبر",
"اکتبر",
"نوامبر",
"دسامبر"
],
monthNamesShort: [ "1","2","3","4","5","6","7","8","9","10","11","12" ],
dayNames: [
"يکشنبه",
"دوشنبه",
"سه‌شنبه",
"چهارشنبه",
"پنجشنبه",
"جمعه",
"شنبه"
],
dayNamesShort: [
"ی",
"د",
"س",
"چ",
"پ",
"ج",
"ش"
],
dayNamesMin: [
"ی",
"د",
"س",
"چ",
"پ",
"ج",
"ش"
],
weekHeader: "هف",
dateFormat: "yy/mm/dd",
firstDay: 6,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.fa );
return datepicker.regional.fa;
} ) );

View file

@ -0,0 +1,37 @@
/* Finnish initialisation for the jQuery UI date picker plugin. */
/* Written by Harri Kilpiö (harrikilpio@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.fi = {
closeText: "Sulje",
prevText: "&#xAB;Edellinen",
nextText: "Seuraava&#xBB;",
currentText: "Tänään",
monthNames: [ "Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu",
"Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu" ],
monthNamesShort: [ "Tammi","Helmi","Maalis","Huhti","Touko","Kesä",
"Heinä","Elo","Syys","Loka","Marras","Joulu" ],
dayNamesShort: [ "Su","Ma","Ti","Ke","To","Pe","La" ],
dayNames: [ "Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai" ],
dayNamesMin: [ "Su","Ma","Ti","Ke","To","Pe","La" ],
weekHeader: "Vk",
dateFormat: "d.m.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.fi );
return datepicker.regional.fi;
} ) );

View file

@ -0,0 +1,37 @@
/* Faroese initialisation for the jQuery UI date picker plugin */
/* Written by Sverri Mohr Olsen, sverrimo@gmail.com */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.fo = {
closeText: "Lat aftur",
prevText: "&#x3C;Fyrra",
nextText: "Næsta&#x3E;",
currentText: "Í dag",
monthNames: [ "Januar","Februar","Mars","Apríl","Mei","Juni",
"Juli","August","September","Oktober","November","Desember" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun",
"Jul","Aug","Sep","Okt","Nov","Des" ],
dayNames: [ "Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leyardagur" ],
dayNamesShort: [ "Sun","Mán","Týs","Mik","Hós","Frí","Ley" ],
dayNamesMin: [ "Su","Má","Tý","Mi","Hó","Fr","Le" ],
weekHeader: "Vk",
dateFormat: "dd-mm-yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.fo );
return datepicker.regional.fo;
} ) );

View file

@ -0,0 +1,37 @@
/* Canadian-French initialisation for the jQuery UI date picker plugin. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "fr-CA" ] = {
closeText: "Fermer",
prevText: "Précédent",
nextText: "Suivant",
currentText: "Aujourd'hui",
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin",
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ],
weekHeader: "Sem.",
dateFormat: "yy-mm-dd",
firstDay: 0,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ""
};
datepicker.setDefaults( datepicker.regional[ "fr-CA" ] );
return datepicker.regional[ "fr-CA" ];
} ) );

View file

@ -0,0 +1,37 @@
/* Swiss-French initialisation for the jQuery UI date picker plugin. */
/* Written Martin Voelkle (martin.voelkle@e-tc.ch). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "fr-CH" ] = {
closeText: "Fermer",
prevText: "&#x3C;Préc",
nextText: "Suiv&#x3E;",
currentText: "Courant",
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin",
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ],
weekHeader: "Sm",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "fr-CH" ] );
return datepicker.regional[ "fr-CH" ];
} ) );

View file

@ -0,0 +1,39 @@
/* French initialisation for the jQuery UI date picker plugin. */
/* Written by Keith Wood (kbwood{at}iinet.com.au),
Stéphane Nahmani (sholby@sholby.net),
Stéphane Raimbault <stephane.raimbault@gmail.com> */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.fr = {
closeText: "Fermer",
prevText: "Précédent",
nextText: "Suivant",
currentText: "Aujourd'hui",
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
monthNamesShort: [ "janv.", "févr.", "mars", "avr.", "mai", "juin",
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
dayNamesMin: [ "D","L","M","M","J","V","S" ],
weekHeader: "Sem.",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.fr );
return datepicker.regional.fr;
} ) );

View file

@ -0,0 +1,37 @@
/* Galician localization for 'UI date picker' jQuery extension. */
/* Translated by Jorge Barreiro <yortx.barry@gmail.com>. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.gl = {
closeText: "Pechar",
prevText: "&#x3C;Ant",
nextText: "Seg&#x3E;",
currentText: "Hoxe",
monthNames: [ "Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño",
"Xullo","Agosto","Setembro","Outubro","Novembro","Decembro" ],
monthNamesShort: [ "Xan","Feb","Mar","Abr","Mai","Xuñ",
"Xul","Ago","Set","Out","Nov","Dec" ],
dayNames: [ "Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado" ],
dayNamesShort: [ "Dom","Lun","Mar","Mér","Xov","Ven","Sáb" ],
dayNamesMin: [ "Do","Lu","Ma","Mé","Xo","Ve","Sá" ],
weekHeader: "Sm",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.gl );
return datepicker.regional.gl;
} ) );

View file

@ -0,0 +1,37 @@
/* Hebrew initialisation for the UI Datepicker extension. */
/* Written by Amir Hardon (ahardon at gmail dot com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.he = {
closeText: "סגור",
prevText: "&#x3C;הקודם",
nextText: "הבא&#x3E;",
currentText: "היום",
monthNames: [ "ינואר","פברואר","מרץ","אפריל","מאי","יוני",
"יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר" ],
monthNamesShort: [ "ינו","פבר","מרץ","אפר","מאי","יוני",
"יולי","אוג","ספט","אוק","נוב","דצמ" ],
dayNames: [ "ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת" ],
dayNamesShort: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ],
dayNamesMin: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ],
weekHeader: "Wk",
dateFormat: "dd/mm/yy",
firstDay: 0,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.he );
return datepicker.regional.he;
} ) );

View file

@ -0,0 +1,37 @@
/* Hindi initialisation for the jQuery UI date picker plugin. */
/* Written by Michael Dawart. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.hi = {
closeText: "बंद",
prevText: "पिछला",
nextText: "अगला",
currentText: "आज",
monthNames: [ "जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून",
"जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर" ],
monthNamesShort: [ "जन", "फर", "मार्च", "अप्रेल", "मई", "जून",
"जूलाई", "अग", "सित", "अक्ट", "नव", "दि" ],
dayNames: [ "रविवार", "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार" ],
dayNamesShort: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ],
dayNamesMin: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ],
weekHeader: "हफ्ता",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.hi );
return datepicker.regional.hi;
} ) );

View file

@ -0,0 +1,37 @@
/* Croatian i18n for the jQuery UI date picker plugin. */
/* Written by Vjekoslav Nesek. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.hr = {
closeText: "Zatvori",
prevText: "&#x3C;",
nextText: "&#x3E;",
currentText: "Danas",
monthNames: [ "Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj",
"Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac" ],
monthNamesShort: [ "Sij","Velj","Ožu","Tra","Svi","Lip",
"Srp","Kol","Ruj","Lis","Stu","Pro" ],
dayNames: [ "Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ],
dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ],
dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ],
weekHeader: "Tje",
dateFormat: "dd.mm.yy.",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.hr );
return datepicker.regional.hr;
} ) );

View file

@ -0,0 +1,36 @@
/* Hungarian initialisation for the jQuery UI date picker plugin. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.hu = {
closeText: "bezár",
prevText: "vissza",
nextText: "előre",
currentText: "ma",
monthNames: [ "Január", "Február", "Március", "Április", "Május", "Június",
"Július", "Augusztus", "Szeptember", "Október", "November", "December" ],
monthNamesShort: [ "Jan", "Feb", "Már", "Ápr", "Máj", "Jún",
"Júl", "Aug", "Szep", "Okt", "Nov", "Dec" ],
dayNames: [ "Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat" ],
dayNamesShort: [ "Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo" ],
dayNamesMin: [ "V", "H", "K", "Sze", "Cs", "P", "Szo" ],
weekHeader: "Hét",
dateFormat: "yy.mm.dd.",
firstDay: 1,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.hu );
return datepicker.regional.hu;
} ) );

View file

@ -0,0 +1,37 @@
/* Armenian(UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.hy = {
closeText: "Փակել",
prevText: "&#x3C;Նախ.",
nextText: "Հաջ.&#x3E;",
currentText: "Այսօր",
monthNames: [ "Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս",
"Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր" ],
monthNamesShort: [ "Հունվ","Փետր","Մարտ","Ապր","Մայիս","Հունիս",
"Հուլ","Օգս","Սեպ","Հոկ","Նոյ","Դեկ" ],
dayNames: [ "կիրակի","եկուշաբթի","երեքշաբթի","չորեքշաբթի","հինգշաբթի","ուրբաթ","շաբաթ" ],
dayNamesShort: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ],
dayNamesMin: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ],
weekHeader: "ՇԲՏ",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.hy );
return datepicker.regional.hy;
} ) );

View file

@ -0,0 +1,37 @@
/* Indonesian initialisation for the jQuery UI date picker plugin. */
/* Written by Deden Fathurahman (dedenf@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.id = {
closeText: "Tutup",
prevText: "&#x3C;mundur",
nextText: "maju&#x3E;",
currentText: "hari ini",
monthNames: [ "Januari","Februari","Maret","April","Mei","Juni",
"Juli","Agustus","September","Oktober","Nopember","Desember" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun",
"Jul","Agus","Sep","Okt","Nop","Des" ],
dayNames: [ "Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu" ],
dayNamesShort: [ "Min","Sen","Sel","Rab","kam","Jum","Sab" ],
dayNamesMin: [ "Mg","Sn","Sl","Rb","Km","jm","Sb" ],
weekHeader: "Mg",
dateFormat: "dd/mm/yy",
firstDay: 0,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.id );
return datepicker.regional.id;
} ) );

View file

@ -0,0 +1,37 @@
/* Icelandic initialisation for the jQuery UI date picker plugin. */
/* Written by Haukur H. Thorsson (haukur@eskill.is). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.is = {
closeText: "Loka",
prevText: "&#x3C; Fyrri",
nextText: "Næsti &#x3E;",
currentText: "Í dag",
monthNames: [ "Janúar","Febrúar","Mars","Apríl","Maí","Júní",
"Júlí","Ágúst","September","Október","Nóvember","Desember" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maí","Jún",
"Júl","Ágú","Sep","Okt","Nóv","Des" ],
dayNames: [ "Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur" ],
dayNamesShort: [ "Sun","Mán","Þri","Mið","Fim","Fös","Lau" ],
dayNamesMin: [ "Su","Má","Þr","Mi","Fi","Fö","La" ],
weekHeader: "Vika",
dateFormat: "dd.mm.yy",
firstDay: 0,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.is );
return datepicker.regional.is;
} ) );

View file

@ -0,0 +1,37 @@
/* Italian initialisation for the jQuery UI date picker plugin. */
/* Written by Antonello Pasella (antonello.pasella@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional[ "it-CH" ] = {
closeText: "Chiudi",
prevText: "&#x3C;Prec",
nextText: "Succ&#x3E;",
currentText: "Oggi",
monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno",
"Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ],
monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu",
"Lug","Ago","Set","Ott","Nov","Dic" ],
dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ],
dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ],
dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ],
weekHeader: "Sm",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional[ "it-CH" ] );
return datepicker.regional[ "it-CH" ];
} ) );

View file

@ -0,0 +1,37 @@
/* Italian initialisation for the jQuery UI date picker plugin. */
/* Written by Antonello Pasella (antonello.pasella@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.it = {
closeText: "Chiudi",
prevText: "&#x3C;Prec",
nextText: "Succ&#x3E;",
currentText: "Oggi",
monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno",
"Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ],
monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu",
"Lug","Ago","Set","Ott","Nov","Dic" ],
dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ],
dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ],
dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ],
weekHeader: "Sm",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.it );
return datepicker.regional.it;
} ) );

View file

@ -0,0 +1,37 @@
/* Japanese initialisation for the jQuery UI date picker plugin. */
/* Written by Kentaro SATO (kentaro@ranvis.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ja = {
closeText: "閉じる",
prevText: "&#x3C;前",
nextText: "次&#x3E;",
currentText: "今日",
monthNames: [ "1月","2月","3月","4月","5月","6月",
"7月","8月","9月","10月","11月","12月" ],
monthNamesShort: [ "1月","2月","3月","4月","5月","6月",
"7月","8月","9月","10月","11月","12月" ],
dayNames: [ "日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日" ],
dayNamesShort: [ "日","月","火","水","木","金","土" ],
dayNamesMin: [ "日","月","火","水","木","金","土" ],
weekHeader: "週",
dateFormat: "yy/mm/dd",
firstDay: 0,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: "年" };
datepicker.setDefaults( datepicker.regional.ja );
return datepicker.regional.ja;
} ) );

View file

@ -0,0 +1,35 @@
/* Georgian (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Lado Lomidze (lado.lomidze@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ka = {
closeText: "დახურვა",
prevText: "&#x3c; წინა",
nextText: "შემდეგი &#x3e;",
currentText: "დღეს",
monthNames: [ "იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი", "ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი" ],
monthNamesShort: [ "იან","თებ","მარ","აპრ","მაი","ივნ", "ივლ","აგვ","სექ","ოქტ","ნოე","დეკ" ],
dayNames: [ "კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი" ],
dayNamesShort: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ],
dayNamesMin: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ],
weekHeader: "კვირა",
dateFormat: "dd-mm-yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.ka );
return datepicker.regional.ka;
} ) );

View file

@ -0,0 +1,37 @@
/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.kk = {
closeText: "Жабу",
prevText: "&#x3C;Алдыңғы",
nextText: "Келесі&#x3E;",
currentText: "Бүгін",
monthNames: [ "Қаңтар","Ақпан","Наурыз","Сәуір","Мамыр","Маусым",
"Шілде","Тамыз","Қыркүйек","Қазан","Қараша","Желтоқсан" ],
monthNamesShort: [ "Қаң","Ақп","Нау","Сәу","Мам","Мау",
"Шіл","Там","Қыр","Қаз","Қар","Жел" ],
dayNames: [ "Жексенбі","Дүйсенбі","Сейсенбі","Сәрсенбі","Бейсенбі","Жұма","Сенбі" ],
dayNamesShort: [ "жкс","дсн","ссн","срс","бсн","жма","снб" ],
dayNamesMin: [ "Жк","Дс","Сс","Ср","Бс","Жм","Сн" ],
weekHeader: "Не",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.kk );
return datepicker.regional.kk;
} ) );

View file

@ -0,0 +1,37 @@
/* Khmer initialisation for the jQuery calendar extension. */
/* Written by Chandara Om (chandara.teacher@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.km = {
closeText: "ធ្វើ​រួច",
prevText: "មុន",
nextText: "បន្ទាប់",
currentText: "ថ្ងៃ​នេះ",
monthNames: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា",
"កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ],
monthNamesShort: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា",
"កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ],
dayNames: [ "អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍" ],
dayNamesShort: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ],
dayNamesMin: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ],
weekHeader: "សប្ដាហ៍",
dateFormat: "dd-mm-yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.km );
return datepicker.regional.km;
} ) );

View file

@ -0,0 +1,37 @@
/* Korean initialisation for the jQuery calendar extension. */
/* Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie and Myeongjin Lee. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ko = {
closeText: "닫기",
prevText: "이전달",
nextText: "다음달",
currentText: "오늘",
monthNames: [ "1월","2월","3월","4월","5월","6월",
"7월","8월","9월","10월","11월","12월" ],
monthNamesShort: [ "1월","2월","3월","4월","5월","6월",
"7월","8월","9월","10월","11월","12월" ],
dayNames: [ "일요일","월요일","화요일","수요일","목요일","금요일","토요일" ],
dayNamesShort: [ "일","월","화","수","목","금","토" ],
dayNamesMin: [ "일","월","화","수","목","금","토" ],
weekHeader: "주",
dateFormat: "yy. m. d.",
firstDay: 0,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: "년" };
datepicker.setDefaults( datepicker.regional.ko );
return datepicker.regional.ko;
} ) );

View file

@ -0,0 +1,38 @@
/* Kyrgyz (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Sergey Kartashov (ebishkek@yandex.ru). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ky = {
closeText: "Жабуу",
prevText: "&#x3c;Мур",
nextText: "Кий&#x3e;",
currentText: "Бүгүн",
monthNames: [ "Январь","Февраль","Март","Апрель","Май","Июнь",
"Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь" ],
monthNamesShort: [ "Янв","Фев","Мар","Апр","Май","Июн",
"Июл","Авг","Сен","Окт","Ноя","Дек" ],
dayNames: [ "жекшемби", "дүйшөмбү", "шейшемби", "шаршемби", "бейшемби", "жума", "ишемби" ],
dayNamesShort: [ "жек", "дүй", "шей", "шар", "бей", "жум", "ише" ],
dayNamesMin: [ "Жк","Дш","Шш","Шр","Бш","Жм","Иш" ],
weekHeader: "Жум",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ""
};
datepicker.setDefaults( datepicker.regional.ky );
return datepicker.regional.ky;
} ) );

View file

@ -0,0 +1,37 @@
/* Luxembourgish initialisation for the jQuery UI date picker plugin. */
/* Written by Michel Weimerskirch <michel@weimerskirch.net> */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.lb = {
closeText: "Fäerdeg",
prevText: "Zréck",
nextText: "Weider",
currentText: "Haut",
monthNames: [ "Januar","Februar","Mäerz","Abrëll","Mee","Juni",
"Juli","August","September","Oktober","November","Dezember" ],
monthNamesShort: [ "Jan", "Feb", "Mäe", "Abr", "Mee", "Jun",
"Jul", "Aug", "Sep", "Okt", "Nov", "Dez" ],
dayNames: [ "Sonndeg", "Méindeg", "Dënschdeg", "Mëttwoch", "Donneschdeg", "Freideg", "Samschdeg" ],
dayNamesShort: [ "Son", "Méi", "Dën", "Mët", "Don", "Fre", "Sam" ],
dayNamesMin: [ "So","Mé","Dë","Më","Do","Fr","Sa" ],
weekHeader: "W",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.lb );
return datepicker.regional.lb;
} ) );

View file

@ -0,0 +1,37 @@
/* Lithuanian (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* @author Arturas Paleicikas <arturas@avalon.lt> */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.lt = {
closeText: "Uždaryti",
prevText: "&#x3C;Atgal",
nextText: "Pirmyn&#x3E;",
currentText: "Šiandien",
monthNames: [ "Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis",
"Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis" ],
monthNamesShort: [ "Sau","Vas","Kov","Bal","Geg","Bir",
"Lie","Rugp","Rugs","Spa","Lap","Gru" ],
dayNames: [ "sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis" ],
dayNamesShort: [ "sek","pir","ant","tre","ket","pen","šeš" ],
dayNamesMin: [ "Se","Pr","An","Tr","Ke","Pe","Še" ],
weekHeader: "SAV",
dateFormat: "yy-mm-dd",
firstDay: 1,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.lt );
return datepicker.regional.lt;
} ) );

View file

@ -0,0 +1,37 @@
/* Latvian (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* @author Arturas Paleicikas <arturas.paleicikas@metasite.net> */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.lv = {
closeText: "Aizvērt",
prevText: "Iepr.",
nextText: "Nāk.",
currentText: "Šodien",
monthNames: [ "Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs",
"Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris" ],
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mai","Jūn",
"Jūl","Aug","Sep","Okt","Nov","Dec" ],
dayNames: [ "svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena" ],
dayNamesShort: [ "svt","prm","otr","tre","ctr","pkt","sst" ],
dayNamesMin: [ "Sv","Pr","Ot","Tr","Ct","Pk","Ss" ],
weekHeader: "Ned.",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.lv );
return datepicker.regional.lv;
} ) );

View file

@ -0,0 +1,37 @@
/* Macedonian i18n for the jQuery UI date picker plugin. */
/* Written by Stojce Slavkovski. */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.mk = {
closeText: "Затвори",
prevText: "&#x3C;",
nextText: "&#x3E;",
currentText: "Денес",
monthNames: [ "Јануари","Февруари","Март","Април","Мај","Јуни",
"Јули","Август","Септември","Октомври","Ноември","Декември" ],
monthNamesShort: [ "Јан","Фев","Мар","Апр","Мај","Јун",
"Јул","Авг","Сеп","Окт","Ное","Дек" ],
dayNames: [ "Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота" ],
dayNamesShort: [ "Нед","Пон","Вто","Сре","Чет","Пет","Саб" ],
dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Са" ],
weekHeader: "Сед",
dateFormat: "dd.mm.yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.mk );
return datepicker.regional.mk;
} ) );

View file

@ -0,0 +1,37 @@
/* Malayalam (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Saji Nediyanchath (saji89@gmail.com). */
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "../widgets/datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}( function( datepicker ) {
datepicker.regional.ml = {
closeText: "ശരി",
prevText: "മുന്നത്തെ",
nextText: "അടുത്തത് ",
currentText: "ഇന്ന്",
monthNames: [ "ജനുവരി","ഫെബ്രുവരി","മാര്‍ച്ച്","ഏപ്രില്‍","മേയ്","ജൂണ്‍",
"ജൂലൈ","ആഗസ്റ്റ്","സെപ്റ്റംബര്‍","ഒക്ടോബര്‍","നവംബര്‍","ഡിസംബര്‍" ],
monthNamesShort: [ "ജനു", "ഫെബ്", "മാര്‍", "ഏപ്രി", "മേയ്", "ജൂണ്‍",
"ജൂലാ", "ആഗ", "സെപ്", "ഒക്ടോ", "നവം", "ഡിസ" ],
dayNames: [ "ഞായര്‍", "തിങ്കള്‍", "ചൊവ്വ", "ബുധന്‍", "വ്യാഴം", "വെള്ളി", "ശനി" ],
dayNamesShort: [ "ഞായ", "തിങ്ക", "ചൊവ്വ", "ബുധ", "വ്യാഴം", "വെള്ളി", "ശനി" ],
dayNamesMin: [ "ഞാ","തി","ചൊ","ബു","വ്യാ","വെ","ശ" ],
weekHeader: "ആ",
dateFormat: "dd/mm/yy",
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: "" };
datepicker.setDefaults( datepicker.regional.ml );
return datepicker.regional.ml;
} ) );

Some files were not shown because too many files have changed in this diff Show more