<?php
/**
 * @copyright Gmed srl
 * @author Mirko Giulianini
 *
 * 
 */

function stream_copy($src, $dest) {
    //$uppo = shell_exec("sudo mkdir ".$dest);
    if (!is_dir(dirname($dest))) {
        $uppo = mkdir(dirname($dest), 0777, true);
    }
    $fsrc = fopen($src, 'r');
    $fdest = fopen($dest, 'w+');
    $len = stream_copy_to_stream($fsrc, $fdest);
    fclose($fsrc);
    fclose($fdest);
    return $len;
}

function delLineFromFile($fileName, $lineNum) {
    // check the file exists
    if (!is_writable($fileName)) {
        // print an error
        print "The file $fileName is not writable";
        // exit the function
        exit;
    }
    else {
        // read the file into an array
        $arr = file($fileName);
    }

    // the line to delete is the line number minus 1, because arrays begin at zero
    $lineToDelete = $lineNum - 1;

    // check if the line to delete is greater than the length of the file
    if ($lineToDelete > sizeof($arr)) {
        // print an error
        print "You have chosen a line number, <b>[$lineNum]</b>,  higher than the length of the file.";
        // exit the function
        exit;
    }

    //remove the line
    unset($arr["$lineToDelete"]);

    // open the file for reading
    if (!$fp = fopen($fileName, 'w+')) {
        // print an error
        print "Cannot open file ($fileName)";
        // exit the function
        exit;
    }

    // if $fp is valid
    if ($fp) {
        // write the array to the file
        foreach ($arr as $line) {
            fwrite($fp, $line);
        }

        // close the file
        fclose($fp);
    }

    echo "done";
}

function getPDFPages($document) {
    $cmd = "pdfinfo";           // Linux
    //$cmd = "C:\\path\\to\\pdfinfo.exe";  // Windows
    // Parse entire output
    exec("$cmd $document", $output);

    // Cerca nell'output la parola PAGES
    $pagecount = 0;
    foreach ($output as $op) {
        // Extract the number
        if (preg_match("/Pages:\s*(\d+)/i", $op, $matches) === 1) {
            $pagecount = intval($matches[1]);
            break;
        }
    }

    return $pagecount;
}

function createPath($path) {
    error_reporting(0);
    if (is_dir($path))
        return true;
    $prev_path = substr($path, 0, strrpos($path, '/', -2) + 1);
    $return = createPath($prev_path);
    //return ($return && is_writable($prev_path)) ? mkdir($path) : false;

    return ($return && (is_writable($prev_path) || is_readable($prev_path))) ? exec('sudo mkdir -m 777 -p ' . $path) : false;
}

/*
  function createPath($path) {
  if (is_dir($path)) return true; //se il path esiste, esco
  $old = umask(0);
  mkdir($path, 0777, true);
  umask($old);
  $parent = dirname($path);
  foreach ($array as $value) {

  }
  }
 */

function url_exists($url) {
    if (!$fp = curl_init($url))
        return false;
    return true;
}

function GetSoapHeaders($response) {
    // Find first occurance of xml tag
    /*
      preg_match('/<soap:Envelope.*?>(.*)<\/soap:Envelope>/i', $response, $match);
      preg_match('/<return.*?>(.*)<\/return>/i', $response, $match);
     */

    $delimiter = '#';
    $startTag = '<return>';
    $endTag = '</return>';
    $regex = $delimiter . preg_quote($startTag, $delimiter)
            . '(.*)'
            . preg_quote($endTag, $delimiter)
            . $delimiter
            . 's';
    preg_match($regex, $response, $match);

    if (isset($match['0'])) {
        $xml = $match['0'];
    } else {
        $xml = false;
    }
    return $xml;
}

function object2array($object) {
    return @json_decode(@json_encode($object), 1);
}

