Your IP : 216.73.216.79


Current Path : /var/www/html/brspace/frontend/controllers/
Upload File :
Current File : /var/www/html/brspace/frontend/controllers/PdfDocumentLinksController.php

<?php

namespace frontend\controllers;

use Yii;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\Controller;
use kartik\mpdf\Pdf;
use frontend\models\PdfDocumentLinks;

/**
 * Site controller
 */
class PdfDocumentLinksController extends Controller
{
    /*** system parameter **/
    public $DYNAWEB_DEFAULT_ROUTE_PAGE;

    public $DYNAWEB_DEFAULT_ROUTE_PARAMS;

    const SOURCE_BASENAME = "http://brspace.bankrakyat.com.my";

    /**
     * {@inheritDoc}
     * @see \yii\web\Controller::beforeAction()
     */
    public function beforeAction($action)
    {
        return parent::beforeAction($action);
    }

    /**
     * @inheritdoc
     */

    public function behaviors()
    {
        return [
            "access" => [
                "class" => AccessControl::className(),
                "only" => [""],
                "rules" => [
                    [
                        "actions" => [],
                        "allow" => true,
                        "roles" => ["?"],
                    ],
                    [
                        "actions" => [],
                        "allow" => true,
                        "roles" => ["@"],
                    ],
                ],
            ],
            "verbs" => [
                "class" => VerbFilter::className(),
                "actions" => [
                    "upload" => ["post"],
                ],
            ],
        ];
    }

    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            "error" => [
                "class" => "yii\web\ErrorAction",
            ],
            "captcha" => [
                "class" => "yii\captcha\CaptchaAction",
                "fixedVerifyCode" => YII_ENV_TEST ? "testme" : null,
            ],
        ];
    }

    /**
     * Displays homepage.
     *
     * @return mixed
     */
    public function actionIndex($frontendpage = null, $params = null)
    {
        $this->DYNAWEB_DEFAULT_ROUTE_PAGE = $frontendpage;
        $this->DYNAWEB_DEFAULT_ROUTE_PARAMS = $params;

        $url = $frontendpage . "/" . $params . Yii::$app->request->queryString;
        $bytes = random_bytes(5);
        $nonce = bin2hex($bytes);

        $base = self::SOURCE_BASENAME;

        /** fetch from db, if not exist, render form upload */
        $model = PdfDocumentLinks::find()
            ->where(["url" => $url])
            ->orderBy("id DESC")
            ->one();

        if (!empty($model)) {
            preg_match("/^(http|https):\/\//", $model->file_name, $matches);

            // for http or https link
            if (!empty($matches)) {
                return $this->redirect($model->file_name);
            }

            return $this->redirect("/" . $model->file_name);
        }

        $model = new PdfDocumentLinks();

        return $this->render("index", compact("model", "url", "nonce", "base"));
    }

    public function actionUpload($url = null)
    {
        $model = new PdfDocumentLinks();
        $model->load(Yii::$app->request->post());

        // ── 1. Validate manual URL input (file_name field) ────────────────────
        $manualUrl = trim($model->file_name ?? "");
        $uploadedFile = \yii\web\UploadedFile::getInstance($model, "file");

        if (!empty($manualUrl) && empty($uploadedFile)) {
            $allowedHost = parse_url(self::SOURCE_BASENAME, PHP_URL_HOST);
            $inputHost = parse_url($manualUrl, PHP_URL_HOST);

            if (
                !preg_match("/^https?:\/\//i", $manualUrl) ||
                $inputHost !== $allowedHost ||
                strpos($manualUrl, "..") !== false ||
                !preg_match('/\.pdf$/i', parse_url($manualUrl, PHP_URL_PATH))
            ) {
                return $this->asJson([
                    "success" => false,
                    "message" => "Invalid or untrusted file URL.",
                ]);
            }

            $model->file_name = filter_var($manualUrl, FILTER_SANITIZE_URL);
        }

        // ── 2. Validate uploaded file ─────────────────────────────────────────
        if (!empty($uploadedFile)) {
            // 2a. Whitelist extension
            $allowedExtensions = ["pdf"];
            $ext = strtolower($uploadedFile->extension);

            if (!in_array($ext, $allowedExtensions, true)) {
                return $this->asJson([
                    "success" => false,
                    "message" =>
                        "Invalid file type. Only PDF files are allowed.",
                ]);
            }

            // 2b. Validate real MIME type from file content (not browser header)
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
            $realMime = $finfo->file($uploadedFile->tempName);

            if ($realMime !== "application/pdf") {
                return $this->asJson([
                    "success" => false,
                    "message" => "File content does not match a valid PDF.",
                ]);
            }

            // 2c. Validate magic bytes (%PDF-)
            $handle = fopen($uploadedFile->tempName, "rb");
            $header = fread($handle, 5);
            fclose($handle);

            if ($header !== "%PDF-") {
                return $this->asJson([
                    "success" => false,
                    "message" => "File does not appear to be a valid PDF.",
                ]);
            }

            // 2d. File size limit — 10 MB
            $maxBytes = 10 * 1024 * 1024;
            if ($uploadedFile->size > $maxBytes) {
                return $this->asJson([
                    "success" => false,
                    "message" => "File exceeds the 10 MB size limit.",
                ]);
            }

            // 2e. Reject empty files
            if ($uploadedFile->size === 0) {
                return $this->asJson([
                    "success" => false,
                    "message" => "Uploaded file is empty.",
                ]);
            }

            // 2f. Randomise filename — never trust the original name
            $model->file_name = bin2hex(random_bytes(16)) . ".pdf";
        }

        // ── 3. Reject if neither a file nor a valid URL was provided ──────────
        if (empty($uploadedFile) && empty($manualUrl)) {
            return $this->asJson([
                "success" => false,
                "message" => "No file or URL provided.",
            ]);
        }

        // ── 4. Original upload/save calls — paths unchanged ───────────────────
        $model->upload("file", "file_name");
        $model->saveFile("file", "file_name");

        if (!$model->save()) {
            return $this->asJson([
                "success" => false,
                "message" => json_encode($model->errors),
            ]);
        }

        if (!empty($model)) {
            preg_match("/^(http|https):\/\//", $model->file_name, $matches);

            if (!empty($matches)) {
                return $this->redirect($model->file_name);
            }

            return $this->redirect("/" . $model->file_name);
        }
    }
}