<?php

/*

 * ********************************************************************************

 * ThinMPI.php

 * Date : 20/12/2005

 * Edited by : Remco de Jong - Umis BV. | remco.dejong@umis.nl | http://www.umis.nl

 * ********************************************************************************

*/



require_once("Security.php");

require_once("LoadConf.php");

require_once("DirectoryResponse.php");

require_once("AcquirerStatusResponse.php");

require_once("AcquirerTrxResponse.php");



class ThinMPI {



    //var to do all the security stuff

    var $security;



    //holds the data of the configuration file

    var $conf;



    /**

     * creates a new ThinMPI core Object

     *

     */

    function ThinMPI ( $conf ) {

        $this->security = & new Security();

        $this->conf = $conf;

    }



    /**

     * strips all whitespaces from the given message

     */    

    function strip( $message ) {

        if ($this->conf["IDEAL_Bank"] != "ABNAMROTEST" OR $this->conf["IDEAL_Bank"] != "ABNAMRO")

				{

				$message = str_replace(' ' , '' , $message);

				}



        $message = str_replace( "\t", "", $message );

        $message = str_replace( "\n", "", $message );

        return $message;

    }



    /**

     * encoding of special chars: <>&"'

     * first decode (maybe some chars are encoded)

     * htmlspecialchars_decode >= PHP 5.1.0RC1

     */    

    function encode_html($text) {

        $trans = array ("&amp;" => "&", "&quot;" => "\"", "&#039;" => "'", "&lt;" => "<", "&gt;" => ">");

        return htmlspecialchars(strtr($text, $trans), ENT_QUOTES);

        // return htmlspecialchars(html_entity_decode($text, ENT_QUOTES), ENT_QUOTES);

    }

 

    /**

     * This function sends a Post Request with the data we want to send

     * @param host (acceptor server adress)

     * @param port (what port are you using 80, 8080)

     * @param path (path where to put the data)

     * @param data_to_send (the xml we want to sent)

     * @return res (response from server)

     */

	function PostToHost($url, $timeout, $data_to_send) 

		{

		$idx = strrpos($url, ":");

    	$host = substr($url, 0, $idx);

    	$url = substr($url, $idx + 1);

    	$idx = strpos($url, "/");

    	$port = substr($url, 0, $idx);

    	$path = substr($url, $idx);



		$this->log("sending to " . $host . ":" . $port . $path . ": " . $data_to_send);



		$fsp = fsockopen($host, $port, $errno, $errstr, "60");

        	if($fsp) 

				{

				fputs($fsp, "POST $path HTTP/1.0\r\n");

	    			if ($this->conf["IDEAL_Bank"] == "ABNAMROTEST" OR $this->conf["IDEAL_Bank"] == "ABNAMRO")

						{

               			fputs($fsp, "Host: " . str_replace("ssl://", "", $host) . "\r\n");

						}

				fputs($fsp, "Accept: text/html\r\n");

				fputs($fsp, "Accept: charset=ISO-8859-1\r\n");

				fputs($fsp, "Content-Length:".strlen($data_to_send)."\r\n");

				fputs($fsp, "Content-Type: text/html; charset=ISO-8859-1\r\n\r\n");

				fputs($fsp, $data_to_send, strlen($data_to_send));



            	while(!feof($fsp)) 

					{

                	$res .= fgets($fsp, 128);

            		}



				fclose($fsp);

				$this->log("receiving from " . $host . ":" . $port . $path . ": " . $res);

				return $res;

				}

				else 

					{

					$this->log("error from " . $host . ":" . $port . $path . ": " . $errstr);

					}

		}



    function PostToHostProxy($proxy, $url, $timeout, $data_to_send) {

    	$idx = strrpos($proxy, ":");

    	$host = substr($proxy, 0, $idx);

    	$idx = strpos($proxy, ":");

    	$port = substr($proxy, $idx+1);

    	

	$this->log("sending to " . $host . ":" . $port . ": " . $data_to_send);

        $fsp = fsockopen($host, $port, $errno, $errstr, $timeout);

        if($fsp) {

            

            fputs($fsp, "POST $url HTTP/1.0\r\n");

            //fputs($fsp, "Host:www.acrm.de:443\r\n");

            fputs($fsp, "Accept: text/html\r\n");

            fputs($fsp, "Connection: Close\r\n");

            fputs($fsp, "Accept: charset=ISO-8859-1\r\n");

            fputs($fsp, "Content-Length:".strlen($data_to_send)."\r\n");

            fputs($fsp, "Content-Type: text/html; charset=ISO-8859-1\r\n\r\n");

            fputs($fsp, $data_to_send, strlen($data_to_send));



            while(!feof($fsp)) {

                $res .= fgets($fsp, 128);

            }

    

            while(!feof($fsp)) {

                $res .= fgets($fsp, 128);

            }

            

            fclose($fsp);

            $this->log("receiving from " . $host . ":" . $port . $path . ": " . $res);

	    return $res;

        }

        else {

            $this->log("error from " . $host . ":" . $port . $path . ": " . $errstr);

            return "Error: " . $errstr;

        }

    }



