<?php

class DefaultController extends Controller
{

    const MAX_UPLOAD_FILE_SIZE_IN_BYTE = 100000;
    const UPLOAD_FILE_MIMETYPE = 'application/x-x509-ca-cert';
    const UPLOAD_FILE_MIMETYPE_APPLICATION_OCTET_STREAM = 'application/octet-stream';
    const UPLOAD_FILE_MIMETYPE_TEXT_PLAIN = 'text/plain';

    /**
     * @var string the default layout for the views. Defaults to '//layouts/column2', meaning
     * using two-column layout. See 'protected/views/layouts/column2.php'.
     */
    public $layout = '//layouts/column2';

    /**
     * @return array action filters
     */
    public function filters()
    {
        return array(
            'accessControl', // perform access control for CRUD operations
        );
    }

    /**
     * Specifies the access control rules.
     * This method is used by the 'accessControl' filter.
     * @return array access control rules
     */
    public function accessRules()
    {
        return array(
            array('allow', // allow all users to perform 'index' and 'view' actions
                'actions' => array(
                    'index',
                    'create',
                    'sendCertToCtrld',
                    'delete',
                    'update',
                    'sendUseCaseToCtrld',
                    'createCA',
                    'sendCAToCtrld',
                    'setUseCase',
                    'getAlreadyUsedUseCases',
                    'downloadCert',
                    'downloadCA',
                    'uploadCert',
                    'uploadData'
                ),
                'expression' => AccessManager::checkSessionPermission($this->module->name),
            ),
            array('deny', // deny all users
                'users' => array('*'),
            ),
        );
    }

    public function actionIndex()
    {
        $model = Certificate::model();
        $model->unsetAttributes();  // clear any default values
        PhonalisaCtrldConnector::hasConnection();
        $this->render('index', array('model' => $model));
    }

    public function actionCreate()
    {
        $model = new Certificate('create');
        $this->performAjaxValidation($model);
        $model->useCase = CertUseCase::getAllUseCasesBinary();
        $onSubmitSuccess = 'function(){writeDataToCtrld("sendCertToCtrld", "#cert-form");}';
        $this->renderPartial('_form', array('model' => $model, 'onSubmitSuccess' => $onSubmitSuccess), false, true);
    }

    public function actionSendCertToCtrld()
    {
        $result = array(
            'status' => 'error',
            'errorMsg' => array(
                'CTRLD' => array(
                    __('GLOBAL_COULD_NOT_CONNECT_TO_CTRLD')
                )
            )
        );

        $model = new Certificate('create');

        $certRequest = @$_REQUEST['Certificate'];
        $useCaseRequest = @$_REQUEST['CertUseCase'];

        if (PhonalisaCtrldConnector::hasConnection()) {
            if (isset($certRequest)) {
                $model->useCase = '';
                if (isset($useCaseRequest)) {
                    $model->useCase = CertUseCase::getFormatedUseCases($useCaseRequest);
                }
                $model->attributes = $certRequest;
                $model = $this->writeCertDataToCtrld($model);
                $result = $this->saveModel($model);
            }
        }
        echo json_encode($result);
        Yii::app()->end();
    }

    protected function saveModel($model)
    {
        $result = array('status' => 'ok');
        if ($model->hasErrors()) {
            $result['status'] = 'error';
            $result['errorMsg'] = $model->getErrors();
        } else {
            if ($model->save()) {
                app()->user->setSuccessFlashMessage();
            } else {
                app()->user->setErrorFlashMessage('CHANGES_NOT_SAVED_CAUSE_OF_AN_ERROR', $model->getFormatedErrors());
            }
        }
        return $result;
    }