function CheckVol($file_to_check, $original_extension, $date_verify = '') {
    $arrXML['error'] = 'Timeout VOL';
    switch ($original_extension) {
        case ".pdf":
            $volTag = "VerifyPDF";
            break;
        case ".p7m":
            $volTag = "VerifyP7M";
            break;

        case ".xml":
            $volTag = "VerifyXML";
            break;

        case ".tsd":
            $volTag = "VerifyTimeStampDataV2";
            break;

        case ".m7m":
            $volTag = "VerifyP7MSubLevel";
            break;
    }


    $parms = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:act="http://actalisVol/">'
            . '<soapenv:Header/>'
            . '<soapenv:Body>'
            . '<act:' . $volTag . '>'
            . '<fileContent>' . base64_encode(file_get_contents($file_to_check)) . '</fileContent>'
            . '<verifAllaData>' . $date_verify . '</verifAllaData>'
            . '</act:' . $volTag . '>'
            . '</soapenv:Body>'
            . '</soapenv:Envelope>';

    //$sXML = sc_webservice("curl ", vol_endpoint, vol_port, "POST", $parms, $curl_param, 30);
    $sXML = CurlXML($parms, vol_endpoint, '60', 'xml');

    $data = GetSoapHeaders($sXML); //Estrapolo la risposta XML

    if ($data === false) { //ARss non risponde
        $resp['global_validation'] = 'KO';
        $resp['vol_status'] = 'VOL Non Disponibile';
        return $resp;
    }

    //Stampo il file
    //file_put_contents('../out.xml', $sXML);


    $oXML = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_PARSEHUGE); //Carico l'XML
    $arrXML = object2array($oXML); //Genero un array dalla risposta
    //var_dump($arrXML);
    $global_validation = false;
    $single_error = false;
    $resp['global_validation'] = 'KO'; //Vediamo poi se lo mettiamo in ok
    $resp['global_validation_message'] = "";
    $resp['vol_status'] = $arrXML['error'];

    //MG 2019 02 - Estrapolo anche il file originale
    $resp['originalFile'] = $arrXML['originalFile'];

    if ($resp['vol_status'] == 'OK') {
        //Controllo se la ripsota è singola o multipla

        if (isset($arrXML['signers']['sigType'])) { //sono in firma singola
            $resp['signers'][0]['status'] = $arrXML['signers']['sigValidation'];
            //$resp['signers'][0]['message'] = $arrXML['signers']['sigValidationMessage'];
            //MG 2018 07 - Gestione degli warning
            $resp['signers'][0]['message'] = is_array($arrXML['signers']['sigValidationMessage']) ? implode(" ", $arrXML['signers']['sigValidationMessage']) : $arrXML['signers']['sigValidationMessage'];

            $resp['signers'][0]['common_name'] = $arrXML['signers']['certificate']['certSubject']['commonName'];
            $resp['signers'][0]['codice_fiscale'] = $arrXML['signers']['certificate']['certSubject']['codiceFiscale'];
            if (($resp['signers'][0]['status'] == 'Valida') || ($resp['signers'][0]['status'] == 'con Warning')) {
                $global_validation = true;
                $resp['global_validation_message'] = $resp['signers'][0]['message'];
            } else {
                $single_error = true;
                $resp['global_validation_message'] = $resp['signers'][0]['message'];
            }
        } else { //sono in firma multipla
            $i = 0;
            foreach ($arrXML['signers'] as $signer) {
                $resp['signers'][$i]['status'] = $signer['sigValidation'];

                //$resp['signers'][$i]['message'] = $signer['sigValidationMessage'];

                $resp['signers'][$i]['message'] = is_array($signer['sigValidationMessage']) ? implode(" ", $signer['sigValidationMessage']) : $signer['sigValidationMessage'];

                $resp['signers'][$i]['common_name'] = $signer['certificate']['certSubject']['commonName'];
                $resp['signers'][$i]['codice_fiscale'] = $signer['certificate']['certSubject']['codiceFiscale'];
                if (($resp['signers'][$i]['status'] == 'Valida') || ($resp['signers'][$i]['status'] == 'con Warning')) {
                    $global_validation = true;
                    $resp['global_validation_message'] .= $resp['signers'][$i]['message'];
                } else {
                    $single_error = true;
                    $resp['global_validation_message'] .= $resp['signers'][$i]['message'];
                }
                $i++;
            }
        }

        //Alla fine controllo se almeno una delle firme è in errore

        if ($single_error) {
            $resp['global_validation'] = 'KO';
        } else {
            if ($global_validation) {
                $resp['global_validation'] = 'OK';
            }
        }
    }

    return $resp;
}

