<?php


/**
 * Check authorization in the http request header and age of timestamp
 * On a failed check the request will be terminated; on suceess it returns
 * the hash
 * @param string  $sMySecret   server api key (from config file)
 * @param int     $iMaxAge     max allowed age in [sec]
 * @return string
 */
function _checkAuth($sMySecret, $iMaxAge=60){
    $aReqHeaders=apache_request_headers();
    _wd('request headers: <pre>'.print_r($aReqHeaders, 1).'</pre>');
    if(!isset($aReqHeaders['Authorization'])){
        _quit('Access denied. Missing authorization.', 403);
    }         
    if(!isset($aReqHeaders['Date'])){
        _quit('Access denied. Missing field "Date:" in the request header.', 403);
    }         

    $sGotHash= preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']);
    $sGotDate= $aReqHeaders['Date'];
    $sGotMethod=$_SERVER['REQUEST_METHOD'];
    $sGotReq=$_SERVER['REQUEST_URI'];


    $sMyData="{$sGotMethod}\n{$sGotReq}\n{$sGotDate}\n";
    $sMyHash= base64_encode(hash_hmac("sha1", $sMyData, $sMySecret));

    _wd('Hash: '.$sGotHash.' -- from header');
    _wd('Hash: '.$sMyHash.' -- rebuilt');
    if($sGotHash!==$sMyHash){
        _quit('Access denied. Invalid hash.', 403);
    }

    $iAge=date('U')-date('U', strtotime($sGotDate));
    _wd('Date: '.$sGotDate.' - age: '.$iAge.' sec');
    if($iAge>$iMaxAge){
        _quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec. Maybe client or server time is out of sync.', 403);
    }
    if($iAge<-$iMaxAge){
        _quit('Access denied. Hash is '.$iAge. ' sec in future but only '.$iMaxAge.' sec are allowed. Maybe client or server is out of sync.', 403);
    }
    return $sMyHash;
}

/**
 * check if a given secret was used already.
 * It is for hardening - allow one time usage of a hash
 * @param string   $lockfile   filename of lockfile with stored hashes
 * @param string   $sMyHash    hash to verify
 * @return boolean
 */
function _checkIfHashWasUsedAlready($lockfile, $sMyHash){
    $bFound=false;
    $handle = @fopen($lockfile, "r");
    if ($handle){
        while (!feof($handle))
        {
            $buffer = fgets($handle);
            if(strstr($buffer, $sMyHash)){
                $bFound = true;
            }
        }
        fclose($handle);
    }
    return $bFound;    
}

/**
 * helper function for enabled one time secret: start cleanup of storage with 
 * used keys if a given filesize was reached. It keeps entries younger a given
 * max age (same max age like in _checkAuth()
 * @param string   $lockfile           filename of lockfile with stored hashes
 * @param integer  $iMaxLockfilesize   size in byte when to start garbage collection
 * @param integer  $iMaxAge            max allowed age in [sec]
 * @return boolean
 */
function _cleanupLockdata($lockfile, $iMaxLockfilesize, $iMaxAge){
    if (filesize($lockfile)<$iMaxLockfilesize){
        return false;
    }
    $sLockdata='';
    $handle = @fopen($lockfile, "r");
    if ($handle){
        while (!feof($handle)) {
            $buffer = fgets($handle);
            $iTimestamp=(int)preg_replace('/\-.*$/', '', $buffer);
            if ($iTimestamp && date('U') - $iTimestamp < $iMaxAge){
                $sLockdata.=$buffer;
            }
        }
        fclose($handle);
        file_put_contents($lockfile, $sLockdata);
    }
    return true;
}

/**
 * end with OK output
 * @param type $Data
 */
function _done($Data){
    echo is_array($Data) 
        ? json_encode($Data, JSON_PRETTY_PRINT)
        : $Data
        ;
    die();
}

/**
 * abort execution with error
 * @param string   $s        message
 * @param integer  $iStatus  http status code to send
 */
function _quit($s, $iStatus=400){
    $aStatus=array(
       400=>'HTTP/1.0 400 Bad Request', 
       403=>'HTTP/1.0 403 Access denied', 
       404=>'HTTP/1.0 404 Not found', 
    );
    header($aStatus[$iStatus]);
    _sendHtml($aStatus[$iStatus], $s);
    die();
}

/**
 * send html page
 * @param type $sTitle
 * @param type $sContent
 */
function _sendHtml($sTitle, $sContent){
    echo '<!doctype html>
<html>
    <head>
        <title>'.$sTitle.'</title>
        <link rel="stylesheet" type="text/css" href="/main.css" media="screen" />
    </head>
    <body>
        <h1><span class="imllogo"></span> CI <span class="subdomain"> packages</span></h1>
        <h2>'.$sTitle.'</h2>

        <p>
            '.$sContent.'
        </p>

        <footer>
            <a href="https://www.iml.unibe.ch/">www.iml.unibe.ch</a>
        </footer>
    </body>
</html>
'
;
}

/**
 * write debug text (if enabled)
 * @global boolean $bDebug
 * @param string  $s       message
 * @param string  $sLevel  level; one of info|
 * @return boolean
 */
function _wd($s, $sLevel='info'){
    global $bDebug;
    if ($bDebug){
        // echo '<div class="debug debug-'.$sLevel.'">DEBUG: '.$s.'</div>';
        echo "DEBUG[$sLevel]: $s<br>\n";
    }
    return true;
}