<?php
ini_set('display_errors', 1);

session_start();
set_time_limit(0);

require_once 'library/PHPMailer/src/PHPMailer.php';

use PHPMailer\PHPMailer\PHPMailer;

/**
 *
 */
class SaveForm
{
    const DB_HOST = '{{ db_host }}';
    const DB_USER = '{{ db_user }}';
    const DB_PASS = '{{ db_password }}';
    const DB_NAME = '{{ db_name }}';

    const CAPTCHAIMAGE = TRUE;
    const reCAPTCHA_V1 = FALSE;
    const reCAPTCHA_PRIV_KEY = FALSE;
    const reCAPTCHA_API_VERIFY = 'https://www.google.com/recaptcha/api/siteverify';

    const PUBLIC_KEY_PATH = __DIR__ . '/../private/public-key.pem'; // Chemin vers la clé publique pour le chiffrement des données.

    const DOSSIER_MYSQL_TABLE = 'data_formulaire'; // Nom de la table où sont enregistrées les données du formulaire.
    const DOSSIER_PATH = __DIR__ . '/../private/dossiers/'; // Chemin vers le dossier "private/dossiers" où sont enregistrés les pièces jointes.

    const EMAIL_NAME_NOREPLY = 'Le Médiateur auprès du Crédit Agricole de Paris et d\'Ile-de-France';
    const EMAIL_NOREPLY = 'noreply@lemediateuraupresdeca-paris.fr';
    const EMAIL_MEDIATEUR = 'jpm.mediateurbancaire@gmail.com'; // adresse email du médiateur
    // const EMAIL_MEDIATEUR = 'andrea.bouchaib@comcicomca.com'; // pour les tests

    const URL_SITE = 'http://www.lemediateuraupresdeca-paris.fr';


    private $pkey = NULL;

    private $dbh = NULL;

    private $out = array(
        'errorno' => 0,
        'errormsg' => array()
    );

    private $output = '';

    private $filesize = 3000 * 1024;