function CheckVol2($file_to_check, $original_extension, $date_verify = '', $permissive_mode = false, $type_ver, $original = false) {
    $arrXML['error'] = 'Timeout VOL';
    $resp['Vol_Exit'] = 0;


    $parms = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:act="http://actalisVol/">'
            . '<soapenv:Header/><soapenv:Body><act:VerificationV2><fileInfo>'
            . '<fileName>' . basename($file_to_check) . '</fileName><fileExtension>' . str_replace(".", "", $original_extension) . '</fileExtension>'
            . '<autoDetectType>true</autoDetectType></fileInfo>'
            . '<fileContent>' . base64_encode(file_get_contents($file_to_check)) . '</fileContent>'
            . '<verifAllaData>' . $date_verify . 'T' . date('h:i:s') . '.000+02:00</verifAllaData></act:VerificationV2>'
            . '</soapenv:Body></soapenv:Envelope>';

    //$sXML = sc_webservice("curl ", vol_endpoint, vol_port, "POST", $parms, $curl_param, 30);
    $sXML = CurlXML($parms, vol_endpoint, '10', 'xml');

    //$data = GetSoapHeaders($sXML); //Estrapolo la risposta XML

    $oXML = xml2array($sXML, 0, 'tag');

    $risp_base = isset($oXML['S:Envelope']['S:Body']['ns2:VerificationV2Response']['ReturnSL']) ? $oXML['S:Envelope']['S:Body']['ns2:VerificationV2Response']['ReturnSL'] : [];

    if (empty($risp_base)) { //ARss non risponde
        $resp['vol_status'] = 'VOL Non Disponibile';
        $resp['Vol_Exit'] = 4;
        return $resp;
    }
    //Stampo il file
    //file_put_contents('../out1_'. time().'.xml', json_encode($risp_base));

    $global_validation = false;
    $single_error = false;
    $resp['global_validation'] = $risp_base['error'];
    $resp['operation'] = $risp_base['operation'];
    $resp['global_validation_message'] = "";
    $error_sign = 0;
    $error_timestamp = 0;
    $error_cert_end = 0;
    $error_cert_rev = 0;

    //MG 2020 02 - Se richiesto estraggo anche il file originale
    if ($original === TRUE) {
        $resp['file_content'] = $risp_base['originalFile'];
    }

    //MG 2019 08 - Gestico i cas idi warning come positivi in caso permissivo
    $ack_return[] = 'Valida';
    if ($permissive_mode === true) {
        $ack_return[] = 'con Warning';
    }

    if ($risp_base['error'] != 'OK') {
        $resp['Vol_Exit'] = 10;
        return $resp;
    }

    //MG 2019 02 - Estrapolo anche il file originale
    //$resp['originalFile'] = $risp_base['originalFile'];
    //Controllo se la ripsota è singola o multipla

    if (isset($risp_base['signers']['sigType'])) { //sono in firma singola
        $resp['signers'][0]['status'] = $risp_base['signers']['sigValidation'];
        //$resp['signers'][0]['message'] = $arrXML['signers']['sigValidationMessage'];
        //MG 2018 07 - Gestione degli warning
        $resp['signers'][0]['message'] = is_array($risp_base['signers']['sigValidationMessage']) ? implode(" ", $risp_base['signers']['sigValidationMessage']) : $risp_base['signers']['sigValidationMessage'];

        $resp['signers'][0]['common_name'] = $risp_base['signers']['certificate']['certSubject']['commonName'];
        $resp['signers'][0]['codice_fiscale'] = $risp_base['signers']['certificate']['certSubject']['codiceFiscale'];

        $resp['signers'][0]['certRevoked'] = $risp_base['signers']['certificate']['certRevocation']['certRevoked'];
        ($resp['signers'][0]['certRevoked'] == "false") ? $error_cert_rev : $error_cert_rev++;

        $resp['signers'][0]['certValid'] = $risp_base['signers']['certificate']['certValid'];
        ($resp['signers'][0]['certValid'] == "false") ? $error_cert_end++ : $error_cert_end;

        $resp['signers'][0]['tsSignVaild'] = isset($risp_base['signers']['timeStamp']['tsSignVaild']) ? $risp_base['signers']['timeStamp']['tsSignVaild'] : null;

        // 2021-04-28 - SR & MG
        $resp['signers'][0]["tsGenTime"] = isset($risp_base['signers']['timeStamp']["tsGenTime"]) ? $risp_base['signers']['timeStamp']["tsGenTime"] : null;


        (in_array($resp['signers'][0]['tsSignVaild'], ["false", null])) ? $error_timestamp++ : $error_timestamp;

        (in_array($risp_base['signers']['sigValidation'], $ack_return)) ? $error_sign : $error_sign++;

        $resp['global_validation_message'] .= $resp['signers'][0]['message'];
    } else { //sono in firma multipla
        $i = 0;
        foreach ($risp_base['signers'] as $signer) {
            $resp['signers'][$i]['status'] = $signer['sigValidation'];

            //$resp['signers'][$i]['message'] = $signer['sigValidationMessage'];

            $resp['signers'][$i]['message'] = is_array($signer['sigValidationMessage']) ? implode(" ", $signer['sigValidationMessage']) : $signer['sigValidationMessage'];

            $resp['signers'][$i]['common_name'] = $signer['certificate']['certSubject']['commonName'];
            $resp['signers'][$i]['codice_fiscale'] = $signer['certificate']['certSubject']['codiceFiscale'];

            $resp['signers'][$i]['certRevoked'] = $signer['certificate']['certRevocation']['certRevoked'];
            ($resp['signers'][$i]['certRevoked'] == "false") ? $error_cert_rev : $error_cert_rev++;

            $resp['signers'][$i]['certValid'] = $signer['certificate']['certValid'];
            ($resp['signers'][$i]['certValid'] == "false") ? $error_cert_end++ : $error_cert_end;

            $resp['signers'][$i]['tsSignVaild'] = isset($signer['timeStamp']['tsSignVaild']) ? $signer['timeStamp']['tsSignVaild'] : null;

            // 2021-04-28 - SR & MG
            $resp['signers'][$i]["tsGenTime"] = isset($signer['timeStamp']["tsGenTime"]) ? $signer['timeStamp']["tsGenTime"] : null;


            (in_array($resp['signers'][$i]['tsSignVaild'], ["false", null])) ? $error_timestamp++ : $error_timestamp;

            (in_array($signer['sigValidation']['sigValidation'], $ack_return)) ? $error_sign : $error_sign++;


            $resp['global_validation_message'] .= $resp['signers'][$i]['message'] . " - ";
            $i++;
        }
    }

    //Alla fine controllo se almeno una delle firme è in errore
    if ($error_sign > 0) {
        $resp['Vol_Exit'] = 6;
    }

    if ($error_timestamp > 0 && $type_ver == 2) {
        $resp['Vol_Exit'] = 7;
    }

    if ($error_cert_rev > 0) {
        $resp['Vol_Exit'] = 9;
    }

    if ($error_cert_end > 0) {
        $resp['Vol_Exit'] = 8;
    }

    return $resp;
}

function GetXDSDoc($xds_url, $xds_port, $xds_path, $repo_uid, $doc_uid, $req_type) {

    $parms = '<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
	<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
		<wsa:To>http://' . $xds_url . ':' . $xds_port . $xds_path . '</wsa:To>
		<wsa:Action>urn:ihe:iti:2007:RetrieveDocumentSet</wsa:Action>
		<wsa:MessageID>' . gen_uuid() . '</wsa:MessageID>
	</soapenv:Header>
	<soapenv:Body>
		<RetrieveDocumentSetRequest xmlns="urn:ihe:iti:xds-b:2007" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
			<DocumentRequest>
				<RepositoryUniqueId>' . $repo_uid . '</RepositoryUniqueId>
				<DocumentUniqueId>' . $doc_uid . '</DocumentUniqueId>
			</DocumentRequest>
		</RetrieveDocumentSetRequest>
	</soapenv:Body>
</soapenv:Envelope>';

    WriteLog(">" . __LINE__ . "< " . "Richiesta->" . $parms, connector_logfile);

    $sXML = CurlXML($parms, "http://" . $xds_url . ":" . $xds_port . $xds_path, '60', 'xml', "application/soap+xml;charset=UTF-8;action=\"urn:ihe:iti:2007:RetrieveDocumentSet\"");

    //WriteLog(">" . __LINE__ . "< " . "Risposta->" . $sXML." ->"."http://" . $xds_url . ":" . $xds_port . $xds_path, connector_logfile,"DEBUG");

    $data = parse_xop_response($sXML);

    return $data;
}