    protected function writeCertDataToCtrld($model)
    {
        $caModel = $this->getCAModel();
        $data = $model->attributes;

        $data["services"] = array();
        foreach (CertUseCase::getCertUseCaseArray() as $value => $translatedName) {
            if ($data["useCase"] & $value) {
                $data["services"][] = CertUseCase::getServiceForUseCase($value);
            }
        }
        unset($data['useCase']);

        $data['commonName'] = $model->commonName;
        $data['ca.crt'] = $caModel->valueForKey('ca_crt', '');
        $data['ca.key'] = $caModel->valueForKey('ca_key', '');

        $result = PhonalisaCtrldConnector::createNewServerCert($data);

        if (!isset($result->status)) {
            $model->addError('CTRLD', __('NETWORK_CERTIFICATE_SETTINGS_CTRLD_ERROR'));
            return $model;
        }

        if ($result->status == 'error') {
            if ($this->errorMessageContainsKeyWords(@$result->errorText, '490 Unknown Error', 'unkown error!')) {
                $message = __('NETWORK_CERTIFICATE_SETTINGS_WRONG_PASSWORD', true, 'messages', array(
                    "{passphrase}" => Certificate::model()->getAttributeLabel('passphrase')
                ));
                $model->addError('CTRLD', $message);
            } else {
                $model->addError('CTRLD', @$result->errorText);
            }
        } else if ($result->status == 'ok') {
            $server_key = 'server.key';
            $server_cert = 'server.crt';
            $server_csr = 'server.csr';
            $model->setValueForKey('server_key', @$result->{$server_key});
            $model->setValueForKey('server_crt', @$result->{$server_cert});
            $model->setValueForKey('server_csr', @$result->{$server_csr});
        }
        $model->passphrase = '';
        $model->scenario = 'phasephraseSaved';
        return $model;
    }

    protected function errorMessageContainsKeyWords($errorMessage, $startsWith, $endsWith)
    {
        if (isset($errorMessage) && startsWith($errorMessage, $startsWith) && endsWith($errorMessage, $endsWith)) {
            return true;
        }
        return false;
    }

    public function actionUploadCert()
    {
        $model = new UploadStoreData('upload');
        $this->performAjaxValidation($model);
        $model->useCase = CertUseCase::getAllUseCasesBinary();
        $uploadCertFile = @$_FILES['UploadStoreData'];
        $certRequest = @$_REQUEST['CertUseCase'];
        if (isset($uploadCertFile) && isset($certRequest)) {
            $this->saveUplodedCertificate($uploadCertFile, $certRequest);
        }
        $onSubmitSuccess = 'function(){writeDataToCtrld("uploadData", "#cert-form");}';
        $this->renderPartial('_form', array('model' => $model, 'onSubmitSuccess' => $onSubmitSuccess), false, true);
    }

    protected function saveUplodedCertificate($uploadCertFile, $certRequest)
    {
        $model = $this->writeUploadDataToStoreModel($uploadCertFile, $certRequest);
        return $this->saveModel($model);
    }