    private $filetypes = array(
        'application/pdf',

        // Word
        'application/msword',
        // 'application/vnd.oasis.opendocument.text',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',

        // Excel
        // 'application/octet-stream', // certains fichiers .xls/.xlsx sont mal reconnus
        'application/vnd.ms-excel',
        // 'application/vnd.ms-office',
        // 'application/vnd.oasis.opendocument.spreadsheet',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',

        // Images
        'image/gif',
        'image/jpeg',
        'image/png',

        // BMP
        'image/bmp',
        // 'image/x-ms-bmp',
        // 'image/x-windows-bmp',
    );
    private $requiredFields = array(
        'reclamation_deja_faite',
        'service_client',
        'etes_vous',
        'quelle_personne',
        'theme_principale',
        // 'valeur',
        // 'texte_estimation',
        'texte_raison',
    );
    private $fields = array(
        'etes_vous' => array('field_name' => 'etes_vous', 'text' => 'Êtes vous'),
        'reclamation_deja_faite' => array('field_name' => 'reclamation_deja_faite', 'text' => 'Avez-vous déjà fait part de votre demande de médiation auprès de votre interlocuteur en agence ?'),
        'date_reclamation' => array('field_name' => 'date_reclamation', 'text' => 'Date de votre première réclamation écrite auprès de votre banque'),
        'service_client' => array('field_name' => 'service_client', 'text' => 'Avez-vous déjà saisi le service client de la caisse régionale de crédit agricole mutuel de paris et d\'ile-de-france ?'),
        'depot_plainte' => array('field_name' => 'depot_plainte', 'text' => 'Avez-vous effectué un dépôt de plainte ?'),
        'quelle_personne' => array('field_name' => 'quelle_personne', 'text' => 'A quel titre intervenez-vous ?'),
        'raison' => array('field_name' => 'raison', 'text' => 'Raison sociale'),
        'civilite' => array('field_name' => 'civilite', 'text' => 'Civilité'),
        'nom' => array('field_name' => 'nom', 'text' => 'Nom'),
        'prenom' => array('field_name' => 'prenom', 'text' => 'Prénom'),
        'email' => array('field_name' => 'email', 'text' => 'Adresse email'),
        'adresse' => array('field_name' => 'adresse', 'text' => 'Adresse'),
        'code_postal' => array('field_name' => 'codepostal', 'text' => 'Code Postal'),
        'ville' => array('field_name' => 'ville', 'text' => 'Ville'),
        'pays' => array('field_name' => 'pays', 'text' => 'Pays'),
        'tel' => array('field_name' => 'tel', 'text' => 'Tel'),
        'numero_compte' => array('field_name' => 'numero_compte', 'text' => 'Numéro de compte bancaire'),
        'votre_agence' => array('field_name' => 'votre_agence', 'text' => 'Votre agence'),
        'civilite2' => array('field_name' => 'civilite2', 'text' => 'Pour qui vous intervenez : Civilité'),
        'nom2' => array('field_name' => 'nom2', 'text' => 'Pour qui vous intervenez : Nom'),
        'prenom2' => array('field_name' => 'prenom2', 'text' => 'Pour qui vous intervenez : Prénom'),
        'email2' => array('field_name' => 'email2', 'text' => 'Pour qui vous intervenez : Adresse email'),
        'adresse2' => array('field_name' => 'adresse2', 'text' => 'Pour qui vous intervenez : Adresse'),
        'code_postal2' => array('field_name' => 'codepostal2', 'text' => 'Pour qui vous intervenez : Code Postal'),
        'ville2' => array('field_name' => 'ville2', 'text' => 'Pour qui vous intervenez : Ville'),
        'pays2' => array('field_name' => 'pays2', 'text' => 'Pour qui vous intervenez : Pays'),
        'tel2' => array('field_name' => 'tel2', 'text' => 'Pour qui vous intervenez : Tel'),
        'numero_compte2' => array('field_name' => 'numero_compte2', 'text' => 'Pour qui vous intervenez : Numéro de compte bancaire'),
        'votre_agence2' => array('field_name' => 'votre_agence2', 'text' => 'Votre agence'),
        'theme' => array('field_name' => 'theme_principale', 'text' => 'Sélectionnez le thème principal de votre demande'),
        'texte_raison' => array('field_name' => 'texte_raison', 'text' => 'Les raisons de votre démarche'),
        'valeur' => array('field_name' => 'valeur', 'text' => 'Valeur de l\'estimation financière de votre litige'),
        'texte_estimation' => array('field_name' => 'texte_estimation', 'text' => 'A quoi correspond cette estimation ?'),
    );
    private $ecryptedFields = array(
        'etes_vous',
        'reclamation_deja_faite',
        'date_reclamation',
        'service_client',
        'depot_plainte',
        'quelle_personne',
        'raison',
        'civilite',
        'nom',
        'prenom',
        'email',
        'adresse',
        'code_postal',
        'ville',
        'pays',
        'tel',
        'numero_compte',
        'votre_agence',
        'civilite2',
        'nom2',
        'prenom2',
        'email2',
        'adresse2',
        'code_postal2',
        'ville2',
        'pays2',
        'tel2',
        'numero_compte2',
        'votre_agence2',
        'theme',
        'texte_raison',
        'valeur',
        'texte_estimation',
        'reclamation_upload',
        'reponse_upload',
        'plainte_upload',
        'pouvoir_upload',
        'file_upload',
    );
    private $filesUploadArray = array(
        array(
            'html_fieldname' => 'reclamation_upload',
            'sql_fieldname' => 'reclamation_upload',
            'name' => "Copie de votre réclamation écrite",
            'required' => true,
        ),
        array(
            'html_fieldname' => 'reponse_upload',
            'sql_fieldname' => 'reponse_upload',
            'required' => false,
        ),
        array(
            'html_fieldname' => 'plainte_upload',
            'sql_fieldname' => 'plainte_upload',
            'required' => false,
        ),
        array(
            'html_fieldname' => 'pouvoir_upload',
            'sql_fieldname' => 'pouvoir_upload',
            'required' => false,
        ),
        array(
            'html_fieldname' => 'file_upload',
            'sql_fieldname' => 'file_upload',
            'required' => false,
        ),
    );

    public function __construct()
    {
        $this->pkey = openssl_pkey_get_public(file_get_contents(self::PUBLIC_KEY_PATH));

        $this->dbh = new PDO(
            'mysql:host=' . self::DB_HOST . ';dbname=' . self::DB_NAME,
            self::DB_USER,
            self::DB_PASS,
            array(
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            )
        );

        $this->exec();
    }

