<?php
/**
 * @copyright Gmed Srl
 * @author Mirko Giulianini <mirko@gmed.it>
 * 
 * ARM
 * funzioni usate in più punti del programma
 */

$GLOBALS["elencoSQL"] = "";

define('log_levels', [
    'DEBUG' => 1,
    'INFO' => 2,
    'WARNING' => 3,
    'ERROR' => 4
]);


$GLOBALS["db_error"] = Array();

//MG 2018 - Gestione multi database
foreach (db_conn as $sk => $sc) {
    if ($sc['active'] === true) {
	    $GLOBALS[$sk] = pg_connect("host=" . $sc['db_ip'] . " dbname=" . $sc['db_name'] . " user=" . $sc['db_user']
                        . " password=" . $sc['db_pwd'] . " port=" . $sc['db_port'])
                        or
                        db_error($sk, $sc['default'], "Could not connect to server " . $sk);  //die("Could not connect to server " . $sk . "\n");
        $GLOBALS['default_db'] = ($sc['default'] === true) ? $sk : $GLOBALS['default_db'];
    }
}


function db_error($sk, $default, $err){
    $GLOBALS["db_error"][$sk] = $err;
    if($default == 1){
        $GLOBALS["db_error"]['default_db'] = $err;
    }
}




function eseguiSQL($sql, $dbconn = null) {
    $act_db = (is_null($dbconn)) ? $GLOBALS["default_db"] : $dbconn;

    if ($GLOBALS["DEBUG_UTENTE"]) {
        $GLOBALS["elencoSQL"] .= "<br /><br /><font color='green'>$sql</font><br />";
    }

    $risultato = $act_db->query($sql);
    if (!$risultato) {
        echo "ERRORE '$sql' : " . mysql_error() . "<br />";
        $GLOBALS[$act_db]->query("ROLLBACK");
        die("FINE!");
    }
}



function returnSingleValue($sql) {
    $ritorno = NULL;
    $query = query_select($sql);
    if (nrRecords($query) > 0) {
        $ritorno = $query[0][0];
    }
    return $ritorno;
}

function nrRecords($ar) {
    $nr = 0;
    if (is_array($ar)) {
        $nr = count($ar);
        if (count($ar) == 1 && $ar[0] == "0" && !isset($ar[0][0])) {
            $nr = 0;
        }
    }
    return $nr;
}



function query_select($query, $descr = "", $dbconn = null, $params = null) {

    $time_start = microtime(true);
    $act_db = (is_null($dbconn)) ? $GLOBALS["default_db"] : $dbconn;

    $GLOBALS["elencoSQL"] .= "<br /><br /><font color='blue'>query_select " . ($descr != "" ? "($descr)" : "") . "=></font><br />$query ";

    if ( $params === null ){
        $risultato = pg_query($GLOBALS[$act_db], $query);
    }
    else {
        $risultato = pg_query_params($GLOBALS[$act_db], $query, $params);
    }
    
    $time_end = microtime(true);
    $time = round(($time_end - $time_start) * 1000, 2);
    if ($time > 1000) {
        $GLOBALS["elencoSQL"] .= "<font color='red'><strong>";
    }
    $GLOBALS["elencoSQL"] .= "<br />durata query : $time MILLIsec. <br />";
    if ($time > 1000) {
        $GLOBALS["elencoSQL"] .= "</strong></font>";
    }
    if (!$risultato) {

        // Enrico Scapin 2020/02: Recupero l' ultimo errore da postgres
        $error = pg_last_error($GLOBALS[$act_db]) ?? "unknown";

        // 28/06/2012 -- SR --
        //ErroreDB("query_select() => ATTENZIONE!! il database ha riscontrato un errore: $error");
        WriteLog("ATTENZIONE!! il database ha riscontrato un errore nella seguente query: base64 ". base64_encode($query) . PHP_EOL
                . " Errore: " . sanitize_text($error), writelog_dir . "log_query_error.log", "ERROR");

    }

    $i = 0;
    $rec[0] = 0;

    if (!empty($risultato)) {
        while ($tmp = pg_fetch_assoc($risultato)) {
            $rec[$i] = $tmp;
            $i = $i + 1;
        }
    }
    return $rec;
}



function query_execute($query, $descr = "", $dbconn = null, $params = null) {

    $time_start = microtime(true);
    $act_db = (is_null($dbconn)) ? $GLOBALS["default_db"] : $dbconn;

    $GLOBALS["elencoSQL"] .= "<br /><br /><font color='green'>query_execute " . ($descr != "" ? "($descr)" : "") . " =></font><br /> " . sanitize_text($query);

    if ( $params === null )
        $risultato = pg_query($GLOBALS[$act_db], $query);
    else
        $risultato = pg_query_params($GLOBALS[$act_db], $query, $params);

    $time_end = microtime(true);

    $time = round(($time_end - $time_start) * 1000, 2);
    if ($time > 1000) {
        $GLOBALS["elencoSQL"] .= "<font color='red'><strong>";
    }
    $GLOBALS["elencoSQL"] .= "<br />durata query : $time MILLIsec. <br />";
    if ($time > 1000) {
        $GLOBALS["elencoSQL"] .= "</strong></font>";
    }

    if (!$risultato) {

        // Enrico Scapin 2020/02: Recupero l' ultimo errore da postgres
        $error = pg_last_error($GLOBALS[$act_db]) ?? "unknown";

        // 28/06/2012 -- SR --
        //ErroreDB("query_select() => ATTENZIONE!! il database ha riscontrato un errore: $error");
        WriteLog("ATTENZIONE!! il database ha riscontrato un errore nella seguente query: base64 ".base64_encode($query) . PHP_EOL
                . " Errore: " . sanitize_text($error), writelog_dir . "log_query_error.log", "ERROR");

    }

    //mysql_close($connessione);
    $a = 1;
    $a = $risultato;
    return $a;
}



function query_fetch($risultato, $row = null) {

    return pg_fetch_assoc($risultato, $row);
}



function ErroreDB($messaggio, $err_mysql = "") {

    $GLOBALS["elencoSQL"] .= "<br /><font color='red'>ERRORE</font><br />$err_mysql<br />";
    $messaggio = str_replace(array("\r", "\n"), '', $messaggio);
    echo '  <script type="text/javascript">
                $(document).ready(function() {
                    var er = "' . substr($messaggio, 0, 200) . '";
                    alert(er,"Errore DB");
                });
            </script>';
}





function CurrentLogLevel() {

    if (file_exists("../log_level.txt")) {
        $actual_level = strtoupper(trim(file_get_contents("../log_level.txt")));
        $actual_level = array_key_exists($actual_level, log_levels) ? $actual_level : "INFO";
    } else {
        file_put_contents("../log_level.txt", "INFO");
        $actual_level = "INFO";
    }

    return $actual_level;
}





function GetLogFile($filename) {

    $dirpath = ".";

    // se $filename è un pathname uso quello come destinazione
    if (basename($filename) != $filename) {
        $info = pathinfo($filename);
        $dirpath = $info["dirname"];
        $filename = $info["basename"];

    // altrimenti uso la costante log_dir come path della directory dove salvare i log $filename
    } else {
        //$dirpath = get_constant(log_dir, realpath(".."));
        if (!defined("log_dir")) {
            $logdir = "..";
            //se è definita la work_dir uso quella come path dei logs
            if (defined("work_dir")) {
                $logdir = work_dir;
            }
            define("log_dir", $logdir);
        }
        $dirpath = constant("log_dir");
    }

    // se non esiste la directory la creo (comprese le sotto directory)
    $dirpath = mkpath($dirpath) ? realpath($dirpath) : "..";

    $logfile = $dirpath . DIRECTORY_SEPARATOR . $filename;

    // se non esiste il file lo creo
    if (!file_exists($logfile)) { @touch($logfile); }

    return $logfile;
}