    protected function writeUploadDataToStoreModel($uploadFileData, $certRequest)
    {
        $model = $this->getUploadStoreDataModel();

        if (
                !file_exists($uploadFileData['tmp_name']['data']) ||
                !isset($uploadFileData['type']['data']) ||
                !isset($uploadFileData['size']['data'])
        ) {
            $model->errorAttribute = __('NETWORK_CERTIFICATE_SETTINGS_UPLOAD_CERT');
            $model->errorMsg = __('NETWORK_CERTIFICATE_SETTINGS_FILE_UPLOAD_ERROR');
            return $model;
        }

        $mimeType = @$uploadFileData['type']['data'];
        
        if ($mimeType !== self::UPLOAD_FILE_MIMETYPE && 
            $mimeType !== self::UPLOAD_FILE_MIMETYPE_APPLICATION_OCTET_STREAM &&
            $mimeType !== self::UPLOAD_FILE_MIMETYPE_TEXT_PLAIN) {
            $model->errorAttribute = __('NETWORK_CERTIFICATE_SETTINGS_UPLOAD_CERT');
            $model->errorMsg = __('NETWORK_CERTIFICATE_SETTINGS_ERROR_MESSAGE_NO_VALID_FORMAT');
            return $model;
        }

        if ((int) $uploadFileData['size']['data'] > self::MAX_UPLOAD_FILE_SIZE_IN_BYTE) {
            $model->errorAttribute = __('NETWORK_CERTIFICATE_SETTINGS_UPLOAD_CERT');
            $text = __('NETWORK_CERTIFICATE_SETTINGS_ERROR_MESSAGE_FILE_TO_LARGE');
            $model->errorMsg = __($text, true, 'messages', array("{size}" => self::MAX_UPLOAD_FILE_SIZE_IN_BYTE / 1000));
            return $model;
        }

        $fileContent = file_get_contents($uploadFileData['tmp_name']['data']);
        $contents = explode("\n", $fileContent);
        $caCommonName = '';
        $certCommonName = '';

        foreach ($contents as $content) {
            $content = trim($content);
            if (startsWith($content, 'Issuer:')) {
                $caCommonName = substr(strstr($content, 'CN='), 3);
            }

            if (startsWith($content, 'Subject:')) {
                $certCommonName = substr(strstr($content, 'CN='), 3);
                if (startsWith($certCommonName, $caCommonName)) {
                    $certCommonName = str_replace($caCommonName . '.', '', $certCommonName);
                }
            }
        }

        if (isset($certRequest)) {
            $model->useCase = '';
            $model->useCase = CertUseCase::getFormatedUseCases($certRequest);
        }

        $model->commonName = $certCommonName;
        $model->fileContent = $fileContent;
        return $model;
    }

    public function actionUploadData()
    {
        $result = array(
            'status' => 'error',
            'errorMsg' => array(
                'CTRLD' => array(
                    __('GLOBAL_COULD_NOT_CONNECT_TO_CTRLD')
                )
            )
        );

        $model = new Certificate('upload');
        $certRequest = @$_REQUEST['CertUseCase'];
        if (PhonalisaCtrldConnector::hasConnection()) {
            $model = $this->uploadDataToCertModel($model);
            $model = $this->writeUseCasesToCtrld($model);
            $result = $this->saveModel($model);
        }
        echo json_encode($result);
        Yii::app()->end();
    }

    protected function uploadDataToCertModel($model)
    {
        $storeDataModel = $this->getUploadStoreDataModel();
        if ($storeDataModel->errorAttribute != '' && $storeDataModel->errorMsg != '') {
            $model->addError($storeDataModel->errorAttribute, $storeDataModel->errorMsg);
            $storeDataModel->delete();
            return $model;
        }
        $model->useCase = $storeDataModel->useCase;
        $model->commonName = $storeDataModel->commonName;
        $model->setValueForKey('server_crt', $storeDataModel->fileContent);
        $storeDataModel->delete();
        return $model;
    }

    protected function getUploadStoreDataModel()
    {
        $models = UploadStoreData::model()->findAll();
        if (isset($models[0])) {
            $model = $models[0];
        } else {
            $model = new UploadStoreData('upload');
        }
        return $model;
    }

    public function actionDelete($id)
    {
        $model = Certificate::model()->findByPk($id);
        $model->delete();
    }

    public function actionUpdate($id)
    {
        $model = $this->loadModel($id);
        $model->scenario = 'update';
        $this->performAjaxValidation($model);
        $onSubmitSuccess = 'function(){writeDataToCtrld("sendUseCaseToCtrld", "#cert-form", "' . $id . '");}';
        $this->renderPartial('_form', array('model' => $model, 'onSubmitSuccess' => $onSubmitSuccess), false, true);
    }