    public function exec()
    {
        try {
            $dossier_id = NULL;

            if (self::reCAPTCHA_PRIV_KEY != '' && self::reCAPTCHA_PRIV_KEY != false) {
                if (self::reCAPTCHA_V1) {
                    $_SESSION['captcha'] = false;
                    if (!isset($_POST['recaptcha_challenge_field']) || !$_POST['recaptcha_challenge_field'] || !isset($_POST['recaptcha_response_field']) || !$_POST['recaptcha_response_field'])
                        throw new Exception('Erreur Captcha', 1);

                    require_once 'library/recaptchalib.php';

                    $resp = recaptcha_check_answer(self::reCAPTCHA_PRIV_KEY, $_SERVER["REMOTE_ADDR"], $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"]);
                    if (!$resp->is_valid)
                        throw new Exception('Erreur Captcha', 1);
                    else
                        $_SESSION['captcha'] = true;
                } else {
                    if (!isset($_POST['g-recaptcha-response']) || !$_POST['g-recaptcha-response'])
                        throw new Exception('Erreur Captcha', 1);

                    $data = array(
                        'secret' => self::reCAPTCHA_PRIV_KEY,
                        'response' => $_POST["g-recaptcha-response"]
                    );

                    $options = array(
                        'http' => array(
                            'method' => 'POST',
                            'content' => http_build_query($data),
                            'header' => "Content-type: application/x-www-form-urlencoded\r\n"
                        )
                    );

                    $context  = stream_context_create($options);
                    $verify = file_get_contents(self::reCAPTCHA_API_VERIFY, FALSE, $context);
                    $captcha_success = json_decode($verify);

                    if ($captcha_success->success == FALSE)
                        throw new Exception('Erreur Captcha', 1);
                }
            } else if (self::CAPTCHAIMAGE === TRUE) {
                if (!isset($_POST['dshghdsg']['sdsgiu']) || !$_POST['dshghdsg']['sdsgiu'] || !$_SESSION['captcha-phrase'] || strtolower($_POST['dshghdsg']['sdsgiu']) != strtolower($_SESSION['captcha-phrase']))
                    throw new Exception('Erreur Captcha', 1);
            }

            if (isset($_POST['pour_qui']) && $_POST['pour_qui'] == '1') {
                $this->fields['civilite']['field_name'] .= '1';
                $this->fields['nom']['field_name'] .= '1';
                $this->fields['prenom']['field_name'] .= '1';
                $this->fields['email']['field_name'] .= '1';
                $this->fields['adresse']['field_name'] .= '1';
                $this->fields['code_postal']['field_name'] .= '1';
                $this->fields['ville']['field_name'] .= '1';
                $this->fields['pays']['field_name'] .= '1';
                $this->fields['tel']['field_name'] .= '1';
            }

            $sql = "INSERT INTO " . self::DOSSIER_MYSQL_TABLE;
            $arrayFields = array();
            $arrayValue = array();
            $arrayData = array();
            $time = time();

            foreach ($this->fields as $key => $value) {
                if (in_array($key, $this->requiredFields) && (!isset($_POST[$value['field_name']]) || trim($_POST[$value['field_name']]) == ''))
                    throw new Exception('Erreur de champ : "' . $value['text'] . '"', 1);

                if (isset($_POST[$value['field_name']])) {
                    $arrayFields[] = $key;
                    $arrayValue[] = ':' . $key;

                    if (in_array($key, $this->ecryptedFields))
                        $arrayData[$key] = base64_encode($this->openssl_public_encrypt_by_chunk($_POST[$value['field_name']]));
                    else
                        $arrayData[$key] = stripslashes($_POST[$value['field_name']]);
                }
            }

            $arrayFields[] = 'registrationDate';
            $arrayValue[] = ':registrationDate';
            $arrayData['registrationDate'] = $time;

            $arrayFields[] = 'guid';
            $arrayValue[] = ':guid';
            $arrayData['guid'] = $this->getGUID();

            $sql .= "(" . implode(', ', $arrayFields) . ")";
            $sql .= " VALUES (" . implode(', ', $arrayValue) . ")";

            $statement = $this->dbh->prepare($sql);
            $statement->execute($arrayData);
            $dossier_id = $this->dbh->lastInsertId();

            $fileReg = '/(.*)\.([a-z]{1,5})$/i';
            if (count($_FILES) > 0 && $dossier_id != 0) {
                mkdir(self::DOSSIER_PATH . 'dossier_' . $dossier_id, 0775);
                $uploaddir = self::DOSSIER_PATH . 'dossier_' . $dossier_id . '/';
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $files_upload_sql_array = array();

                foreach ($this->filesUploadArray as $file_upload_field) {
                    if (
                        isset($_FILES[$file_upload_field['html_fieldname']])
                        && is_array($_FILES[$file_upload_field['html_fieldname']])
                        && isset($_FILES[$file_upload_field['html_fieldname']]['name'])
                        && is_array($_FILES[$file_upload_field['html_fieldname']]['name'])
                    ) {
                        $pj = array();
                        foreach ($_FILES[$file_upload_field['html_fieldname']]['name'] as $key => $valeur) {
                            if (
                                !is_uploaded_file($_FILES[$file_upload_field['html_fieldname']]['tmp_name'][$key])
                                || !$_FILES[$file_upload_field['html_fieldname']]['tmp_name'][$key]
                                || !$valeur
                            )
                                continue;

                            if (!in_array(finfo_file($finfo, $_FILES[$file_upload_field['html_fieldname']]['tmp_name'][$key]), $this->filetypes))
                                throw new Exception("Type de fichier non autorisé pour : " . $valeur, 1);

                            if ($_FILES[$file_upload_field['html_fieldname']]['size'][$key] > $this->filesize)
                                throw new Exception("Poids de fichier non autorisé pour : " . $valeur, 1);

                            $fname = $this->rename_file(basename($valeur));
                            $fname2 = $fname;

                            $iCopy = 1;
                            while (file_exists($uploaddir . $fname . '.enc')) {
                                preg_match($fileReg, $fname2, $pi);
                                $fname = $pi[1] . '_' . $iCopy . '.' . $pi[2];
                                $iCopy++;
                            }

                            $pj[] = $fname;

                            $uploadfile = $uploaddir . $fname;
                            if (!move_uploaded_file($_FILES[$file_upload_field['html_fieldname']]['tmp_name'][$key], $uploadfile))
                                throw new Exception("Erreur lors de l'envoi du fichier : " . $valeur, 1);

                            $this->encrypt_file($uploadfile, $uploadfile . '.enc');

                            if (!file_exists($uploadfile . '.enc'))
                                throw new Exception("Erreur lors du traitement du fichier : " . $valeur, 1);
                        }

                        $sql_data = base64_encode($this->openssl_public_encrypt_by_chunk(implode(';', $pj)));

                        if (!$pj || !implode(';', $pj) || !$sql_data)
                            throw new Exception("Erreur lors du traitement du fichier : " . $file_upload_field['name'], 1);

                        $files_upload_sql_array[] = array(
                            'sql_fieldname' => $file_upload_field['sql_fieldname'],
                            'sql_data' => $sql_data
                        );
                    } else if ($file_upload_field['required'] && (
                        !isset($_FILES[$file_upload_field['html_fieldname']])
                        || !$_FILES[$file_upload_field['html_fieldname']]
                        || !isset($_FILES[$file_upload_field['html_fieldname']]['name'])
                        || !$_FILES[$file_upload_field['html_fieldname']]['name']
                    )) {
                        throw new Exception("Erreur lors de l'envoi du fichier : " . $file_upload_field['name'], 1);
                    }
                }


                if ($files_upload_sql_array) {
                    $sql_set = array();
                    $arrayData = array();
                    $arrayData['id'] = $dossier_id;

                    foreach ($files_upload_sql_array as $files_upload_sql) {
                        $sql_set[] = $files_upload_sql['sql_fieldname'] . ' = :' . $files_upload_sql['sql_fieldname'];
                        $arrayData[$files_upload_sql['sql_fieldname']] = $files_upload_sql['sql_data'];
                    }

                    $statement = $this->dbh->prepare('UPDATE ' . self::DOSSIER_MYSQL_TABLE . ' SET ' . implode(', ', $sql_set) . ' WHERE id = :id');
                    $statement->execute($arrayData);
                }

                // @exec('chmod -R 775 ' . escapeshellarg($uploaddir));
            } else if (!$_FILES || !count($_FILES)) {
                throw new Exception("Erreur lors du traitement de votre dossier. Si le problème persiste, vous pouvez <a href=\"/faq/\" style=\"text-decoration: underline;\">nous contacter par email ou voie postale</a>.", 1);
            }

            if (isset($_POST['email']) && $_POST['email'])
                $this->sendMailToUser(array('email' => $_POST['email']));

            $this->sendMailToMediateur(array('id' => $dossier_id, 'theme' => $_POST['theme_principale']));

            $this->out();
        } catch (PDOException $e) {
            $this->out['errorno'] = $e->getCode();
            $this->out['errormsg'][] = $e->getMessage();

            if (isset($dossier_id) && is_numeric($dossier_id) && $dossier_id) {
                $statement = $this->dbh->prepare('DELETE FROM ' . self::DOSSIER_MYSQL_TABLE . ' WHERE id = :id');
                $arrayData = array();
                $arrayData['id'] = $dossier_id;
                $statement->execute($arrayData);

                $dir = self::DOSSIER_PATH . 'dossier_' . $dossier_id . '/';
                if (is_dir($dir))
                    $this->rrmdir($dir);
            }

            $this->out();
        } catch (Exception $e) {
            $this->out['errorno'] = $e->getCode();
            $this->out['errormsg'][] = $e->getMessage();

            if ($this->out['errorno'] > 0 && isset($dossier_id) && is_numeric($dossier_id) && $dossier_id) {
                $statement = $this->dbh->prepare('DELETE FROM ' . self::DOSSIER_MYSQL_TABLE . ' WHERE id = :id');
                $arrayData = array();
                $arrayData['id'] = $dossier_id;
                $statement->execute($arrayData);

                $dir = self::DOSSIER_PATH . 'dossier_' . $dossier_id . '/';
                if (is_dir($dir))
                    $this->rrmdir($dir);
            }

            $this->out();
        }
    }

    private function out()
    {
        header('Content-Type: application/json');
        echo json_encode($this->out);
        exit;
    }

    private function sendMailToUser($data)
    {
        $mail = new PHPMailer;

        $mail->CharSet = "UTF-8";

        $mail->setFrom(self::EMAIL_NOREPLY, self::EMAIL_NAME_NOREPLY);
        $mail->addAddress($data['email']);

        $mail->isHTML(true);
        $mail->Subject = 'Accusé de réception';

        $html = file_get_contents(__DIR__ . '/library/email_template/accuse-reception.html');
        $html = str_replace('{$url_site}', self::URL_SITE, $html);

        $mail->Body = $html;
        $mail->send();
    }

    private function sendMailToMediateur($data)
    {
        $mail = new PHPMailer;

        $mail->CharSet = "UTF-8";

        $mail->setFrom(self::EMAIL_NOREPLY, self::EMAIL_NAME_NOREPLY);
        $mail->addAddress(self::EMAIL_MEDIATEUR);

        $mail->isHTML(true);
        $mail->Subject = 'Nouvelle demande | ' . $data['theme'];

        $html = file_get_contents(__DIR__ . '/library/email_template/mediateur.html');
        $html = str_replace('{$url_site}', self::URL_SITE, $html);
        $html = str_replace('{$objet}', $data['theme'], $html);
        $html = str_replace('{$id}', $data['id'], $html);

        $mail->Body = $html;
        $mail->send();
    }

    private function rename_file($str)
    {
        $str = htmlentities($str, ENT_NOQUOTES, 'UTF-8');

        $str = preg_replace('#&([A-za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $str);
        $str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $str); // pour les ligatures e.g. '&oelig;'
        $str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères

        $str = preg_replace('/[^A-Za-z0-9\._-]/', '_', $str);
        return preg_replace('/[_]+/', '_', $str);
    }

    private function getGUID()
    {
        if (function_exists('com_create_guid')) {
            return com_create_guid();
        } else {
            mt_srand((float)microtime() * 10000); //optional for php 4.2.0 and up.
            $charid = strtoupper(md5(uniqid(rand(), true)));
            $hyphen = chr(45);
            $uuid = chr(123)
                . substr($charid, 0, 8) . $hyphen
                . substr($charid, 8, 4) . $hyphen
                . substr($charid, 12, 4) . $hyphen
                . substr($charid, 16, 4) . $hyphen
                . substr($charid, 20, 12)
                . chr(125);
            return $uuid;
        }
    }

    private function encrypt_file($source, $destination)
    {
        if (is_file($source) === true) {
            passthru('openssl smime -encrypt -binary -aes-256-cbc -in "' . $source . '" -out "' . $destination . '" -outform DER "' . self::PUBLIC_KEY_PATH . '"', $tout);

            unlink($source);

            return true;
        }
        return false;
    }

    private function openssl_public_encrypt_by_chunk($data)
    {
        $a_key = openssl_pkey_get_details($this->pkey);
        if (!$a_key)
            die('Failed to retrieve public key informations');

        $chunkSize = ceil($a_key['bits'] / 8) - 11;
        $this->output = '';

        while ($data) {
            $chunk = substr($data, 0, $chunkSize);
            $data = substr($data, $chunkSize);
            $encrypted = '';
            if (!openssl_public_encrypt($chunk, $encrypted, $this->pkey))
                die('Failed to encrypt data');
            $this->output .= $encrypted;
        }

        return $this->output;
    }

    private function rrmdir($dir)
    {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." && $object != "..") {
                    if (is_dir($dir . "/" . $object))
                        $this->rrmdir($dir . "/" . $object);
                    else
                        unlink($dir . "/" . $object);
                }
            }
            rmdir($dir);
        }
    }
}

$saveForm = new SaveForm();
$saveForm->exec();
