mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-05-02 00:32:15 +09:00
Merge branch 'develop' into pr/session-class
This commit is contained in:
commit
483ac84796
454 changed files with 10659 additions and 30145 deletions
|
|
@ -136,7 +136,6 @@ class Config
|
|||
// Save the main config file.
|
||||
$buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n";
|
||||
$result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false;
|
||||
//if (!$result) return false;
|
||||
|
||||
// Save XE-compatible config files.
|
||||
$db_info = \Context::convertDBInfo(self::$_config);
|
||||
|
|
@ -148,7 +147,7 @@ class Config
|
|||
Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff);
|
||||
$buff = '<?php' . "\n\n" . $warning . "\n\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n";
|
||||
Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff);
|
||||
return true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -236,10 +236,33 @@ class Debug
|
|||
'file' => $query['called_file'],
|
||||
'line' => $query['called_line'],
|
||||
'method' => $query['called_method'],
|
||||
'backtrace' => $query['backtrace'],
|
||||
'backtrace' => $query['backtrace'] ?: array(),
|
||||
);
|
||||
|
||||
self::$_queries[] = $query_object;
|
||||
|
||||
// Add the entry to the error log if the result wasn't successful.
|
||||
if ($query['result'] === 'error')
|
||||
{
|
||||
$error_object = (object)array(
|
||||
'type' => 'Query Error',
|
||||
'time' => $query_object->time,
|
||||
'message' => $query['errstr'] . ' (code ' . intval($query['errno']) . ')',
|
||||
'file' => $query_object->file,
|
||||
'line' => $query_object->line,
|
||||
'backtrace' => $query_object->backtrace ?: array(),
|
||||
);
|
||||
|
||||
self::$_errors[] = $error_object;
|
||||
|
||||
if (config('debug.write_error_log') === 'all')
|
||||
{
|
||||
$log_entry = strtr(sprintf('Query Error: %s in %s on line %d', $error_object->message, $error_object->file, intval($error_object->line)), "\0\r\n\t\v\e\f", ' ');
|
||||
error_log($log_entry . \PHP_EOL . self::formatBacktrace($error_object->backtrace));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the entry to the slow query log.
|
||||
if ($query_object->query_time && $query_object->query_time >= config('debug.log_slow_queries'))
|
||||
{
|
||||
self::$_slow_queries[] = $query_object;
|
||||
|
|
|
|||
7
common/framework/drivers/cache/redis.php
vendored
7
common/framework/drivers/cache/redis.php
vendored
|
|
@ -143,6 +143,10 @@ class Redis implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
return null;
|
||||
}
|
||||
if (ctype_digit($value))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = unserialize($value);
|
||||
if ($value === false)
|
||||
|
|
@ -168,7 +172,8 @@ class Redis implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
try
|
||||
{
|
||||
return $this->_conn->setex($key, $ttl, serialize($value)) ? true : false;
|
||||
$value = (is_scalar($value) && ctype_digit($value)) ? $value : serialize($value);
|
||||
return $this->_conn->setex($key, $ttl, $value) ? true : false;
|
||||
}
|
||||
catch (\RedisException $e)
|
||||
{
|
||||
|
|
|
|||
36
common/framework/drivers/cache/sqlite.php
vendored
36
common/framework/drivers/cache/sqlite.php
vendored
|
|
@ -61,6 +61,7 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
protected function _connect($filename)
|
||||
{
|
||||
$this->_dbh = new \SQLite3($filename);
|
||||
$this->_dbh->busyTimeout(250);
|
||||
$this->_dbh->exec('PRAGMA journal_mode = MEMORY');
|
||||
$this->_dbh->exec('PRAGMA synchronous = OFF');
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return class_exists('\\SQLite3', false) && config('crypto.authentication_key') !== null;
|
||||
return class_exists('\\SQLite3', false) && config('crypto.authentication_key') !== null && stripos(\PHP_SAPI, 'win') === false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,8 +118,18 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
$table = 'cache_' . (crc32($key) % 32);
|
||||
$stmt = $this->_dbh->prepare('SELECT v, exp FROM ' . $table . ' WHERE k = :key');
|
||||
if (!$stmt)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
if (!$result)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = $result->fetchArray(\SQLITE3_NUM);
|
||||
if ($row)
|
||||
{
|
||||
|
|
@ -154,6 +165,11 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
$table = 'cache_' . (crc32($key) % 32);
|
||||
$stmt = $this->_dbh->prepare('INSERT OR REPLACE INTO ' . $table . ' (k, v, exp) VALUES (:key, :val, :exp)');
|
||||
if (!$stmt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
||||
$stmt->bindValue(':val', serialize($value), \SQLITE3_TEXT);
|
||||
$stmt->bindValue(':exp', $ttl ? (time() + $ttl) : 0, \SQLITE3_INTEGER);
|
||||
|
|
@ -173,6 +189,11 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
$table = 'cache_' . (crc32($key) % 32);
|
||||
$stmt = $this->_dbh->prepare('DELETE FROM ' . $table . ' WHERE k = :key');
|
||||
if (!$stmt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
||||
return $stmt->execute() ? true : false;
|
||||
}
|
||||
|
|
@ -189,9 +210,19 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
{
|
||||
$table = 'cache_' . (crc32($key) % 32);
|
||||
$stmt = $this->_dbh->prepare('SELECT 1 FROM ' . $table . ' WHERE k = :key AND (exp = 0 OR exp >= :exp)');
|
||||
if (!$stmt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
||||
$stmt->bindValue(':exp', time(), \SQLITE3_INTEGER);
|
||||
$result = $stmt->execute();
|
||||
if (!$result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = $result->fetchArray(\SQLITE3_NUM);
|
||||
if ($row)
|
||||
{
|
||||
|
|
@ -215,14 +246,17 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|||
*/
|
||||
public function incr($key, $amount)
|
||||
{
|
||||
$this->_dbh->exec('BEGIN');
|
||||
$current_value = $this->get($key);
|
||||
$new_value = intval($current_value) + $amount;
|
||||
if ($this->set($key, $new_value))
|
||||
{
|
||||
$this->_dbh->exec('COMMIT');
|
||||
return $new_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_dbh->exec('ROLLBACK');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\MailInterface
|
|||
* Create a new instance of the current mail driver, using the given settings.
|
||||
*
|
||||
* @param array $config
|
||||
* @return void
|
||||
* @return object
|
||||
*/
|
||||
public static function getInstance(array $config)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -160,13 +160,16 @@ class Woorimail extends Base implements \Rhymix\Framework\Drivers\MailInterface
|
|||
$data['receiver_nickname'] = implode(',', $data['receiver_nickname']);
|
||||
|
||||
// Define connection options.
|
||||
$headers = array(
|
||||
'Accept' => 'application/json, text/javascript, */*; q=0.1',
|
||||
);
|
||||
$options = array(
|
||||
'timeout' => 5,
|
||||
'useragent' => 'PHP',
|
||||
);
|
||||
|
||||
// Send the API request.
|
||||
$request = \Requests::post(self::$_url, array(), $data, $options);
|
||||
$request = \Requests::post(self::$_url, $headers, $data, $options);
|
||||
$result = @json_decode($request->body);
|
||||
|
||||
// Parse the result.
|
||||
|
|
|
|||
120
common/framework/drivers/sms/base.php
Normal file
120
common/framework/drivers/sms/base.php
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Drivers\SMS;
|
||||
|
||||
/**
|
||||
* The base class for other SMS drivers.
|
||||
*/
|
||||
abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
||||
{
|
||||
/**
|
||||
* The configuration is stored here.
|
||||
*/
|
||||
protected $_config = null;
|
||||
|
||||
/**
|
||||
* The driver specification is stored here.
|
||||
*/
|
||||
protected static $_spec = array();
|
||||
|
||||
/**
|
||||
* Config keys used by this driver are stored here.
|
||||
*/
|
||||
protected static $_required_config = array();
|
||||
protected static $_optional_config = array();
|
||||
|
||||
/**
|
||||
* Direct invocation of the constructor is not permitted.
|
||||
*/
|
||||
protected function __construct(array $config)
|
||||
{
|
||||
$this->_config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the current SMS driver, using the given settings.
|
||||
*
|
||||
* @param array $config
|
||||
* @return object
|
||||
*/
|
||||
public static function getInstance(array $config)
|
||||
{
|
||||
return new static($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the human-readable name of this SMS driver.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName()
|
||||
{
|
||||
return class_basename(get_called_class());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields required by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getRequiredConfig()
|
||||
{
|
||||
return static::$_required_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields optionally used by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOptionalConfig()
|
||||
{
|
||||
return static::$_optional_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of API types supported by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPITypes()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spec for this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPISpec()
|
||||
{
|
||||
return static::$_spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param object $original
|
||||
* @return bool
|
||||
*/
|
||||
public function send(array $messages, \Rhymix\Framework\SMS $original)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
123
common/framework/drivers/sms/coolsms.php
Normal file
123
common/framework/drivers/sms/coolsms.php
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Drivers\SMS;
|
||||
|
||||
/**
|
||||
* The CoolSMS SMS driver.
|
||||
*/
|
||||
class CoolSMS extends Base implements \Rhymix\Framework\Drivers\SMSInterface
|
||||
{
|
||||
/**
|
||||
* API specifications.
|
||||
*/
|
||||
protected static $_spec = array(
|
||||
'max_recipients' => 1000,
|
||||
'sms_max_length' => 90,
|
||||
'sms_max_length_in_charset' => 'CP949',
|
||||
'lms_supported' => true,
|
||||
'lms_supported_country_codes' => array(82),
|
||||
'lms_max_length' => 2000,
|
||||
'lms_max_length_in_charset' => 'CP949',
|
||||
'lms_subject_supported' => true,
|
||||
'lms_subject_max_length' => 40,
|
||||
'mms_supported' => true,
|
||||
'mms_supported_country_codes' => array(82),
|
||||
'mms_max_length' => 2000,
|
||||
'mms_max_length_in_charset' => 'CP949',
|
||||
'mms_subject_supported' => true,
|
||||
'mms_subject_max_length' => 40,
|
||||
'image_allowed_types' => array('jpg', 'gif', 'png'),
|
||||
'image_max_dimensions' => array(2048, 2048),
|
||||
'image_max_filesize' => 300000,
|
||||
'delay_supported' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Config keys used by this driver are stored here.
|
||||
*/
|
||||
protected static $_required_config = array('api_key', 'api_secret');
|
||||
protected static $_optional_config = array('sender_key');
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param object $original
|
||||
* @return bool
|
||||
*/
|
||||
public function send(array $messages, \Rhymix\Framework\SMS $original)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sender = new \Nurigo\Api\Message($this->_config['api_key'], $this->_config['api_secret']);
|
||||
$status = true;
|
||||
|
||||
foreach ($messages as $i => $message)
|
||||
{
|
||||
$options = new \stdClass;
|
||||
if ($this->_config['sender_key'])
|
||||
{
|
||||
$options->sender_key = $this->_config['sender_key'];
|
||||
$options->type = 'CTA';
|
||||
}
|
||||
else
|
||||
{
|
||||
$options->type = $message->type;
|
||||
}
|
||||
$options->from = $message->from;
|
||||
$options->to = implode(',', $message->to);
|
||||
$options->text = $message->content ?: $message->type;
|
||||
$options->charset = 'utf8';
|
||||
$options->srk = 'K0009334574';
|
||||
if ($message->delay && $message->delay > time())
|
||||
{
|
||||
$options->datetime = gmdate('YmdHis', $message->delay + (3600 * 9));
|
||||
}
|
||||
if ($message->country && $message->country != 82)
|
||||
{
|
||||
$options->country = $message->country;
|
||||
}
|
||||
if ($message->subject)
|
||||
{
|
||||
$options->subject = $message->subject;
|
||||
}
|
||||
if ($message->image)
|
||||
{
|
||||
$options->image = $message->image;
|
||||
}
|
||||
foreach ($original->getExtraVars() as $key => $value)
|
||||
{
|
||||
$options->$key = $value;
|
||||
}
|
||||
$result = $sender->send($options);
|
||||
if (!$result->success_count)
|
||||
{
|
||||
$error_codes = implode(', ', $result->error_list ?: array('Unknown'));
|
||||
$original->addError('Error (' . $error_codes . ') while sending message ' . ($i + 1) . ' of ' . count($messages) . ' to ' . $options->to);
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
catch (\Nurigo\Exceptions\CoolsmsException $e)
|
||||
{
|
||||
$message->errors[] = class_basename($e) . ': ' . $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
common/framework/drivers/sms/dummy.php
Normal file
89
common/framework/drivers/sms/dummy.php
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Drivers\SMS;
|
||||
|
||||
/**
|
||||
* The dummy SMS driver.
|
||||
*/
|
||||
class Dummy extends Base implements \Rhymix\Framework\Drivers\SMSInterface
|
||||
{
|
||||
/**
|
||||
* API specifications.
|
||||
*/
|
||||
protected static $_spec = array(
|
||||
'max_recipients' => 100,
|
||||
'sms_max_length' => 90,
|
||||
'sms_max_length_in_charset' => 'CP949',
|
||||
'lms_supported' => true,
|
||||
'lms_supported_country_codes' => array(82),
|
||||
'lms_max_length' => 2000,
|
||||
'lms_max_length_in_charset' => 'CP949',
|
||||
'lms_subject_supported' => true,
|
||||
'lms_subject_max_length' => 40,
|
||||
'mms_supported' => true,
|
||||
'mms_supported_country_codes' => array(82),
|
||||
'mms_max_length' => 2000,
|
||||
'mms_max_length_in_charset' => 'CP949',
|
||||
'mms_subject_supported' => false,
|
||||
'mms_subject_max_length' => 40,
|
||||
'image_allowed_types' => array(),
|
||||
'image_max_dimensions' => array(1024, 1024),
|
||||
'image_max_filesize' => 300000,
|
||||
'delay_supported' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Sent messages are stored here for debugging and testing.
|
||||
*/
|
||||
protected $_sent_messages = array();
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param object $original
|
||||
* @return bool
|
||||
*/
|
||||
public function send(array $messages, \Rhymix\Framework\SMS $original)
|
||||
{
|
||||
foreach ($messages as $message)
|
||||
{
|
||||
$this->_sent_messages[] = $message;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sent messages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSentMessages()
|
||||
{
|
||||
return $this->_sent_messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset sent messages.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetSentMessages()
|
||||
{
|
||||
$this->_sent_messages = array();
|
||||
}
|
||||
}
|
||||
72
common/framework/drivers/smsinterface.php
Normal file
72
common/framework/drivers/smsinterface.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Drivers;
|
||||
|
||||
/**
|
||||
* The SMS driver interface.
|
||||
*/
|
||||
interface SMSInterface
|
||||
{
|
||||
/**
|
||||
* Create a new instance of the current SMS driver, using the given settings.
|
||||
*
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public static function getInstance(array $config);
|
||||
|
||||
/**
|
||||
* Get the human-readable name of this SMS driver.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName();
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields required by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getRequiredConfig();
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields optionally used by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOptionalConfig();
|
||||
|
||||
/**
|
||||
* Get the list of API types supported by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPITypes();
|
||||
|
||||
/**
|
||||
* Get the spec for this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPISpec();
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported();
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param array $messages
|
||||
* @param object $original
|
||||
* @return bool
|
||||
*/
|
||||
public function send(array $messages, \Rhymix\Framework\SMS $original);
|
||||
}
|
||||
|
|
@ -16,8 +16,8 @@ class FilenameFilter
|
|||
public static function clean($filename)
|
||||
{
|
||||
// Replace dangerous characters with safe alternatives, maintaining meaning as much as possible.
|
||||
$illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '@', '#', '$', '%', '^', '&', '*', '?');
|
||||
$replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '_', '_', '_', '', '');
|
||||
$illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '$', '%', '^', '*', '?');
|
||||
$replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '', '');
|
||||
$filename = str_replace($illegal, $replace, $filename);
|
||||
|
||||
// Remove control characters.
|
||||
|
|
@ -85,4 +85,22 @@ class FilenameFilter
|
|||
// Trim trailing slashes.
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file has an extension that would allow direct download.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDirectDownload($filename)
|
||||
{
|
||||
if (preg_match('/\.(as[fx]|avi|flac|flv|gif|jpe?g|m4[av]|midi?|mkv|moov|mov|mp[1234]|mpe?g|ogg|png|qt|ram?|rmm?|swf|wav|web[mp]|wm[av])$/i', $filename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rhymix\Framework\Filters;
|
||||
|
||||
use Rhymix\Framework\Config;
|
||||
use Rhymix\Framework\Security;
|
||||
use Rhymix\Framework\Storage;
|
||||
|
||||
|
|
@ -11,9 +12,9 @@ use Rhymix\Framework\Storage;
|
|||
class HTMLFilter
|
||||
{
|
||||
/**
|
||||
* HTMLPurifier instance is cached here.
|
||||
* HTMLPurifier instances are cached here.
|
||||
*/
|
||||
protected static $_htmlpurifier;
|
||||
protected static $_instances = array();
|
||||
|
||||
/**
|
||||
* Pre-processing and post-processing filters are stored here.
|
||||
|
|
@ -69,18 +70,42 @@ class HTMLFilter
|
|||
* Filter HTML content to block XSS attacks.
|
||||
*
|
||||
* @param string $input
|
||||
* @param array|bool $allow_classes (optional)
|
||||
* @param bool $allow_editor_components (optional)
|
||||
* @param bool $allow_widgets (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function clean($input)
|
||||
public static function clean($input, $allow_classes = false, $allow_editor_components = true, $allow_widgets = false)
|
||||
{
|
||||
foreach (self::$_preproc as $callback)
|
||||
{
|
||||
$input = $callback($input);
|
||||
}
|
||||
|
||||
$input = self::_preprocess($input);
|
||||
$output = self::getHTMLPurifier()->purify($input);
|
||||
$output = self::_postprocess($output);
|
||||
if ($allow_classes === true)
|
||||
{
|
||||
$allowed_classes = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_array($allow_classes))
|
||||
{
|
||||
$allowed_classes = array_values($allow_classes);
|
||||
}
|
||||
else
|
||||
{
|
||||
$allowed_classes = Config::get('mediafilter.classes') ?: array();
|
||||
}
|
||||
|
||||
if ($allow_widgets)
|
||||
{
|
||||
$allowed_classes[] = 'zbxe_widget_output';
|
||||
}
|
||||
}
|
||||
|
||||
$input = self::_preprocess($input, $allow_editor_components, $allow_widgets);
|
||||
$output = self::getHTMLPurifier($allowed_classes)->purify($input);
|
||||
$output = self::_postprocess($output, $allow_editor_components, $allow_widgets);
|
||||
|
||||
foreach (self::$_postproc as $callback)
|
||||
{
|
||||
|
|
@ -93,17 +118,27 @@ class HTMLFilter
|
|||
/**
|
||||
* Get an instance of HTMLPurifier.
|
||||
*
|
||||
* @param array|null $allowed_classes (optional)
|
||||
* @return object
|
||||
*/
|
||||
public static function getHTMLPurifier()
|
||||
public static function getHTMLPurifier($allowed_classes = null)
|
||||
{
|
||||
// Keep separate instances for different sets of allowed classes.
|
||||
if ($allowed_classes !== null)
|
||||
{
|
||||
$allowed_classes = array_unique($allowed_classes);
|
||||
sort($allowed_classes);
|
||||
}
|
||||
$key = sha1(serialize($allowed_classes));
|
||||
|
||||
// Create an instance with reasonable defaults.
|
||||
if (self::$_htmlpurifier === null)
|
||||
if (!isset(self::$_instances[$key]))
|
||||
{
|
||||
// Get the default configuration.
|
||||
$config = \HTMLPurifier_Config::createDefault();
|
||||
|
||||
// Customize the default configuration.
|
||||
$config->set('Attr.AllowedClasses', $allowed_classes);
|
||||
$config->set('Attr.AllowedFrameTargets', array('_blank'));
|
||||
$config->set('Attr.DefaultImageAlt', '');
|
||||
$config->set('Attr.EnableID', true);
|
||||
|
|
@ -143,11 +178,11 @@ class HTMLFilter
|
|||
self::_supportCSS3($config);
|
||||
|
||||
// Cache our instance of HTMLPurifier.
|
||||
self::$_htmlpurifier = new \HTMLPurifier($config);
|
||||
self::$_instances[$key] = new \HTMLPurifier($config);
|
||||
}
|
||||
|
||||
// Return the cached instance.
|
||||
return self::$_htmlpurifier;
|
||||
return self::$_instances[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -226,6 +261,7 @@ class HTMLFilter
|
|||
));
|
||||
|
||||
// Support additional properties.
|
||||
$def->addAttribute('i', 'aria-hidden', 'Text');
|
||||
$def->addAttribute('img', 'srcset', 'Text');
|
||||
$def->addAttribute('iframe', 'allowfullscreen', 'Bool');
|
||||
}
|
||||
|
|
@ -378,12 +414,17 @@ class HTMLFilter
|
|||
* Rhymix-specific preprocessing method.
|
||||
*
|
||||
* @param string $content
|
||||
* @param bool $allow_editor_components (optional)
|
||||
* @param bool $allow_widgets (optional)
|
||||
* @return string
|
||||
*/
|
||||
protected static function _preprocess($content)
|
||||
protected static function _preprocess($content, $allow_editor_components = true, $allow_widgets = false)
|
||||
{
|
||||
// Encode widget and editor component properties so that they are not removed by HTMLPurifier.
|
||||
$content = self::_encodeWidgetsAndEditorComponents($content);
|
||||
if ($allow_editor_components || $allow_widgets)
|
||||
{
|
||||
$content = self::_encodeWidgetsAndEditorComponents($content, $allow_editor_components, $allow_widgets);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
|
@ -391,9 +432,11 @@ class HTMLFilter
|
|||
* Rhymix-specific postprocessing method.
|
||||
*
|
||||
* @param string $content
|
||||
* @param bool $allow_editor_components (optional)
|
||||
* @param bool $allow_widgets (optional)
|
||||
* @return string
|
||||
*/
|
||||
protected static function _postprocess($content)
|
||||
protected static function _postprocess($content, $allow_editor_components = true, $allow_widgets = false)
|
||||
{
|
||||
// Define acts to allow and deny.
|
||||
$allow_acts = array('procFileDownload');
|
||||
|
|
@ -435,7 +478,7 @@ class HTMLFilter
|
|||
}, $content);
|
||||
|
||||
// Restore widget and editor component properties.
|
||||
$content = self::_decodeWidgetsAndEditorComponents($content);
|
||||
$content = self::_decodeWidgetsAndEditorComponents($content, $allow_editor_components, $allow_widgets);
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
|
@ -443,11 +486,27 @@ class HTMLFilter
|
|||
* Encode widgets and editor components before processing.
|
||||
*
|
||||
* @param string $content
|
||||
* @param bool $allow_editor_components (optional)
|
||||
* @param bool $allow_widgets (optional)
|
||||
* @return string
|
||||
*/
|
||||
protected static function _encodeWidgetsAndEditorComponents($content)
|
||||
protected static function _encodeWidgetsAndEditorComponents($content, $allow_editor_components = true, $allow_widgets = false)
|
||||
{
|
||||
return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) {
|
||||
$regexp = array();
|
||||
if ($allow_editor_components)
|
||||
{
|
||||
$regexp[] = 'editor_component="[^"]+"';
|
||||
}
|
||||
if ($allow_widgets)
|
||||
{
|
||||
$regexp[] = 'class="zbxe_widget_output"';
|
||||
}
|
||||
if (!count($regexp))
|
||||
{
|
||||
return $content;
|
||||
}
|
||||
|
||||
return preg_replace_callback('!<(div|img)([^>]*)(' . implode('|', $regexp) . ')([^>]*)>!i', function($match) {
|
||||
$tag = strtolower($match[1]);
|
||||
$attrs = array();
|
||||
$html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) {
|
||||
|
|
@ -476,10 +535,25 @@ class HTMLFilter
|
|||
* Decode widgets and editor components after processing.
|
||||
*
|
||||
* @param string $content
|
||||
* @param bool $allow_editor_components (optional)
|
||||
* @param bool $allow_widgets (optional)
|
||||
* @return string
|
||||
*/
|
||||
protected static function _decodeWidgetsAndEditorComponents($content)
|
||||
protected static function _decodeWidgetsAndEditorComponents($content, $allow_editor_components = true, $allow_widgets = false)
|
||||
{
|
||||
if (!$allow_editor_components)
|
||||
{
|
||||
$content = preg_replace('!(<(?:div|img)[^>]*)\s(editor_component="(?:[^"]+)")!i', '$1', $content);
|
||||
}
|
||||
if (!$allow_widgets)
|
||||
{
|
||||
$content = preg_replace('!(<(?:div|img)[^>]*)\s(widget="(?:[^"]+)")!i', '$1blocked-$2', $content);
|
||||
}
|
||||
if (!$allow_editor_components && !$allow_widgets)
|
||||
{
|
||||
return $content;
|
||||
}
|
||||
|
||||
return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) {
|
||||
$attrs = array();
|
||||
$decoded_properties = Security::decrypt($match[4]);
|
||||
|
|
|
|||
820
common/framework/sms.php
Normal file
820
common/framework/sms.php
Normal file
|
|
@ -0,0 +1,820 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The SMS class.
|
||||
*/
|
||||
class SMS
|
||||
{
|
||||
/**
|
||||
* Instance properties.
|
||||
*/
|
||||
public $driver = null;
|
||||
protected $caller = '';
|
||||
protected $from = null;
|
||||
protected $to = array();
|
||||
protected $subject = '';
|
||||
protected $content = '';
|
||||
protected $attachments = array();
|
||||
protected $extra_vars = array();
|
||||
protected $delay_timestamp = 0;
|
||||
protected $force_sms = false;
|
||||
protected $allow_split_sms = true;
|
||||
protected $allow_split_lms = true;
|
||||
protected $errors = array();
|
||||
protected $sent = false;
|
||||
|
||||
/**
|
||||
* Static properties.
|
||||
*/
|
||||
public static $default_driver = null;
|
||||
public static $custom_drivers = array();
|
||||
|
||||
/**
|
||||
* Set the default driver.
|
||||
*
|
||||
* @param object $driver
|
||||
* @return void
|
||||
*/
|
||||
public static function setDefaultDriver(Drivers\SMSInterface $driver)
|
||||
{
|
||||
self::$default_driver = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default driver.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function getDefaultDriver()
|
||||
{
|
||||
if (!self::$default_driver)
|
||||
{
|
||||
$default_driver = config('sms.type');
|
||||
$default_driver_class = '\Rhymix\Framework\Drivers\SMS\\' . $default_driver;
|
||||
if (class_exists($default_driver_class))
|
||||
{
|
||||
$default_driver_config = config('sms.' . $default_driver) ?: array();
|
||||
self::$default_driver = $default_driver_class::getInstance($default_driver_config);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$default_driver = Drivers\SMS\Dummy::getInstance(array());
|
||||
}
|
||||
}
|
||||
return self::$default_driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom mail driver.
|
||||
*/
|
||||
public static function addDriver(Drivers\SMSInterface $driver)
|
||||
{
|
||||
self::$custom_drivers[] = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of supported mail drivers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSupportedDrivers()
|
||||
{
|
||||
$result = array();
|
||||
foreach (Storage::readDirectory(__DIR__ . '/drivers/sms', false) as $filename)
|
||||
{
|
||||
$driver_name = substr($filename, 0, -4);
|
||||
$class_name = '\Rhymix\Framework\Drivers\SMS\\' . $driver_name;
|
||||
if ($class_name::isSupported())
|
||||
{
|
||||
$result[$driver_name] = array(
|
||||
'name' => $class_name::getName(),
|
||||
'required' => $class_name::getRequiredConfig(),
|
||||
'optional' => $class_name::getOptionalConfig(),
|
||||
'api_types' => $class_name::getAPITypes(),
|
||||
'api_spec' => $class_name::getAPISpec(),
|
||||
);
|
||||
}
|
||||
}
|
||||
foreach (self::$custom_drivers as $driver)
|
||||
{
|
||||
if ($driver->isSupported())
|
||||
{
|
||||
$result[strtolower(class_basename($driver))] = array(
|
||||
'name' => $driver->getName(),
|
||||
'required' => $driver->getRequiredConfig(),
|
||||
'optional' => $driver->getOptionalConfig(),
|
||||
'api_types' => $driver->getAPITypes(),
|
||||
'api_spec' => $class_name::getAPISpec(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ksort($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->driver = self::getDefaultDriver();
|
||||
$this->from = trim(preg_replace('/[^0-9]/', '', config('sms.default_from'))) ?: null;
|
||||
$this->allow_split_sms = (config('sms.allow_split.sms') !== false);
|
||||
$this->allow_split_lms = (config('sms.allow_split.lms') !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sender's phone number.
|
||||
*
|
||||
* @param string $number Phone number
|
||||
* @return bool
|
||||
*/
|
||||
public function setFrom($number)
|
||||
{
|
||||
$this->from = preg_replace('/[^0-9]/', '', $number);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sender's phone number.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a recipient.
|
||||
*
|
||||
* @param string $number Phone number
|
||||
* @param string $country Country code (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public function addTo($number, $country = 0)
|
||||
{
|
||||
$this->to[] = (object)array(
|
||||
'number' => preg_replace('/[^0-9]/', '', $number),
|
||||
'country' => intval(preg_replace('/[^0-9]/', '', $country)),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of recipients without country codes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRecipients()
|
||||
{
|
||||
return array_map(function($recipient) {
|
||||
return $recipient->number;
|
||||
}, $this->to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of recipients with country codes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRecipientsWithCountry()
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of recipients grouped by country code.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRecipientsGroupedByCountry()
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->to as $recipient)
|
||||
{
|
||||
$result[$recipient->country][] = $recipient->number;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the subject.
|
||||
*
|
||||
* @param string $subject
|
||||
* @return bool
|
||||
*/
|
||||
public function setSubject($subject)
|
||||
{
|
||||
$this->subject = utf8_trim(utf8_clean($subject));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subject.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the subject (alias to setSubject).
|
||||
*
|
||||
* @param string $subject
|
||||
* @return bool
|
||||
*/
|
||||
public function setTitle($subject)
|
||||
{
|
||||
return $this->setSubject($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subject (alias to getSubject).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content.
|
||||
*
|
||||
* @param string $content
|
||||
* @return bool
|
||||
*/
|
||||
public function setBody($content)
|
||||
{
|
||||
$this->content = utf8_trim(utf8_clean($content));
|
||||
$this->content = strtr($this->content, array("\r\n" => "\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content (alias to setBody).
|
||||
*
|
||||
* @param string $content
|
||||
* @return void
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
return $this->setBody($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content (alias to getBody).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a file.
|
||||
*
|
||||
* @param string $local_filename
|
||||
* @param string $display_filename (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public function attach($local_filename, $display_filename = null)
|
||||
{
|
||||
if ($display_filename === null)
|
||||
{
|
||||
$display_filename = basename($local_filename);
|
||||
}
|
||||
if (!Storage::exists($local_filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->attachments[] = (object)array(
|
||||
'type' => 'mms',
|
||||
'local_filename' => $local_filename,
|
||||
'display_filename' => $display_filename,
|
||||
'cid' => null,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of attachments to this message.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttachments()
|
||||
{
|
||||
return $this->attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an extra variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function setExtraVar($key, $value)
|
||||
{
|
||||
$this->extra_vars[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an extra variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getExtraVar($key)
|
||||
{
|
||||
return isset($this->extra_vars[$key]) ? $this->extra_vars[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all extra variables.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getExtraVars()
|
||||
{
|
||||
return $this->extra_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all extra variables.
|
||||
*
|
||||
* @param array $vars
|
||||
* @return void
|
||||
*/
|
||||
public function setExtraVars(array $vars)
|
||||
{
|
||||
$this->extra_vars = $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay sending the message.
|
||||
*
|
||||
* Delays (in seconds) less than 1 year will be treated as relative to the
|
||||
* current time. Greater values will be interpreted as a Unix timestamp.
|
||||
*
|
||||
* This feature may not be implemented by all drivers.
|
||||
*
|
||||
* @param int $when Unix timestamp
|
||||
* @return bool
|
||||
*/
|
||||
public function setDelay($when)
|
||||
{
|
||||
if ($when <= (86400 * 365))
|
||||
{
|
||||
$when = time() + $when;
|
||||
}
|
||||
if ($when <= time())
|
||||
{
|
||||
$when = 0;
|
||||
}
|
||||
|
||||
$this->delay_timestamp = intval($when);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Unix timestamp of when to send the message.
|
||||
*
|
||||
* This method always returns a Unix timestamp, even if the original value
|
||||
* was given as a relative delay.
|
||||
*
|
||||
* This feature may not be implemented by all drivers.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDelay()
|
||||
{
|
||||
return $this->delay_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force this message to use SMS (not LMS or MMS).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forceSMS()
|
||||
{
|
||||
$this->force_sms = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unforce this message to use SMS (not LMS or MMS).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unforceSMS()
|
||||
{
|
||||
$this->force_sms = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this message is forced to use SMS.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isForceSMS()
|
||||
{
|
||||
return $this->force_sms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow this message to be split into multiple SMS.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function allowSplitSMS()
|
||||
{
|
||||
$this->allow_split_sms = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow this message to be split into multiple LMS.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function allowSplitLMS()
|
||||
{
|
||||
$this->allow_split_lms = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow this message to be split into multiple SMS.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disallowSplitSMS()
|
||||
{
|
||||
$this->allow_split_sms = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow this message to be split into multiple LMS.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disallowSplitLMS()
|
||||
{
|
||||
$this->allow_split_lms = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if splitting this message into multiple SMS is allowed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSplitSMSAllowed()
|
||||
{
|
||||
return $this->allow_split_sms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if splitting this message into multiple LMS is allowed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSplitLMSAllowed()
|
||||
{
|
||||
return $this->allow_split_lms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
// Get caller information.
|
||||
$backtrace = debug_backtrace(0);
|
||||
if(count($backtrace) && isset($backtrace[0]['file']))
|
||||
{
|
||||
$this->caller = $backtrace[0]['file'] . ($backtrace[0]['line'] ? (' line ' . $backtrace[0]['line']) : '');
|
||||
}
|
||||
|
||||
$output = \ModuleHandler::triggerCall('sms.send', 'before', $this);
|
||||
if(!$output->toBool())
|
||||
{
|
||||
$this->errors[] = $output->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config('sms.default_force') && config('sms.default_from'))
|
||||
{
|
||||
$this->setFrom(config('sms.default_from'));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if ($this->driver)
|
||||
{
|
||||
$messages = $this->_formatSpec($this->driver->getAPISpec());
|
||||
if (count($messages))
|
||||
{
|
||||
$this->sent = $this->driver->send($messages, $this) ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors[] = 'No recipients selected';
|
||||
$this->sent = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors[] = 'No SMS driver selected';
|
||||
$this->sent = false;
|
||||
}
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
$this->errors[] = class_basename($e) . ': ' . $e->getMessage();
|
||||
$this->sent = false;
|
||||
}
|
||||
|
||||
$output = \ModuleHandler::triggerCall('sms.send', 'after', $this);
|
||||
if(!$output->toBool())
|
||||
{
|
||||
$this->errors[] = $output->getMessage();
|
||||
}
|
||||
|
||||
return $this->sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the message was sent.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSent()
|
||||
{
|
||||
return $this->sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get caller information.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCaller()
|
||||
{
|
||||
return $this->caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error message.
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function addError($message)
|
||||
{
|
||||
$this->errors[] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the current message according to an API spec.
|
||||
*
|
||||
* @param array $spec API specifications
|
||||
* @return array
|
||||
*/
|
||||
protected function _formatSpec(array $spec)
|
||||
{
|
||||
// Initialize the return array.
|
||||
$result = array();
|
||||
|
||||
// Get the list of recipients.
|
||||
$recipients = $this->getRecipientsGroupedByCountry();
|
||||
|
||||
// Group the recipients by country code.
|
||||
foreach ($recipients as $country_code => $country_recipients)
|
||||
{
|
||||
// Merge recipients into groups.
|
||||
if ($spec['max_recipients'] > 1)
|
||||
{
|
||||
$country_recipients = array_chunk($country_recipients, $spec['max_recipients']);
|
||||
}
|
||||
|
||||
// Send to each set of merged recipients.
|
||||
foreach ($country_recipients as $recipient_numbers)
|
||||
{
|
||||
// Populate the item.
|
||||
$item = new \stdClass;
|
||||
$item->type = 'SMS';
|
||||
$item->from = $this->getFrom();
|
||||
$item->to = $recipient_numbers;
|
||||
$item->country = $country_code;
|
||||
if ($spec['delay_supported'])
|
||||
{
|
||||
$item->delay = $this->getDelay() ?: 0;
|
||||
}
|
||||
|
||||
// Get message content.
|
||||
$subject = $this->getSubject();
|
||||
$content = $this->getContent();
|
||||
$attachments = $attachments = $this->getAttachments();
|
||||
|
||||
// Determine the message type.
|
||||
if (!$this->isForceSMS() && ($spec['lms_supported'] || $spec['mms_supported']))
|
||||
{
|
||||
// Check attachments, subject, and message length.
|
||||
if ($spec['mms_supported'] && count($attachments))
|
||||
{
|
||||
$item->type = 'MMS';
|
||||
}
|
||||
elseif ($spec['lms_supported'] && $subject)
|
||||
{
|
||||
$item->subject = $subject;
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
elseif ($spec['lms_supported'] && $this->_getLengthInCharset($content, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'])
|
||||
{
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->type = 'SMS';
|
||||
}
|
||||
|
||||
// Check the country code.
|
||||
if ($item->type === 'MMS' && $country_code && is_array($spec['mms_supported_country_codes']) && !in_array($country_code, $spec['mms_supported_country_codes']))
|
||||
{
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
if ($item->type === 'LMS' && $country_code && is_array($spec['lms_supported_country_codes']) && !in_array($country_code, $spec['lms_supported_country_codes']))
|
||||
{
|
||||
$item->type = 'SMS';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove subject and attachments if the message type is SMS.
|
||||
if ($item->type === 'SMS')
|
||||
{
|
||||
if ($subject)
|
||||
{
|
||||
$content = $subject . "\n" . $content;
|
||||
unset($item->subject);
|
||||
}
|
||||
$attachments = array();
|
||||
}
|
||||
|
||||
// If message subject is not supported, prepend it to the content instead.
|
||||
if ($item->subject && !$spec[strtolower($item->type) . '_subject_supported'])
|
||||
{
|
||||
$content = $item->subject . "\n" . $content;
|
||||
unset($item->subject);
|
||||
}
|
||||
elseif ($item->subject && $this->_getLengthInCharset($item->subject, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_subject_max_length'])
|
||||
{
|
||||
$subject_parts = $this->_splitString($item->subject, $spec[strtolower($item->type) . '_subject_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
|
||||
$subject_short = array_shift($subject_parts);
|
||||
$subject_remainder = utf8_trim(substr($item->subject, strlen($subject_short)));
|
||||
$item->subject = $subject_short;
|
||||
$content = $subject_remainder . "\n" . $content;
|
||||
}
|
||||
|
||||
// Split the content if necessary.
|
||||
if (($item->type === 'SMS' && $this->allow_split_sms) || ($item->type !== 'SMS' && $this->allow_split_lms))
|
||||
{
|
||||
if ($this->_getLengthInCharset($content, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_max_length'])
|
||||
{
|
||||
$content_parts = $this->_splitString($content, $spec[strtolower($item->type) . '_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$content_parts = array($content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$content_parts = array($content);
|
||||
}
|
||||
|
||||
// Generate a message for each part of the content and attachments.
|
||||
$message_count = max(count($content_parts), count($attachments));
|
||||
$last_content = $item->type;
|
||||
for ($i = 1; $i <= $message_count; $i++)
|
||||
{
|
||||
// Get the message content.
|
||||
if ($content_part = array_shift($content_parts))
|
||||
{
|
||||
$item->content = $last_content = $content_part;
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->content = $last_content ?: $item->type;
|
||||
}
|
||||
|
||||
// Get the attachment.
|
||||
if ($attachment = array_shift($attachments))
|
||||
{
|
||||
$item->image = $attachment->local_filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->image);
|
||||
}
|
||||
|
||||
// Clone the item to make a part.
|
||||
$cloneitem = clone $item;
|
||||
|
||||
// Determine the best message type for this part.
|
||||
if ($cloneitem->type !== 'SMS' && !$cloneitem->subject)
|
||||
{
|
||||
$cloneitem->type = $attachment ? 'MMS' : ($this->_getLengthInCharset($content_part, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'] ? 'LMS' : 'SMS');
|
||||
}
|
||||
|
||||
// Add the cloned part to the result array.
|
||||
$result[] = $cloneitem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the message parts.
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of a string in another character set.
|
||||
*
|
||||
* @param string $str String to measure
|
||||
* @param string $charset Character set to measure length
|
||||
* @return
|
||||
*/
|
||||
protected function _getLengthInCharset($str, $charset)
|
||||
{
|
||||
$str = @iconv('UTF-8', $charset . '//IGNORE', $str);
|
||||
return strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into several short chunks.
|
||||
*
|
||||
* @param string $str String to split
|
||||
* @param int $max_length Maximum length of a chunk
|
||||
* @param string $charset Character set to measure length
|
||||
* @return array
|
||||
*/
|
||||
protected function _splitString($str, $max_length, $charset)
|
||||
{
|
||||
$str = utf8_trim(utf8_normalize_spaces($str, true));
|
||||
$chars = preg_split('//u', $str, -1, \PREG_SPLIT_NO_EMPTY);
|
||||
$result = array();
|
||||
$current_entry = '';
|
||||
$current_length = 0;
|
||||
|
||||
foreach ($chars as $char)
|
||||
{
|
||||
$char_length = strlen(@iconv('UTF-8', $charset . '//IGNORE', $char));
|
||||
if (($current_length + $char_length > $max_length) || ($current_length + $char_length > $max_length - 7 && ctype_space($char)))
|
||||
{
|
||||
$result[] = trim($current_entry);
|
||||
$current_entry = $char;
|
||||
$current_length = $char_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
$current_entry .= $char;
|
||||
$current_length += $char_length;
|
||||
}
|
||||
}
|
||||
|
||||
if ($current_entry !== '')
|
||||
{
|
||||
$result[] = trim($current_entry);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -714,6 +714,36 @@ class Storage
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a directory only if it is empty.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $delete_empty_parents (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteEmptyDirectory($dirname, $delete_empty_parents = false)
|
||||
{
|
||||
$dirname = rtrim($dirname, '/\\');
|
||||
if (!self::isDirectory($dirname) || !self::isEmptyDirectory($dirname))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @rmdir($dirname);
|
||||
if (!$result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($delete_empty_parents)
|
||||
{
|
||||
self::deleteEmptyDirectory(dirname($dirname), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current umask.
|
||||
*
|
||||
|
|
@ -742,7 +772,7 @@ class Storage
|
|||
/**
|
||||
* Determine the best umask for this installation of Rhymix.
|
||||
*
|
||||
* @return int
|
||||
* @return string
|
||||
*/
|
||||
public static function recommendUmask()
|
||||
{
|
||||
|
|
@ -756,27 +786,7 @@ class Storage
|
|||
$file_uid = fileowner(__FILE__);
|
||||
|
||||
// Get the UID of the current PHP process.
|
||||
if (function_exists('posix_geteuid'))
|
||||
{
|
||||
$php_uid = posix_geteuid();
|
||||
}
|
||||
else
|
||||
{
|
||||
$testfile = \RX_BASEDIR . 'files/cache/uidcheck';
|
||||
if (self::exists($testfile))
|
||||
{
|
||||
self::delete($testfile);
|
||||
}
|
||||
if (self::write($testfile, 'TEST'))
|
||||
{
|
||||
$php_uid = fileowner($testfile);
|
||||
self::delete($testfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$php_uid = -1;
|
||||
}
|
||||
}
|
||||
$php_uid = self::getServerUID();
|
||||
|
||||
// If both UIDs are the same, set the umask to 0022.
|
||||
if ($file_uid == $php_uid)
|
||||
|
|
@ -790,4 +800,36 @@ class Storage
|
|||
return '0000';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UID of the server process.
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public static function getServerUID()
|
||||
{
|
||||
if (function_exists('posix_geteuid'))
|
||||
{
|
||||
return posix_geteuid();
|
||||
}
|
||||
else
|
||||
{
|
||||
$testfile = \RX_BASEDIR . 'files/cache/uidcheck_' . time();
|
||||
if (self::exists($testfile))
|
||||
{
|
||||
self::delete($testfile);
|
||||
}
|
||||
|
||||
if (self::write($testfile, 'TEST'))
|
||||
{
|
||||
$uid = fileowner($testfile);
|
||||
self::delete($testfile);
|
||||
return $uid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue