<?php 

require('../../majoris_config.php');
require('../../majoris_core_config.php');

date_default_timezone_set('Asia/Tehran');

if( MAJORIS_DEBUG_DISPLAY ){
    error_reporting(E_ALL);
    ini_set('display_errors', '1');
} else {
    error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
    ini_set('display_errors', '0');
}


if( MAJORIS_DEBUG_LOG ){

    ini_set("log_errors", 1);
    ini_set("error_log", "./z_php_error.log");
    set_exception_handler(function($e) {
        error_log(
            date('Y-m-d H:i:s').' | '.$_SERVER['REQUEST_URI'].' | '.$e->getMessage().PHP_EOL,
            3,
            "./z_sql_error.log"
        );
    });

} else {

    ini_set("log_errors", 0);
    ini_set("error_log", "./z_php_error.log");
    set_exception_handler(function($e) {
        error_log(
            date('Y-m-d H:i:s').' | '.$_SERVER['REQUEST_URI'].' | '.$e->getMessage().PHP_EOL,
            3,
            "./z_sql_error.log"
        );
    });

}


foreach ( glob("./class/*.php" ) as $filename )  include $filename;
db::init();

//load texts from language file to use in api classes:
$lang = include('./libs/lang/fa.php');
lang::init($lang);

foreach ( glob("./libs/*.php" ) as $filename )  include $filename;

foreach ( glob("./model/*.php" ) as $filename )  include $filename;

require "routes.php";







// Method: POST, PUT, GET etc
// Data: array("param" => "value") ==> index.php?param=value

function CallAPI($method, $url, $data = false)
{
    $curl = curl_init();

    switch ($method)
    {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);
            if ($data)
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            break;
        case "PUT":
            curl_setopt($curl, CURLOPT_PUT, 1);
            break;
        default:
            if (!is_bool($data))
                $url = sprintf("%s?%s", $url, http_build_query($data));
    }

    // Optional Authentication:
    // curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    // curl_setopt($curl, CURLOPT_USERPWD, "username:password");

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // https://stackoverflow.com/a/4372730/4565796

    $result = curl_exec($curl);

    if(curl_errno($curl)){
        return 'curl error: ' . curl_error($curl);
    }else{
        curl_close($curl);
        return $result;
    }



}



function check_for_max_device_limit_exceed( $userid , $device_name , $device_id, $app_id){


    $panelconfig = db::fetch("SELECT `data` FROM `panelconfig` WHERE `app_id` = ?",[$app_id]);
    $json_panelconfig = json_decode($panelconfig['data'], TRUE);
    $max_device = $json_panelconfig["device_signin_limit"];
    if($max_device == "" || $max_device == 0){ // unlimited logins
        
        //We have unlimited room for a new device login for this user:
        //Update or insert a new session (if not exists) for this device id , device name and user id
        update_or_insert_a_new_session($userid,$device_name,$device_id,$app_id);
        return false;

    }else{

        $active_device_count = db::rowCount("SELECT * FROM `sessions` WHERE `user_id` = ? AND `status` = 1 AND `app_id` = ?",[ $userid , $app_id]);

        if( $active_device_count >= $max_device ){

            $device_id_exist = db::exist("SELECT * FROM `sessions` WHERE `user_id` = ? AND `device_id` = ? AND `status` = 1 AND `app_id` = ?",[ $userid , $device_id , $app_id]);
            if($device_id_exist){
                //Sessions are full but user is trying to login with already active device. let him in.
                return false;
            }else{ 
                //Dont let User in! User exceeds the limits!
                return true;
            }
            
        }else{
            
            //We have enough room for a new device login for this user:
            //Update or insert a new session (if not exists) for this device id , device name and user id
            update_or_insert_a_new_session($userid,$device_name,$device_id,$app_id);
            return false;
            
        }

    }



}



function update_or_insert_a_new_session ($userid,$device_name,$device_id, $app_id){


    $device_id_exist = db::exist("SELECT * FROM `sessions` WHERE `user_id` = ? AND `device_id` = ? AND `status` = 1 AND `app_id` = ?",[ $userid , $device_id , $app_id ]);
            
    if(!$device_id_exist){

        $deactive_session_exist = db::exist("SELECT * FROM `sessions` WHERE `user_id` = ? AND `device_id` = ? AND `status` = 0  AND `app_id` = ?",[ $userid , $device_id , $app_id ]);
        if($deactive_session_exist){

            //just update previously deactived session:
            $sessionrow = db::fetch( "SELECT * FROM `sessions` WHERE `user_id` = ? AND `device_id` = ? AND `status` = 0 AND `app_id` = ?",[ $userid , $device_id , $app_id ] );
            $session_id = $sessionrow['id'];
            $result     = db::update( "sessions" , [ "status" => 1 ] , " id = " . $session_id );

        }else{

            //there is no previously deactived session, save a new session
            $sessionid = db::insert("sessions",
            [
                'user_id'     => $userid,
                'app_id'      => $app_id,
                'status'      => 1,
                'device_name' => $device_name,
                'device_id'   => $device_id
            ],false);
            
        }

    }
    

}


function result_print($code , $fa , $en){

    return array(
        "code" => $code,
        "fa"   => $fa,
        "en"   => $en
    );

}

function result_print2($code , $fa , $en , $data){

    return array(
        "code" => $code,
        "fa"   => $fa,
        "en"   => $en,
        "data" => $data
    );

}

function result_print3($code , $fa , $en , $data , $data2){

    return array(
        "code" => $code,
        "fa"   => $fa,
        "en"   => $en,
        "data" => $data,
        "data2" => $data2
    );

}


