prevent duplicate file names

This commit is contained in:
Roman 2021-03-31 13:58:56 +02:00
parent 2d4c2f5aaa
commit 11e83028c5
5 changed files with 49 additions and 16 deletions

@ -250,7 +250,6 @@ namespace Api\File {
class GetRestrictions extends FileAPI { class GetRestrictions 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());
$this->csrfTokenRequired = false;
$this->loginRequired = true; $this->loginRequired = true;
} }
@ -564,6 +563,24 @@ namespace Api\File {
$md5Hash = @hash_file('md5', $tmpPath); $md5Hash = @hash_file('md5', $tmpPath);
$sha1Hash = @hash_file('sha1', $tmpPath); $sha1Hash = @hash_file('sha1', $tmpPath);
$filePath = $uploadDir . "/" . $md5Hash . $sha1Hash; $filePath = $uploadDir . "/" . $md5Hash . $sha1Hash;
// check if a file with this name already exists
$res = $sql->select($sql->count())
->from("UserFile")
->where(new Compare("name", $fileName))
->where(new Compare("user_id", $userId))
->where(new Compare("parent_id", $parentId))
->execute();
if ($res === false) {
return $this->createError($sql->getLastError());
} else {
if ($res[0]["count"] > 0) {
return $this->createError("Error uploading file: a file or directory with this name already exists " .
"in the given upload directory");
}
}
// save file to disk and database
if (file_exists($filePath) || move_uploaded_file($tmpPath, $filePath)) { if (file_exists($filePath) || move_uploaded_file($tmpPath, $filePath)) {
$res = $sql->insert("UserFile", array("name", "directory", "path", "user_id", "parent_id")) $res = $sql->insert("UserFile", array("name", "directory", "path", "user_id", "parent_id"))
->addRow($fileName, false, $filePath, $userId, $parentId) ->addRow($fileName, false, $filePath, $userId, $parentId)
@ -571,9 +588,7 @@ namespace Api\File {
->execute(); ->execute();
if ($res === false) { if ($res === false) {
$this->lastError = $sql->getLastError(); return $this->createError($sql->getLastError());
$this->success = false;
return false;
} else { } else {
$fileId = $sql->getLastInsertId(); $fileId = $sql->getLastInsertId();
} }
@ -581,6 +596,7 @@ namespace Api\File {
return $this->createError("Could not create file: $fileName"); return $this->createError("Could not create file: $fileName");
} }
// connect to the token, if present
if (!is_null($token)) { if (!is_null($token)) {
$res = $sql->insert("UserFileTokenFile", array("file_id", "token_id")) $res = $sql->insert("UserFileTokenFile", array("file_id", "token_id"))
->addRow($fileId, $tokenId) ->addRow($fileId, $tokenId)

@ -16,6 +16,7 @@ class file_api extends DatabaseScript {
$queries[] = $sql->insert("ApiPermission", array("method", "groups", "description")) $queries[] = $sql->insert("ApiPermission", array("method", "groups", "description"))
->onDuplicateKeyStrategy(new UpdateStrategy(array("method"), array("method" => new Column("method")))) ->onDuplicateKeyStrategy(new UpdateStrategy(array("method"), array("method" => new Column("method"))))
->addRow("File/GetRestrictions", array(), "Allows users to view global upload restrictions")
->addRow("File/Download", array(), "Allows users to download files when logged in, or using a given token") ->addRow("File/Download", array(), "Allows users to download files when logged in, or using a given token")
->addRow("File/Upload", array(), "Allows users to upload files when logged in, or using a given token") ->addRow("File/Upload", array(), "Allows users to upload files when logged in, or using a given token")
->addRow("File/ValidateToken", array(), "Allows users to validate a given token") ->addRow("File/ValidateToken", array(), "Allows users to validate a given token")
@ -40,9 +41,9 @@ class file_api extends DatabaseScript {
->addString("name", 64, false) ->addString("name", 64, false)
->addString("path", 512, true) ->addString("path", 512, true)
->addInt("parent_id", true) ->addInt("parent_id", true)
->addInt("user_id", true) ->addInt("user_id")
->primaryKey("uid") ->primaryKey("uid")
->unique("parent_id", "name") ->unique("user_id", "parent_id", "name")
->foreignKey("parent_id", "UserFile", "uid", new CascadeStrategy()) ->foreignKey("parent_id", "UserFile", "uid", new CascadeStrategy())
->foreignKey("user_id", "User", "uid", new CascadeStrategy()); ->foreignKey("user_id", "User", "uid", new CascadeStrategy());

@ -352,6 +352,15 @@ abstract class SQL {
$column = $this->columnName($condition->getColumn()); $column = $this->columnName($condition->getColumn());
$value = $condition->getValue(); $value = $condition->getValue();
$operator = $condition->getOperator(); $operator = $condition->getOperator();
if ($value === null) {
if ($operator === "=") {
return "$column IS NULL";
} else if ($operator === "!=") {
return "$column IS NOT NULL";
}
}
return $column . $operator . $this->addValue($value, $params); return $column . $operator . $this->addValue($value, $params);
} else if ($condition instanceof CondBool) { } else if ($condition instanceof CondBool) {
return $this->columnName($condition->getValue()); return $this->columnName($condition->getValue());

@ -278,13 +278,14 @@ export function FileBrowser(props) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
const cancelToken = filesToUpload[i].cancelToken; const cancelToken = filesToUpload[i].cancelToken;
if (cancelToken) { if (cancelToken && filesToUpload[i].progress < 1) {
cancelToken.cancel("Upload cancelled"); cancelToken.cancel("Upload cancelled");
}
let files = filesToUpload.slice(); let files = filesToUpload.slice();
files.splice(i, 1); files.splice(i, 1);
setFilesToUpload(files); setFilesToUpload(files);
} }
}
if (writePermissions) { if (writePermissions) {
@ -306,15 +307,17 @@ export function FileBrowser(props) {
</span> </span>
</div> : <></> </div> : <></>
} }
<Icon icon={done ? "check" : "spinner"} className={"status-icon " + (done ? "text-success" : "text-secondary")} /> <Icon icon={done ? (file.success ? "check" : "times") : "spinner"}
<Icon icon={"times"} className={"text-danger cancel-button fa-2x"} title={"Cancel Upload"} onClick={(e) => onCancelUpload(e, i)}/> className={"status-icon " + (done ? (file.success ? "text-success" : "text-danger") : "text-secondary")} />
<Icon icon={"times"} className={"text-danger cancel-button fa-2x"}
title={"Cancel Upload"} onClick={(e) => onCancelUpload(e, i)}/>
</span> </span>
); );
} }
uploadZone = <> uploadZone = <>
<div className={"p-3"}> <div className={"p-3"}>
<label><b>Destination Directory:</b></label> <label><b>Upload Directory:</b></label>
<select value={popup.directory} className={"form-control"} <select value={popup.directory} className={"form-control"}
onChange={(e) => onPopupChange(e, "directory")}> onChange={(e) => onPopupChange(e, "directory")}>
{options} {options}
@ -524,8 +527,12 @@ export function FileBrowser(props) {
setFilesToUpload(newFiles); setFilesToUpload(newFiles);
api.upload(file, token, parentId, cancelToken, (e) => onUploadProgress(e, fileIndex)).then((res) => { api.upload(file, token, parentId, cancelToken, (e) => onUploadProgress(e, fileIndex)).then((res) => {
let newFiles = filesToUpload.slice();
newFiles[fileIndex].success = res.success;
setFilesToUpload(newFiles);
if (res.success) { if (res.success) {
// setFilesToUpload([]);
fetchFiles(); fetchFiles();
} else { } else {
pushAlert(res); pushAlert(res);

2
js/files.min.js vendored

File diff suppressed because one or more lines are too long