    public function actionSendUseCaseToCtrld($id = '')
    {
        $result = array(
            'status' => 'error',
            'errorMsg' => array(
                'CTRLD' => array(
                    __('GLOBAL_COULD_NOT_CONNECT_TO_CTRLD')
                )
            )
        );

        $model = Certificate::model()->findByPk($id);
        $certRequest = @$_REQUEST['CertUseCase'];
        if (PhonalisaCtrldConnector::hasConnection()) {
            if ($model) {
                $model->scenario = 'update';
                $model->useCase = '';
                if (isset($certRequest)) {
                    $model->useCase = CertUseCase::getFormatedUseCases($certRequest);
                }
                $model = $this->writeUseCasesToCtrld($model);
                $result = $this->saveModel($model);
            } else {
                $result['errorMsg'] = array('MODEL' => array(__('CHANGES_NOT_SAVED_CAUSE_OF_AN_ERROR')));
            }
        }
        echo json_encode($result);
        Yii::app()->end();
    }

    protected function writeUseCasesToCtrld($model)
    {
        $useCases = $model->getUseCaseAsArray();
        foreach ($useCases as $useCase) {
            if ((int) $useCase === 0) {
                continue;
            }
            $model = $this->storePemForService($model, $useCase);
        }
        $model->passphrase = '';
        $model->scenario = 'useCase';
        return $model;
    }

    protected function storePemForService($model, $useCase)
    {
        $data['cert'] = $model->valueForKey('server_crt', '') . $model->valueForKey('server_key', '');
        $data['service'] = CertUseCase::getServiceForUseCase($useCase);
        $result = PhonalisaCtrldConnector::storePemForService($data);
        if (!isset($result->status)) {
            if (!$model->hasErrors('CTRLD')) {
                $model->addError('CTRLD', __('GLOBAL_CTRLD_CALL_TO_UNKNOWN_FUNCTION_ERROR'));
            }
            return $model;
        }

        if ($result->status == 'error') {
            $model->addError('CTRLD => ' . CertUseCase::getCertUseCase($useCase) . '', @$result->errorText);
        }
        return $model;
    }

    /**
     * Returns the data model based on the primary key given in the GET variable.
     * If the data model is not found, an HTTP exception will be raised.
     * @param integer the ID of the model to be loaded
     */
    public function loadModel($id)
    {
        $model = Certificate::model()->findByPk($id);
        if ($model === null) {
            throw new CHttpException(404, 'The requested page does not exist.');
        }
        return $model;
    }

    /**
     * Performs the AJAX validation.
     * @param CModel the model to be validated
     */
    protected function performAjaxValidation($model, $form = 'cert-form')
    {
        if (isset($_POST['ajax']) && $_POST['ajax'] === $form) {
            echo CActiveForm::validate($model);
            Yii::app()->end();
        }
    }

    public function renderUseCases($checkedCheckboxes, $form, $model)
    {
        $useCases = CertUseCase::getCertUseCaseArray();
        $html = '';
        foreach ($useCases as $useCase => $label) {
            $html.= $form->checkBoxRow($model, $label, array(
                'name' => 'CertUseCase[' . $useCase . ']',
                'checked' => in_array($useCase, $checkedCheckboxes)
            ));
        }
        return $html;
    }

    public function actionCreateCA()
    {
        $model = $this->getCAModel();
        $this->performAjaxValidation($model, 'cert-ca-form');
        $this->renderPartialWithContextHelp('_setCA', array('model' => $model), false, true);
    }

    public function actionSendCAToCtrld()
    {
        $result = array(
            'status' => 'error',
            'errorMsg' => array(
                'CTRLD' => array(
                    __('GLOBAL_COULD_NOT_CONNECT_TO_CTRLD')
                )
            )
        );
        $model = $this->getCAModel();
        $this->performAjaxValidation($model, 'cert-ca-form');
        $certRequest = @$_REQUEST['CertificateAuthority'];

        if (PhonalisaCtrldConnector::hasConnection()) {
            if (isset($certRequest)) {
                $model->attributes = $certRequest;
                $model = $this->writeCADataToCtrld($model);
                $result = $this->saveModel($model);
            }
        }
        echo json_encode($result);
        Yii::app()->end();
    }