    /**

     * This method logs the message given to a file.

     */

    function log($message) {

    	if ($this->conf["IDEAL_LogFile"] == "")

    		return;

	$file = fopen($this->conf["IDEAL_LogFile"], 'a');

        fputs($file, $message, strlen($message));

        fputs($file, "\r\n\r\n");

        fclose($file);

    }



    /**

     * This method extracts a single value from a given xml-file

     */

    function parseFromXml ($key, $xml) {

        $begin = 0;

        $end = 0;



        $begin = strpos($xml, "<" . $key . ">");

        if($begin===false) {

          return false;

        }



        $begin += strlen($key) + 2;

        $end = strpos($xml, "</" . $key . ">");



        if($end===false) {

          return false;

        }



        $result = substr($xml, $begin, $end - $begin);

        $result = str_replace( "&amp;", "&", $result );



        return $result;

    }



    function parseError($answer, $res) {

	$errorMsg = $this->parseFromXml("errorMessage", $answer);

	$errorCode = $this->parseFromXml("errorCode", $answer);

	$errorDetail = $this->parseFromXml("errorDetail", $answer);

	$sugAction = $this->parseFromXml("suggestedAction", $answer);

	$sugExpPeriod = $this->parseFromXml("suggestedExpirationPeriod", $answer);

	$consMsg = $this->parseFromXml("consumerMessage", $answer);

	

	$res->setErrorMessage( $errorMsg );

	$res->setErrorCode( $errorCode );

	$res->setErrorDetail( $errorDetail );

	$res->setSuggestedAction( $sugAction );

	$res->setSuggestedExpirationPeriod( $sugExpPeriod );

	$res->setConsumerMessage( $consMsg );

	

	$res->setOk(false);

	return $res;

    }

    

    /**

     * this method processes a request regardless of the type. 

    */

    function ProcessRequest( $requesttype ) {

        if(is_a($requesttype, "DirectoryRequest")) {

          return $this->processDirRequest( $requesttype );

        } else if( is_a($requesttype, "AcquirerStatusRequest") ) {

          return $this->processStatusRequest( $requesttype );

        } else if( is_a($requesttype, "AcquirerTrxRequest") ) {

            return $this->processTrxRequest( $requesttype );

        }

    }



    /**

     * This method sends HTTP XML DirectoryRequest to the Acquirer system.

     * Befor calling, all mandatory properties have to be set in the Request object

     * by calling the associated setter methods.

     * If the request was successful, the response Object is returned.

     * @param Request Object filled with necessary data for the XML Request

     * @return Response Object with the data of the XML response.

     */

