php upload restrictions

This commit is contained in:
Roman 2021-03-30 23:00:34 +02:00
parent 89badafbb1
commit c847650b1f
5 changed files with 96 additions and 15 deletions

@ -150,6 +150,32 @@ namespace Api {
->where(new CondNull("valid_until"), new Compare("valid_until", $sql->now(), ">=")); ->where(new CondNull("valid_until"), new Compare("valid_until", $sql->now(), ">="));
} }
} }
private static function unitToBytes($var) : int {
if (is_int($var) || is_numeric($var)) {
return intval($var);
} else {
preg_match("/(\\d+)([KMG])/", $var, $re);
if ($re) {
$units = ["K","M","G"];
$value = intval($re[1]);
$unitIndex = array_search($re[2], $units);
return $value * pow(1024, $unitIndex + 1);
} else {
return -1; // some weird error here
}
}
}
protected function getMaxFileSizePHP() : int {
$uploadMaxFilesize = $this->unitToBytes(ini_get("upload_max_filesize"));
$postMaxSize = $this->unitToBytes(ini_get("post_max_size"));
return min($uploadMaxFilesize, $postMaxSize);
}
protected function getMaxFiles() : int {
return intval(ini_get("max_file_uploads"));
}
} }
} }
@ -204,9 +230,12 @@ namespace Api\File {
$this->result["files"] = $this->createFileList($res); $this->result["files"] = $this->createFileList($res);
if ($row["token_type"] === "upload") { if ($row["token_type"] === "upload") {
$maxFiles = ($row["maxFiles"] ?? 0);
$maxSize = ($row["maxSize"] ?? 0);
$this->result["restrictions"] = array( $this->result["restrictions"] = array(
"maxFiles" => $row["maxFiles"] ?? 0, "maxFiles" => ($maxFiles <= 0 ? $this->getMaxFiles() : min($this->getMaxFiles(), $maxFiles)),
"maxSize" => $row["maxSize"] ?? 0, "maxSize" => ($maxSize <= 0 ? $this->getMaxFileSizePHP() : min($this->getMaxFileSizePHP(), $maxSize)),
"extensions" => $row["extensions"] ?? "", "extensions" => $row["extensions"] ?? "",
"parentId" => $row["parentId"] ?? 0 "parentId" => $row["parentId"] ?? 0
); );
@ -218,6 +247,27 @@ namespace Api\File {
} }
} }
class GetRestrictions extends FileAPI {
public function __construct(User $user, bool $externalCall = false) {
parent::__construct($user, $externalCall, array());
$this->csrfTokenRequired = false;
$this->loginRequired = true;
}
public function execute($values = array()) {
if (!parent::execute($values)) {
return false;
}
$this->result["restrictions"] = array(
"maxFiles" => $this->getMaxFiles(),
"maxSize" => $this->getMaxFileSizePHP()
);
return true;
}
}
class RevokeToken extends FileAPI { class RevokeToken extends FileAPI {
public function __construct(User $user, bool $externalCall = false) { public function __construct(User $user, bool $externalCall = false) {
parent::__construct($user, $externalCall, array( parent::__construct($user, $externalCall, array(

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebPackConfiguration">
<option name="mode" value="DISABLED" />
</component>
</project>

@ -2,6 +2,7 @@
padding: 0; padding: 0;
border: none; border: none;
vertical-align: middle; vertical-align: middle;
font-size: 0.9em;
} }
.file-control-buttons { .file-control-buttons {
@ -61,7 +62,3 @@
.file-table td:nth-child(n+3), .file-table th:nth-child(n+3) { .file-table td:nth-child(n+3), .file-table th:nth-child(n+3) {
text-align: center; text-align: center;
} }
.file-table tr, .file-table td {
height: 40px;
}

@ -57,7 +57,7 @@ export function FileBrowser(props) {
</svg>; </svg>;
} }
function createFileIcon(mimeType, size=2) { function createFileIcon(mimeType, size="2x") {
let icon = ""; let icon = "";
if (mimeType !== null) { if (mimeType !== null) {
mimeType = mimeType.toLowerCase().trim(); mimeType = mimeType.toLowerCase().trim();
@ -92,7 +92,7 @@ export function FileBrowser(props) {
icon = "file" + (icon ? ("-" + icon) : icon); icon = "file" + (icon ? ("-" + icon) : icon);
} }
return <Icon icon={icon} type={"far"} className={"p-1 align-middle fa-" + size + "x"} /> return <Icon icon={icon} type={"far"} className={"p-1 align-middle fa-" + size} />
} }
function formatSize(size) { function formatSize(size) {
@ -134,9 +134,37 @@ export function FileBrowser(props) {
return ids; return ids;
} }
// TODO: add more mime type names or use an directory here?
function getTypeName(type) {
if (type.toLowerCase() === "directory") {
return "Directory";
}
switch (type.toLowerCase()) {
case "image/jpeg":
return "JPEG-Image";
case "image/png":
return "PNG-Image";
case "application/pdf":
return "PDF-Document";
case "text/plain":
return "Text-Document"
case "application/x-dosexec":
return "Windows Executable";
case "application/vnd.oasis.opendocument.text":
return "OpenOffice-Document";
default:
return type;
}
}
function createFileList(elements, indentation=0) { function createFileList(elements, indentation=0) {
let rows = []; let rows = [];
let i = 0; let i = 0;
const scale = 0.45;
const iconSize = "lg";
const values = Object.values(elements); const values = Object.values(elements);
for (const fileElement of values) { for (const fileElement of values) {
let name = fileElement.name; let name = fileElement.name;
@ -148,13 +176,13 @@ export function FileBrowser(props) {
let svg = []; let svg = [];
if (indentation > 0) { if (indentation > 0) {
for (let i = 0; i < indentation - 1; i++) { for (let i = 0; i < indentation - 1; i++) {
svg.push(svgLeft(0.75)); svg.push(svgLeft(scale));
} }
if (i === values.length - 1) { if (i === values.length - 1) {
svg.push(svgEnd(0.75)); svg.push(svgEnd(scale));
} else { } else {
svg.push(svgMiddle(0.75)); svg.push(svgMiddle(scale));
} }
} }
@ -162,14 +190,14 @@ export function FileBrowser(props) {
<tr key={"file-" + uid} data-id={uid} className={"file-row"}> <tr key={"file-" + uid} data-id={uid} className={"file-row"}>
<td> <td>
{ svg } { svg }
{ createFileIcon(mimeType) } { createFileIcon(mimeType, iconSize) }
</td> </td>
<td> <td>
{fileElement.isDirectory ? name : {fileElement.isDirectory ? name :
<a href={"/api/file/download?id=" + uid + token} download={true}>{name}</a> <a href={"/api/file/download?id=" + uid + token} download={true}>{name}</a>
} }
</td> </td>
<td>{type}</td> <td>{getTypeName(type)}</td>
<td>{size}</td> <td>{size}</td>
<td> <td>
<input type={"checkbox"} checked={!!fileElement.selected} <input type={"checkbox"} checked={!!fileElement.selected}
@ -215,7 +243,7 @@ export function FileBrowser(props) {
const file = filesToUpload[i]; const file = filesToUpload[i];
uploadedFiles.push( uploadedFiles.push(
<span className={"uploaded-file"} key={i}> <span className={"uploaded-file"} key={i}>
{ createFileIcon(file.type, 3) } { createFileIcon(file.type, "3x") }
<span>{file.name}</span> <span>{file.name}</span>
<Icon icon={"times"} onClick={(e) => onRemoveUploadedFile(e, i)}/> <Icon icon={"times"} onClick={(e) => onRemoveUploadedFile(e, i)}/>
</span> </span>

2
js/files.min.js vendored

File diff suppressed because one or more lines are too long