function parse_xop_response($response) {
    if (strpos($response, "Content-Type: application/xop+xml") !== false) {
        $xopSpilt = substr($response, 0, stripos($response, "\r\n"));
        $responses = explode($xopSpilt, $response);
        for ($i = 0, $size = count($responses); $i < $size; ++$i) {
            //if (stripos($responses[$i], "Content-ID: <soapPart>") > 1)
            if (stripos($responses[$i], "Content-ID: ") > 1) {
                //$responses[$i] = substr($responses[$i], stripos($responses[$i], "Content-ID: <soapPart>") + 26);
                $responses[$i] = substr($responses[$i], stripos($responses[$i], "Content-ID: ") + 75);
                $xopElements = explode("<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" href=\"", $responses[$i]);
                if (count($xopElements) > 1) {
                    $cid = rawurldecode(substr($xopElements[1], 4, stripos($xopElements[1], "\"/>") - 4));
                    $xopElements[1] = substr($xopElements[1], stripos($xopElements[1], "\"") + 3);
                    for ($x = 0, $size = count($responses); $x < $size; ++$x) {
                        if (stripos($responses[$x], "Content-ID: <" . $cid . ">") > 1) {
                            $object = substr($responses[$x], stripos($responses[$x], "Content-ID: <" . $cid . ">") + strlen($cid) + 18);
                        }
                    }
                    //$response = $xopElements[0] . base64_encode($object) . $xopElements[1];
                    $response = base64_encode(substr($object, 0, -2)); //Restituisco solo il base64 trimmato per non prendere porcherie dell'ultima riga
                    return $response;
                }
            }
        }
    }
    return false;
}

function CurlKripton($json_content) {
    $headers = array(
        "Content-type: application/json;charset=\"utf-8\"",
        "Accept: application/json",
        "Cache-Control: no-cache",
        "Pragma: no-cache",
        "SOAPAction: \"run\""
    );

    //open connection
    $ch = curl_init();

    $url = fe_endpoint . "kw_connector/index.php";

    echo $url . "<br>";

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 240); //Impostato fisso a 240 secondi
    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
    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)) {
        $result_arr = array(
            'status' => false,
            'descr' => curl_error($ch)
        );

        $result = json_encode($result_arr);
    }

    //close connection
    curl_close($ch);


    $oJSON = json_decode($result, true);

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

function CurlXML($xml_content, $url, $timeout = 240, $return_type = "array", $content_type = "text/xml", $action = "http://") {
    //open connection

    ob_start();
    $out = fopen('php://output', 'w');

    $ch = curl_init();

    //CURLOPT_HEADER => 0,
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_STDERR, $out);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_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
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: " . $content_type,
        "Content-length: " . strlen($xml_content), "Connection: close", "charset=UTF-8", "SOAPAction: " . $action));

    //execute post
    $result = curl_exec($ch);


    //MG 2017 08 - Gestisco gli errori
    if (curl_errno($ch)) {
        $result_arr = array(
            'status' => false,
            'curl_error' => true,
            'descr' => curl_error($ch)
        );

        $result = json_encode($result_arr);
    }

    //close connection
    curl_close($ch);
    fclose($out);
    $debug = ob_get_clean();

    //echo($debug);
    // $xml = simplexml_load_string($result);
    // $json = json_encode($xml);

    $oJSON = ($return_type == "array") ? xml2array($result) : $result;

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

function is_connected($url, $port = '80') {
    $connected = fsockopen($url, $port);
    //website, port  (try 80 or 443)
    if ($connected) {
        $is_conn = true; //action when connected
        fclose($connected);
    } 
    else {
        $is_conn = false; //action in connection failure
    }
    return $is_conn;
}

