# StattBuchung Hilfezentrum : client.php

Created by Benedikt Wismans, last modified on Sep 21, 2017
PHP-Datei client.php zum Einbinden

    <?php

    /**
     *	Ein HTTP-Client in PHP unter Verwendung der curl-Bibliothek für den Zugriff auf die APIs
     *  im SDW-/BiDat-Ökosystem. Sollte im produktiven Einsatz immer SSL-gesichert verwendet werden.
     *
     *  Autor: sdw.systems
     */

    define("opcLogon", 1003);
    define("opcLogoff", 1002);

    define("httpOK", 200);
    define("httpMovedTemp", 302);

    //	Der Frontcontroller liegt im geschützten Bereich. Jeder Aufruf dieser URL mit egal was hinten dran verlangt
    //	Authentifizierung oder eine authentifizierte Session
    define("frontcontroller", "/SDW/resources/fc");

    //	Die Loginform liegt im public-Bereich. Aufurfe von URLs wie dem Frontcontroller werden bei nicht authtifizierten
    //	Session an diese URL weitergeleitet zur Authentifizierung
    define("loginform", '/SDW/j_security_check');

    /**
     *	Öffnet eine Verbindung zu einem Tomcatcontainer mit container based authentifiaction.
     *  Der Vorgag ist 2 stufig, als 3. Stufe wird dann noch das eigentliche Login gemacht.
     *
     *  @param	{String}	$url	Die URL des Servers in der Form http(s)://fqdn:port, natürlich ohne Slash am Ende
     *  @param	{String}	&user	Der Nutzername des Accounts
     *  @param	{String}	&pass	Das Kennwort des Accounts
     *  @param	{Boolean}	$debug	Debug-Flag
     *  @returns {Object}	Ein Assoc Array mit Metadaten der geöffneten und authentifizierten Session. Muss in folgende requests reingesteckt werden.
     *
     */
    function login($url, $user, $pass, $debug) {

    	$sessionId = uniqid('', true);
    	$cookie='/tmp/'.$sessionId;

    	//	stage 1: call protected ressource: Frontcontroller with Opcode Login
    	$curl = curl_init();
    	curl_setopt_array($curl, array(
    		CURLOPT_COOKIESESSION => 1,
    		CURLOPT_RETURNTRANSFER => 1,
        	CURLOPT_URL => $url.frontcontroller.'?opc='.opcLogon,
        	CURLOPT_USERAGENT => 'PHP Request '.$sessionId
    	));
        //	Save SdessionCookie JSESSIONID  returned by server
    	curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);
    	curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpOK) {
    		throw new Exception('HTTP-Response STATUS<>200', $http_code);
    	}
    	curl_close($curl);

    	//	stage 2 : send credentials using sessioncookie
    	$curl = curl_init();

    	curl_setopt_array($curl, array(
    		CURLOPT_RETURNTRANSFER => 1,
    	    CURLOPT_URL => $url.loginform,
    	    CURLOPT_USERAGENT => 'PHP Request '.$sessionId,
    	    CURLOPT_POST => 1,
    	    CURLOPT_HTTPHEADER => array(
    			'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
    			'Connection: Keep-Alive'
    		)
    	));
    	//	Put credentials into postbody invisible in logs
    	curl_setopt($curl, CURLOPT_POSTFIELDS,'j_username='.urlencode($user).'&j_password='.urlencode($pass));
        //	Send SdessionCookie JSESSIONID
        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie);
    	curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpMovedTemp) {
    		throw new Exception('HTTP-Response STATUS<>302', $http_code);
    	}
    	curl_close($curl);

    	//	stage 3: call protected ressource again which is now accessible due to 302:moved temporarily
    	$curl = curl_init();
    	curl_setopt_array($curl, array(
    		CURLOPT_RETURNTRANSFER => 1,
        	CURLOPT_URL => $url.frontcontroller.'?opc='.opcLogon,
        	CURLOPT_USERAGENT => 'PHP Request '.$sessionId
    	));

        //	Send SessionCookie JSESSIONID
        curl_setopt($curl, CURLOPT_COOKIEFILE, $cookie);
        //	Session has changed after logon due to security issue so save new cookie
        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);

    	$longonInfo=curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpOK) {
    		throw new Exception('HTTP-Response STATUS<>200', $http_code);
    	}
    	curl_close($curl);

    	//	convert result from JSON to assoc array and add session infos
    	$sessionInfo=json_decode($longonInfo, true);
    	$sessionInfo ['sessionId'] = $sessionId;
    	$sessionInfo ['cookie'] = $cookie;
    	$sessionInfo ['url'] = $url;
    	$sessionInfo ['user'] = $user;
    	$sessionInfo ['pass'] = "";

    	if ($debug) showResponse($sessionInfo);

    	return $sessionInfo;
    }

    /**
     * LogOff. Abmelden vom System und dadurch kill der Session.
     * Ruft nur Get mit dem richtigen Opcode auf
     * @param	{Object}	$sessionInfo	Assoc Array, wird nach dem erfolgreichen Anmelden erzeugt, siehe login
     * @param	{Boolean}	$debug			Debug-Flag für Bildschirmausgaben
     * @returns	{Object}	Das Ergebnis des requests, ein JOSN-Objekt das als Assoc Array zurückgegeben wird
     */
    function logoff($sessionInfo, $debug) {
    	return get($sessionInfo, opcLogoff, null, $debug);
    }

    /**
     *	Get-Methode im Rahmen der geöffneten Session.
     *
     *  Wirft Exceptions falls der request schiefläuft. Falls die Session nicht authentifiziert ist wird
     *  der Aufruf vom Tomcat stillschweigend an die Login-Seite verwiesen. Das sollte also nicht passieren.
     *
     * @param	{Object}	$sessionInfo	Assoc Array, wird nach dem erfolgreichen Anmelden erzeugt, siehe login
     * @paeam	{OPCODE}	$opcode			Long. Der Opcode für den request
     * @param	{Object}	$params			Assoc Array mit Parametern für den get-Aufruf
     * @param	{String}	$data			JSON-Objekt als String, die Payload für den Postbody
     * @param	{Boolean}	$debug			Debug-Flag für Bildschirmausgaben
     * @returns	{Object}	Das Ergebnis des requests, ein JOSN-Objekt das als Assoc Array zurückgegeben wird
     */
    function get($sessionInfo, $opcode, $params, $debug) {

    	$url=$sessionInfo ['url'].frontcontroller.'?opc='.$opcode;
    	if ($params) {
    		foreach ($params as $name => $value) {
    			$url .= "&".urlencode($name)."=".urlencode($value);
    		}
    	}

    	$curl = curl_init();
    	curl_setopt_array($curl, array(
    		CURLOPT_RETURNTRANSFER => 1,
        	CURLOPT_URL => $url,
        	CURLOPT_USERAGENT => 'PHP Request '.$sessionInfo ['sessionId']
    	));

        //	Send SessionCookie JSESSIONID
        curl_setopt($curl, CURLOPT_COOKIEFILE, $sessionInfo ['cookie']);
        //	Session may have changed so better save new cookie always
        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);

    	$jsonString=curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpOK) {
    		throw new Exception('HTTP-Response STATUS<>200', $http_code);
    	}
    	curl_close($curl);

    	if ($debug) showResponse(json_decode($jsonString, true));

    	return json_decode($jsonString, true);
    }

    /**
     *	Post-Methode im Rahmen der geöffneten Session.
     *
     *  Wirft Exceptions falls der request schiefläuft. Falls die Session nicht authentifiziert ist wird
     *  der Aufurf vom Tomcat stillschweigend an die Login-Seite verwiesen. Das sollte also nicht passieren.
     *
     * @param	{Object}	$sessionInfo	Assoc Array, wird nach dem erfolgriechen Anmelden erzeugt, siehe login
     * @paeam	{OPCODE}	$opcode			Long. Der Opcode für den request
     * @param	{Object}	$params			Assoc Array mit Parametern fpr die URL analog zum get-Aufruf
     * @param	{String}	$data			JSON-Objekt als String, die Payload für den Postbody
     * @param	{Boolean}	$debug			Debug-Flag für Bildschirmausgaben
     * @returns	{Object}	Das Ergebnis des requests, ein JOSN-Objekt das als Assoc Array zurückgegeben wird
     */
    function post($sessionInfo, $opcode, $params, $data, $debug) {

    	$url=$sessionInfo ['url'].frontcontroller.'?opc='.$opcode;
    	if ($params) {
    		foreach ($params as $name => $value) {
    			$url .= "&".urlencode($name)."=".urlencode($value);
    		}
    	}

    	$curl = curl_init();
    	curl_setopt_array($curl, array(
    		CURLOPT_RETURNTRANSFER => 1,
        	CURLOPT_URL => $url,
        	CURLOPT_USERAGENT => 'PHP Request '.$sessionInfo ['sessionId'],
        	CURLOPT_POST => 1,
    	    CURLOPT_HTTPHEADER => array(
    			'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
    			'Connection: Keep-Alive'
    		)
    	));

        //	Send SdessionCookie JSESSIONID
        curl_setopt($curl, CURLOPT_COOKIEFILE, $sessionInfo ['cookie']);
        //	Session may have changed so better save new cookie always
        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);
       	//	Put credentail into postbody invisible in logs
    	curl_setopt($curl, CURLOPT_POSTFIELDS,'data='.urlencode($data));
        //	Send SdessionCookie JSESSIONID
    	$jsonString=curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpOK) {
    		throw new Exception('HTTP-Response STATUS<>200', $http_code);
    	}
    	curl_close($curl);

    	if ($debug) showResponse(json_decode($jsonString, true));

    	return json_decode($jsonString, true);
    }

    /**
     *	Download-Methode im Rahmen der geöffneten Session um ein File herunterzuladen.
     *
     *  Wirft Exceptions falls der request schiefläuft. Falls die Session nicht authentifiziert ist wird
     *  der Aufurf vom Tomcat stillschweigend an die Login-Seite verwiesen. Das sollte also nicht passieren.
     *
     * @param	{Object}	$sessionInfo	Assoc Array, wird nach dem erfolgriechen Anmelden erzeugt, siehe login
     * @paeam	{OPCODE}	$opcode			Long. Der Opcode für den request
     * @param	{Object}	$params			Assoc Array mit Parametern fpr die URL analog zum get-Aufruf
     * @param	{File}		$file			Geöffneter und zum Schreiben bereiter Filhandle im lokalen Dateisystem
     * @param	{Boolean}	$debug			Debug-Flag für Bildschirmausgaben
     * @returns	{File}		Den Filehandel
     */
    function download($sessionInfo, $opcode, $params, $file, $debug) {

    	$url=$sessionInfo ['url'].frontcontroller.'?opc='.$opcode;
    	if ($params) {
    		foreach ($params as $name => $value) {
    			$url .= "&".urlencode($name)."=".urlencode($value);
    		}
    	}

    	$curl = curl_init();
    	curl_setopt_array($curl, array(
    		CURLOPT_FILE => $file,
    		CURLOPT_TIMEOUT => 20,
        	CURLOPT_URL => $url,
        	CURLOPT_USERAGENT => 'PHP Request '.$sessionInfo ['sessionId']
    	));

        //	Send SdessionCookie JSESSIONID
        curl_setopt($curl, CURLOPT_COOKIEFILE, $sessionInfo ['cookie']);
        //	Session may have changed so better save new cookie always
        curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);

    	curl_exec($curl);

    	if (curl_errno($curl)) {
    		throw new Exception(curl_error($curl), curl_errno($curl));
    	}
    	$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    	if ($http_code!=httpOK) {
    		throw new Exception('HTTP-Response STATUS<>200', $http_code);
    	}
    	curl_close($curl);

    	return $file;
    }

    /**
     * Helper-Methode für Debugzwecke
     *
     */
    function showResponse($response) {
    	foreach ($response as $name => $value) {
    		echo "$name = $value"."</br>";
    	}
    }

    ?>