Summary: Starting with a new instance running PHP 8.2, address all exceptions that come up through some basic browsing/usage. For `strlen(null)` issues I generally tried to resolve if the value should be non-null at the point of issue, and attempt to address at earlier call-site. There were not many of these that I could determine. In the rest of those cases I would replace with a null-and-strlen check, or use `phutil_nonempty_string` if I was certain the value was a string and it was more convenient. Hitting all code-paths is challenging, so I would search for `strlen` within radius of files I was modifying and evaluate to address those uses in the same manner. Notes: - `AphrontRequest::getStr` only ever returns a string, and is safe to use `phutil_nonempty_string`. - `PhabricatorEnv::getEnvConfig` can return non-string things so any values coming from there should never use `phutil_nonempty_string`. - `AphrontRequest::getHTTPHeader` indicates it could return wild so `phutil_nonempty_string` should not be used. - `AphrontRequest::getURIData` isn't clear if it could return non-string data, so never use `phutil_nonempty_string`. Refs T13588 Test Plan: I'm running an instance on 8.2 and went through the basic setup/installation, startup and usage, including setup issues and configurations/settings. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: aklapper, Korvin, epriestley Maniphest Tasks: T13588 Differential Revision: https://secure.phabricator.com/D21862
143 lines
3.9 KiB
PHP
143 lines
3.9 KiB
PHP
<?php
|
|
|
|
final class FileAllocateConduitAPIMethod
|
|
extends FileConduitAPIMethod {
|
|
|
|
public function getAPIMethodName() {
|
|
return 'file.allocate';
|
|
}
|
|
|
|
public function getMethodDescription() {
|
|
return pht('Prepare to upload a file.');
|
|
}
|
|
|
|
protected function defineParamTypes() {
|
|
return array(
|
|
'name' => 'string',
|
|
'contentLength' => 'int',
|
|
'contentHash' => 'optional string',
|
|
'viewPolicy' => 'optional string',
|
|
'deleteAfterEpoch' => 'optional int',
|
|
);
|
|
}
|
|
|
|
protected function defineReturnType() {
|
|
return 'map<string, wild>';
|
|
}
|
|
|
|
protected function execute(ConduitAPIRequest $request) {
|
|
$viewer = $request->getUser();
|
|
|
|
$hash = $request->getValue('contentHash');
|
|
$name = $request->getValue('name');
|
|
$view_policy = $request->getValue('viewPolicy');
|
|
$length = $request->getValue('contentLength');
|
|
|
|
$properties = array(
|
|
'name' => $name,
|
|
'authorPHID' => $viewer->getPHID(),
|
|
'isExplicitUpload' => true,
|
|
);
|
|
|
|
if ($view_policy !== null) {
|
|
$properties['viewPolicy'] = $view_policy;
|
|
}
|
|
|
|
$ttl = $request->getValue('deleteAfterEpoch');
|
|
if ($ttl) {
|
|
$properties['ttl.absolute'] = $ttl;
|
|
}
|
|
|
|
$file = null;
|
|
if ($hash !== null) {
|
|
$file = PhabricatorFile::newFileFromContentHash(
|
|
$hash,
|
|
$properties);
|
|
}
|
|
|
|
if ($hash !== null && !$file) {
|
|
$chunked_hash = PhabricatorChunkedFileStorageEngine::getChunkedHash(
|
|
$viewer,
|
|
$hash);
|
|
$file = id(new PhabricatorFileQuery())
|
|
->setViewer($viewer)
|
|
->withContentHashes(array($chunked_hash))
|
|
->executeOne();
|
|
}
|
|
|
|
if ($name !== null && strlen($name) && !$hash && !$file) {
|
|
if ($length > PhabricatorFileStorageEngine::getChunkThreshold()) {
|
|
// If we don't have a hash, but this file is large enough to store in
|
|
// chunks and thus may be resumable, try to find a partially uploaded
|
|
// file by the same author with the same name and same length. This
|
|
// allows us to resume uploads in Javascript where we can't efficiently
|
|
// compute file hashes.
|
|
$file = id(new PhabricatorFileQuery())
|
|
->setViewer($viewer)
|
|
->withAuthorPHIDs(array($viewer->getPHID()))
|
|
->withNames(array($name))
|
|
->withLengthBetween($length, $length)
|
|
->withIsPartial(true)
|
|
->setLimit(1)
|
|
->executeOne();
|
|
}
|
|
}
|
|
|
|
if ($file) {
|
|
return array(
|
|
'upload' => (bool)$file->getIsPartial(),
|
|
'filePHID' => $file->getPHID(),
|
|
);
|
|
}
|
|
|
|
// If there are any non-chunk engines which this file can fit into,
|
|
// just tell the client to upload the file.
|
|
$engines = PhabricatorFileStorageEngine::loadStorageEngines($length);
|
|
if ($engines) {
|
|
return array(
|
|
'upload' => true,
|
|
'filePHID' => null,
|
|
);
|
|
}
|
|
|
|
// Otherwise, this is a large file and we want to perform a chunked
|
|
// upload if we have a chunk engine available.
|
|
$chunk_engines = PhabricatorFileStorageEngine::loadWritableChunkEngines();
|
|
if ($chunk_engines) {
|
|
$chunk_properties = $properties;
|
|
|
|
if ($hash !== null) {
|
|
$chunk_properties += array(
|
|
'chunkedHash' => $chunked_hash,
|
|
);
|
|
}
|
|
|
|
$chunk_engine = head($chunk_engines);
|
|
$file = $chunk_engine->allocateChunks($length, $chunk_properties);
|
|
|
|
return array(
|
|
'upload' => true,
|
|
'filePHID' => $file->getPHID(),
|
|
);
|
|
}
|
|
|
|
// None of the storage engines can accept this file.
|
|
if (PhabricatorFileStorageEngine::loadWritableEngines()) {
|
|
$error = pht(
|
|
'Unable to upload file: this file is too large for any '.
|
|
'configured storage engine.');
|
|
} else {
|
|
$error = pht(
|
|
'Unable to upload file: the server is not configured with any '.
|
|
'writable storage engines.');
|
|
}
|
|
|
|
return array(
|
|
'upload' => false,
|
|
'filePHID' => null,
|
|
'error' => $error,
|
|
);
|
|
}
|
|
|
|
}
|