<?php

class serveur {

    const publicKey = 'DK_LOG_KEY';
    const iniFile = 'config/clients.ini';
    const access_log = 'logs/access.log';//'../../logs_agent_serveur/access.log';
    const errors_log = 'logs/errors.log';//'../../logs_agent_serveur/errors.log';

    private $privateKey;
    private $ip;
    private $ip_agent;
    private $ext_path;

    // Switch to true for DEBUG, then look at : decalog-webservices/serveur/logs/access.log
    public $log_access = false;     
    public $log_error = true;
    
    function __construct($sIdClient, $sExtPath = './') {
        if (!file_exists($sExtPath.'logs')) mkdir($sExtPath.'logs');
        $this->ip = $_SERVER['REMOTE_ADDR'];
        $this->ext_path = $sExtPath;
        $this->displayInfos();
        $this->readKeyClient($this->ext_path.self::iniFile, $sIdClient);
    }

    private function readKeyClient($sFile, $sClient) {
        try {
            $aTabConfig = @parse_ini_file($sFile, true);
            if (empty($aTabConfig)) throw new Exception;
            $this->privateKey = $aTabConfig[$sClient]['key'];
            $this->ip_agent = $aTabConfig[$sClient]['ip_agent'];
            $this->returnLog('Clé privée : '.$this->privateKey);
        }
        catch (Exception $e) {
            $this->returnError('Impossible de lire le fichier de configuration serveur', '530', true);
        }
    }

    //Retourne l'adresse IP du client appelé
    public function getIpClient() {
        return $this->ip_agent;
    }

    //Génération de la clé de connexion
    public function generateHashKey() {
        return md5(self::publicKey.$this->privateKey.date('Ymd'));
    }

    //Création de l'archive des classes nécessaires
    public function zip($idclient, $sName, $sVersion, $sHost, $sDest, $mode = '') {
        try {
            $zip = new ZipArchive();
            $zipName = $sName."_class_v".$sVersion.".zip";
            $filename = $this->ext_path."sigb_class/".$zipName;
            $res = $zip->open($filename, ZIPARCHIVE::CREATE);
            if ($res != TRUE) throw new Exception('open : '.$res);
			
			if (!$zip->addFile($this->ext_path."sigb_class/7za.exe", "7za.exe")) throw new Exception('Impossible d\'ajouter 7za.exe');
       	    if (!$zip->addFile($this->ext_path."sigb_class/wget.exe", "wget.exe")) throw new Exception('Impossible d\'ajouter wget.exe');
			if (!$zip->addFile($this->ext_path."sigb_class/update_agent.bat", "update_agent.bat")) throw new Exception('Impossible d\'ajouter update_agent.bat');
			if (!$zip->addFile($this->ext_path."sigb_class/update_agent.sh", "update_agent.sh")) throw new Exception('Impossible d\'ajouter update_agent.sh');
			if (!$zip->addFile($this->ext_path."sigb_class/majAgent.php", "majAgent.php")) throw new Exception('Impossible d\'ajouter majAgent.php');
			if (!$zip->addFile($this->ext_path."sigb_class/client.php", "client.php")) throw new Exception('Impossible d\'ajouter client.php');
			
            if (!$zip->addFile($this->ext_path."sigb_class/Stats.class.php", "Stats.class.php")) throw new Exception('Impossible d\'ajouter Stats.class.php');
            if (!$zip->addFile($this->ext_path."sigb_class/sigb.class.php", "sigb.class.php")) throw new Exception('Impossible d\'ajouter sigb.class.php');
            if ($sName == 'gabi') {
                if (!$zip->addFile($this->ext_path."sigb_class/JsonHttpClient.class.php", "JsonHttpClient.class.php")) throw new Exception('Impossible d\'ajouter JsonHttpClient.class.php');
            }
            if (!$zip->addFile($this->ext_path."sigb_class/".$sName.".class.php", $sName.".class.php")) throw new Exception('Impossible d\'ajouter '.$sName.'.class.php');
            if (!$zip->close()) throw new Exception('Impossible de fermer l\'archive');
            $this->returnLog('Archive de la classe "'.$sName.'" créée');
            //Calcul MD5 du fichier
            $md5file = md5_file($filename);
            //Envoi du fichier vers client
            if (!$this->copyZip($idclient, $filename, $sHost, $sDest.($mode == 'scp' || $mode == 'curl' ? '' : $sName."_class_v".$sVersion.".zip"), $mode)) return false;
            $this->returnLog('Archive envoyée');
            return array($zipName,$md5file);
        }
        catch (Exception $e) {
            $this->returnError('Erreur ZIP : '.$e->getMessage(), '510', true);
        }
    }

    //Copie de fichier
    private function copyZip($idclient, $sFile, $sHost, $sDest, $mode) {
        try {
            //Copie du fichier vers le client
            $this->returnLog('Déplacement de l\'archive vers le client...');
            if ($mode == 'scp') {
                $this->returnLog('Connexion SSH vers '.$sHost);
                if (!$connexionSSH = @ssh2_connect($sHost)) throw new Exception('ssh2_connect('.$sHost.')');
                if (!@ssh2_auth_password($connexionSSH, 'rr', 'rr')) throw new Exception('echec authentification SSH');
                if (!ssh2_scp_send($connexionSSH, $sFile, $sDest)) throw new Exception('erreur scp de '.$sFile.' vers '.$sDest);
            }
            else if ($mode == 'curl') {
                $result = $this->curl_send_file($idclient, 'http://'.$sHost.$sDest, $sFile, 'maj');
                if (!$result) {
                    return false;
                }
                else {
                    unlink($sFile);
                    $this->returnLog($result);
                }
            }
            else {
                if (!rename($sFile, $sDest)) throw new Exception('Impossible de déplacer l\'archive vers le client');
            }
            return true;
        }
        catch (Exception $e) {
            $this->returnError('Erreur copie : '.$e->getMessage(), '520', true);
            return false;
        }
    }