function zombie_check($session_id = null) {
    ## Verifico gli fk già in elaborazione
    WriteLog("   >ZombieCheck Start<", archiver_logfile, "INFO");

    $active_sess = 0;

    $my_files = query_select("select distinct session_start, nr_doc, session_id, doc_size, id, pid "
            . "from kxa_session where session_id <> '" . $session_id . "' and session_end is NULL");

    echo "Zombie Check<br>";
    var_dump($my_files);

    $session_parts = explode("-", $session_id);

    $canale = $session_parts[0];
    echo "Canale: " . $canale . "<br>";





    for ($x = 0; $x < nrRecords($my_files); $x++) {

        $zombie_start = $my_files[$x]['session_start'];
        $zombie_doc = $my_files[$x]['nr_doc'];
        $zombie_size = $my_files[$x]['doc_size'];
        $dead_session_id = $my_files[$x]['session_id'];
        $session_pk = $my_files[$x]['id'];
        $zombie_pid = $my_files[$x]['pid'];
        $zombie_tot = nrRecords($my_files);

        echo "Sessione : " . $dead_session_id . "<br>";


        if (!IsNull($zombie_pid)) {
            //Nuova gestione. Controllo solo pid per capire se attivo
            if (posix_getpgid($zombie_pid) === FALSE) {
                //Chiudo la sessione
                $adesso2 = date('Y-m-d H:i:s');
                $update_table = 'kxa_session';
                $update_where = "session_id = '" . $dead_session_id . "'";
                $update_fields = array(
                    "session_end = '" . $adesso2 . "'",
                    "nr_doc = '0'",
                    "status = 8"
                );
                $update_sql = 'UPDATE ' . $update_table
                        . ' SET ' . implode(', ', $update_fields)
                        . ' WHERE ' . $update_where;
                query_execute($update_sql);

                //MG 2017 08 - Riporto a STORED la tabella submission per essere presa di nuovo in considerazione
                $update_table = 'submissions';
                $update_where = "kxa_session_fk = '" . $session_pk . "'";
                $update_fields = array(
                    "status = 'STORED'",
                    "kxa_session_fk = NULL"
                );
                $update_sql = 'UPDATE ' . $update_table
                        . ' SET ' . implode(', ', $update_fields)
                        . ' WHERE ' . $update_where;
                query_execute($update_sql);
                WriteLog("   >ZombieCheck< Sessione $dead_session_id bloccato - PID:$zombie_pid inesistente ", archiver_logfile, "WARNING");
            } else {
                WriteLog("   >ZombieCheck< Sessione $dead_session_id, dimensione: " . formatSizeUnits($zombie_size) . " - PID:$zombie_pid attivo", archiver_logfile, "INFO");
                $active_sess++;
            }
        } else {

            $dim_sessione = $zombie_size / 1024;
            echo "Dimensioni sessione: " . $dim_sessione . " Kb<br>";

            $adesso = date('Y-m-d H:i:s');
            echo "Ora attuale: " . $adesso . "<br>";
            $my_zombie = query_select("select session_start, session_end, nr_doc, doc_size from kxa_session where session_end IS NOT NULL "
                    . "and nr_doc <> 0 and doc_size <> 0  ORDER BY id DESC LIMIT 2");
            //print_r($my_zombie);
            $sess_1_start = $my_zombie[0]['session_start'];
            $sess_1_stop = $my_zombie[0]['session_end'];
            $sess_1_doc = $my_zombie[0]['nr_doc'];
            $sess_1_size = $my_zombie[0]['doc_size'];

            $sess_2_start = $my_zombie[1]['session_start'];
            $sess_2_stop = $my_zombie[1]['session_end'];
            $sess_2_doc = $my_zombie[1]['nr_doc'];
            $sess_2_size = $my_zombie[1]['doc_size'];

            //Sessione 1

            $med_1_size = $sess_1_size / 1024;
            echo "Dimensione ultima Sessione: " . $med_1_size . " Kb<br>";

            $differenceInSeconds1 = strtotime($sess_1_stop) - strtotime($sess_1_start);
            echo "Secondi Differenza1: " . $differenceInSeconds1 . "<br>";

            $kbs1 = ($differenceInSeconds1 == 0) ? 0 : ($med_1_size / $differenceInSeconds1);

            $eta_doc1 = ($kbs1 == 0) ? 0 : ($dim_sessione / $kbs1);

            $estimated_end = floor(strtotime($zombie_start) + ($eta_doc1 * ($zombie_tot * 3)));
            $stima_fine = date("Y-m-d H:i:s", $estimated_end);

            $estimated_end_reale = floor(strtotime($zombie_start) + ($eta_doc1 * $zombie_tot));
            $stima_fine_reale = date("Y-m-d H:i:s", $estimated_end_reale);

            echo "Kb/sec1: " . $kbs1 . "<br>";

            echo "Eta doc1: " . $eta_doc1 . "<br>";
            echo "Fine Stimata1 300%: " . $stima_fine . "<br>";

            if (strtotime($stima_fine) > strtotime($adesso)) {
                echo "<br><b>dentro stima</b><br><br>";
                $session1 = true;
            } else {
                echo "<br><b>fuori stima</b><br><br>";
                $session1 = false;
            }

            //Sessione 2

            $med_2_size = $sess_2_size / 1024;
            echo "Dimensioni penultima sessione: " . $med_2_size . " Kb<br>";

            $differenceInSeconds2 = strtotime($sess_2_stop) - strtotime($sess_2_start);
            echo "Secondi Differenza2: " . $differenceInSeconds2 . "<br>";

            $kbs2 = ($differenceInSeconds2 == 0) ? 0 : ($med_2_size / $differenceInSeconds2);

            $eta_doc2 = ($kbs2 == 0) ? 0 : ($dim_sessione / $kbs2);
            $estimated_end2 = floor(strtotime($zombie_start) + ($eta_doc2 * ($zombie_tot * 3)));
            echo "Integer: $estimated_end2<br>";
            $stima_fine2 = date("Y-m-d H:i:s", $estimated_end2);


            echo "Kb/sec2: " . $kbs2 . "<br>";

            echo "Eta doc2: " . $eta_doc2 . "<br>";
            echo "Fine Stimata2 300%: " . $stima_fine2 . "<br>";

            if (strtotime($stima_fine2) > strtotime($adesso)) {
                echo "<br><b>dentro stima</b><br><br>";
                $session2 = true;
            } else {
                echo "<br><b>fuori stima</b><br><br>";
                $session2 = false;
            }
            //Controllo attività sul file log
            WriteLog("   >ZombieCheck< Sessione $dead_session_id, dimensione: " . formatSizeUnits($zombie_size) . " Stima fine: " . $stima_fine_reale, archiver_logfile, "INFO");
            $actual_log = work_dir . "tmp/" . $dead_session_id . ".log";
            if (!file_exists($actual_log)) {
                echo "   >ZombieCheck< Sessione chiusa nel frattempo, genero un file fake per evitare loop";
                //WriteLog("   >ZombieCheck< Sessione chiusa nel frattempo, genero un file fake $dead_session_id per evitare loop. Non preoccupatevi", archiver_logfile, "WARNING");
                touch($actual_log);
                continue;
            }
            $fp1 = fopen($actual_log, "r");
            $fst1 = fstat($fp1);

            sleep(3); //Aspetto 5 secondi
            if (!file_exists($actual_log)) {
                echo "Sessione chiusa nel frattempo";
                continue;
            }

            $chek_zip = glob(work_dir . "tmp/$dead_session_id.zip.*")[0];


            $zip_freeze = false;
            if (!IsNull($chek_zip)) {

                //Ne dovrebbe esistere solo 1
                $rilevazione1 = fopen($chek_zip, "r");
                $rilevazione1s = fstat($rilevazione1);
                $rilevazione1l = $rilevazione1s[7];
                sleep(6);
                $rilevazione2 = fopen($chek_zip, "r");
                $rilevazione2s = fstat($rilevazione2);
                $rilevazione2l = $rilevazione2s[7]; //dimensione
                $rilevazione2t = $rilevazione2s[10]; //tempo ultima modifica inode

                $percent = (100 * $rilevazione2s['size']) / $zombie_size;

                WriteLog("   >ZombieCheck< ZIP: $chek_zip Percentuale: " . $percent, archiver_logfile);

                WriteLog("   >ZombieCheck<  1->$rilevazione1l   2->$rilevazione2l", archiver_logfile);
                if ($rilevazione1l == $rilevazione2l) {
                    $zip_freeze = true;
                }
            }

            $fp2 = fopen($actual_log, "r");
            $fst2 = fstat($fp2);

            $adesso_d = strtotime("-10 minutes"); //Per sicurezza di arrivo allo stoprsync
            $last_inode = $fst1[10];

            echo "Controllo se il file: " . $actual_log . " E' ancora attivo....<br>";
            $dead = false;
            if ($fst1[7] == $fst2[7]) { //Controllo se aumenta di dimensione
                echo "file morto<br>";
                if ($last_inode < $adesso_d) {

                    $dead = ($fst1[7] != 0) ? true : false;
                    echo "file veramente morto ma solo se non è uguale a zero la dimensione, altrimenti vuol dire che è il fake log >$dead<<br>";
                }
            } else {
                echo "file vivo<br>";
            }

            WriteLog("   >ZombieCheck< Logfile, restano: " . substr(file_get_contents($actual_log), -10) . "Dim1: " . $fst1[7] . " -Dim2: " . $fst2[7], archiver_logfile, "INFO");


            //Faccio il controllo finale
            if ((!$session1 && !$session2) || $dead || $zip_freeze) {
                //In array_var metto tutte le variabili utili per la notifica
                $arr_var = array("eta1" => $stima_fine,
                    "eta2" => $stima_fine2,
                    "sess_dim" => $dim_sessione,
                    "kbs1" => $kbs1,
                    "kbs2" => $kbs2,
                );
                echo "Merda!<br>";

                //Chiudo la sessione
                $adesso2 = date('Y-m-d H:i:s');
                $update_table = 'kxa_session';
                $update_where = "session_id = '" . $dead_session_id . "'";
                $update_fields = array(
                    "session_end = '" . $adesso2 . "'",
                    "nr_doc = '0'",
                    "status = 8"
                );
                $update_sql = 'UPDATE ' . $update_table
                        . ' SET ' . implode(', ', $update_fields)
                        . ' WHERE ' . $update_where;
                query_execute($update_sql);

                //MG 2017 08 - Riporto a STORED la tabella submission per essere presa di nuovo in considerazione
                $update_table = 'submissions';
                $update_where = "kxa_session_fk = '" . $session_pk . "'";
                $update_fields = array(
                    "status = 'STORED'",
                    "kxa_session_fk = NULL"
                );
                $update_sql = 'UPDATE ' . $update_table
                        . ' SET ' . implode(', ', $update_fields)
                        . ' WHERE ' . $update_where;
                query_execute($update_sql);

                $mb_sess = round(($arr_var['sess_dim'] / 1024), 2);



                $mail_body = "Sessione: " . $dead_session_id
                        . "<br>Dimensioni sessione Attuale Mb: " . $mb_sess
                        . "<br>Sessione 1 al 200%: Fine Prevista " . $arr_var['eta1'] . " - Velocita Kbs: " . round($arr_var['kbs1'], 2)
                        . "<br>Sessione 2 al 200%: Fine Prevista " . $arr_var['eta2'] . " - Velocita Kbs: " . round($arr_var['kbs2'], 2)
                        . "<br>Il file log morto dalle: " . date("Y-m-d H:i:s", $last_inode) . ""
                        . "<br>Zip bloccato al : " . round($percent, 4) . "%"; // In formato HTML

                WriteLog(">" . __LINE__ . "<->" . $mail_body, archiver_logfile, "WARNING");
            } else {
                $active_sess++;
                echo "Tutto OK!";
            }
        }
    }
    WriteLog("   >ZombieCheck Stop< Check nr: " . $active_sess, archiver_logfile, "INFO");
    return $active_sess;
}

