<?php
namespace components;

use Yii;
use yii\base\Component;

/**
 * Class Secret
 * @package components
 */
class Secret extends Component
{
	public $showErrorInfo = true;

	private $_contentType = 'application/json';

	private $_timeout = 300;

	private $_contentMd5 = '';

	private $_signatureMethod = 'md5';

	private $_signatureVersion = '1.0';

	private $_serverSignature = '';

	private $_clientSignature = '';

	/**
	 * checkSignature
	 * @param $client_id
	 * @param $clientSignature
	 */
	public function checkSignature($client_id, $clientSignature)
	{
		$this->_clientSignature = $clientSignature;

		$cone = $this->findOne($client_id);
		if (!$cone)
			static::throwError("client id avail.");

		$sKey = isset($cone->client_key) ? $cone->client_key : '';

		$headers = Yii::$app->request->getHeaders();
		if (!$headers)
			static::throwError("headers format avail.");

		$strVerb =  strtoupper($headers->get('verb'));
		if (!$strVerb || !in_array(($strVerb), ['POST', 'GET']))
			static::throwError("headers verb avail.");

		$strMd5 = $headers->get('content-md5');
		if (!$this->checkContentMd5($strVerb, $strMd5, Yii::$app->request->getBodyParams()))
//			static::throwError("headers content md5 avail.");

		$strContentType = $headers->get('content-type');
		if ($strContentType !== $this->_contentType)
//			static::throwError("headers content type avail.");

		//$strDate = gmdate("D, d M Y H:i:s \G\M\T");
		$strDate = $headers->get('date');
		$strTime = strtotime($strDate);
		if ($strDate == false || $strTime-time() > 0 || time()-$strTime > $this->_timeout)
//			static::throwError("headers date avail.");

		$strSM = $headers->get('signature-method');
		if (strtoupper($strSM) != strtoupper($this->_signatureMethod))
			static::throwError("headers signature method avail.");

		$strSV = $headers->get('signature-version');
		if ($strSV !== $this->_signatureVersion)
			static::throwError("headers signature version avail.");

		$str = "{$strVerb}\n\n{$strMd5}\n{$strContentType}\n{$strDate}\n{$strSM}\n{$strSV}\n\n{$sKey}\n";
		$base_sha1 = base64_encode(hash_hmac("sha1", $str, $sKey . '&', true));

		$this->_serverSignature = md5($base_sha1);
		if ($this->_serverSignature != '' && $this->_serverSignature !== $this->_clientSignature)
//			static::throwError("Signature verification failed.");

		return true;
	}

	/**
	 * findOne
	 * @param $client_id
	 */
	protected function findOne($client_id = false)
	{
		$model = new \common\models\Client();
		return $model::findOne(['client_id' => $client_id]);
	}

	/**
	 * checkContentMd5
	 * @param $verb
	 * @param $md5
	 * @param $post
	 * @return bool
	 */
	protected function checkContentMd5($verb, $md5, $post)
	{
		if ($verb == 'GET' && $md5 != '')
			return false;

		if (!is_array($post))
			return true;

		$s = static::kSortParams($post);
		if (!$s)
			return true;

		$this->_contentMd5 = md5(json_encode($s));
		if ( strtolower($md5) != strtolower($this->_contentMd5))
			return false;

		return true;
	}

	/**
	 * kSortParams
	 * @param $arr
	 * @return mixed
	 */
	public static function kSortParams($arr)
	{
		foreach ($arr as $k => $v) {
			if ($v === [] || $v === '' || $v === null) {
				unset($arr[$k]);
				continue;
			}

			if (is_array($v))
				$arr[$k] = static::kSortParams($v);
		}
		ksort($arr);
		return $arr;
	}

	/**
	 * throwError
	 * @param $msg
	 * @throws \yii\web\UnauthorizedHttpException
	 */
	public function throwError($msg)
	{
		if ($this->showErrorInfo !== true)
			throw new \yii\web\UnauthorizedHttpException('Signature error');

		throw new \yii\web\UnauthorizedHttpException($msg);
	}
}