function WriteLog($l_txt, $log_file, $level = "INFO", $doc_channel = null, $external_id = null, $attach = "") {

    // 2020-08-28 - SR
    // PERCHE'????? se il file è consistente, perchè creare una directory????

    //if (!file_exists($log_file) && !file_exists(__DIR__ . "../logs/")) {
    if (!file_exists($log_file) && !file_exists(writelog_dir)) {
        mkpath(__DIR__ . "../logs/");
    }

    $log_file = GetLogFile($log_file);

    $num_level = log_levels[$level];
    $num_actual_level = log_levels[CurrentLogLevel()];

    if (demo_mode === TRUE) {
        $l_txt = "**DEMO MODE ATTIVA** " . $l_txt;
    }

    $l_txt_ins = date('Y-m-d H:i:s') . " - " . $level . " - (pid " . getmypid() . ") - " . $l_txt;

    $myfile = 0;

    if ($num_level >= $num_actual_level) {

        //ES 20200314 - Gestisco la dimensione del log
        if (!defined("log_size_limit")) {
            define("log_size_limit", 6291456); //6MB per default // 10485760 = 10MB
        }

        //Truncate del file di log (partendo dall'alto)
        //se manca meno dell'1% al raggiungimento della soglia limite allora tronca il file del 30%
        ftruncatestart($log_file, constant("log_size_limit"));

        $myfile = file_put_contents($log_file, $l_txt_ins . PHP_EOL, FILE_APPEND | LOCK_EX);
    }

    //Se è un caso WARNING o ERROR INVIO A KFE
    if ($num_level > log_levels['INFO']) {
        if(exec_as == "cmd"){
            $l_send_descr = writelog_dir . "CMD - " . $l_txt;
        }
        else {
          $l_send_descr = basename($_SERVER["REQUEST_URI"] ?? $_SERVER["SCRIPT_FILENAME"], ".php") . " - " . $l_txt;
        }
        if (!file_exists(writelog_dir)) {
            mkdir(writelog_dir, 0755, TRUE);
        }
        //Valutare notifiche
        $html_error = "<tr><td>" . $level . "</td><td>" . date('Y-m-d H:i:s') . "</td><td>" . $l_send_descr . "</td></tr>";
        $myerror = file_put_contents(writelog_dir . $level . ".txt", $html_error . PHP_EOL, FILE_APPEND | LOCK_EX);
    }

    return $myfile;
}








function ftruncatestart($filePath, $maxFileSize) {
    $currentSize = filesize($filePath);
    // se la dimensione del file ha quasi raggiunto il limite imposto (meno dell'1%)
    if ($currentSize > ($maxFileSize - $maxFileSize*0.1)) {
        // tolgo il 30% della dimensione massima impostata
        $offset = $currentSize - ($maxFileSize - $maxFileSize*0.3); //se il limite impostato è 6MB lo tronco a 6MB-30%
        if ($offset > 0) {
            //$logsToKeep = file_get_contents($filePath, NULL, NULL, ($maxFileSize / 3), $maxFileSize);
            $logsToKeep = file_get_contents($filePath, NULL, NULL, $offset, $maxFileSize);
            file_put_contents($filePath, $logsToKeep, LOCK_EX);
        }
    }
    return true;
}




function IsNull($question) {
    if (is_array($question)) {
        return empty($question);
    } else {
        return (!isset($question) || @trim($question) === '');
    }
}



function getUserIP() {
    if (PROXY_use) {
        $client = @$_SERVER['HTTP_CLIENT_IP'];
        $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
        $remote = @$_SERVER['REMOTE_ADDR'];

        if (filter_var($client, FILTER_VALIDATE_IP)) {
            $ip = $client;
        } elseif (filter_var($forward, FILTER_VALIDATE_IP)) {
            $ip = $forward;
        } else {
            $ip = $remote;
        }
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }

    return $ip;
}




function verifyEmail($toemail, $fromemail, $getdetails = false) {
    // Get the domain of the email recipient
    $email_arr = explode('@', $toemail);
    $domain = array_slice($email_arr, -1);
    $domain = $domain[0];
    $details = "";

    // Trim [ and ] from beginning and end of domain string, respectively
    $domain = ltrim($domain, '[');
    $domain = rtrim($domain, ']');

    if ('IPv6:' == substr($domain, 0, strlen('IPv6:'))) {
        $domain = substr($domain, strlen('IPv6') + 1);
    }

    $mxhosts = array();
    // Check if the domain has an IP address assigned to it
    if (filter_var($domain, FILTER_VALIDATE_IP)) {
        $mx_ip = $domain;
    } else {
        // If no IP assigned, get the MX records for the host name
        getmxrr($domain, $mxhosts, $mxweight);
    }

    if (!empty($mxhosts)) {
        $mx_ip = $mxhosts[array_search(min($mxweight), $mxhosts)];
    } else {
        // If MX records not found, get the A DNS records for the host
        if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $record_a = dns_get_record($domain, DNS_A);
            // else get the AAAA IPv6 address record
        } elseif (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $record_a = dns_get_record($domain, DNS_AAAA);
        }

        if (!empty($record_a)) {
            $mx_ip = $record_a[0]['ip'];
        } else {
            // Exit the program if no MX records are found for the domain host
            $result = 'invalid';
            $details .= 'No suitable MX records found.';

            return ((true == $getdetails) ? array($result, $details) : $result);
        }
    }

    // Open a socket connection with the hostname, smtp port 25
    $connect = @fsockopen($mx_ip, 25);

    if ($connect) {

        // Initiate the Mail Sending SMTP transaction
        if (preg_match('/^220/i', $out = fgets($connect, 1024))) {

            // Send the HELO command to the SMTP server
            fputs($connect, "HELO $mx_ip\r\n");
            $out = fgets($connect, 1024);
            $details .= $out . "\n";

            // Send an SMTP Mail command from the sender's email address
            fputs($connect, "MAIL FROM: <$fromemail>\r\n");
            $from = fgets($connect, 1024);
            $details .= $from . "\n";

            // Send the SCPT command with the recepient's email address
            fputs($connect, "RCPT TO: <$toemail>\r\n");
            $to = fgets($connect, 1024);
            $details .= $to . "\n";

            // Close the socket connection with QUIT command to the SMTP server
            fputs($connect, 'QUIT');
            fclose($connect);

            // The expected response is 250 if the email is valid
            if (!preg_match('/^250/i', $from) || !preg_match('/^250/i', $to)) {
                $result = 'invalid';
            } else {
                $result = 'valid';
            }
        }
    } else {
        $result = 'invalid';
        $details .= 'Could not connect to server';
    }
    if ($getdetails) {
        return array($result, $details);
    } else {
        return $result;
    }
}




function phoneOperator($phone) {
    if (!preg_match('/((313)|(3[2-9]{1}[0-9]{1}))([0-9]{7})/', $phone)) // invalid Italy mobile number
        return 'unkown';
    $mobileOperators = array(
        '/^3/' => 'valid',
    );
    $operatorKey = array_filter(array_keys($mobileOperators), function ($operator) use ($phone) {
        return preg_match($operator, $phone);
    });
    return !empty($operatorKey) ? $mobileOperators[array_shift($operatorKey)] : 'invalid';
}



function randomNumber($length) {
    $result = '';

    for ($i = 0; $i < $length; $i++) {
        $result .= mt_rand(0, 9);
    }

    return $result;
}