function get_next_in_sequence($str) {
    $letters = range('a', 'z');
    $arr = str_split($str);
    // Replace each character with numeric equivalent
    foreach ($arr as $key => $char) {
        $arr[$key] = array_search($char, $letters);
    }
    $digits = count($arr) - 1; // Count digits
    for ($i = $digits; $i > -1; $i--) { // Starting at the right-most spot, move left
        if ($i == $digits) // If this is the right most spot
            $arr[$i]++; // Increment it
        if ($arr[$i] == 26) { // If this spot has moved past "z"
            $arr[$i] = 0; // Set it to "a"
            if ($i != 0) // Unless it is the left most spot
                $arr[$i - 1]++;  // Carry the one to the next spot
        }
    }
    // Rebuild characters from numeric equivalent
    foreach ($arr as $key => $char) {
        $arr[$key] = $letters[$char];
    }
    $str = implode($arr);
    return $str;
}

function ext_gtw_channels($arr_ext_sys) {

    $arr_out = [];
    foreach ($arr_ext_sys as $this_gtw_key => $this_gateway_values) {

        $get_history = query_select("select sum(size_sent) as tot_sent, sum(size_elab) as tot_elab, min(active) as active from to_ext_gtw "
                . "where gtw_pk = $this_gtw_key AND date = '" . date('Y-m-d') . "'");
        $totsent = IsNull($get_history[0]['tot_sent']) ? 0 : $get_history[0]['tot_sent'];
        $totelab = IsNull($get_history[0]['tot_elab']) ? 0 : $get_history[0]['tot_elab'];
        $active = $get_history[0]['active'];

        if (($totsent == 0) || ($totelab == 0)) {
            $percent = 0;
        } else {
            $percent = (100 * $totelab) / $totsent;
        }

        $gtw_performance = $totsent - $totelab;

        foreach ($this_gateway_values["channels"] as $channel)
        {
            if (!isset($arr_out[$channel]))
            {
                $arr_out[$channel] = [];
            }

            if ($active == 1) {
                $arr_out[$channel][$this_gtw_key] = $gtw_performance;
            }
        }

    }

    foreach ($arr_out as $channel => $gateways) {
        $max_perf_gw = null;
        if (!empty($gateways)) {
            $best = min($gateways);
            $max_perf_gw = array_search($best, $gateways);
        }
        $arr_out[$channel]['max_performance'] = $max_perf_gw;
    }

    return $arr_out;

}


