Untitled
unknown
php
2 years ago
50 kB
9
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 mail@turnsaas.com', 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...