function sanitize_plain_text(string $input){

    // XSS safety:
    // Remove HTML tags completely
    $input = strip_tags($input);
    
    // Remove characters that can break HTML or attributes
    $input = str_replace(
        ['<', '>', '"', "'", '=', '`'],
        '',
        $input
    );

    return $input;
}

function sanitize_textarea($input) {
    if (empty($input) || !is_string($input)) {
        return '';
    }
    
    $text = $input;
    
    // 1. Detect and neutralize <script> tags (case insensitive)
    $text = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i', '[removed]', $text);
    
    // 2. Detect and neutralize <iframe> tags
    $text = preg_replace('/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/i', '[removed]', $text);
    
    // 3. Detect and neutralize <object>, <embed>, <applet> tags
    $text = preg_replace('/<(object|embed|applet)\b[^>]*>.*?<\/\1>/i', '[removed]', $text);
    
    // 4. Neutralize event handlers (on* attributes)
    // This catches: onclick=, onerror=, onload=, onsuccess=, etc.
    $text = preg_replace('/\bon\w+\s*=/i', 'data-removed-event=', $text);
    
    // 5. Neutralize javascript: protocol
    $text = preg_replace('/javascript\s*:/i', 'removed:', $text);
    
    // 6. Neutralize data: protocol with base64 or html (common XSS vector)
    $text = preg_replace('/data\s*:\s*text\/html/i', 'removed:text/html', $text);
    
    // 7. Detect suspicious patterns like <img src=x onerror=...>
    $text = preg_replace_callback('/<img[^>]*>/i', function($matches) {
        if (preg_match('/on\w+\s*=/i', $matches[0])) {
            return '[removed]';
        }
        return $matches[0];
    }, $text);
    
    // 8. Neutralize <style> tags (can contain expression() in IE or other exploits)
    $text = preg_replace('/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/i', '[removed]', $text);
    
    // 9. Detect <meta> tag manipulation
    $text = preg_replace('/<meta\b[^>]*>/i', '[removed]', $text);
    
    // 10. Detect <link> tags (can load external malicious CSS)
    $text = preg_replace('/<link\b[^>]*>/i', '[removed]', $text);
    
    // 11. Detect <base> tag (can hijack relative URLs)
    $text = preg_replace('/<base\b[^>]*>/i', '[removed]', $text);
    
    // 12. Neutralize vbscript: protocol
    $text = preg_replace('/vbscript\s*:/i', 'removed:', $text);
    
    // 13. Detect eval, Function constructor attempts (common in reflected XSS)
    $text = preg_replace('/\beval\s*\(/i', 'removed(', $text);
    $text = preg_replace('/\bnew\s+Function\s*\(/i', 'removed Function(', $text);
    
    // 14. Detect suspicious dollar sign patterns (jQuery, AJAX libs)
    $text = preg_replace('/\$\s*\.\s*(ajax|get|post|getJSON|load)/i', 'removed.$1', $text);
    $text = preg_replace('/\$\s*\([^)]*\)\s*\.\s*(html|append|prepend|after|before)/i', 'removed.$1', $text);
    
    // 15. Detect expression() - old IE CSS exploit
    $text = preg_replace('/expression\s*\(/i', 'removed(', $text);
    
    // 16. Detect import statements (ES6 dynamic imports can be dangerous)
    $text = preg_replace('/\bimport\s*\(/i', 'removed(', $text);
    
    // 17. Detect document.cookie access attempts
    $text = preg_replace('/document\s*\.\s*cookie/i', 'removed.cookie', $text);
    
    // 18. Detect window.location manipulation
    $text = preg_replace('/window\s*\.\s*location/i', 'removed.location', $text);
    $text = preg_replace('/location\s*\.\s*(href|replace|assign)/i', 'removed.$1', $text);
    
    return $text;
}

function hasXssInJson($json) {

    //most used for xss attacks in json form respones
    //generated by Cluade AI with 70% safety in xss detection.

    if (empty($json)) {
        return false;
    }
    
    // Convert to lowercase for case-insensitive matching
    $jsonLower = strtolower($json);
    
    // List of suspicious keywords/patterns
    $suspectPatterns = [
        '<script',           // <script> tags
        '</script',
        '<iframe',           // <iframe> tags
        'javascript:',       // javascript: protocol
        'onerror=',          // Event handlers
        'onload=',
        'onclick=',
        'onmouseover=',
        'onfocus=',
        'onblur=',
        '<img',              // <img> with events is common XSS
        '<svg',              // <svg> can contain scripts
        '<object',
        '<embed',
        'eval(',             // JavaScript eval
        'expression(',       // CSS expression
        'vbscript:',
        'data:text/html',    // Data URI with HTML
        '<meta',
        '<link',
        'document.cookie',
        'document.write',
        'window.location',
        '.innerhtml',
        'alert(',            // Common test payload
        'prompt(',
        'confirm(',
    ];
    
    // Check each pattern
    foreach ($suspectPatterns as $pattern) {
        if (strpos($jsonLower, $pattern) !== false) {
            return true; // Suspicious found!
        }
    }
    
    // Additional check: look for HTML-encoded attacks
    // Sometimes attackers encode < as &#60; or %3C
    $encodedPatterns = [
        '&#60;script',      // &#60; is <
        '%3cscript',        // %3c is <
        '&#x3c;script',
        '&lt;script',
        '&#60;img',
        '%3cimg',
    ];
    
    foreach ($encodedPatterns as $pattern) {
        if (strpos($jsonLower, $pattern) !== false) {
            return true;
        }
    }
    
    return false; // Looks safe
}
