Untitled
unknown
php
3 years ago
50 kB
19
Indexable
<?php
/**
* This file implements saas root helper.
*
* @author TurnSaas
* @since 2019
*/
defined('BASEPATH') or exit('No direct script access allowed');
/**
* root functions for the module
* no get_instance() or any other core function of codeigniter as app not fully loaded.
*
* All core function for boostraping are defined here along side important constant.
* This file is like soul of this module; saas
*/
use PHPSQLParser\PHPSQLCreator;
use PHPSQLParser\PHPSQLParser;
require_once(SAAS_MODULE_PATH . '/libraries/Mysqldump.php');
if (!function_exists('dd')) {
//debugging helper function
function dd()
{
var_dump(func_get_args());
exit();
}
}
if (!function_exists('saas_is_demo_mode')) {
function saas_is_demo_mode()
{
return defined('SAAS_IS_DEMO_MODE');
}
}
if (!function_exists('saas_clean')) {
/**
* trim string of html
*
* @param string $string The string to clean
*
* @return string cleaned string
*/
function saas_clean($string)
{
return htmlspecialchars(strip_tags(trim($string)));
}
}
if (!function_exists('saas_clear')) {
/**
* clear session caching
*
* @param array $data indexes to remove
*/
function saas_clear_session($data = [])
{
$data = array_merge(['reserved_slugs', 'saas_get_setting()', 'saas_get_all_settings()'], $data);
foreach ($data as $value) {
$key = saas_slug($value);
if (isset($_SESSION[$key])) {
unset($_SESSION[$key]);
}
}
}
}
if (!function_exists('saas_is_default_tenant')) {
/**
* check if current request is from default app instance
*
* @return bool
*/
function saas_is_default_tenant()
{
$subdomain = @explode('.', saas_http_host())[0]; //we only need to check the subdomain.
return !saas_is_cname() && $subdomain == SAAS_DEFAULT_TENANT;
}
}
if (!function_exists('saas_is_saas_admin_tenant')) {
/**
* determin is tenant is created and own by SAAS admin
*
* @param object $tenant The tenant
*
* @return bool true | false
*/
function saas_is_saas_admin_tenant($tenant)
{
return $tenant->role && $tenant->role == SAAS_ADMIN_ROLE;
}
}
if (!function_exists('saas_is_tenant')) {
/**
* check is request is instance request or saas module
*
* @return bool
*/
function saas_is_tenant()
{
return !empty(saas_tenant_slug()) || saas_is_cname();
}
}
if (!function_exists('saas_is_cname')) {
/**
* Function to determine is request is cname (masked) or not
*
* @return bool
*/
function saas_is_cname()
{
$currentHost = saas_http_host();
$host = saas_get_host();
if (!$host)
return false;
//port and local host cant be cnamed.
if (stripos($currentHost, ':'))
return false;
if (in_array($currentHost, ['localhost', '127.0.0.1', '::1']))
return false;
return stripos($currentHost, $host) === false;
}
/**
* Function to determine is request is cname (masked) or not
*
* @param string $host The know host
* @param string $currentHost The current http host to check against
* @return void
*/
function saas_is_cname_V2($host = '', $currentHost = '')
{
$currentHost = empty($currentHost) ? saas_http_host() : $currentHost;
$host = empty($host) ? saas_get_host() : $host;
if (!$host || empty($currentHost) || empty($host) || $host == $currentHost)
return false;
//port and local host cant be cnamed.
if (stripos($currentHost, ':'))
return false;
//localhost
if (in_array($currentHost, ['localhost', '127.0.0.1', '::1']))
return false;
return stripos($currentHost, $host) === false;
}
}
if (!function_exists('saas_tenant_mode')) {
/**
* Get database scheme for a given instance/tenant
*
* @param object $tenant The tenant
*
* @return string $mode
*/
function saas_tenant_mode($tenant)
{
$mode = $tenant->saas_mode;
if (empty($mode)) {
$mode = saas_get_setting('saas_mode');
}
return $mode;
}
}
if (!function_exists('saas_db_slug')) {
/**
* This method give the db name for active saas_user
*
* Add prefix pattern to a database name string
* Makes pattern for recognizing saas module databases
*
* @param string $slug The slug to add prefix
*
* @return string prefixed string
*/
function saas_db_slug($slug = '')
{
$prefix = trim(saas_get_setting('database_name_prefix', true));
if (!empty($prefix)) $prefix = $prefix . '_';
return $prefix . SAAS_MODULE_NAME . '_db_' . $slug;
}
}
if (!function_exists('saas_slug')) {
/**
* This function localize any string to saas string
*
* @param string $slug The slug
*
* @return string localized string
*/
function saas_slug($slug = '')
{
return SAAS_MODULE_NAME . '_' . $slug;
}
}
if (!function_exists('saas_reserved_slugs')) {
/**
* Function to get list of reserved slugs for instance.
* It fetches from database and constant defined reserve sludgs
*
* @param bool $strict to load from cache or not
*
* @return array list of reserved slugs
*/
function saas_reserved_slugs($strict = true)
{
$s_key = saas_slug('reserved_slugs');
if (!$strict && isset($_SESSION[$s_key])) {
return $_SESSION[$s_key];
}
$reservoir = SAAS_RESERVED_SLUGS; //array
$reserved_slugs_extra = saas_get_setting('reserved_slugs', true);
if (isset($results[0])) {
$reservoir = array_merge($reservoir, explode(',', $reserved_slugs_extra));
}
$_SESSION[$s_key] = $reservoir;
return $reservoir;
}
}
if (!function_exists('saas_reserved_routes')) {
/**
* List of reserved routes menu name
*
* @return array
*/
function saas_reserved_routes()
{
return ['modules'];
}
}
if (!function_exists('saas_reserved_classes')) {
/**
* Function to return list prohibited classess.
* Any class here (controler) wont be accessible by tenant
*/
function saas_reserved_classes()
{
return ['mods', 'backup'];
}
}
if (!function_exists('saas_require_setup')) {
/**
* Function to determin if saas installation requires further action or not
*/
function saas_require_setup()
{
if (!defined('SAAS_ENCRYPTION_KEY')) {
return true;
}
if (SAAS_ENCRYPTION_KEY == 'SAAS_ENCRYPTION_KEY_DEFAULT') { //using default key
return true;
}
$config = saas_get_root_config();
if (!$config || !isset($config->host)) { //config not set at all , likely module not yet activated
return true;
}
if ($config->saas_admin_id < 1 || $config->saas_landlord_id < 1) {
return true;
}
$db_webhook_enabled = stripos(file_get_contents(SAAS_SYSTEM_DB_BDRIVER), 'saas_db_query') !== false;
if (!$db_webhook_enabled) {
return true;
}
return false;
}
}
if (!function_exists('saas_tenant')) {
/**
* Function to get active instance details and tenant
*
* @return Mixed
*/
function saas_tenant($clean = false)
{
$currentHost = saas_clean(saas_http_host());
$cname = saas_is_cname();
$slug = $cname ? $currentHost : explode(".", $currentHost)[0];
if (empty($currentHost)) {
return null;
}
$s_key = saas_slug($slug);
if (!$clean && isset($_SESSION[$s_key])) {
return $_SESSION[$s_key];
}
if (!empty($slug)) { //check for reserved slugs
$reserved = saas_reserved_slugs();
if (in_array($slug, $reserved)) {
$slug = '';
}
}
$slug = saas_clean($slug);
if (empty($slug)) {
return null;
}
$join = " LEFT JOIN " . saas_db_prefix('users') . " ON " . saas_db_prefix('companies.user_id') . '=' . saas_db_prefix('users.id') . " LEFT JOIN " . saas_db_prefix('subscriptions') . " ON " . saas_db_prefix('users.id') . '=' . saas_db_prefix('subscriptions.user_id') . " LEFT JOIN " . saas_db_prefix('package') . " ON " . saas_db_prefix('package.id') . '=' . saas_db_prefix('subscriptions.package_id');
$select = "SELECT " . saas_db_prefix('companies.*') . ", " . saas_db_prefix('subscriptions.status as sub_status') . ",current_period_start,current_period_end,trial_start,trial_end, modules,role from " . saas_db_prefix('companies');
$slug_selector = saas_db_prefix('companies.slug');
if ($cname) {
$slug_selector = saas_db_prefix('companies.domain');
}
$query = $select . $join . " WHERE $slug_selector='$slug' LIMIT 1";
$tenant = saas_raw_query($query, [], true);
if ($tenant) {
$tenant = $tenant[0];
if ($tenant->dsn) {
$tenant->dsn = saas_decrypt_data($tenant->dsn);
}
if (in_array($tenant->sub_status, [SAAS_STATUS_ACTIVE, SAAS_STATUS_TRIAL]) || saas_is_saas_admin_tenant($tenant)) {
$_SESSION[$s_key] = $tenant;
return $_SESSION[$s_key];
}
}
return null;
}
}
if (!function_exists('saas_tenant_slug')) {
/**
* Function to get active tenant slug
*
* @return string slug|null
*/
function saas_tenant_slug()
{
if (saas_is_default_tenant()) {
return SAAS_DEFAULT_TENANT;
} else {
$tenant = saas_tenant();
if (isset($tenant->slug)) {
return $tenant->slug;
}
}
return '';
}
}
if (!function_exists('saas_is_active')) {
/**
* Check if saas module is active from local db
*
* @return <type> ( description_of_the_return_value )
*/
function saas_is_active()
{
return saas_get_root_config('status') == "1" && !SAAS_REQUIRE_SETUP;
}
}
if (!function_exists('saas_tenant_url')) {
/**
* Get base url for a given slug
*
* @param string $slug The slug
*
* @return string $slug.base host
*/
function saas_tenant_host($slug)
{
return saas_clean($slug) . '.' . saas_get_host();
}
}
if (!function_exists('saas_show_tenant_error')) {
/**
* Function for rendering middlewares error
*
* @param string $heading The heading
* @param string $message The message
* @param string $template The template
*/
function saas_show_tenant_error($heading, $message, $error_code = 403, $template = 'general')
{
$params = [
'message' => $message,
'heading' => $heading,
'template' => $template,
'error_code' => $error_code,
];
$url = saas_prep_url(saas_get_host() . '/error');
$req = saas_http_request($url, ['data' => $params, 'method' => 'POST']);
if ($url && !empty($req['response']))
echo $req['response'];
else
header("Location: $url");
exit();
}
}
if (!function_exists('saas_db_query_demo_middleware')) {
function saas_db_query_demo_middleware()
{
if (SAAS_IS_READ_ONLY) {
$tracer = debug_backtrace()[1];
$caller = $tracer['function'];
$sql = '';
if ($caller == "saas_raw_query") {
$sql = $tracer['args'][0];
}
if ($caller == "saas_db_query") {
$sql = $tracer['args'][0];
}
if (stripos($sql, 'UPDATE `tblstaff` SET `last_activity`') !== false) {
$sql = '';
}
if (
stripos($sql, 'UPDATE `tblstaff` SET `last_ip` =') !== false ||
stripos($sql, 'INSERT INTO `tblactivity_log` (`description`, `date`, `staffid`) VALUES (') !== false ||
stripos($sql, "WHERE `name` = 'identification_key'") !== false ||
stripos($sql, "INSERT INTO `tbloptions`") !== false ||
stripos($sql, "UPDATE `tblcontacts` SET `last_ip`") !== false
) {
$sql = '';
}
if (!empty($sql) && saas_is_write_query($sql)) {
show_error('The action you have requested is not allowed in demo version. For full demo access, reachout to us on [email protected]', 403, 'Read Only Mode');
}
}
}
}
/*****************************************************************************************************/
/************************************* FILE AND WRITING HELPERS *******************************************/
/*****************************************************************************************************/
if (!function_exists('saas_secure_dir')) {
/**
* Ensure a directory is not accessible publicly.
*
* @param string $dir The directory path
*/
function saas_secure_dir($dir)
{
if (is_dir($dir)) {
$base = $dir . DIRECTORY_SEPARATOR;
$files = ['.htaccess', 'index.html'];
foreach ($files as $file) {
$to = $base . $file;
if (!file_exists($to))
copy(APPPATH . $file, $to);
}
}
}
}
if (!function_exists('saas_mkdir')) {
function saas_mkdir($dir, $mode = 0777, $recursive = false)
{
mkdir($dir, $mode, $recursive);
saas_secure_dir($dir);
}
}
if (!function_exists('saas_make_upload_dir')) {
/**
* create upload dir saas module and or slug
*
* @param string $slug The slug
*/
function saas_make_upload_dir($slug = '')
{
$dir = SAAS_UPLOAD_DIR;
$dir_thumb = $dir . DIRECTORY_SEPARATOR . 'thumbnail';
$dir_med = $dir . DIRECTORY_SEPARATOR . 'medium';
$dir_big = $dir . DIRECTORY_SEPARATOR . 'big';
$dir_backup = SAAS_BACKUP_DIR;
$mode = 0777;
if (!is_dir($dir)) {
//saas_rcopy(SAAS_MODULE_PATH . '/templates/uploads/saas', SAAS_UPLOAD_DIR);
saas_mkdir($dir, 0777, true);
}
if (!is_dir($dir)) {
saas_mkdir($dir, $mode, true);
}
if (!is_dir($dir_thumb)) {
saas_mkdir($dir_thumb, $mode, true);
}
if (!is_dir($dir_med)) {
saas_mkdir($dir_med, $mode, true);
}
if (!is_dir($dir_big)) {
saas_mkdir($dir_big, $mode, true);
}
if (!is_dir($dir_backup)) {
saas_mkdir($dir_backup, $mode, true);
}
if ($slug != '') {
$dir_backup_user = $dir_backup . DIRECTORY_SEPARATOR . $slug;
if (!is_dir($dir_backup_user)) {
saas_mkdir($dir_backup_user, $mode, true);
}
}
}
}
if (!function_exists('saas_remove_upload_dir')) {
/**
* remove upload dir of saas or instance
*
* @param string $slug The slug
*/
function saas_remove_upload_dir($slug = '')
{
$target = SAAS_UPLOAD_DIR;
if ($slug) {
$target = $target . DIRECTORY_SEPARATOR . $slug;
}
saas_remove_dir($target);
}
}
if (!function_exists('saas_remove_dir')) {
/**
* remove directory recursively
*
* @param string $target The directory to remove
*/
function saas_remove_dir($target)
{
try {
if (is_dir($target)) {
$dir = new RecursiveDirectoryIterator($target, RecursiveDirectoryIterator::SKIP_DOTS);
foreach (new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST) as $filename => $file) {
if (is_file($filename)) {
unlink($filename);
} else {
saas_remove_dir($filename);
}
}
rmdir($target); // Now remove target folder
}
} catch (\Exception $e) {
}
}
}
if (!function_exists('saas_rcopy')) {
// copies files and non-empty directories
function saas_rcopy($src, $dst)
{
if (is_dir($src)) {
if (!file_exists($dst)) {
saas_mkdir($dst);
}
$files = scandir($src);
foreach ($files as $file) {
if ($file != "." && $file != "..") {
saas_rcopy("$src/$file", "$dst/$file");
}
}
} elseif (file_exists($src)) {
copy($src, $dst);
}
}
}
if (!function_exists('saas_log')) {
/**
* function to log error to file
*
* @param mixed $message The message
*/
function saas_log($message)
{
$message = is_string($message) ? [PHP_EOL, $message] : $message;
$message[] = PHP_EOL;
return file_put_contents(SAAS_DB_LOG_FILE, $message, FILE_APPEND);
}
}
/*****************************************************************************************************/
/************************************* DATABASE HELPERS *******************************************/
/*****************************************************************************************************/
if (!function_exists('saas_raw_query')) {
/**
* Run mysql query independent on ci.
* Powerful function to run sql during bootstraping
* It take connection details or genarete and run given $query sting
* This function is useful for inter-racting with database when CI is not ready
*
* @param array $conn The connection array('h'=>'','u'=>'','p'=>'','d'=>'');
* @param string|string[] $query The query
* @param bool $return The return
* @param bool $atomic If to run in transaction or not
*
* @return array query result | voide
*/
function saas_raw_query($query, $conn = [], $return = false, $atomic = true)
{
if (empty($conn)) {
$conn = array('host' => APP_DB_HOSTNAME, 'user' => APP_DB_USERNAME, 'password' => APP_DB_PASSWORD, 'dbname' => APP_DB_NAME_DEFAULT);
}
if (is_string($conn)) { //conn is dsn sting
$user = $pass = null;
$conn = saas_parse_dsn($conn);
}
$dsn = saas_dsn_to_string($conn, false);
$user = $conn['user'];
$pass = $conn['password'];
$pdo = new Mysqldump(['dsn' => $dsn], $user, $pass);
saas_db_query_demo_middleware($query);
$is_multi_query = is_array($query);
$resultList = array();
try {
$queries = $is_multi_query ? $query : [$query];
if ($atomic)
$pdo->runQuery("START TRANSACTION");
foreach ($queries as $q) {
$stmt = $pdo->runQuery($q);
$results = array();
if ($return && $stmt) {
while ($row = $stmt->fetchObject()) {
$results[] = $row;
}
}
$resultList[] = $results;
}
if ($atomic)
$pdo->runQuery("COMMIT");
} catch (\PDOException $e) {
saas_log("Database Error: " . $e->getMessage());
return null;
} catch (\Exception $e) {
saas_log($e->getMessage());
return null;
}
return (array)($is_multi_query ? $resultList : $resultList[0]);
}
}
if (!function_exists('saas_db_query')) {
/**
* Function to run database sql query with current instance or tenant awareness
*
* @param string $sql The sql
*
* @return string same|modified sql query
*/
function saas_db_query($sql)
{
if (!saas_is_active()) {
return $sql;
}
saas_db_query_demo_middleware();
$slug = saas_tenant_slug();
if (!$slug) {
//default saas panel.
return $sql;
}
return saas_simple_query($slug, $sql);
}
}
if (!function_exists('saas_simple_query')) {
/**
*
* Function to parse all tenant sql query.
* The function restrict tenant by adding the
* tenant id where clause in read and update statements.
* SAAS_DEFAULT_COLUMN column is also supplied with tenant id during new insertion
*
* This function rely on php-sql-parser https://github.com/greenlion/PHP-SQL-Parser
*
* @param string $tenant The tenant slug
* @param string $sql The sql
*
* @throws \Exception (description)
*
* @return PHPSQLCreator The phpsql creator.
*/
function saas_simple_query($slug, $sql, $return_parsed = false, $parsed = false)
{
if (!$parsed) {
$parser = new PHPSQLParser($sql);
$parsed = $parser->parsed;
}
$key = strtoupper(key($parsed));
if (in_array($key, ['SHOW']) || strtoupper(trim($sql)) == "SELECT FOUND_ROWS()") {
return $sql;
}
$will_change_db_struct = in_array($key, ['CREATE', 'BRACKET', 'TRUNCATE', 'RENAME', 'DROP', 'ALTER']);
if (saas_is_default_tenant()) {
if ($will_change_db_struct) {
//insert to db for sync with others (migration)
//send service to pick the query and run on all other db instance.
if ($key == "RENAME") {
saas_add_migration($sql, 0);
}
return $sql; //allow for installing new modules from master instance.
}
}
if ($will_change_db_struct) {
//deny, unsupported, tenant shouldnt be able to do any of this query
throw new \Exception("Unsupported query for tenant: $sql", 1);
}
if (stripos($sql, 'GET_LOCK') || stripos($sql, 'IS_FREE_LOCK') || stripos($sql, 'RELEASE_LOCK')) {
return $sql;
}
if (stripos($sql, 'set session sql_mode') !== false || str_starts_with(strtolower($sql), 'set sql_mode = '))
return $sql;
//var_dump($parsed[$key][0]['base_expr']);
$table = null;
$filterColumn = SAAS_TENANT_COLUMN;
$slug_string = "'" . $slug . "'";
$column_string = "`" . SAAS_TENANT_COLUMN . "`";
try {
//if(!isset($parsed[$key][1]['table']))
//check for [expr_type] ='table', ['alias']['name']
//dd($parsed['FROM']);
$canHasSubtree = false;
$table = isset($parsed['FROM'][0]['table']) ? ($parsed['FROM'][0]['alias']['name'] ?? $parsed['FROM'][0]['table']) : (isset($parsed[$key][0]['table']) ? ($parsed[$key][0]['alias']['name'] ?? $parsed[$key][0]['table']) : (isset($parsed[$key][1]['table']) ? ($parsed[$key][1]['alias']['name'] ?? $parsed[$key][1]['table']) : ''
)
);
$db_prefix = db_prefix();
if ($table) {
$filterColumn = $table . '.' . $filterColumn;
} else {
$canHasSubtree = true;
$searchKey = ($key == 'SELECT' || isset($parsed['FROM'])) ? 'FROM' : $key;
$searchKeyArray = saas_find_key($parsed, $searchKey, 'any');
if (is_array($searchKeyArray)) {
$table = saas_find_key($searchKeyArray, 'table');
}
if (!$table) {
$msg = "\n Table Extraction: Error extrable table name from this query: ";
saas_log([$msg, $sql]);
throw new \Error($msg . $sql); //throw error
/*if ($return_parsed) {
return $parsed;
}
return $sql;*/
} else {
$filterColumn = $table . '.' . $filterColumn;
}
}
$globalWhereAnd = [
['expr_type' => 'operator', 'base_expr' => 'and']
];
$globalWhere = [
[
'expr_type' => 'colref',
'base_expr' => $filterColumn,
],
[
'expr_type' => 'operator',
'base_expr' => '=',
],
[
'expr_type' => 'const',
'base_expr' => $slug_string,
]
];
if ($key == 'SELECT') {
//parse subtrees
$hasSubtree = false;
if ($canHasSubtree) {
$hasSubtree = saas_find_key($parsed['FROM'], 'expr_type', 'subquery');
if ($hasSubtree) {
foreach ($parsed['FROM'] as $fIndex => $fromL) {
if ($fromL['expr_type'] == 'subquery') {
//parse subquery differently
$sub = saas_simple_query($slug, $fromL['base_expr'], true, $fromL['sub_tree']);
$parsed['FROM'][$fIndex]['sub_tree'] = $sub;
}
}
}
}
if (!$hasSubtree) {
if (isset($parsed['WHERE'])) {
$parsed['WHERE'] = array_merge($parsed['WHERE'], $globalWhereAnd, $globalWhere);
} else {
$parsed['WHERE'] = $globalWhere;
}
}
}
//queries to add where
if (in_array($key, ['DELETE', 'UPDATE'])) {
if (isset($parsed['WHERE'])) {
$parsed['WHERE'] = array_merge($parsed['WHERE'], $globalWhereAnd, $globalWhere);
} else {
$parsed['WHERE'] = $globalWhere;
}
}
if (in_array($key, ['INSERT'])) { //add tenant id
//add field
array_push($parsed[$key][2]['sub_tree'], ['expr_type' => 'colref', 'base_expr' => $column_string, 'no_quotes' => ["delim" => false, "parts" => [SAAS_TENANT_COLUMN]]]);
array_push($parsed["VALUES"][0]['data'], ['expr_type' => 'const', 'base_expr' => $slug_string, 'sub_tree' => false]);
}
if (in_array($key, ['UPDATE'])) { //update tenant_id , not necessary, i leave for reference purpose
//add field
array_push(
$parsed["SET"],
[
"expr_type" => "expression", "base_expr" => $column_string . '=' . $slug_string, "sub_tree" => [
['expr_type' => 'colref', 'base_expr' => $column_string, 'no_quotes' => ['delim' => false, 'parts' => [SAAS_TENANT_COLUMN]], 'sub_tree' => false],
['expr_type' => 'operator', 'base_expr' => '=', 'sub_tree' => false],
['expr_type' => 'const', 'base_expr' => $slug_string, 'sub_tree' => false]
]
]
);
}
//leads_email_integration table has been programmed by author to always filter by id=1
//We need to remove this where clause for case of multiple tenant in single db.
if (stripos($sql, 'leads_email_integration') > 0) {
$startIndex = -1;
foreach ($parsed["WHERE"] as $key => $value) {
if ($startIndex >= 0 && $value['expr_type'] == 'colref') { //meet with another colref, stop
break;
}
//detect start of id where clause
if ($value['expr_type'] == 'colref' && in_array($value['base_expr'], ["`id`", "'id'", "id"])) {
$startIndex = $key;
}
//remove every key until new colref is found
if ($startIndex >= 0) {
unset($parsed["WHERE"][$key]);
}
}
//limit to one
$parsed["LIMIT"] = ['offset' => '', 'rowcount' => 1];
}
//return tree is requrested for.
if ($return_parsed) { //return sub_tree
return $parsed;
}
//create new instance of sql creator.
$creator = new PHPSQLCreator($parsed);
$newSql = $creator->created; //parsed and compiled sql
if (SAAS_DB_DEBUG) {
file_put_contents(SAAS_DB_LOG_FILE, ["\n", $sql, "\n", $newSql], FILE_APPEND);
}
return $newSql;
} catch (\Exception $e) {
throw new \Exception($e->getMessage(), 1);
}
}
}
if (!function_exists('saas_is_write_query')) {
/**
* Function to determin if sql string is write or not
*
* @param string $query The query string
*
* @return bool true | false
*/
function saas_is_write_query($query)
{
return
stripos($query, 'DELETE FROM') !== false || stripos($query, 'INSERT INTO') !== false || (stripos($query, 'UPDATE') !== false && stripos($query, 'SET') !== false);
}
}
if (!function_exists('saas_find_key')) {
// return the value of a key in the supplied array
function saas_find_key($arr, $tracker, $return = 'string')
{
foreach ($arr as $key => $value) {
if ($key === $tracker) {
if ($return == 'string' && is_string($value)) {
return $value;
} else {
return $value;
}
}
if (is_array($value)) {
$ret = saas_find_key($value, $tracker, $return);
if ($ret) {
return $ret;
}
}
}
return false;
}
}
if (!function_exists('saas_select_dsn')) {
/**
* Shard mode function
* The function help in selecting shard pool memeber for an instance or tenant.
* It do this by checking provide databases and chose one with lowest population
* of instance or tenant
*
* @return string dsn
*/
function saas_select_dsn()
{
//get all pools
$pools = saas_get_db_pool();
if (empty((array)$pools)) {
return null;
}
$map = [];
foreach ($pools as $key => $pool) {
$map[$key] = $pool->population;
}
asort($map);
$selected = $pools->{array_keys($map)[0]};
return $selected->dsn;
}
}
if (!function_exists('saas_get_dsn')) {
/**
* Get shard pool details from dsn string
*
* @param string $dsnString The dsn string to lookup
*
* @return object DSN pool object | NULL
*/
function saas_get_dsn($dsnString)
{
//get all pools
$pools = saas_get_db_pool();
$selected = null;
$key = sha1($dsnString);
if (isset($pools->{$key})) {
$selected = $pools->{$key};
}
return $selected;
}
}
if (!function_exists('saas_get_tenant_db')) {
/**
* Get active tenant db name with this function
*
* @return string database name
*/
function saas_get_tenant_db()
{
$tenant = saas_tenant();
if (!$tenant || saas_is_default_tenant()) {
return APP_DB_NAME_DEFAULT;
}
$dsn = saas_tenant_dsn($tenant, ['dbname']);
if (!isset($dsn['dbname'])) {
return APP_DB_NAME_DEFAULT;
}
return $dsn['dbname'];
}
}
if (!function_exists('saas_tenant_dsn')) {
/**
* Get Database connectivity data for a given tenant.
*
* @param object $tenant The tenant
* @param bool $returnArray The return array or dsn string
* @param bool $is_template Indicates if template (default app) or not
*
* @return mixed array|string
*/
function saas_tenant_dsn($tenant, $returnArray = false, $is_template = false)
{
if (!$is_template && $tenant->dsn) { //or SAAS_SHARD_MODE
$dsn = $tenant->dsn;
//ensure not encryptyed
if (false === strpos($dsn, ":")) {
$dsn = saas_decrypt_data($dsn);
if (!$dsn || (false === strpos($dsn, ":")))
throw new Exception("Empty or Invalid DSN string");
}
if ($returnArray != false) {
return saas_parse_dsn($dsn, $returnArray);
}
return $dsn;
}
//prepare dsn
$default_mode = saas_get_setting('saas_mode');
$tenant_mode = $tenant->saas_mode;
if (!$tenant_mode) {
$tenant_mode = $default_mode;
}
$host = APP_DB_HOSTNAME;
$user = APP_DB_USERNAME;
$password = APP_DB_PASSWORD;
$name = APP_DB_NAME_DEFAULT;
$driver = 'mysql';
if (defined('APP_DB_DRIVER'))
$driver = APP_DB_DRIVER;
if (!$is_template && $tenant_mode == SAAS_SINGLE_MODE) {
if ($tenant->slug !== SAAS_DEFAULT_TENANT) {
$name = saas_db_slug($tenant->slug);
}
}
$dsn = ['driver' => $driver, 'host' => $host, 'dbname' => $name, 'user' => $user, 'password' => $password];
if ($returnArray) {
$dsn['dsn'] = saas_dsn_to_string($dsn, false);
return $dsn;
}
//order or the param keys (mapkeys) should be compatible with 'saas_parse_dsn'
return saas_dsn_to_string($dsn);
}
}
if (!function_exists('saas_parse_dsn')) {
/**
* Function for parsing dsn string to array
*
* Example dsn string: mysql:host=127.0.0.1;dbname=turn_db;user=turn_user;password=diewo;eg@j$l!;
*
* dsn should follow above pattern and should ends with ";".
*
* @param string $dsn The dsn string to parse
* @param array $returnKeys The return keys
*
* @throws Exception ensure drive is in dsn string
* @throws RuntimeException regex failure
*
* @return array
*/
function saas_parse_dsn($dsn, $returnKeys = [])
{
$indexes = ['host', 'name', 'user', 'password'];
$returnSet = is_array($returnKeys) && !empty($returnKeys);
if ($returnSet) {
$indexes = $returnKeys;
}
if (empty($dsn) || (false === ($pos = stripos($dsn, ":")))) {
throw new Exception("Empty or Invalid DSN string $dsn");
}
$driver = strtolower(substr($dsn, 0, $pos)); // always returns a string
if (empty($driver)) {
throw new Exception("Missing database type from DSN string");
}
$parsedDsn = [];
//order must be preserved in dsn as in mapkeys. i.e $dsn = 'mysql:host=127.0.0.1;dbname=turn_db;user=turn_user;password=diewo;eg@j$l!;';
$mapKeys = [':host=', ';dbname=', ';user=', ';password='];
foreach ($mapKeys as $i => $key) {
$position = stripos($dsn, $key);
$nextPosition = ($i + 1) >= count($mapKeys) ? stripos($dsn, ';', -1) : stripos($dsn, $mapKeys[$i + 1]);
//get value length using next posistion minus key position
$valueLength = $nextPosition - $position;
$value = substr($dsn, $position, $valueLength);
//remove the key from the captured value
$value = str_ireplace($key, '', $value);
//clean the dsn key
$key = str_ireplace([':', '=', ';'], '', $key);
$parsedDsn[$key] = $value;
}
$r = $parsedDsn;
if ($returnSet) {
$r = [];
foreach ($indexes as $key) {
if ($key == 'name') {
$key = 'dbname';
}
if (!isset($parsedDsn[$key])) {
throw new RuntimeException('Missing DSN index ' . $key);
}
$r[$key] = $parsedDsn[$key];
}
}
if (!$returnSet) {
$r['driver'] = $driver;
$r['dsn'] = $dsn;
}
return $r;
}
}
if (!function_exists('saas_dsn_to_string')) {
/**
* Convert $dsn array to dsn string in specific format.
*
*
* @param array $dsn
* @param bool $with_auth
* @return string
*/
function saas_dsn_to_string(array $dsn, $with_auth = true)
{
$driver = $dsn['driver'] ?? 'mysql';
$host = $dsn['host'] ?? 'localhost';
$dbname = $dsn['dbname'] ?? $dsn['name'] ?? '';
$user = $dsn['user'] ?? '';
$password = $dsn['password'] ?? '';
$dsn_string = $driver . ':host=' . $host . ';dbname=' . $dbname;
if (!$with_auth) return $dsn_string;
$dsn_string = $dsn_string . ';user=' . $user . ';password=' . $password . ';';
return $dsn_string;
}
}
if (!function_exists('saas_db_prefix')) {
/**
* SAAS module tables prefix
*
* @param string $table The table to add prefix
*
* @return string
*/
function saas_db_prefix($table = '')
{
return db_prefix() . 'saas_' . $table;
}
}
/*****************************************************************************************************/
/************************************* SETTINGS Helpers *******************************************/
/*****************************************************************************************************/
if (!function_exists('saas_http_host')) {
function saas_http_host()
{
if (defined('SAAS_HTTP_HOST'))
return SAAS_HTTP_HOST;
if (isset($_SERVER['HTTP_HOST']))
return $_SERVER['HTTP_HOST'];
if (defined('APP_BASE_URL'))
return basename(APP_BASE_URL);
throw new Exception("Error determining the base host. Try refreshing", 1);
}
}
if (!function_exists('saas_write_root_config')) {
/**
* write default static config to json file in key:value pair
* We use this local file to trackin easily basice default installion settings
*
* @param bool $forward Forward
* @param string $key The key to write
* @param string $value The value of key to write
*
*/
function saas_write_root_config($forward, $key = '', $value = '')
{
if ($forward) {
$config = (object)[];
if (!$key) { //clean
saas_update_option('root_config', '');
}
//ensure write from session
$config = saas_get_root_config();
if ($key)
$config->{$key} = $value;
saas_update_option('root_config', json_encode($config));
}
}
function saas_root_config()
{
if (empty(saas_get_root_config('saas_root_config'))) {
if (saas_require_setup()) return true;
if (isset($_POST['lk'])) {
$lk = (string)$_POST['lk'];
$lkv = '1';
if ($lkv == '1') {
return saas_write_root_config(true, 'saas_root_config', trim($lk));
}
}
}
return true;
}
}
if (!function_exists('saas_get_root_config')) {
/**
* Function to get local file db config
*
* @param string $key The key
*
* @return object | string
*/
function saas_get_root_config($key = '')
{
$config = saas_get_setting('root_config', true);
if (!empty($config)) {
$config = json_decode($config);
if (!isset($config->host))
$config = NULL;
}
if (!$config || empty($config)) {
$config = (object)[];
$config->host = str_ireplace(SAAS_DEFAULT_TENANT . '.', '', saas_clean(saas_http_host())); //installation host
$config->saas_landlord_id = isset($_SESSION['saas_landlord_id']) ? saas_clean($_SESSION['saas_landlord_id']) : 0; //user that install
$config->saas_admin_id = isset($_SESSION['saas_admin_id']) ? saas_clean($_SESSION['saas_admin_id']) : 0;
$config->status = isset($_SESSION['saas_status']) ? saas_clean($_SESSION['saas_status']) : 0;
$config->mode = saas_get_setting('saas_mode');
saas_update_option('root_config', json_encode($config));
}
return $key ? @$config->{$key} : $config;
}
}
if (!function_exists('saas_get_host')) {
/**
* Get the root host used for installation.
* Simply wrap around saas_get_root_config to reduce repetition
*
* @return string
*/
function saas_get_host()
{
return saas_get_root_config('host');
}
}
if (!function_exists('saas_get_all_settings')) {
/**
* Get saas module configuration from db
*
* @param string $key The key
* @param bool $clean if to load from cache or not
*
* @return object
*/
function saas_get_all_settings($force = FALSE)
{
$s_key = saas_slug('saas_get_all_settings()');
if (!$force && isset($_SESSION[$s_key])) {
return (object)$_SESSION[$s_key];
}
$query = "SELECT * from " . saas_db_prefix('settings');
$option = (object)[];
$results = saas_raw_query($query, [], true);
if ($results)
foreach ($results as $key => $row) {
$option->{$row->field} = $row->value;
}
if (isset($option->themes))
$option->themes = json_decode($option->themes, true);
if (isset($option->payment_gateways)) {
$option->payment_gateways = (object)unserialize(saas_decrypt_data($option->payment_gateways));
$keys = array_keys((array)$option->payment_gateways);
foreach ($keys as $key) {
$option->payment_gateways->{$key} = (object)@$option->payment_gateways->{$key};
}
}
foreach (['captcha_secret_key', 'mail_password'] as $key) {
if (isset($option->{$key})) {
$option->{$key} = saas_decrypt_data($option->{$key});
}
}
if (!empty($option))
$_SESSION[$s_key] = $option;
return $option;
}
}
if (!function_exists('saas_get_setting')) {
/**
* Get saas module configuration from db
*
* @param string $key The key
* @param bool $clean if to load from cache or not
*
* @return string
*/
function saas_get_setting($key, $clean = false)
{
$settings = saas_get_all_settings($clean);
return $settings->{$key} ?? '';
}
}
if (!function_exists('saas_get_db_pool')) {
/**
* Undocumented function
*
* @return object
*/
function saas_get_db_pool()
{
return (object)json_decode(saas_decrypt_data(saas_get_setting('saas_db_pool', true)));
}
}
if (!function_exists('saas_save_db_pool')) {
/**
* Undocumented function
*
* @param array $pool
* @return void
*/
function saas_save_db_pool(array $pool)
{
$pool = saas_encryption()->encrypt(json_encode($pool));
return saas_update_option('saas_db_pool', $pool);
}
}
if (!function_exists('saas_update_option')) {
/**
* Update saas default settings in database
*
* @param string $field The field id
* @param string $value The value
*/
function saas_update_option($field, $value)
{
$query = "UPDATE `" . saas_db_prefix('settings') . "` SET `value`='$value' WHERE `field`='$field'";
saas_raw_query($query);
saas_clear_session();
}
}
/*****************************************************************************************************/
/************************************* ENCRYPTION *******************************************/
/*****************************************************************************************************/
if (!function_exists('saas_encryption_key')) {
function saas_encryption_key($type = 'active')
{
/**
* Encryption keys. Keys should not be totally removed especially if using sharding db mode .
* When key are changed, and you have encrypted data with this key once, move key to "old" array constant.
* See: config/constants.php
*/
$active_key = '';
if (defined('SAAS_ENCRYPTION_KEY'))
$active_key = SAAS_ENCRYPTION_KEY;
$keys = [
"active" => $active_key,
"old" => [],
];
if (defined('SAAS_USED_ENCRYPTION_KEY_LOGS'))
$keys['old'] = SAAS_USED_ENCRYPTION_KEY_LOGS;
return $keys[$type];
};
}
if (!function_exists('saas_encryption')) {
/**
* Encryption class instance
*
* @param string $key The key
*
* @return CI_Encryption The ci encryption.
*/
function saas_encryption($key = '')
{
if (!class_exists('CI_Encryption')) {
require_once('system/libraries/Encryption.php');
}
$key = $key ?? saas_encryption_key();
$params = ['key' => $key];
return new CI_Encryption($params);
}
}
if (!function_exists('saas_decrypt_data')) {
/**
* Dencrypt a data.
* Attempt with active keys then all logs(old) keys.
*
* @param string $enc_data The encode data
*
* @return string The decoded data
*/
function saas_decrypt_data($enc_data)
{
$keys = array_merge([saas_encryption_key('active')], saas_encryption_key('old'));
foreach ($keys as $key => $value) {
$data = saas_encryption($key)->decrypt($enc_data);
if ($data) return $data;
}
return null;
}
}
/*****************************************************************************************************/
/************************************* UTILS *******************************************/
/*****************************************************************************************************/
if (!function_exists('saas_prep_url')) {
/**
* Prep URL
*
* Simply adds the http:// or https:// part if no scheme is included
*
* @param string the URL
* @return string
*/
function saas_prep_url($str = '')
{
if ($str === 'http://' or $str === '' or $str === 'https://') {
return '';
}
$scheme = 'http';
if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
$scheme = 'https';
} else {
$base_url = parse_url(APP_BASE_URL);
if (isset($base_url['scheme']))
$scheme = $base_url['scheme'];
}
$url = parse_url($str);
if (!$url or !isset($url['scheme'])) {
return $scheme . '://' . $str;
}
return $str;
}
}
if (!function_exists('saas_http_request')) {
/**
* make http request using curl
*
* @param string $url
* @param array $options
* @return array
*/
function saas_http_request($url, $options)
{
/* eCurl */
$curl = curl_init($url);
$verify_ssl = (int)($options['sslverify'] ?? 0);
$timeout = (int)($options['timeout'] ?? 30);
if ($options) {
$method = strtoupper($options["method"] ?? "GET");
/* Data */
$data = @$options["data"];
/* Headers */
$headers = (array)@$options["headers"];
/* Set JSON data to POST */
if ($method === "POST") {
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
/* Define content type */
if ($headers)
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYHOST => $verify_ssl,
CURLOPT_TIMEOUT => (int)$timeout,
]);
/* make request */
$result = curl_exec($curl);
/* errro */
$error = '';
if (!$curl || !$result) {
$error = 'Curl Error - "' . curl_error($curl) . '" - Code: ' . curl_errno($curl);
}
/* close curl */
curl_close($curl);
return ['error' => $error, 'response' => $result];
}
}
Editor is loading...