<?php
/*
 *
 * Copyright 2008-2010 GuardTime AS
 *
 * This file is part of the GuardTime PHP SDK.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/**
 * @package http
 */

/**
 * A collection of static methods for basic operations with timestamps.
 *
 * @package http
 */
class GTHttpClient {

    /**
     * Creates timestamp for the given dataHash using stamperUrl.
     *
     * @static
     * @throws GTException thrown if timestamp creation fails
     * @param  GTDataHash $dataHash data hash to create the timestamp for
     * @param  string $stamperUrl stamping service url
     * @return GTTimestamp a newly created short-term (signed) timestamp
     */
    public static function create(GTDataHash $dataHash, $stamperUrl) {

        $algorithm = new X509AlgorithmIdentifier();
        $algorithm->setAlgorithm($dataHash->getHashAlgorithm()->getOid());

        $message = new TSPMessageImprint();
        $message->setHashedMessage($dataHash->getHashedMessage());
        $message->setHashAlgorithm($algorithm);

        $request = new TSPTimeStampReq();
        $request->setMessageImprint($message);

        $bytes = $request->encodeDER();
        $bytes = GTUtil::fromByteArray($bytes);

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $stamperUrl);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $bytes);

        $bytes = curl_exec($curl);

        if ($bytes === false) {
            throw new GTException("Error creating timestamp, CURL error: " . curl_error($curl));
        }

        $bytes = GTUtil::toByteArray($bytes);

        $response = new TSPTimeStampResp();
        $response->decode(ASN1DER::decode($bytes));

        return new GTTimestamp($response->getToken());
    }

    /**
     * Extends the timestamp using the given extension service URL.
     *
     * @static
     * @throws GTException if timestamp extension fails
     * @param  GTTimestamp $timestamp timestamp to be extended
     * @param  string $verifierUrl extension service URL
     * @return void
     */
    public static function extend(GTTimestamp $timestamp, $verifierUrl) {

        $request = new GTCertTokenRequest();
        $request->setHistoryIdentifier(new GTBigInteger($timestamp->getProperty(GTTimestamp::HISTORY_ID)));

        $bytes = $request->encodeDER();
        $bytes = GTUtil::fromByteArray($bytes);

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $verifierUrl);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $bytes);

        $bytes = curl_exec($curl);

        if ($bytes === false) {
            throw new GTException("Error extending timestamp, CURL error: " . curl_error($curl));
        }

        $bytes = GTUtil::toByteArray($bytes);

        $response = new GTCertTokenResponse();
        $response->decode(ASN1DER::decode($bytes));

        $timestamp->extend($response);

    }

    /**
     * Verifies the given timestamp against the given dataHash.
     *
     * If a timestamp is not yet extended, automatic extension is attempted.
     * If the automatic extension is successfull the extended timestamp is verified
     * against the given publicationsFile.
     *
     * If the timestamp is already extended then the timestamp is verified against
     * the given publications file.
     *
     * If the timestamp is not extended and extension is not possible the timestamp
     * will be verified against short term signing certificates.
     *
     * @static
     * @throws GTException
     * @param  GTTimestamp $timestamp the timestamp to verify
     * @param  GTDataHash $dataHash data hash to verify the timestamp against
     * @param  null|string $verifierUrl extension url if automatic extension is to be attempted, null otherwise
     * @param  GTPublicationsFile $publicationsFile publications file to verify the timestamp against
     *
     * @return GTVerificationResultHttp tmestamp verification result
     */
    public static function verify(GTTimestamp $timestamp, GTDataHash $dataHash, $verifierUrl, GTPublicationsFile $publicationsFile) {

        if (empty($timestamp)) {
            throw new GTException("Parameter timestamp is required");
        }

        if (empty($dataHash)) {
            throw new GTException("Parameter dataHash is required");
        }

        if ($publicationsFile == null) {
            throw new GTException("Invalid publications file: null");
        }

        $result = new GTVerificationResultHttp();

        if ($verifierUrl === null) {
            $extendable = false;

        } else {
            $extendable = $timestamp->isExtendable($publicationsFile);

        }

        if ($extendable) {

            $result->updateStatus(GTVerificationResultHttp::EXTENSION_ATTEMPTED);

            $request = new GTCertTokenRequest();
            $request->setHistoryIdentifier(new GTBigInteger($timestamp->getProperty(GTTimestamp::HISTORY_ID)));

            $bytes = $request->encodeDER();
            $bytes = GTUtil::fromByteArray($bytes);

            $curl = curl_init();

            curl_setopt($curl, CURLOPT_URL, $verifierUrl);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $bytes);

            $bytes = curl_exec($curl);

            if ($bytes === false) {
                $result->updateStatus(GTVerificationResultHttp::SERVICE_UNREACHABLE_FAILURE);
            }

            $bytes = GTUtil::toByteArray($bytes);

            $response = null;

            try {
                $response = new GTCertTokenResponse();
                $response->decode(ASN1DER::decode($bytes));

            } catch (GTException $e) {
                $result->updateStatus(GTVerificationResultHttp::RESPONSE_FORMAT_FAILURE);
                return $result;
            }

            $statusCode = ($response === null) ? -1 : $response->getStatusCode();

            if ($statusCode == 0) {
                $result->updateStatus(GTVerificationResultHttp::TIMESTAMP_GRANTED);

            } else if ($statusCode == 1) {
                $result->updateStatus(GTVerificationResultHttp::TIMESTAMP_GRANTED_WITH_MODS);

            } else if ($statusCode == 2) {
                $result->updateStatus(GTVerificationResultHttp::TIMESTAMP_REJECTED);

            } else if ($statusCode == 3) {
                $result->updateStatus(GTVerificationResultHttp::TIMESTAMP_WAITING);

            } else if ($statusCode == 4) {
                $result->updateStatus(GTVerificationResultHttp::REVOCATION_WARNING);

            } else if ($statusCode == 5) {
                $result->updateStatus(GTVerificationResultHttp::REVOCATION_NOTIFICATION);

            }

            if ($statusCode == 0 || $statusCode == 1) {

                try {
                    $timestamp->extend($response);

                    $result->updateStatus(GTVerificationResultHttp::TIMESTAMP_EXTENDED);

                } catch (GTException $e) {

                    $gtResult = new GTVerificationResult();
                    $gtResult->updateErrors(GTVerificationResult::SYNTACTIC_CHECK_FAILURE);

                    $result->setGtResult($gtResult);

                    return $result;
                }

                $gtResult = $timestamp->verify($dataHash, $publicationsFile);

                $result->setGtResult($gtResult);

            } else if ($statusCode > 1) {

                $failCode = ($response == null) ? -1 : $response->getFailCode();

                if ($failCode == -1) {
                    $result->updateErrors(GTVerificationResultHttp::SERVICE_UNREACHABLE_FAILURE);

                } else if ($failCode == 0) {
                    $result->updateErrors(GTVerificationResultHttp::INVALID_ALGORITHM_FAILURE);

                } else if ($failCode == 2) {
                    $result->updateErrors(GTVerificationResultHttp::INVALID_REQUEST_FAILURE);

                } else if ($failCode == 5) {
                    $result->updateErrors(GTVerificationResultHttp::INVALID_DATA_FORMAT_FAILURE);

                } else if ($failCode == 14) {
                    $result->updateErrors(GTVerificationResultHttp::TIME_NOT_AVAILBLE_FAILURE);

                } else if ($failCode == 15) {
                    $result->updateErrors(GTVerificationResultHttp::UNACCEPTED_POLICY_FAILURE);

                } else if ($failCode == 16) {
                    $result->updateErrors(GTVerificationResultHttp::UNACCEPTED_EXTENSION_FAILURE);

                } else if ($failCode == 17) {
                    $result->updateErrors(GTVerificationResultHttp::ADDITIONAL_INFO_NOT_AVAILABLE_FAILURE);

                } else if ($failCode == 25) {
                    $result->updateErrors(GTVerificationResultHttp::SYSTEM_FAILURE);

                } else if ($failCode == 100) {
                    $result->updateErrors(GTVerificationResultHttp::TIMESTAMP_TOO_NEW_FAILURE);

                } else if ($failCode == 101) {
                    $result->updateErrors(GTVerificationResultHttp::TIMESTAMP_TOO_OLD_FAILURE);

                }

                if ($failCode != 100 && $failCode != 101) {
                    return $result;
                }

            }
        }

        $gtResult = $timestamp->verify($dataHash, $publicationsFile);

        $result->setGtResult($gtResult);

        return $result;
    }


    /**
     * Downloads publications file from the given url.
     *
     * Only basic syntax checking on the downloaded publications file is performaed.
     * It is strongly recommended to verify the downloaded publications file.
     *
     * @static
     * @throws GTException
     * @param  string $url publications file URL
     * @return GTPublicationsFile publications file
     */
    public static function getPublicationsFile($url) {

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);

        $bytes = curl_exec($curl);

        if ($bytes === false) {
            throw new GTException("Error downloading publications file, CURL error: " . curl_error($curl));
        }

        return new GTPublicationsFile(GTUtil::toByteArray($bytes));
    }

}

?>