    public function getCAModel()
    {
        $models = CertificateAuthority::model()->findAll();
        if (isset($models[0])) {
            $model = $models[0];
        } else {
            $model = new CertificateAuthority();
        }
        return $model;
    }

    protected function writeCADataToCtrld($model)
    {
        $result = PhonalisaCtrldConnector::createNewCA($model->attributes);
        if (!isset($result->status)) {
            $model->addError('CTRLD', __('NETWORK_CERTIFICATE_SETTINGS_CTRLD_ERROR'));
            return $model;
        }

        if ($result->status == 'error') {
            $model->addError('CTRLD', @$result->errorText);
        } else if ($result->status == 'ok') {
            $ca_crt = 'ca.crt';
            $ca_key = 'ca.key';
            $model->setValueForKey('ca_crt', @$result->{$ca_crt});
            $model->setValueForKey('ca_key', @$result->{$ca_key});
        }
        $model->passphrase = '';
        $model->scenario = 'phasephraseSaved';
        return $model;
    }

    public function getTabularFormTabs($form, $model)
    {
        $tabs = array();
        $count = 0;
        $foundCA = $this->foundCAinDB();
        $checkedCheckboxes = $model->getUseCaseAsArray();
        $translateKeys = array();

        if ($model->scenario == 'upload') {
            $translateKeys['_uploadCert'] = __('NETWORK_CERTIFICATE_SETTINGS_TAB_MENU_GENERAL_DATA');
        } else if ($model->isNewRecord) {
            $translateKeys['_create'] = __('NETWORK_CERTIFICATE_SETTINGS_TAB_MENU_GENERAL_DATA');
        }
        $translateKeys['_certUseCase'] = __('NETWORK_CERTIFICATE_SETTINGS_UPDATE');

        if ($model->scenario === 'create' && !$foundCA) {
            $model->addError('CA', __('NETWORK_CERTIFICATE_SETTINGS_NO_CA_TOOLTIP'));
            unset($translateKeys['_certUseCase']);
        }

        foreach ($translateKeys as $view => $tabLabel) {
            $tabs[] = array(
                'active' => $count++ === 0,
                'label' => $tabLabel,
                'content' => $this->renderPartialWithContextHelp($view, array(
                    'form' => $form,
                    'model' => $model,
                    'foundCA' => $foundCA,
                    'checkedCheckboxes' => $checkedCheckboxes
                        ), true),
            );
        }
        return $tabs;
    }

    protected function foundCAinDB()
    {
        $result = false;
        $modelCount = CertificateAuthority::model()->countByAttributes(array());
        if ((int) $modelCount >= 1) {
            $result = true;
        }
        return $result;
    }

    public function actionGetAlreadyUsedUseCases()
    {
        $models = Certificate::model()->findAll();
        $useCases = '';
        $result = '';
        $id = '';
        if (isset($_REQUEST['id'])) {
            $id = $_REQUEST['id'];
        }

        foreach ($models as $model) {
            if ($model->useCase != '' && $model->id != $id) {
                $useCases+= $model->useCase;
            }
        }
        if ($useCases != '') {
            $newModel = new Certificate();
            $newModel->useCase = $useCases;
            $result = $newModel->getUseCaseAsArray();
        }

        echo json_encode($result);
        Yii::app()->end();
    }

    public function actionDownloadCert($id)
    {
        $model = Certificate::model()->findByPk($id);
        if ($model) {
            $attributes = $model->valueForKey('server_crt', '') . $model->valueForKey('server_key', '');
            $this->downloadCertificate($attributes, $model->commonName . ".pem");
        }
    }

    public function actionDownloadCA()
    {
        $model = $this->getCAModel();
        $attributes = $model->valueForKey('ca_crt', '');
        $this->downloadCertificate($attributes, "ca.crt");
    }

    protected function downloadCertificate($attributes, $filename)
    {
        header('Content-type: text/plain');
        header("Content-disposition: attachment; filename=$filename");
        header("Pragma: no-cache");
        header("Expires: 0");
        echo $attributes;
    }
}