function codiceFiscale($cf) {
    if ($cf == '')
        return false;

    if (strlen($cf) != 16)
        return false;

    $cf = strtoupper($cf);
    if (!preg_match("/[A-Z0-9]+$/", $cf))
        return false;
    $s = 0;
    for ($i = 1; $i <= 13; $i += 2) {
        $c = $cf[$i];
        if ('0' <= $c and $c <= '9')
            $s += ord($c) - ord('0');
        else
            $s += ord($c) - ord('A');
    }

    for ($i = 0; $i <= 14; $i += 2) {
        $c = $cf[$i];
        switch ($c) {
            case '0': $s += 1; break;
            case '1': $s += 0; break;
            case '2': $s += 5; break;
            case '3': $s += 7; break;
            case '4': $s += 9; break;
            case '5': $s += 13; break;
            case '6': $s += 15; break;
            case '7': $s += 17; break;
            case '8': $s += 19; break;
            case '9': $s += 21; break;
            case 'A': $s += 1; break;
            case 'B': $s += 0; break;
            case 'C': $s += 5; break;
            case 'D': $s += 7; break;
            case 'E': $s += 9; break;
            case 'F': $s += 13; break;
            case 'G': $s += 15; break;
            case 'H': $s += 17; break;
            case 'I': $s += 19; break;
            case 'J': $s += 21; break;
            case 'K': $s += 2; break;
            case 'L': $s += 4; break;
            case 'M': $s += 18; break;
            case 'N': $s += 20; break;
            case 'O': $s += 11; break;
            case 'P': $s += 3; break;
            case 'Q': $s += 6; break;
            case 'R': $s += 8; break;
            case 'S': $s += 12; break;
            case 'T': $s += 14; break;
            case 'U': $s += 16; break;
            case 'V': $s += 10; break;
            case 'W': $s += 22;  break;
            case 'X': $s += 25; break;
            case 'Y': $s += 24; break;
            case 'Z': $s += 23; break;
        }
    }

    if (chr($s % 26 + ord('A')) != $cf[15])
        return false;

    return true;
}





function RandomStr($length = 8) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $randomString;
}




function CurlJS($json_content, $url, $timeout = 120, $return_js = true) {
    //open connection
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json_content);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17'); //Da capire se serve davvero
    if (use_jwt) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: text/plain", "Content-length: " . strlen($json_content), "Connection: close"));
    } else {
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/json", "Content-length: " . strlen($json_content), "Connection: close"));
    }
    //execute post
    $result = curl_exec($ch);

    //MG 2017 08 - Gestisco gli errori
    if (curl_errno($ch)) {
        //MG 2018 06 - Provo con l'ip
        $host = parse_url($url, PHP_URL_HOST);
        $ip = gethostbyname($host);
        $new_url = str_replace($host, $ip, $url);

        curl_setopt($ch, CURLOPT_URL, $new_url);

        $result = curl_exec($ch);

        if (curl_errno($ch)) {
            $result_arr = array(
                'status' => false,
                'curl_error' => true,
                'descr' => curl_error($ch)
            );
        }
    }

    if ($return_js === false) {
        //Gestisco la sola risposta della pagina
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        $result_arr = array(
            'status' => true,
            'code' => $httpcode
        );
        $result = json_encode($result_arr);
    }

    //close connection
    curl_close($ch);


    $oJSON = json_decode($result, true);

    return $oJSON; //Genero un array dalla risposta
}





/**
 * Classe di utility per la decodifica dei jwt token
 *
 * @author Giacomo
 * @author Mirko
 */
class clsJwtUtility {

    function __construct() {

    }

    /**
     * Verifica la validit� del token jwt
     * @param type $msg messaggio da verificare (header_urlbase64.payload_urlbase64)
     * @param type $signature signatura del token da verificare
     * @param type $key chiave segreta
     * @param type $alg algoritmo di cifratura
     * @return boolean true se verificato, false altrimenti
     */
    public static function verify($msg, $signature, $key, $alg) {
        $hash = hash_hmac($alg, $msg, $key, true);
        if (function_exists('hash_equals')) {
            return hash_equals($signature, $hash);
        }
        if (strlen($signature) != strlen($hash)) {
            return false;
        } else {
            $res = $signature ^ $hash;
            $ret = 0;
            for ($i = strlen($res) - 1; $i >= 0; $i--) {
                $ret |= ord($res[$i]);
            }
            return !$ret;
        }
    }

    public static function verifyJWT(string $algo, string $jwt, string $publicKeyFile): bool {
        list($headerEncoded, $payloadEncoded, $signatureEncoded) = explode('.', $jwt);

        $dataEncoded = "$headerEncoded.$payloadEncoded";

        $signature = clsJwtUtility::urlsafeB64Decode($signatureEncoded);

        $publicKey = "file://" . $publicKeyFile;

        $publicKeyResource = openssl_pkey_get_public($publicKey);

        $result = openssl_verify($dataEncoded, $signature, $publicKeyResource, $algo);

        if ($result === -1) {
            throw new RuntimeException("Failed to verify signature: " . implode("\n", getOpenSSLErrors()));
        }

        return (bool) $result;
    }

    /**
     * Codifica di un messaggio
     * @param $payload messaggio da codificare
     * @param $signature firma
     * @param $header intestazione
     * @param $algo algoritmo utilizzato
     * @return messaggio codificato
     */
    public static function create($payload, $signature, $header = '{"typ":"JWT","alg":"HS256"}', $algo = "sha256") {
        // Codifico l'header Base64Url String
        $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
        // Codifico il Payload Base64Url String
        $base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
        // Creo  la Signature Hash
        $signature_hash = hash_hmac($algo, $base64UrlHeader . "." . $base64UrlPayload, $signature, true);
        // Encode Signature to Base64Url String
        $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature_hash));
        // Create JWT
        $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
        return $jwt;
    }

    /**
     * Decodifica un messaggio codificato in urlBase64
     * @param type $input messaggio da decodificare
     * @return messaggio decodificato
     */
    public static function urlsafeB64Decode($input) {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $input .= str_repeat('=', $padlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

}

function gen_uuid() {
    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            // 32 bits for "time_low"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            // 16 bits for "time_mid"
            mt_rand(0, 0xffff),
            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand(0, 0x0fff) | 0x4000,
            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand(0, 0x3fff) | 0x8000,
            // 48 bits for "node"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
    );
}

function isJson($string) {
    @json_decode($string);
    return (json_last_error() == JSON_ERROR_NONE);
}

function getBinaryStringForAesCryptAlgorythm($binary) {
    //try reading binary key
    $filename = $binary;
    //check filename
    if ($filename === false) {
        //unable to read the file
        return false;
    }
    //get file handle ('b' is used for compatibility when reading binary files)
    $handle = fopen($filename, "rb");

    //DEBUG
    //echo '<pre>'; var_dump($handle); echo '</pre>';
    //check handle
    if ($handle === false) {
        //unable to open the file
        return false;
    }
    //get contents of the file as a string
    $contents = fread($handle, filesize($filename));

    //DEBUG
    //echo '<pre>'; var_dump($contents); echo '</pre>';
    //close handle
    fclose($handle);

    //return string
    return $contents;
}

function getPassAppToRequestToken($passAppString, $binaryKey) {
    $alg = MCRYPT_RIJNDAEL_128; //AES
    $mode = MCRYPT_MODE_ECB;

    $iv_size = mcrypt_get_iv_size($alg, $mode);
    $block_size = mcrypt_get_block_size($alg, $mode);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM); // pull from /dev/urandom

    $passAppString = pkcs5AddPad($passAppString, $block_size);

    $ciphertext = mcrypt_encrypt($alg, $binaryKey, $passAppString, $mode, $iv);


    //encode the resulting cipher text so it can be represented by a string
    $ciphertext_base64 = base64_encode($ciphertext);
    //$ciphertext_base64 = hash('sha256', $ciphertext_base64);
    //return string
    return $ciphertext_base64;
}

