| Current Path : /var/www/html/brspace/frontend/controllers/ |
| 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);
}
}
}