    function processDirRequest( $req ) {

    	

    	if ($req->getMerchantID() == "")

    		$req->setMerchantID($this->conf["IDEAL_MerchantID"]);

    	if ($req->getSubID() == "")

		$req->setSubID($this->conf["IDEAL_SubID"]);

	if ($req->getAuthentication() == "")

		$req->setAuthentication($this->conf["IDEAL_AuthenticationType"]);



        $res = & new DirectoryResponse();



        if (!$req->checkMandatory()) {

            $res->setErrorMessage("required fields missing.");

            return $res;

        }



	// build concatenated string

	$timestamp = gmdate(Y) . "-" . gmdate(m) . "-" . gmdate(d) . "T" . gmdate(H) . ":" . gmdate(i) . ":" . gmdate(s) . ".000Z";

	$token = "";

	$tokenCode = "";

        if ("SHA1_RSA" == $req->getAuthentication()) {

        	$message =  $timestamp . $req->getMerchantID() . $req->getSubID();

        	$message = $this->strip( $message );



        	//build fingerprint of your own certificate

        	$token = $this->security->createCertFingerprint($this->conf["IDEAL_Privatecert"]);



		//sign the part of the message that need to be signed

            	$tokenCode = $this->security->signMessage( $this->conf["IDEAL_Privatekey"], $this->conf["IDEAL_PrivatekeyPass"], $message );

		//encode with base64

            	$tokenCode = base64_encode( $tokenCode );

        }



	$reqMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

		. "<DirectoryReq xmlns=\"http://www.idealdesk.com/Message\" version=\"1.1.0\">\n"

		. "<createDateTimeStamp>" . $timestamp . "</createDateTimeStamp>\n"

		. "<Merchant>\n"

		. "<merchantID>" . $this->encode_html($req->getMerchantID()) . "</merchantID>\n"

		. "<subID>" . $req->getSubID() . "</subID>\n"

		. "<authentication>" . $req->getAuthentication() . "</authentication>\n"

		. "<token>" . $token . "</token>\n"

		. "<tokenCode>" . $tokenCode . "</tokenCode>\n"

		. "</Merchant>\n"

		. "</DirectoryReq>";



        if ($this->conf["PROXY"] == "") {

			$answer = $this->PostToHost(

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerURL"].":".$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerPORT"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerDirPath"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerDirFile"], 

			$this->conf["IDEAL_AcquirerTimeout"], $reqMsg);

        }  else {

        	$answer = $this->PostToHostProxy($this->conf["PROXY"], $this->conf["PROXYACQURL"], $this->conf["IDEAL_AcquirerTimeout"], $reqMsg); 

        }

        

	if( strpos($answer, "Error: ") != false) {

		$res->setErrorMessage(substr($answer, 7));

		return $res;

	}



        if( $this->parseFromXml("errorCode", $answer) ) {

        	return $this->parseError($answer, $res);

        }



        $acquirerID = $this->parseFromXml("acquirerID", $answer);

        $res->setAcqirerID( $acquirerID );



        while (strpos($answer, "<issuerID>")) {

                $issuerID = $this->parseFromXml("issuerID", $answer);

                $issuerName = $this->parseFromXml("issuerName", $answer);

                $issuerList = $this->parseFromXml("issuerList", $answer);



                $bean = & new IssuerBean();

                $bean->setIssuerID($issuerID);

                $bean->setIssuerName($issuerName);

                $bean->setIssuerList($issuerList);

                $res->addIssuer($bean);

                $answer = substr($answer, strpos($answer, "</issuerList>") + 13);

	}



        $res->setOk(true);