function pkcs5AddPad($text, $blocksize) {
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function formatSizeUnits($bytes) {
    if ($bytes >= 1073741824) {
        $bytes = number_format($bytes / 1073741824, 0,',','.') . ' GB';
    } elseif ($bytes >= 1048576) {
        $bytes = number_format($bytes / 1048576, 0,',','.') . ' MB';
    } elseif ($bytes >= 1024) {
        $bytes = number_format($bytes / 1024, 0,',','.') . ' KB';
    } elseif ($bytes > 1) {
        $bytes = $bytes . ' bytes';
    } elseif ($bytes == 1) {
        $bytes = $bytes . ' byte';
    } else {
        $bytes = '0 bytes';
    }

    return $bytes;
}

function getWeekDay($date) {
    $weekdagen = Array(
        'Monday' => 'Lunedi',
        'Tuesday' => 'Martedi',
        'Wednesday' => 'Mercoledi',
        'Thursday' => 'Giovedi',
        'Friday' => 'Venerdi',
        'Saturday' => 'Sabato',
        'Sunday' => 'Domenica'
    );
    return $weekdagen[date("l", strtotime($date))];
}

function getITMonth($date) {
    $weekdagen = Array(
        '01' => 'Gennaio',
        '02' => 'Febbraio',
        '03' => 'Marzo',
        '04' => 'Aprile',
        '05' => 'Maggio',
        '06' => 'Giugno',
        '07' => 'Luglio',
        '08' => 'Agosto',
        '09' => 'Settebre',
        '10' => 'Ottobre',
        '11' => 'Novembre',
        '12' => 'Dicembre',
    );
    return $weekdagen[date("m", strtotime($date))];
}

function array_to_xml($array, &$xml_user_info) {
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            if (!is_numeric($key)) {
                $subnode = $xml_user_info->addChild("$key");
                array_to_xml($value, $subnode);
            } else {
                $key_sub_p = key($value);
                if (is_array($value[$key_sub_p])) {
                    $subnode = $xml_user_info->addChild("$key_sub_p");
                    array_to_xml($value[$key_sub_p], $subnode);
                } else {
                    foreach ($value as $sub1k => $sub1v) {
                        $xml_user_info->addChild("$sub1k", htmlspecialchars("$sub1v"));
                    }
                }
            }
        } else {
            $xml_user_info->addChild("$key", htmlspecialchars("$value"));
        }
    }
}

function isValidXml($content) {
    $content = trim($content);
    if (empty($content)) {
        return false;
    }
    //html go to hell!
    if (stripos($content, '<!DOCTYPE html>') !== false) {
        return false;
    }

    libxml_use_internal_errors(true);
    simplexml_load_string($content);
    $errors = libxml_get_errors();
    libxml_clear_errors();

    return empty($errors);
}

function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
    if (!$contents)
        return array();
    if (!function_exists('xml_parser_create')) {
        return array();
    }
    $parser = xml_parser_create('');
    xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); # http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
    xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    xml_parse_into_struct($parser, trim($contents), $xml_values);
    xml_parser_free($parser);
    if (!$xml_values)
        return;
    $xml_array = array();
    $parents = array();
    $opened_tags = array();
    $arr = array();
    $current = &$xml_array;
    $repeated_tag_index = array();
    foreach ($xml_values as $data) {
        unset($attributes, $value);
        extract($data);
        $result = array();
        $attributes_data = array();
        if (isset($value)) {
            if ($priority == 'tag')
                $result = $value;
            else
                $result['value'] = $value;
        }
        if (isset($attributes) and $get_attributes) {
            foreach ($attributes as $attr => $val) {
                if ($priority == 'tag')
                    $attributes_data[$attr] = $val;
                else
                    $result['attr'][$attr] = $val;
            }
        }
        if ($type == "open") {
            $parent[$level - 1] = &$current;
            if (!is_array($current) or ( !in_array($tag, array_keys($current)))) {
                $current[$tag] = $result;
                if ($attributes_data)
                    $current[$tag . '_attr'] = $attributes_data;
                $repeated_tag_index[$tag . '_' . $level] = 1;
                $current = &$current[$tag];
            } else {
                if (isset($current[$tag][0])) {
                    $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
                    $repeated_tag_index[$tag . '_' . $level] ++;
                } else {
                    $current[$tag] = array($current[$tag], $result);
                    $repeated_tag_index[$tag . '_' . $level] = 2;
                    if (isset($current[$tag . '_attr'])) {
                        $current[$tag]['0_attr'] = $current[$tag . '_attr'];
                        unset($current[$tag . '_attr']);
                    }
                }
                $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
                $current = &$current[$tag][$last_item_index];
            }
        } elseif ($type == "complete") {
            if (!isset($current[$tag])) {
                $current[$tag] = $result;
                $repeated_tag_index[$tag . '_' . $level] = 1;
                if ($priority == 'tag' and $attributes_data)
                    $current[$tag . '_attr'] = $attributes_data;
            } else {
                if (isset($current[$tag][0]) and is_array($current[$tag])) {
                    $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
                    if ($priority == 'tag' and $get_attributes and $attributes_data) {
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
                    }
                    $repeated_tag_index[$tag . '_' . $level] ++;
                } else {
                    $current[$tag] = array($current[$tag], $result);
                    $repeated_tag_index[$tag . '_' . $level] = 1;
                    if ($priority == 'tag' and $get_attributes) {
                        if (isset($current[$tag . '_attr'])) {
                            $current[$tag]['0_attr'] = $current[$tag . '_attr'];
                            unset($current[$tag . '_attr']);
                        }
                        if ($attributes_data) {
                            $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
                        }
                    }
                    $repeated_tag_index[$tag . '_' . $level] ++;
                }
            }
        } elseif ($type == 'close') {
            $current = &$parent[$level - 1];
        }
    }
    return($xml_array);
}

function libxml_display_error($error) {
    $return = "<br/>\n";
    switch ($error->level) {
        case LIBXML_ERR_WARNING:
            $return .= "<b>Warning $error->code</b>: ";
            break;
        case LIBXML_ERR_ERROR:
            $return .= "<b>Error $error->code</b>: ";
            break;
        case LIBXML_ERR_FATAL:
            $return .= "<b>Fatal Error $error->code</b>: ";
            break;
    }
    $return .= trim($error->message);
    if ($error->file) {
        $return .= " in <b>$error->file</b>";
    }
    $return .= " on line <b>$error->line</b>\n";

    return $return;
}

function libxml_display_errors() {
    $errors = libxml_get_errors();
    foreach ($errors as $error) {
        print libxml_display_error($error);
    }
    libxml_clear_errors();
}

function CheckHash($input_file, $hash_to_verify, $algo = "sha256") {
    /*
      @ $input_file - 			E' il path completo del file da verifcare.
      @ &hash_to_verify - 	E' l'hash calcolato da kripton oggetto di verifica
     */
    $calculated_hash = hash_file($algo, $input_file);
    if (strtolower($calculated_hash) == strtolower($hash_to_verify)) {
        return true;
    } else {
        $calculated_hash = strtolower(base64_encode(hash_file($algo, $input_file, true)));
        if ($calculated_hash == strtolower($hash_to_verify)) {
            return true;
        } else {
            return false;
        }
    }
}
/*
function mkpath($path) {
    if (empty($path)) return false;
    if (@mkdir($path) or file_exists($path))
        return true;
    return (mkpath(dirname($path)) and mkdir($path));
}
 */

//creo il path (recursively) sotto chown apache
//per modalità sudo usare createPath sotto kxa_function
function mkpath($path) {

    //se il $path è vuoto o se il file/directory esiste già, esco.
    if (strlen($path) === 0 || file_exists($path)) return true;

    //NB: mkdir create le sotto directory con i permessi dell'utente corrente,
    //solo l'ultima prende il permesso passato nel parametro mkdir 0777
    //quindi è necessario resettarli con umask prima di crearle perchè le crei tutte con 0777
    /*
    $old = umask(0);
    @mkdir($path, 0777, true);
    umask($old);
    */
    //lo lancio da sudo
    exec('sudo mkdir -m 777 -p ' . $path);

    return file_exists($path);
}