function ext_gtw_check($get_date_start, $get_date_end, $idx_days = 30) {
    $process_to_check = query_select("SELECT * FROM to_ext_gtw WHERE date BETWEEN '$get_date_start' AND '$get_date_end' ORDER BY gtw_pk, date");
    $arr_ext_sys = json_decode(ext_sys_endpoint, TRUE);

    $max_perf = [];
    foreach ($process_to_check as $single_process) {
        $arr_out[$single_process['gtw_pk']]['address'] = $arr_ext_sys[$single_process['gtw_pk']]['address'];
        $arr_out[$single_process['gtw_pk']]['active'] = $arr_ext_sys[$single_process['gtw_pk']]['active'];
        $arr_out[$single_process['gtw_pk']]['date'][$single_process['date']]['size_sent'] = (int) $single_process['size_sent'];
        $arr_out[$single_process['gtw_pk']]['date'][$single_process['date']]['size_elab'] = (int) $single_process['size_elab'];
        $arr_out[$single_process['gtw_pk']]['date'][$single_process['date']]['size_sent_pretty'] = formatSizeUnits($single_process['size_sent']);
        $arr_out[$single_process['gtw_pk']]['date'][$single_process['date']]['size_elab_pretty'] = formatSizeUnits($single_process['size_elab']);
        $arr_active_gtw[] = $single_process['gtw_pk'];
    }

    foreach ($arr_ext_sys as $this_gtw_k => $this_gateway_values) {

        //2020 04 - Logica di carico
        $idx_days1 = $idx_days + 1;
        $get_history = query_select("select sum(size_sent) as tot_sent, sum(size_elab) as tot_elab from to_ext_gtw "
                . "where gtw_pk = $this_gtw_k AND date > now() - interval '" . $idx_days1 . " days' AND date < now() - interval '1 days'");
        $sent30 = IsNull($get_history[0]['tot_sent']) ? 0 : $get_history[0]['tot_sent'];
        $elab30 = IsNull($get_history[0]['tot_elab']) ? 0 : $get_history[0]['tot_elab'];

        if (($sent30 == 0) || ($elab30 == 0)) {
            $percent30 = 0;
        } else {

            $percent30 = (100 * $elab30) / $sent30;
        }

        //$percent30 = (100 * $elab30) / $sent30;

        $arr_out[$this_gtw_k][$idx_days . '_day_performance_idx'] = round($percent30, 2);

        $get_history = query_select("select sum(size_sent) as tot_sent, sum(size_elab) as tot_elab, min(active) as active from to_ext_gtw "
                . "where gtw_pk = $this_gtw_k AND date = '" . date('Y-m-d') . "'");
        $sent1 = IsNull($get_history[0]['tot_sent']) ? 0 : $get_history[0]['tot_sent'];
        $elab1 = IsNull($get_history[0]['tot_elab']) ? 0 : $get_history[0]['tot_elab'];
        $active1 = $get_history[0]['active'];

        if (($sent1 == 0) || ($elab1 == 0)) {
            $percent1 = 0;
        } else {

            $percent1 = (100 * $elab1) / $sent1;
        }

        $gtw_performance = $sent1 - $elab1;

        $arr_out[$this_gtw_k]['today_performance_idx'] = round($percent1, 2);
        $arr_out[$this_gtw_k]['today_delta'] = $gtw_performance;

        //Genero un array con le performace singole
        if ($active1 == 1) {
            $max_perf[$this_gtw_k] = $gtw_performance;
        }


        if (!in_array($this_gtw_k, $arr_active_gtw)) {
            $arr_out[$this_gtw_k]['address'] = $this_gateway_values['address'];
            $arr_out[$this_gtw_k]['active'] = $this_gateway_values['active'];
        }

        $arr_out[$this_gtw_k]['channels'] = $this_gateway_values['channels'];
    }

//Adesso prendo il gateway con la migliore performance
    $best = min($max_perf);
    $arr_out['max_performance_gtw'] = array_search($best, $max_perf);

    return $arr_out;
}