    //Envoi de fichier via CURL
    private function curl_send_file($idclient, $sURL, $sFile, $mode) {
        try {
            $this->returnLog('cURL : envoi de '.$sFile.' ('.filesize($sFile).' octets) vers '.$sURL);
            if (!$ch = curl_init()) throw new Exception('curl_init');
            if (!curl_setopt($ch, CURLOPT_HEADER, true)) throw new Exception('curl option : CURLOPT_HEADER');
            if (!@curl_setopt($ch, CURLOPT_BINARYTRANSFER, true)) throw new Exception('curl option : CURLOPT_BINARYTRANSFER');
            if (!curl_setopt($ch, CURLOPT_VERBOSE, true)) throw new Exception('curl option : CURLOPT_VERBOSE');
            if (!curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) throw new Exception('curl option : CURLOPT_RETURNTRANSFER');
            if (!curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)")) throw new Exception('curl option : CURLOPT_USERAGENT');
            if (!curl_setopt($ch, CURLOPT_URL, $sURL)) throw new Exception('curl option : CURLOPT_URL');
            if (!curl_setopt($ch, CURLOPT_INFILESIZE, filesize($sFile))) throw new Exception('curl option : CURLOPT_INFILESIZE');
            if (!curl_setopt($ch, CURLOPT_POST, true)) throw new Exception('curl option : CURLOPT_POST');            
            if (!curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect: '))) throw new Exception('curl option : CURLOPT_HTTPHEADER');
            if (version_compare(PHP_VERSION, '5.5.0') >= 0) {
                if (!curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false)) throw new Exception('curl option : CURLOPT_SAFE_UPLOAD');
            }
            $post = array(
                    'idclient' => $idclient,
                    'mode' => $mode,
                    'file' => new CURLFile((substr($sFile, 0, 2) == './' ? substr($_SERVER['SCRIPT_FILENAME'], 0, strrpos($_SERVER['SCRIPT_FILENAME'], "/") + 1) : '').$sFile)
            );
            print "<br>".$_SERVER['SCRIPT_FILENAME']."<br>";
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
            $response = curl_exec($ch);
            return $response;
        }
        catch (Exception $e) {
            $this->returnError('Erreur curl_send_file : '.$e->getMessage(), '520', false);
            return false;
        }
    }

    //Appel du client
    public function callClient($sURL, $sAction) {
        $this->returnLog('callClient');
        $error_mode = '';
        try {
            $arrContextOptions=array(
                "ssl"=>array(
                    "verify_peer"=>false,
                    "verify_peer_name"=>false,
                ),
		"http"=>array(
		    "header" => "User-Agent:MyAgent/1.0\r\n"
		),
            );
            $ret_file_exists = @file_get_contents(substr($sURL, 0, strpos($sURL, "client/") + 7)."lock.php", false, stream_context_create($arrContextOptions));
            if ($ret_file_exists === FALSE) throw new Exception('Vérification lock impossible');
            $this->returnLog("Retour fichier lock : ".$ret_file_exists);
            if ($ret_file_exists == '200') {
                    $this->returnLog('Lock...');
                    sleep(2); //Attente pour le déblocage
            }
            $retour = @file_get_contents($sURL, false, stream_context_create($arrContextOptions));
            if ($retour === FALSE) throw new Exception('Injoignable');
        }
        catch (Exception $e) {
            $this->returnError('Erreur d\'accès au client : '.$e->getMessage(), '400', true);
        }
        //Cas d'erreur
        if ($retour == 'maj') {
            $error_mode = 'maj';
        }
        if ($retour == 'install') {
            $error_mode = 'install';
        }

        // Do NOT ... Ajout de la réponse au log
        //$this->returnLog($retour);
        return array('error' => $error_mode, 'retour' => $retour);
    }

    private function remote_file_exists($sURL) {
        $ch = curl_init($sURL);
        if (!curl_setopt($ch, CURLOPT_NOBODY, true)) throw new Exception('options CURL');
        if (!curl_exec($ch)) throw new Exception('execution CURL');
        $retcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $retcode;
        }

    public function returnError($sError, $sErrorCode, $bExit = false) {
        if ($this->log_error) {
            file_put_contents($this->ext_path.self::errors_log, date("Ymd-H:i:s")." >> Serveur : Erreur : ".$sError." (code : ".$sErrorCode.")"."\r\n", FILE_APPEND);
        }
        if ($bExit) exit();
    }

    public function returnLog($sLog) {
        if ($this->log_access) {
            file_put_contents($this->ext_path.self::access_log, date("Ymd-H:i:s")." >> Serveur : ".$sLog."\r\n", FILE_APPEND);
        }
    }

    //Affichage des infos de la classe
    private function displayInfos() {
        $this->returnLog('Création classe serveur -- IP : '.$this->ip);
    }

}

?>