function build_data_files($boundary, $fields, $files) {
    $data = '';
    $eol = "\r\n";

    $delimiter = '-------------' . $boundary;

    foreach ($fields as $name => $content) {
        $data .= "--" . $delimiter . $eol
                . 'Content-Disposition: form-data; name="' . $name . "\"" . $eol . $eol
                . $content . $eol;
    }


    foreach ($files as $name => $content) {
        $data .= "--" . $delimiter . $eol
                . 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $content[0] . '"' . $eol
                //. 'Content-Type: image/png'.$eol
                . 'Content-Transfer-Encoding: binary' . $eol
        ;

        $data .= $eol;
        $data .= $content[1] . $eol;
    }
    $data .= "--" . $delimiter . "--" . $eol;


    return $data;
}

function check_alive($url, $timeout = 10, $successOn = array(200, 301)) {
    $out['status'] = false;
    $out['code'] = "";

    $ch = curl_init($url);
    // Set request options
    curl_setopt_array($ch, array(
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_NOBODY => true,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_USERAGENT => "page-check/1.0",
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_SSL_VERIFYPEER => 0
    ));
    // Execute request
    curl_exec($ch);
    // Check if an error occurred
    if (curl_errno($ch)) {
        $out['code'] = curl_errno($ch);
        curl_close($ch);
        return $out;
    }
    // Get HTTP response code
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $out['status'] = (in_array($code, $successOn)) ? true : false;
    curl_close($ch);
    $out['code'] = $code;
    return $out;
}


function GetConstants() {

    $my_cfg = query_select("select name, value from kxa_config_uni where active = 1");

    for ($x = 0; $x < nrRecords($my_cfg); $x++) {
        $name = $my_cfg[$x]['name'];
        $value = $my_cfg[$x]['value'];
		if (!defined($name)) {
			define($name, $value);
		}
    }
}

//ENRICO: Sarebbe bello ma purtroppo è necessario installare l'estensione e riavviare l'apache
//https://www.php.net/manual/en/runkit.installation.php
function ReloadInclude() {
   /* Enrico Scapin 2020-03: Ricarico gli include dopo l'aggiornamento
    */
  $files = get_included_files();
  foreach($files as $file) {
    if (runkit_lint_file($file)) {
        //runkit_import($path, RUNKIT_IMPORT_FUNCTIONS | RUNKIT_IMPORT_CLASSES | RUNKIT_IMPORT_OVERRIDE);
      runkit_import($file, RUNKIT_IMPORT_CLASSES | RUNKIT_IMPORT_FUNCTIONS | RUNKIT_IMPORT_CLASS_PROPS | RUNKIT_IMPORT_CLASS_METHODS | RUNKIT_IMPORT_CLASS_CONSTS | RUNKIT_IMPORT_OVERRIDE);
    } else {
      return false;
    }
  }
}

function CheckUpdating() {
    /* Enrico Scapin 2020-02: Verifica aggiornamento in corso
     * se c'è un aggiornamento rimango in attesa del suo completamento
     * se scade il timeout di 5 minuti sblocco il LOCK e continuo
     */
    $isupdated = false;

    $flag = "../lock_update";
    $timestamp = time();
    $freeze = 301; // 60*5 = 300 = 5 minuti
    $locked = true;
    do {
        while ((file_exists($flag)) && $freeze-- > 0) {
            $isupdated = true;
            if ($freeze % 10 == 0) { //scrivo solo ogni 10sec per evitare di intasare il log
                WriteLog("*** AGGIORNAMENTO IN CORSO - IN ATTESA {$_SERVER['PHP_SELF']} " . (300-$freeze) . " sec ****", crontab_logfile, "INFO");
            }
            sleep(1); //riprova ogni 5 secondi
        }
        //Processo bloccato
        if ($freeze == 0 && file_exists($flag)) {
            if (filemtime($flag) > $timestamp) {
                //Il lock è stato creato successivamente e significa che un altro processo sta già aggiornando
                //quindi mi rimetto in attesa
                $freeze = 301;
                $locked = true;
            } else {
                //sblocco il lock di aggiornamento forzatamente per timeout
                unlink($flag);
                $locked = false;
                WriteLog("Processo aggiornamento sbloccato forzatamente per timeout", crontab_logfile, "WARNING");
            }
        } else {
           $locked = false;
        }
    } while($locked);

    //TODO: vedere funzione ReloadInclude
    // se era in corso un aggiornamento prima dello sblocco ricarico gli include (perchè potenzialmente più aggiornati!)
    //if ($isupdated) {
    //    ReloadInclude();
    //}
}

function isBinary($str) {
    return preg_match('~[^\x20-\x7E\t\r\n]~', $str) > 0;
}

function getBearerToken() {
    $headers = getAuthorizationHeader();
    // HEADER: Get the access token from the header
    if (!empty($headers)) {
        if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
            return $matches[1];
        }
    }
    return null;
}

function getAuthorizationHeader() {
    $headers = null;
    if (isset($_SERVER['Authorization'])) {
        $headers = trim($_SERVER["Authorization"]);
    } else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
        $headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
    } elseif (function_exists('apache_request_headers')) {
        $requestHeaders = apache_request_headers();
        // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
        $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
        //print_r($requestHeaders);
        if (isset($requestHeaders['Authorization'])) {
            $headers = trim($requestHeaders['Authorization']);
        }
    }
    return $headers;
}

function UniqueMachineID($salt = "G5M%rd") {

//$result = shell_exec("sudo dmidecode -t 4 | grep ID | sed 's/.*ID://;s/ //g'"); //F.B. 20170612 Identificativo precedente
    $result = shell_exec("sudo dmidecode --string system-uuid"); //F.B. 20170612 Identificativo nuovo
    return md5($salt . md5(trim($result)));
}

function get_constant($constant, $defaultValue) {
    // se non è definita la costante la scrive nel config con il valore di default passato come parametro
    if (!defined($constant)) {
        add_constant($constant, $defaultValue);
        if (!define($constant, $defaultValue)) {
            throw new Exception("ERROR: Cannot define constant '$constant' with value '$defaultValue'");
        }
    }
    return constant($constant);
}

function add_constant($constant, $value) {
    $value = (in_array(strtolower($value), ['0', '1', 'true', 'false'])) ? $value : "'" . $value . "'";
    $data = "\ndefine('$constant',$value);" . PHP_EOL;
    $fp = fopen('../conf/config.inc.php', 'a');
    fwrite($fp, $data);
    fclose($fp);
}