function dir_is_empty($dir) {
    $handle = opendir($dir);
    while (false !== ($entry = readdir($handle))) {
        if ($entry != "." && $entry != "..") {
            closedir($handle);
            return false;
        }
    }
    closedir($handle);
    return true;
}

function file_in_use($file) {
    $directory = dirname($file);
    $filename = basename($file);
    return (exec("sudo lsof +d $directory | grep -c -i $filename") == "0");
}

function rrmdir($path) {
    $dir = opendir($path);
    while(false !== ( $file = readdir($dir)) ) {
        if (( $file != '.' ) && ( $file != '..' )) {
            $full = $path . '/' . $file;
            if ( is_dir($full) ) {
                rrmdir($full);
            }
            else {
                unlink($full);
            }
        }
    }
    closedir($dir);
    rmdir($path);
}


function deleteFile($path) {

    //se path è vuoto, esco
    if (empty($path))
    	return true;

    //se non esiste il file, esco
    if (!file_exists($path))
        return true;

    //se è una directory chiamo la funzione dedicata
    if (is_dir($path))
        return deleteFolder($path);

    $is_deleted = @unlink($path);

    //se la cancellazione va a buon fine, esco
    if ($is_deleted)
        return true;

    $dir = dirname($path);
    if (empty($dir)) $dir = $path;

    //faccio l'override di utente:gruppo della directory del file e tutti i figli
    exec("sudo chown -R apache:apache " . escapeshellarg($dir));

    //faccio l'override dei permessi della directory del file (permesso di scrittura e esecuzione, entrambi richiesti per la cancellazione)
    ////Deleting, linking, and renaming files require execute permission too, as discussed below).
    ////This is because such operations also require access to a file's inode in addition to the file's name.
    ////While read permission will allow access to the name of a file in a directory, execute permission is needed to access the inodes of files in that directory.
    if (!is_writable($dir) || !is_executable($dir)) {
        $old = umask(0);
        if (!@chmod($dir, "0777"))
        	exec("sudo chmod 0777 " . escapeshellarg($dir));
        umask($old);
    }

    $i=0;
    do {
        //riprovo la cancellazione (aspetto 100us se il file risulta ancora aperto/in uso - totale attesa 1ms)
        $is_deleted = file_exists($path) ? @unlink($path) : true;
        if (!$is_deleted) usleep(100);
    } while (++$i <= 10 && !$is_deleted && file_in_use($path));

    //non sono stato in grado di cancellare il file
    if (file_exists($path))
        return false;

    //cancellazione completata
    return true;
}

function deleteFolder($path) {

    //se path è vuoto, esco
    if (empty($path))
    	return true;

    //se non esiste il file, esco
    if (!file_exists($path))
        return true;

    //se è un file chiamo la funzione dedicata
    if (is_file($path))
        return deleteFile($path);

    //cancello la directory
    $is_deleted = @rmdir($path);

    //se la cancellazione va a buon fine, esco
    if ($is_deleted)
        return true;

    //faccio l'override di utente:gruppo della directory e tutti i figli
    exec("sudo chown -R apache:apache " . escapeshellarg($path));
    //cambio i permessi (r/w/e) della directory e tutti i figli
    exec("sudo chmod -R 0777 " . escapeshellarg($path));

    //faccio l'override dei permessi della directory del file (permesso di scrittura e esecuzione, entrambi richiesti per la cancellazione)
    ////Deleting, linking, and renaming files require execute permission too, as discussed below).
    ////This is because such operations also require access to a file's inode in addition to the file's name.
    ////While read permission will allow access to the name of a file in a directory, execute permission is needed to access the inodes of files in that directory.
    $dir = dirname($path);
    if (empty($dir)) $dir = $path;
    if (!is_writable($dir) || !is_executable($dir)) {
        $old = umask(0);
        if (!@chmod($dir, "0777"))
        	exec("sudo chmod 0777 " . escapeshellarg($dir));
        umask($old);
    }

    //cancellazione ricorsiva di tutti i files e directories
    exec("sudo rm -rf " . escapeshellarg($path));

    //se esiste ancora la directory, verifico se ci sono file in uso
    if (file_exists($path)) {

        $count = intval(exec("sudo lsof +D $path | awk 'NR>1{print $1}' | wc -l"));
        if ($count > 0) {
            error_log("Trovati $count files in uso nella directory $path: \n" . shell_exec("sudo lsof +D $path | head"));
        }

        //non sono stato in grado di cancellare la directory
        return false;
    }

    //cancellazione completata
    return true;

}