        return $res;

    }



    /**

     * This method sends HTTP XML AcquirerTrxRequest to the Acquirer system.

     * Befor calling, all mandatory properties have to be set in the Request object

     * by calling the associated setter methods.

     * If the request was successful, the response Object is returned.

     * @param Request Object filled with necessary data for the XML Request

     * @return Response Object with the data of the XML response.

     */

    function processTrxRequest($req) {



    	if ($req->getMerchantID() == "")

    		$req->setMerchantID($this->conf["IDEAL_MerchantID"]);

    	if ($req->getSubID() == "")

		$req->setSubID($this->conf["IDEAL_SubID"]);

	if ($req->getAuthentication() == "")

		$req->setAuthentication($this->conf["IDEAL_AuthenticationType"]);

	if ($req->getMerchantReturnURL() == "")

		$req->setMerchantReturnURL($this->conf["IDEAL_MerchantReturnURL"]);

	if ($req->getCurrency() == "")

		$req->setCurrency($this->conf["IDEAL_Currency"]);

	if ($req->getExpirationPeriod() == "")

		$req->setExpirationPeriod($this->conf["IDEAL_ExpirationPeriod"]);

	if ($req->getLanguage() == "")

		$req->setLanguage($this->conf["IDEAL_Language"]);

	if ($req->getEntranceCode() == "")

		$req->setEntranceCode($this->conf["ENTRANCECODE"]);

	if ($req->getDescription() == "")

		$req->setDescription($this->conf["IDEAL_DESCRIPTION"]);

		

        $res = new AcquirerTrxResponse();



        if (!$req->checkMandatory()) {

            $res->setErrorMessage ("required fields missing.");

            return $res;

        }



	// build concatenated string

	$timestamp = gmdate(Y) . "-" . gmdate(m) . "-" . gmdate(d) . "T" . gmdate(H) . ":" . gmdate(i) . ":" . gmdate(s) . ".000Z";

	$token = "";

	$tokenCode = "";

        if ( "SHA1_RSA" == $req->getAuthentication() ) {

		$message = $timestamp

                        . $req->getIssuerID()

                        . $req->getMerchantID()

                        . $req->getSubID()

                        . $req->getMerchantReturnURL()

                        . $req->getPurchaseID()

                        . $req->getAmount()

                        . $req->getCurrency()

                        . $req->getLanguage()

                        . $req->getDescription()

                        . $req->getEntranceCode();

		$message = $this->strip( $message );



        	//create fingerprint so the receiver knows what certificate to use

        	$token = $this->security->createCertFingerprint($this->conf["IDEAL_Privatecert"]);



        	//sign the message

        	$tokenCode = $this->security->signMessage( $this->conf["IDEAL_Privatekey"], $this->conf["IDEAL_PrivatekeyPass"], $message );

        	//encode it with base64

        	$tokenCode = base64_encode( $tokenCode );

    	}

        $reqMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

                . "<AcquirerTrxReq xmlns=\"http://www.idealdesk.com/Message\" version=\"1.1.0\">\n"

                . "<createDateTimeStamp>" . $timestamp .  "</createDateTimeStamp>\n"

                . "<Issuer>" . "<issuerID>" . $this->encode_html($req->getIssuerID()) . "</issuerID>\n"

                . "</Issuer>\n" 

                . "<Merchant>" . "<merchantID>" . $this->encode_html($req->getMerchantID()) . "</merchantID>\n"

                . "<subID>" . $req->getSubID() . "</subID>\n"

                . "<authentication>" . $req->getAuthentication() . "</authentication>\n"

                . "<token>" . $token . "</token>\n" 

                . "<tokenCode>" . $tokenCode . "</tokenCode>\n"

                . "<merchantReturnURL>" . $this->encode_html($req->getMerchantReturnURL()) . "</merchantReturnURL>\n" 

                . "</Merchant>\n" 

                . "<Transaction>" . "<purchaseID>" . $this->encode_html($req->getPurchaseID()) . "</purchaseID>\n" 

                . "<amount>" . $req->getAmount() . "</amount>\n" 

                . "<currency>" . $req->getCurrency() . "</currency>\n" 

                . "<expirationPeriod>" . $req->getExpirationPeriod() . "</expirationPeriod>\n" 

                . "<language>" . $req->getLanguage() . "</language>\n" 

                . "<description>" . $this->encode_html($req->getDescription()) . "</description>\n" 

                . "<entranceCode>" . $this->encode_html($req->getEntranceCode()) . "</entranceCode>\n" 

                . "</Transaction>" . "</AcquirerTrxReq>";

                

        if ($this->conf["PROXY"] == "") {

        	$answer = $this->PostToHost(

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerURL"].":".$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerPORT"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerTransPath"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerTransFile"], 

			$this->conf["IDEAL_AcquirerTimeout"], $reqMsg);

        }  else {

        	$answer = $this->PostToHostProxy($this->conf["PROXY"], $this->conf["PROXYACQURL"], $this->conf["IDEAL_AcquirerTimeout"], $reqMsg); 

        }

            

	if( strpos($answer, "Error: ") != false) {

		$res->setErrorMessage(substr($answer, 7));

		return $res;

	}



        if( $this->parseFromXml("errorCode", $answer) ) {

        	return $this->parseError($answer, $res);

        }



	$issuerUrl = $this->ParseFromXml("issuerAuthenticationURL", $answer);

	$transactionID = $this->parseFromXml("transactionID", $answer);

       	$res->setIssuerAuthenticationURL($issuerUrl);

       	$res->setTransactionID($transactionID);

	$res->setOk(true);

	return $res;

    }



    /**

     * This method sends HTTP XML AcquirerStatusRequest to the Acquirer system.

     * Befor calling, all mandatory properties have to be set in the Request object

     * by calling the associated setter methods.

     * If the request was successful, the response Object is returned.

     * @param Request Object filled with necessary data for the XML Request

     * @return Response Object with the data of the XML response.

     */

    function processStatusRequest($req) {



    	if ($req->getMerchantID() == "")

    		$req->setMerchantID($this->conf["IDEAL_MerchantID"]);

    	if ($req->getSubID() == "")

		$req->setSubID($this->conf["IDEAL_SubID"]);

	if ($req->getAuthentication() == "")

		$req->setAuthentication($this->conf["IDEAL_AuthenticationType"]);



        $res = & new AcquirerStatusResponse();



        if (!$req->checkMandatory()) {

            $res->setErrorMessage("required fields missing.");

            return $res;

        }



        // build concatenated string

	$timestamp = gmdate(Y) . "-" . gmdate(m) . "-" . gmdate(d) . "T" . gmdate(H) . ":" . gmdate(i) . ":" . gmdate(s) . ".000Z";

	$token = "";

	$tokenCode = "";

        if ("SHA1_RSA" == $req->getAuthentication()) {

            $message = $timestamp . $req->getMerchantID() . $req->getSubID() . $req->getTransactionID();

            $message = $this->strip( $message );

                            

            //create fingerprint of your own certificate

            $token = $this->security->createCertFingerprint($this->conf["IDEAL_Privatecert"]);

            //sign the message

            $tokenCode = $this->security->signMessage( $this->conf["IDEAL_Privatekey"], $this->conf["IDEAL_PrivatekeyPass"], $message );

	    //encode with base64

            $tokenCode = base64_encode( $tokenCode );

	}

        $reqMsg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

		. "<AcquirerStatusReq xmlns=\"http://www.idealdesk.com/Message\" version=\"1.1.0\">\n"

		. "<createDateTimeStamp>" . $timestamp . "</createDateTimeStamp>\n"

		. "<Merchant>" . "<merchantID>" . $this->encode_html($req->getMerchantID()) . "</merchantID>\n"

		. "<subID>" . $req->getSubID() . "</subID>\n" 

		. "<authentication>" . $req->getAuthentication() . "</authentication>\n" 

		. "<token>" . $token . "</token>\n" 

		. "<tokenCode>" . $tokenCode . "</tokenCode>\n"

		. "</Merchant>\n" 

		. "<Transaction>" . "<transactionID>" . $this->encode_html($req->getTransactionID()) . "</transactionID>\n" 

		. "</Transaction>" . "</AcquirerStatusReq>";



        if ($this->conf["PROXY"] == "") {

        	$answer = $this->PostToHost(

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerURL"].":".$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerPORT"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerStatPath"].

			$this->conf[$this->conf["IDEAL_Bank"]]["IDEAL_AcquirerStatFile"], 

			$this->conf["IDEAL_AcquirerTimeout"], $reqMsg);

        }  else {

        	$answer = $this->PostToHostProxy($this->conf["PROXY"], $this->conf["PROXYACQURL"], $this->conf["IDEAL_AcquirerTimeout"], $reqMsg); 

        }

            

	if( strpos($answer, "Error: ") != false) {

		$res->setErrorMessage(substr($answer, 7));

		return $res;

	}



        if( $this->parseFromXml("errorCode", $answer) ) {

        	return $this->parseError($answer, $res);

        }



        $status = $this->parseFromXml("status", $answer); 

        if ( strtoupper("Success") == strtoupper($status) ) {

               	$res->setAuthenticated(true);

        } else {

        	$res->setAuthenticated(false);

        }

	$creationTime = $this->ParseFromXml("createDateTimeStamp", $answer);

	$transactionID = $this->ParseFromXml("transactionID", $answer);

	$consumerAccountNumber = $this->parseFromXml("consumerAccountNumber", $answer);

	$consumerName = $this->ParseFromXml("consumerName", $answer);

	$consumerCity = $this->ParseFromXml("consumerCity", $answer);

	$res->setTransactionID($transactionID);

	$res->setConsumerAccountNumber($consumerAccountNumber);

	$res->setConsumerName($consumerName);

	$res->setConsumerCity($consumerCity);



        // now check the signature

        //create signed message string

        $message = $creationTime . $transactionID . $status . $consumerAccountNumber;

        $message = $this->strip( $message );

           

	//now we want to check the signature that has been sent

        $signature64 = $this->ParseFromXml("signatureValue", $answer);

        //decode the base64 encoded signature

        $sig = base64_decode($signature64);



        //get the fingerprint out of the response

        $fingerprint = $this->ParseFromXml("fingerprint", $answer);



        //search for the certificate file with the given fingerprint

        $certfile = $this->security->getCertificateName($fingerprint, $this->conf);



	if($certfile == false) {

		$res->setAuthenticated(false);

		$res->setErrorMessage("Fingerprint unknown!");

		return $res;

	}

            

        $valid = $this->security->verifyMessage($certfile, $message, $sig );

            

        if( $valid != 1 ) {

                $res->setAuthenticated(false);

                $res->setErrorMessage("Bad signature!");

        	return $res;

        }



        $res->setOk(true);

        return $res;

    }



}



?>