function GetEndpoints() {
    $ret['status'] = false;
    $current_node = '../actual_endpoint';

    $check_endpoint = endpoints['check_endpoint']; // ritorna es: 'fe_endpoint'
    $max_dr = endpoints['max_dr']; // Numero massimo di host nei singoli array
    WriteLog($check_endpoint . " - " . $max_dr, crontab_logfile, "DEBUG", '000');

    if (file_exists($current_node)) {
        $ies = trim(file_get_contents($current_node));
        //Se il valore è diverso da zero ed è piu di un giorno che non ho fatto un check provo a vedere se il nodo primario è di nuovo raggiungibile
        if ((filemtime($current_node) + 43200 < time()) && $ies != 0) {
            //Verifico se il nodo primario è tornato raggiungibile
            $check_pri = check_alive(endpoints[$check_endpoint][0], 3);
            if ($check_pri['status'] === false) {
                //L'endpoint  primario non è tornato raggiungibile
                WriteLog(endpoints[$check_endpoint][0] . " NON raggiungibile - SONO in DR", crontab_logfile, "WARNING", '000');
                file_put_contents($current_node, $ies);
            } else {
                file_put_contents($current_node, '0');
                $ies = 0;
            }
        }
    } else {
        file_put_contents($current_node, '0');
        $ies = 0;
    }
    //Adesso verifico la raggiungibilità del nodo
    $check_actual = check_alive(endpoints[$check_endpoint][$ies], 3);
    WriteLog(endpoints[$check_endpoint][$ies] . "->Verifica1: " . $check_actual['status'], crontab_logfile, "DEBUG", '000');
    //Se non risponde riparto a fare un check da capo su tutti gli host
    if ($check_actual['status'] === false) {
        for ($xes = 0; $xes < $max_dr; $xes++) {
            $check_recursive = check_alive(endpoints[$check_endpoint][$xes], 3);
            WriteLog(endpoints[$check_endpoint][$xes] . "->Verifica2: " . $check_recursive['status'], crontab_logfile, "DEBUG", '000');
            if ($check_recursive['status'] === true) {
                //Trovato un nodo, esco
                WriteLog(endpoints[$check_endpoint][$xes] . " attivo", crontab_logfile, "INFO", '000');
                foreach (endpoints as $single_endpoint => $single_value) {
                    if (is_array($single_value)) {
                        define($single_endpoint, $single_value[$xes]);
                        $ret[$single_endpoint] = $single_value[$xes];
                        WriteLog($single_endpoint . " -> " . $single_value[$xes], crontab_logfile, "DEBUG", '000');
                    }
                }
                $ret['status'] = true;
                file_put_contents($current_node, $xes);
                break;
            }
        }
    } else {
        foreach (endpoints as $single_endpoint => $single_value) {
            if (is_array($single_value)) {
                define($single_endpoint, $single_value[$ies]);
                $ret[$single_endpoint] = $single_value[$ies];
                WriteLog($single_endpoint . " -> " . $single_value[$ies], crontab_logfile, "DEBUG", '000');
            }
        }
        $ret['status'] = true;
    }



    return $ret;
}

function RemoveFiles($paths, $timeago) {
    $path = rtrim($paths, '/');
    $files = array();
    $index = array();
    if ($handle = opendir($path)) {
        #clearstatcache();   # not needed, unlink() clears the cache automatically
        while (false !== ($file = readdir($handle))) {
            if ($file != "." && $file != "..") {
                if (is_dir($path . '/' . $file)) {
                    echo "elaboro: " . $path . '/' . $file . '<br>';
                    RemoveFiles($path . '/' . $file, $timeago);
                    $ex_stf = rmdir($path . '/' . $file);
                    echo 'Delete folder ' . $ex_stf . '-> ' . $path . '/' . $file . "<br>";
                } else {
                    clearstatcache();
                    if (is_file($path . '/' . $file) && filemtime($path . '/' . $file) < $timeago) {
                    	$ex_std = @unlink($path . '/' . $file);
			echo 'Delete file ' . $ex_std . '-> ' . $path . '/' . $file . "<br>";
                    }
                }
            }
        }
    }
    closedir($handle);
}

##Funzioni per x-adapter

function cryptare($text, $key, $alg, $crypt) {
    // Parameters:
    // $text = The text that you want to encrypt.
    // $key = The key you're using to encrypt.
    // $alg = The algorithm.
    // $crypt = 1 if you want to crypt, or 0 if you want to decrypt.
    $encrypted_data = "";
    switch ($alg) {
        case "3des":
            $td = mcrypt_module_open('tripledes', '', 'ecb', '');
            break;
        case "cast-128":
            $td = mcrypt_module_open('cast-128', '', 'ecb', '');
            break;
        case "gost":
            $td = mcrypt_module_open('gost', '', 'ecb', '');
            break;
        case "rijndael-128":
            $td = mcrypt_module_open('rijndael-128', '', 'ecb', '');
            break;
        case "twofish":
            $td = mcrypt_module_open('twofish', '', 'ecb', '');
            break;
        case "arcfour":
            $td = mcrypt_module_open('arcfour', '', 'ecb', '');
            break;
        case "cast-256":
            $td = mcrypt_module_open('cast-256', '', 'ecb', '');
            break;
        case "loki97":
            $td = mcrypt_module_open('loki97', '', 'ecb', '');
            break;
        case "rijndael-192":
            $td = mcrypt_module_open('rijndael-192', '', 'ecb', '');
            break;
        case "saferplus":
            $td = mcrypt_module_open('saferplus', '', 'ecb', '');
            break;
        case "wake":
            $td = mcrypt_module_open('wake', '', 'ecb', '');
            break;
        case "blowfish-compat":
            $td = mcrypt_module_open('blowfish-compat', '', 'ecb', '');
            break;
        case "des":
            $td = mcrypt_module_open('des', '', 'ecb', '');
            break;
        case "rijndael-256":
            $td = mcrypt_module_open('rijndael-256', '', 'ecb', '');
            break;
        case "xtea":
            $td = mcrypt_module_open('xtea', '', 'ecb', '');
            break;
        case "enigma":
            $td = mcrypt_module_open('enigma', '', 'ecb', '');
            break;
        case "rc2":
            $td = mcrypt_module_open('rc2', '', 'ecb', '');
            break;
        default:
            $td = mcrypt_module_open('blowfish', '', 'ecb', '');
            break;
    }

    $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    $key = substr($key, 0, mcrypt_enc_get_key_size($td));
    mcrypt_generic_init($td, $key, $iv);

    if ($crypt) {
        $encrypted_data = mcrypt_generic($td, $text);
    } else {
        $encrypted_data = mdecrypt_generic($td, $text);
    }

    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    return $encrypted_data;
}

function get_client_ip_server() {
    $ipaddress = '';
    if (getenv('HTTP_CLIENT_IP'))
        $ipaddress = getenv('HTTP_CLIENT_IP');
    else if (getenv('HTTP_X_FORWARDED_FOR'))
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
    else if (getenv('HTTP_X_FORWARDED'))
        $ipaddress = getenv('HTTP_X_FORWARDED');
    else if (getenv('HTTP_FORWARDED_FOR'))
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
    else if (getenv('HTTP_FORWARDED'))
        $ipaddress = getenv('HTTP_FORWARDED');
    else if (getenv('REMOTE_ADDR'))
        $ipaddress = getenv('REMOTE_ADDR');
    else
        $ipaddress = 'UNKNOWN';
    return $ipaddress;
}

function security($security_token = 'xxx', $doc_channel = '000', $source_ip = '0.0.0.0', $node_id = '') {

    //MG 2015 10 - Aggiungere la gestione della data e del NO-IP

    if (base64_encode(base64_decode($security_token, true)) === $security_token) {
        $security_token_string = $security_token;
    } else {
        $arr_out['status'] = false;
        $arr_out['descr'] = 'no_token';
        return $arr_out;
    }

    $security_string = cryptare(base64_decode($security_token_string), '$%&(VHJ&%(', 'rc2', 0);
    $security_array = unserialize($security_string);

    WriteLog(__LINE__ . " --" . json_encode($security_array), kr_log_file, "DEBUG");

    if (is_array($security_array)) {
        $arr_out['status'] = true;
        $arr_out['descr'] = '';

        if (!in_array($doc_channel, $security_array['channels'])) {
            $arr_out['status'] = false;
            $arr_out['descr'] = 'no_channel';
        }
        if (!in_array($source_ip, $security_array['source_ip'])) {
            $arr_out['status'] = false;
            $arr_out['descr'] = 'no_ip';
        }
        //Se ho una licenza a scadenza do la possibilità di fare test su tutti i nodi fino alla data indicata
        if (strtotime($security_array['end_date']) > strtotime(date('Y-m-d'))) {
            $arr_out['status'] = true;
            $arr_out['descr'] = 'demo_licence';
        }
        if (!in_array($node_id, $security_array['node_id'])) {
            $arr_out['status'] = false;
            $arr_out['descr'] = 'node_not_valid';
        }
    } else {
        $arr_out['status'] = false;
        $arr_out['descr'] = 'no_token';
    }


    return $arr_out;
}

//Errori x sendDoc e sendIndex
function error_send($err = null, $struct = 'phrase', $message = '') {
    $error['kr_return']['status'] = 'nack';
    $error[$struct] = '';
    switch ($err) {
        case "empty":
            $error['kr_return']['errorDescription'] = 'There was nothing passed to the webservice, so nothing can be returned.';
            break;
        case "no_channel":
            $error['kr_return']['errorDescription'] = "You are not allowed to check this channel";
            break;
        case "not_found":
            $error['kr_return']['errorDescription'] = 'The Document is not present on Kripton, please try again.';
            break;
        case "no_token":
            $error['kr_return']['errorDescription'] = 'No token provided, please contact sales department.';
            break;
        case "no_ip":
            $error['kr_return']['errorDescription'] = 'You are not allowed to connect to Kripton';
            break;
        case "node_not_valid":
            $error['kr_return']['status']['message'] = 'You are not allowed to use this node';
            break;
        case 'custom':
            $error['kr_return']['errorDescription'] = $message;
            break;
        default:
            $error['kr_return']['errorDescription'] = 'There was a problem with your request, please contact service support.';
            break;
    }
    return $error;
}

//Errori x GetStatus
function error($err = null, $struct = 'phrase', $message = '') {
    $error['kr_return']['status']['status'] = 'warning';
    $error[$struct] = '';
    switch ($err) {
        case "empty":
            $error['kr_return']['status']['message'] = 'There was nothing passed to the webservice, so nothing can be returned.';
            break;
        case "no_channel":
            $error['kr_return']['status']['message'] = "You are not allowed to check this channel";
            break;
        case "not_found":
            $error['kr_return']['status']['message'] = 'The Document is not present on Kripton, please try again.';
            break;
        case "no_token":
            $error['kr_return']['status']['message'] = 'No token provided, please contact sales department.';
            break;
        case "no_ip":
            $error['kr_return']['status']['message'] = 'You are not allowed to connect to Kripton';
            break;
        case "node_not_valid":
            $error['kr_return']['status']['message'] = 'You are not allowed to use this node';
            break;
        case 'custom':
            $error['kr_return']['status']['message'] = $message;
            break;
        default:
            $error['kr_return']['status']['message'] = 'There was a problem with your request, please contact service support.';
            break;
    }
    return $error;
}

function ArrayCleaner($input) {
    foreach ($input as &$value) {
        $value = (empty($value)) ? "" : $value;
        if (is_array($value)) {
            $value = ArrayCleaner($value);
        }
    }

    return array_filter($input, function($item) {
        return $item !== null;
    });
}

function json_validate($string)
{
    // decode the JSON data
    $result = json_decode($string);

    // switch and check possible JSON errors
    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            $error = ''; // JSON is valid // No error has occurred
            break;
        case JSON_ERROR_DEPTH:
            $error = 'The maximum stack depth has been exceeded.';
            break;
        case JSON_ERROR_STATE_MISMATCH:
            $error = 'Invalid or malformed JSON.';
            break;
        case JSON_ERROR_CTRL_CHAR:
            $error = 'Control character error, possibly incorrectly encoded.';
            break;
        case JSON_ERROR_SYNTAX:
            $error = 'Syntax error, malformed JSON.';
            break;
        // PHP >= 5.3.3
        case JSON_ERROR_UTF8:
            $error = 'Malformed UTF-8 characters, possibly incorrectly encoded.';
            break;
        // PHP >= 5.5.0
        case JSON_ERROR_RECURSION:
            $error = 'One or more recursive references in the value to be encoded.';
            break;
        // PHP >= 5.5.0
        case JSON_ERROR_INF_OR_NAN:
            $error = 'One or more NAN or INF values in the value to be encoded.';
            break;
        case JSON_ERROR_UNSUPPORTED_TYPE:
            $error = 'A value of a type that cannot be encoded was given.';
            break;
        default:
            $error = 'Unknown JSON error occured.';
            break;
    }

    if ($error !== '') {
        // throw the Exception or exit // or whatever :)
        WriteLog("Json Validation Error : $error" , log_file, "WARNING");
    }

    // everything is OK
    return $result;
}

function sanitize_text($text) {
    return htmlentities($text, ENT_QUOTES, 'UTF-8');
}

function sanitize_sql_text($text) {
    //Esempio: sanitize_sql_text('text')  = text
    return pg_escape_string($text);
}

function sanitize_sql_query($text) {
    //Esempio: sanitize_sql_query('text')  = 'text'
    return pg_escape_literal($text);
}

function sanitize_sql_identifier($text) {
    //Esempio: sanitize_sql_identifier('text')  = "text"
    return pg_escape_identifier($text);
}
/*
 *  ES 2020-01:  GESTIONE ERRORI
 *  Callback per intercettare gli errori non gestiti
 */
define('WORKING_DIRECTORY', getcwd());

set_exception_handler('exception_handler');
set_error_handler('error_handler');
//register_shutdown_function( "fatal_handler" );

//loggo solo gli ERRORI e i WARNING (no NOTICE)
function is_an_error_to_log($error_type) {

    // Error type is a FATAL,ERROR or WARNING
    if (in_array($error_type, [0, E_ERROR, E_RECOVERABLE_ERROR, E_USER_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_WARNING])) {
        return true;
    }
    return false;
}

function exception_handler($exception) {

    // Error reporting is currently turned off or suppressed with @
    if (error_reporting() === 0) {
        return false;
    }

    if (!defined("generic_logfile")) {
        define("generic_logfile", "../log_generic.log");
    }

    if (is_an_error_to_log($exception->getCode())) {
        WriteLog(
        	format_error(
        		$exception->getCode(),
        		$exception->getMessage(),
        		$exception->getFile(),
        		$exception->getLine(),
        		array()
        	),
        	generic_logfile,
        	(($exception->getCode() == E_WARNING) ? "WARNING" : "ERROR")
        );
    }

    restore_exception_handler();
    throw $exception;
}




function error_handler($code, $message, $file, $line, $vars) {

    // Error reporting is currently turned off or suppressed with @
    if (error_reporting() === 0) {
        return false;
    }

    if (!defined("generic_logfile")) {
        define("generic_logfile", "../log_generic.log");
    }

    if (is_an_error_to_log($code)) {
        WriteLog(
        	format_error(
        		$code,
        		$message,
        		$file,
        		$line,
        		$vars
        	),
        	generic_logfile,
        	(($code == E_WARNING) ? "WARNING" : "ERROR")
        );
    }

    return false;
}




//ES: deprecated
//function debug_string_backtrace() {
//    ob_start();
//    debug_print_backtrace();
//    $trace = ob_get_contents();
//    ob_end_clean();
//
//    // Remove first item from backtrace as it's this function which
//    // is redundant.
//    $trace = PHP_EOL . preg_replace ('/#\d\s+' . '(debug_string_backtrace|format_error)' . '[^\n]*\n/', '', $trace);
//
//    // Renumber backtrace items.
//    //$trace = preg_replace ('/^#(\d+)/me', '\'#\' . ($1 - 1)', $trace);
//    $trace = preg_replace_callback('/^#(\d+)/m', function($matches) {
//        foreach($matches as $match){
//            return '#' . ($match[1] -1) . '';
//        }
//    }, $trace);
//
//    return $trace;
//}

//ES: Custom var_dump per il log delle variabili della funzione in errore

//Only the first 100 characters of each string are logged (full length is shown)
//Only the first 25 elements of an array are logged (full length is shown)
//Only the first 10 levels of nested objects/arrays are logged
function var_debug($variable,$strlen=100,$width=25,$depth=10,$i=0,&$objects = array())
{
    $search = array("\0", "\a", "\b", "\f", "\n", "\r", "\t", "\v");
    $replace = array('\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v');

    $string = '';

    switch(gettype($variable)) {
    case 'boolean':      $string.= $variable?'true':'false'; break;
    case 'integer':      $string.= $variable;                break;
    case 'double':       $string.= $variable;                break;
    case 'resource':     $string.= '[resource]';             break;
    case 'NULL':         $string.= "null";                   break;
    case 'unknown type': $string.= '???';                    break;
    case 'string':
      $len = strlen($variable);
      $variable = str_replace($search,$replace,substr($variable,0,$strlen),$count);
      $variable = substr($variable,0,$strlen);
      $variable = htmlspecialchars($variable);
      if ($len<$strlen) $string.= '"'.$variable.'"';
      else $string.= 'string('.$len.'): "'.$variable.'"...';
      break;
    case 'array':
      $len = count($variable);
      if ($i==$depth) $string.= 'array('.$len.') {...}';
      elseif(!$len) $string.= 'array(0) {}';
      else {
        $keys = array_keys($variable);
        $spaces = str_repeat(' ',$i*2);
        $string.= "array($len)\n".$spaces.'{';
        $count=0;
        foreach($keys as $key) {

            //ignoro le variabili standard
            if (in_array($key, ["_GET", "_POST", "_SERVER", "_SESSION", "_COOKIE", "_FILES", "_REQUEST", "_ENV", "GLOBALS"])) continue;

            if ($count==$width) {
              $string.= "\n".$spaces."  ...";
              break;
            }
            $string.= "\n".$spaces."  [$key] => ";
            $string.= var_debug($variable[$key],$strlen,$width,$depth,$i+1,$objects);
            $count++;
        }
        $string.="\n".$spaces.'}';
      }
      break;
    case 'object':
      $id = array_search($variable,$objects,true);
      if ($id!==false)
        $string.=get_class($variable).'#'.($id+1).' {...}';
      else if($i==$depth)
        $string.=get_class($variable).' {...}';
      else {
        $id = array_push($objects,$variable);
        $array = (array)$variable;
        $spaces = str_repeat(' ',$i*2);
        $string.= get_class($variable)."#$id\n".$spaces.'{';
        $properties = array_keys($array);
        foreach($properties as $property) {
          $name = str_replace("\0",':',trim($property));
          $string.= "\n".$spaces."  [$name] => ";
          $string.= var_debug($array[$property],$strlen,$width,$depth,$i+1,$objects);
        }
        $string.= "\n".$spaces.'}';
      }
      break;
    }

    if ($i>0) return $string;

    $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
    do $caller = array_shift($backtrace); while ($caller && !isset($caller['file']));
    if ($caller) $string = $caller['file'].':'.$caller['line']."\n".$string;

    return $string;
}

function debug_backtrace_clean() {

    $debug = (CurrentLogLevel() == "DEBUG");

    $stack = "<ul class=\"dlist\">"  . PHP_EOL;
    $i = 1;
    $trace = debug_backtrace();

    foreach($trace as $node) {

        //ignoro queste call di stack
        if ($node['function'] == "format_error" || $node['function'] == "debug_backtrace_clean" || $node['function'] == "error_handler" || $node['function'] == "exception_handler" ) continue;

        $stack .= "\t<li>#$i ";
        $stack .= array_key_exists('file', $node) ? htmlspecialchars($node['file']) : "";
        $stack .= array_key_exists('line', $node) ? "(" .$node['line'].")" : "";
        $stack .= " ";

        if(isset($node['class'])) {
            $stack .= htmlspecialchars($node['class']) . "->";
        }
        $stack .= array_key_exists('function', $node) ? htmlspecialchars($node['function']) . "()" :  "";

        if ($debug)  { //Aggiungo accordation con le variabili
            $rand = RandomStr(6);
            $stack .= "\n<input id=\"$rand\" class=\"toggle\" type=\"checkbox\"><label for=\"$rand\">Args</label><div class=\"expand\">\n";
            $stack .= array_key_exists('args', $node) ? htmlspecialchars(var_debug($node['args'])) : " ";
            $stack .= "</div>";
        }

        $stack .=  "\t</li>" . PHP_EOL;
        $i++;
    }
    $stack .= "\t\t</ul>";
    return $stack;
}

function format_error( $errno, $errstr, $errfile, $errline, $vars ) {
    //$trace = print_r( debug_backtrace( false ), true );
    //$trace = debug_string_backtrace();
    $trace = debug_backtrace_clean();

    // definisce una matrice associativa con i messaggi di errore
    $errortype = array (
                0                 => "Fatal",
                E_ERROR           => "Error",
                E_WARNING         => "Warning",
                E_PARSE           => "Parsing Error",
                E_NOTICE          => "Notice",
                E_CORE_ERROR      => "Core Error",
                E_CORE_WARNING    => "Core Warning",
                E_COMPILE_ERROR   => "Compile Error",
                E_COMPILE_WARNING => "Compile Warning",
                E_USER_ERROR      => "User Error",
                E_USER_WARNING    => "User Warning",
                E_USER_NOTICE     => "User Notice",
                E_STRICT          => "Runtime Notice",
                E_RECOVERABLE_ERROR => "Abort"
                );

    $content = " Unhandled Error: " . @$_SERVER["REQUEST_URI"] . PHP_EOL
        . "<div class=\"dtable\">" . PHP_EOL
        . "  <div class=\"drow\">" . PHP_EOL
        . "    <div class=\"dcol dhead\">Message</div>" . PHP_EOL
        . "    <div class=\"dcol dtext\">" . htmlspecialchars($errstr) . "</div>" . PHP_EOL
        . "  </div>" . PHP_EOL
        . "  <div class=\"drow\">" . PHP_EOL
        . "    <div class=\"dcol dhead\">Type</div>". PHP_EOL
        . "    <div class=\"dcol dtext\">$errortype[$errno] ($errno)</div>". PHP_EOL
        . "  </div>". PHP_EOL
        . "  <div class=\"drow\">" . PHP_EOL
        . "    <div class=\"dcol dhead\">File</div>". PHP_EOL
        . "     <div class=\"dcol dtext\">" . htmlspecialchars($errfile) . "</div>". PHP_EOL
        . "   </div>". PHP_EOL
        . "  <div class=\"drow\">" . PHP_EOL
        . "    <div class=\"dcol dhead\">Line</div>". PHP_EOL
        . "    <div class=\"dcol dtext\">$errline</div>". PHP_EOL
        . "  </div>". PHP_EOL
        . "  <div class=\"drow\">" . PHP_EOL
        . "    <div class=\"dcol dhead\">Trace</div>". PHP_EOL
        . "    <div class=\"dcol dtext\">$trace</div>". PHP_EOL
        . "  </div>". PHP_EOL
        . "</div>";
    return $content;
}


// calcolo eta'
//function determine_age($birth_date, $current_year = null) {
//    $birth_date_time = strtotime($birth_date);
//    $to_date = date('m/d/Y', $birth_date_time);
//
//    list($birth_month, $birth_day, $birth_year) = explode('/', $to_date);
//
//    if (!$current_year) {
//        $current_year = date("Y");
//    }
//
//    $this_year_birth_date = $birth_month . '/' . $birth_day . '/' . $current_year;
//    $this_year_birth_date_timestamp = strtotime($this_year_birth_date);
//
//    $years_old = $current_year - $birth_year;
//
//    if ($now < $this_year_birth_date_timestamp) {
//        $years_old = $years_old - 1;
//    }
//
//    return $years_old;
//}

function determine_age($dob, $condate = null)
{
    if (!$condate) {
        $condate = date("Y-m-d");
    }

    $birthdate = new DateTime(date("Y-m-d", strtotime(implode('-', array_reverse(explode('/', $dob))))));
    $today = new DateTime(date("Y-m-d", strtotime(implode('-', array_reverse(explode('/', $condate))))));
    $age = $birthdate->diff($today)->y;
    return $age;
}



